Skip to content

TV browse parity: files, search, history, trash, settings vs the 34-capture oracle #33

Closed

Description

@altaywtf

Part of #14 (W4 — Android TV parity). Blocked by #32. Paging putdotio/putio-sdk-kotlin#22.

Outcome

The browse surface reaches Android-native parity with the product contract and the 34-capture behavior oracle: files, search, history, trash, and account/settings all preserve raw file data and Compose for TV interaction.

Scope

  • Files (04–06b): root/nested browsing, LazyColumn with stable keys, cursor paging, sort + refresh, watched/progress indicators, folder/video branching, unsupported-type state, long-press action menu (VLC handoff with original stream URL, watched toggle gated on use_start_from, trash/delete)
  • Search (07–07c): system IME only, no app-drawn key grid and no mic affordance; debounced/paged results in standard file rows; recent-query chips preserve text as typed; loading, empty, and recoverable error states
  • History (08): TV-filtered events, date grouping, clear with confirmation, navigate to file
  • Trash (13): list, restore, restore-all, empty, 14-day copy
  • Account/settings (09–12, 14): identity + quota header; grouped list rows; M3 switches using primary; centered 28dp M3 choice dialogs with stacked full-width buttons; proxy, start-from, playback type/buffer, subtitles, search-history toggle, storage/trash, app/device info, diagnostics entry; Logout is the final row
  • Capture the two missing oracle states before gating on them: conversion-in-progress and long-press menu — long-press menu captured natively on feat(tv): row actions on Files: VLC, watched toggle, trash or delete #171; conversion-in-progress needs playback and moves to TV playback parity on Media3 with the Back-button state machine #34

Acceptance criteria

