Fix the trailer intent so SmartTube can read the video id #61

Closed
justinzeus wants to merge 0 commits from fm/tv-trailer-intent into fm/tv-round2
Owner

Root cause

The trailer button launched SmartTube correctly but with a URI SmartTube cannot read the video id from.

  • Wolfskin side: TitleTrailer.kt:20 built Intent(ACTION_VIEW, Uri.parse("vnd.youtube:$video")), an opaque URI (no //), then pinned the resolved package with setPackage.
  • SmartTube side (yuliskov/SmartTube master):
    • smarttubetv/src/main/AndroidManifest.xml:114-124: SplashActivity (exported) accepts VIEW with schemes vnd.youtube and vnd.youtube.launch. That is why the activity resolves and launches.
    • common/.../utils/IntentExtractor.java:39: the documented form is vnd.youtube://8kKDjRmHp0g. Line 65-66: else if (VND_SCHEME.equals(uri.getScheme())) { videoId = uri.getHost(); }. Uri.getHost() is null for an opaque vnd.youtube:ID, so isValid (line 173-175, length 11) fails and extractVideoId returns null.
    • common/.../presenters/SplashPresenter.java:312-319: with no earlier processor matching, the last chain entry opens the default view and shows the toast Can't process intent: .... That is the notification Justin saw.
  • Package name is not part of the problem: SmartTube's variants (org.smarttube.stable, org.smarttube.beta, app.smarttube, app.smarttube.fdroid in smarttubetv/build.gradle) all share the same manifest, and Wolfskin takes the package from queryIntentActivities instead of hardcoding one.

Fix

vnd.youtube:$video becomes vnd.youtube://$video (one line). Same scheme, so the existing <queries> entry, handler lookup, explicit package and no-browser-fallback behaviour are unchanged. The official YouTube app also accepts the authority form.

Test

TitleTrailerTest (the launched-intent case) now registers and asserts the authority form: action VIEW, explicit package, scheme vnd.youtube, and data.host == id (the value SmartTube reads). The id used is ab_-defghij, so _ and - are covered. docker/gradle.sh debug :app:testDefaultDebugUnitTest --tests ...TitleTrailerTest: 3 tests, 0 failures. Nothing else was run (slim test budget).

Not verified on device

The TV box was not touched. One thing to check later on the box: tap the trailer button and confirm SmartTube plays the trailer instead of showing Can't process intent.

## Root cause The trailer button launched SmartTube correctly but with a URI SmartTube cannot read the video id from. - Wolfskin side: `TitleTrailer.kt:20` built `Intent(ACTION_VIEW, Uri.parse("vnd.youtube:$video"))`, an opaque URI (no `//`), then pinned the resolved package with `setPackage`. - SmartTube side (yuliskov/SmartTube master): - `smarttubetv/src/main/AndroidManifest.xml:114-124`: `SplashActivity` (exported) accepts `VIEW` with schemes `vnd.youtube` and `vnd.youtube.launch`. That is why the activity resolves and launches. - `common/.../utils/IntentExtractor.java:39`: the documented form is `vnd.youtube://8kKDjRmHp0g`. Line 65-66: `else if (VND_SCHEME.equals(uri.getScheme())) { videoId = uri.getHost(); }`. `Uri.getHost()` is null for an opaque `vnd.youtube:ID`, so `isValid` (line 173-175, length 11) fails and `extractVideoId` returns null. - `common/.../presenters/SplashPresenter.java:312-319`: with no earlier processor matching, the last chain entry opens the default view and shows the toast `Can't process intent: ...`. That is the notification Justin saw. - Package name is not part of the problem: SmartTube's variants (`org.smarttube.stable`, `org.smarttube.beta`, `app.smarttube`, `app.smarttube.fdroid` in `smarttubetv/build.gradle`) all share the same manifest, and Wolfskin takes the package from `queryIntentActivities` instead of hardcoding one. ## Fix `vnd.youtube:$video` becomes `vnd.youtube://$video` (one line). Same scheme, so the existing `<queries>` entry, handler lookup, explicit package and no-browser-fallback behaviour are unchanged. The official YouTube app also accepts the authority form. ## Test `TitleTrailerTest` (the launched-intent case) now registers and asserts the authority form: action `VIEW`, explicit package, scheme `vnd.youtube`, and `data.host == id` (the value SmartTube reads). The id used is `ab_-defghij`, so `_` and `-` are covered. `docker/gradle.sh debug :app:testDefaultDebugUnitTest --tests ...TitleTrailerTest`: 3 tests, 0 failures. Nothing else was run (slim test budget). ## Not verified on device The TV box was not touched. One thing to check later on the box: tap the trailer button and confirm SmartTube plays the trailer instead of showing `Can't process intent`.
justinzeus added 1 commit 2026-11-07 19:48:06 +01:00
Launch trailers with the vnd.youtube authority form SmartTube can parse
All checks were successful
review/code-claude Code APPROVE: no blocking finding
review/design-claude Design PASS: no visible change, no blocker
8be769b67c
Author
Owner

Delta review of the trailer PR at head 8be769b67c176dd07f8809deb03c8f74a79742bf.

Code verdict: APPROVE (review/code-claude = success).
Design verdict: PASS (review/design-claude = success). No blocking finding.

What I checked:

  • The only production change is TitleTrailer.kt:21: vnd.youtube:$video -> vnd.youtube://$video. The scheme is the same. The handler lookup (queryIntentActivities, MATCH_DEFAULT_ONLY, first exported), the explicit setPackage, the exception handling and the no-browser-fallback behaviour are untouched.
  • The <queries> entry in app/src/default/AndroidManifest.xml:6 is scheme-only, so it matches both URI forms. [INFERENCE from intent-filter matching rules]
  • The test now asserts action VIEW, explicit package, scheme and data.host == "ab_-defghij", the value SmartTube reads. It would fail on the old opaque form. The id includes _ and -. The other two trailer tests keep their intent.
  • I did not open SmartTube's source. The getHost() claim follows the PR's cited lines and matches Uri semantics.

Non-blocking notes:

  1. android.net.Uri keeps the id's case and does not validate characters, so mixed-case ids and _/- survive.
  2. The id is passed through unvalidated from the trailer URL. A malformed value containing /, ? or # would now split into host and path. Real ids are 11 characters of [A-Za-z0-9_-], so no change is needed.
  3. The on-box check is still open: tap Trailer and confirm SmartTube plays it instead of showing "Can't process intent".

Full report (delta reviews 3): /root/.treehouse/firstmate-7bab20/2/firstmate/data/round2-review/report.md. Not run: Gradle/make, nothing on the box.

Delta review of the trailer PR at head `8be769b67c176dd07f8809deb03c8f74a79742bf`. **Code verdict: APPROVE (`review/code-claude` = success).** **Design verdict: PASS (`review/design-claude` = success).** No blocking finding. **What I checked:** - The only production change is `TitleTrailer.kt:21`: `vnd.youtube:$video` -> `vnd.youtube://$video`. The scheme is the same. The handler lookup (`queryIntentActivities`, `MATCH_DEFAULT_ONLY`, first exported), the explicit `setPackage`, the exception handling and the no-browser-fallback behaviour are untouched. - The `<queries>` entry in `app/src/default/AndroidManifest.xml:6` is scheme-only, so it matches both URI forms. [INFERENCE from intent-filter matching rules] - The test now asserts action `VIEW`, explicit package, scheme and `data.host == "ab_-defghij"`, the value SmartTube reads. It would fail on the old opaque form. The id includes `_` and `-`. The other two trailer tests keep their intent. - I did not open SmartTube's source. The `getHost()` claim follows the PR's cited lines and matches `Uri` semantics. **Non-blocking notes:** 1. `android.net.Uri` keeps the id's case and does not validate characters, so mixed-case ids and `_`/`-` survive. 2. The id is passed through unvalidated from the trailer URL. A malformed value containing `/`, `?` or `#` would now split into host and path. Real ids are 11 characters of `[A-Za-z0-9_-]`, so no change is needed. 3. The on-box check is still open: tap Trailer and confirm SmartTube plays it instead of showing "Can't process intent". Full report (delta reviews 3): `/root/.treehouse/firstmate-7bab20/2/firstmate/data/round2-review/report.md`. Not run: Gradle/make, nothing on the box.
justinzeus closed this pull request 2026-11-07 21:14:29 +01:00
All checks were successful
review/code-claude Code APPROVE: no blocking finding
review/design-claude Design PASS: no visible change, no blocker

Pull request closed

Sign in to join this conversation.
No reviewers
No labels
exploring
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: justinzeus/wolfskin#61
No description provided.