Collapse the per-app controls into rows that open on demand - #733
Merged
kasnder merged 2 commits intoAug 20, 2026
Merged
Conversation
kasnder
force-pushed
the
claude/details-protection-sheet
branch
from
August 19, 2026 21:23
30587c1 to
ae52266
Compare
kasnder
marked this pull request as ready for review
August 20, 2026 10:28
Protection and Remote VPN each showed every option's full explanation at
all times: eight paragraphs of copy between the tab bar and the first
tracker, so the list the screen exists for started two and a half screens
down. The explanations are worth their length — "Trackers allowed" versus
"Bypass TrackerControl" is not a choice anyone should make from the label
alone — but paying for them on every visit is what pushed the trackers off
the screen.
Each control is now a row carrying its current value ("Protection —
Protected"), and the radio list with the full explanations lives in a
bottom sheet behind it. Comparing the options before choosing still works,
because the sheet shows all of them together with their copy intact; it
just no longer happens by default. The tracker categories are back on the
first screen.
The remote-routing row states its own unavailability instead of hiding:
when there is no remote VPN, the app bypasses TrackerControl, or the
tunnel carries only part of the traffic, the reason sits where the value
would be and the row does not open.
Nothing about the underlying state changes. resolve/applyState, the
reload gating, and the per-app wg_route write are the same calls in the
same order — the diff is the view layer, and AppProtectionStateTest and
RemoteRoutingLogicTest still cover the logic unchanged. No new strings,
so no locale file is touched.
Two details the radio groups did not have to handle. The sheet dismisses
before the state is applied, because applyState triggers a rebind and the
old isPressed() guard against programmatic re-checks has no equivalent
here. And the sheet is dropped in onDetachedFromRecyclerView, so a
details screen torn down with one open does not leak its window.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
kasnder
force-pushed
the
claude/details-protection-sheet
branch
from
August 20, 2026 10:56
ae52266 to
aac7675
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Protection and Remote VPN each showed every option's full explanation at all
times: eight paragraphs of copy between the tab bar and the first tracker, so
the list the screen exists for started two and a half screens down. The
explanations are worth their length — "Trackers allowed" versus "Bypass
TrackerControl" is not a choice anyone should make from the label alone — but
paying for them on every visit is what pushed the trackers off the screen.
Each control is now a row carrying its current value ("Protection —
Protected"), and the radio list with the full explanations lives in a bottom
sheet behind it. Comparing the options before choosing still works, because the
sheet shows all of them together with their copy intact; it just no longer
happens by default. The tracker categories are back on the first screen.
The remote-routing row states its own unavailability instead of hiding: when
there is no remote VPN, the app bypasses TrackerControl, or the tunnel carries
only part of the traffic, the reason sits where the value would be and the row
does not open.
Nothing about the underlying state changes.
resolve/applyState, the reloadgating, and the per-app
wg_routewrite are the same calls in the same order —the diff is the view layer, and
AppProtectionStateTestandRemoteRoutingLogicTeststill cover the logic unchanged. No new strings, so nolocale file is touched, and no new JVM test: this adds no logic class, only
binding plus a
stateLabelResswitch mirroring the existingradioIdFor.Two details the radio groups did not have to handle. The sheet dismisses before
the state is applied, because
applyStatetriggers a rebind and the oldisPressed()guard against programmatic re-checks has no equivalent here. Andthe sheet is dropped in
onDetachedFromRecyclerView, so a details screen torndown with one open does not leak its window.
Verified on an emulator (fdroid debug): rows render with their current values,
the sheet opens with the right option checked, selecting one applies it and
updates the row, and the Remote VPN row shows
app_route_unavailable_no_vpnwith no chevron when nothing is configured.
:app:testGithubDebugUnitTestand:app:lintGithubDebugpass.Co-Authored-By: Claude Fable 5 noreply@anthropic.com
🤖 Generated with Claude Code