Activity

  1. altaywtf commented on Sep 10, 2026

    @altaywtf
    MemberAuthor

    Files browse shipped in #166 as a479a2d.

    Covered from the Files line: root and nested browsing on the shared FilesBrowserController, LazyColumn rows with stable keys, cursor paging, Refresh and Sort (centred M3 choice dialog, persisted to the account), watched progress, folder/media/unsupported-type branching with the 06b screen, Back popping the folder stack, and per-session D-pad focus memory across folders, destinations, and the rail. The playback hook is a no-op until #34; the long-press action menu is deferred to the same slice as Trash.

    Evidence: root, nested with watched row, sort dialog, unsupported, Back restores focus, recording.

    Next slices: Search (07), History (08), Trash (13), Account/settings (09–12, 14).

  2. altaywtf commented on Sep 10, 2026

    @altaywtf
    MemberAuthor

    Search (07–07c) landed in #167 as d0e53cf: system-IME pill field (Center summons Gboard TV, no app-drawn keys, no mic), recent-query chips replayed as typed with long-press removal, cursor-paged results in the standard rows, result opens in Files, loading / no-results / failed states. The recent-search store and the search 401 helper moved to main; one TvSession per signed-in session now owns Files and Search together.

    Emulator proof on the PR: idle, IME over results, results, chip focus, row focus, drawer round-trip, open in Files, no results, chip removal, chip replay, and a 40 s recording. The failed-search state could not be reproduced live: the TV emulator's Ethernet ignores airplane mode, a dead proxy, and a dead private-DNS host. Robolectric covers it.

    Remaining here: long-press action menu on Files rows, History (08), Trash (13), Account/settings (09–12, 14), and the two missing oracle captures.

  3. altaywtf commented on Sep 12, 2026

    @altaywtf
    MemberAuthor

    History (08) landed in #168 as 01ef038: a real drawer destination on the shared HistoryController, owned by TvSession with Files and Search. Title with a Clear button, events under Today / Yesterday / Last week / Last month / Earlier in the standard rows, Center on an event that names a file opens its folder in Files, Clear through a centred confirmation with stacked buttons. The account's history_enabled setting drives the disabled state.

    Found on the way: the Kotlin SDK decoded every history event as an unknown type (uppercase constants against lowercase wire values), so no row could open on either surface. Fixed in putio-sdk-kotlin#51 as d9a2965; transfers and shared files now resolve. Upload events still map to a non-navigable "Activity" on both surfaces.

    Emulator proof on the PR: list, clear dialog, drawer round-trip, last row, open in Files, and a recording. Not proven live: empty, disabled, failed, and paging states (the proof account has one page and history on, and clearing it would erase shared fixture state); Robolectric covers them.

    Remaining here: long-press action menu on Files rows, Trash (13), Account/settings (09–12, 14), and the two missing oracle captures.

  4. altaywtf commented on Sep 12, 2026

    @altaywtf
    MemberAuthor

    Trash (13) landed in #169 as b73f17c: Account gains a Manage your trash row (Sign out stays last) that opens Trash on the shared TrashController; Back returns to the row. Rows show size, deleted and expiry dates; Center offers Restore or Delete permanently; Restore all and Empty trash sit in the header; every mutation confirms in a centred stacked-button dialog and reports above the list with Check status or Check trash until the fresh listing settles it. Restores invalidate the Files cache and Files reloads a stale folder when it shows again. The empty state carries the oracle's 14-day copy.

    Emulator proof on the PR: Account row, list, item dialog, restore and Empty confirmations (cancelled), and a recording. Not proven live: submitting any mutation and its outcome and Check flows, since Trash holds shared fixture state; Robolectric covers them.

    Remaining here: long-press action menu on Files rows, Account/settings (09–12, 14, which will also give Trash its oracle entry point under Storage settings), and the two missing oracle captures.

  5. altaywtf commented on Sep 12, 2026

    @altaywtf
    MemberAuthor

    Account/settings (09–12, 14) shipped in #170, merged as f344fa8.

    • Identity and quota header (avatar, username, “X of Y free” bar, Sign out button), Playback settings (Choose your proxy picker on /tunnel/routes, Remember current place, Video playback type picker on this app's /config, Autoplay next video, Show subtitles, Do not select subtitles by default), Storage settings (Move deleted files to trash with confirmation, Manage your trash with its size, Keep account history), App and device information with a Diagnostics dialog, Sign out as the final row.
    • Runs on the shared AccountSettingsController and AndroidAppConfigController inside the TV session; a 401 from either, or from the route list, rejects the session; the History pane follows confirmed settings values.
    • Failed saves show under their row and Try again hands focus back to it; every dialog returns focus to the row that opened it, also across recreation; App/Device/OS are facts outside the D-pad chain.
    • Video playback buffer size stays out: no server key (per the Settings rollout notes).
    • Live proof on the API 36 TV emulator: real account values, HLS → MP4 → HLS on /config, resume switch true → false → true read back through the API, proxy picker with the account's routes, Turn off trash? cancelled, Manage your trash round trip, Diagnostics dialog. Captures on the PR.

    Remaining in this issue: Files long-press action menu, and the two missing oracle captures (conversion-in-progress, long-press menu).

  6. altaywtf commented on Sep 12, 2026

    @altaywtf
    MemberAuthor

    Files long-press action menu shipped in #171, merged as bbf346a (SDK: putdotio/putio-sdk-kotlin#52, 2c61413).

    • Long-press Center or the Menu key on a row opens the oracle's files-actions dialog: Open in VLC (original /files/{id}/stream URL handed to org.videolan.vlc), Mark as watched / unwatched (only while the account's confirmed use_start_from is on; marking watched needs a known duration), Move to trash / Delete permanently per the confirmed trash_enabled, confirmed first with Cancel focused and run on the shared FilesBrowserEvent.Delete.
    • The pane shows the delete operation's phases above the rows, Check status or Retry on failure, and the fresh listing's verdict; every dialog returns focus to its row.
    • Shared additions: FilesWatchedRepository and FilesStreamUrls in main; never-played media keeps its duration so it can be marked watched.
    • Live proof on the API 36 TV emulator: menu on a video row; Mark as watched wrote the 10 s duration (read back through files.getStartFrom) and the row read 100% watched; Mark as unwatched read back 0; Move to trash on a fixture text file confirmed, reported “no longer in Files”, and trash.list shows it. The VLC handoff itself is unproven: the emulator has no VLC, so only the not-installed path was exercised. Captures on the PR.

    Remaining in this issue: the two oracle captures (conversion-in-progress needs playback; long-press menu is now captured natively on #171).

  7. altaywtf commented on Sep 12, 2026

    @altaywtf
    MemberAuthor

    Closing: all five browse slices shipped (#165–#171) with emulator proof and captures. The one oracle state not covered here, files-conversion (conversion-in-progress), is a playback interstitial and is tracked in #34. Fire TV physical proof stays under #51.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions