You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Make the App Block permission setup resilient to Android's Restricted Settings model and provide an in-app, device-aware guide instead of sending users into system settings without enough context.
This matters especially for Habiter because Android builds are frequently installed directly as APKs / from GitHub rather than through Google Play. For covered apps installed from a downloaded or local file, modern Android can block sensitive special access until the user explicitly allows restricted settings from Habiter's App info page. Android 17 explicitly lists Usage Access (OPSTR_GET_USAGE_STATS) and Display over other apps (OPSTR_SYSTEM_ALERT_WINDOW) among the restricted special permissions for covered/sideloaded apps.
The app must not bypass or weaken Android security. It should explain the state, route the user to the correct supported settings surface, re-check on return, and provide verified instructions for common Android/OEM variants.
The controller then opens the OS permission surface. On resume, reconcilePermissions() only has a positive path:
if (_state.stage ==AppBlockOnboardingStage.usageEducation &&
value.usageAccess) {
awaitdiscover();
}
If access is still unavailable, the user is effectively left at the same onboarding step with no explanation of why Android refused the toggle and no recovery path.
A typical sideloaded install can therefore become:
Habiter
-> "Analyze apps"
-> Android Usage Access settings
-> Habiter toggle unavailable / blocked
-> user returns
-> Habiter asks for the same permission again
-> dead end
That is not sufficient for a permission that is a prerequisite for App Block discovery.
Platform behavior that must be handled
Google documents Restricted Settings on Android 13+ and instructs users to enable them from:
Settings
-> Apps
-> Habiter / App info
-> More / three-dot menu
-> Allow restricted settings
The user then returns to the original special-access page and grants the requested access.
The Android 17 Compatibility Definition is even more explicit for sideloaded/downloaded-file installs:
apps installed from a downloaded file or local file are covered applications;
Usage Access is an enforced restricted setting;
Display over other apps is an enforced restricted setting;
confirmation must be obtained from the covered application's AppInfo page;
implementations are expected to use the platform's restricted-setting confirmation model rather than apps bypassing it.
The exact native implementation may use a richer typed model, but the UI must never collapse all non-granted states into "try the same button again".
1. Sideload-aware Usage Access flow
Recommended journey:
Usage Access education
-> Open Usage Access settings
-> app goes to background
-> user returns
-> reconcile actual permission state
|
+-- granted -> continue to local app analysis
|
+-- still denied -> show contextual recovery UI
|
+-- sideload/restricted likely
| -> explain Restricted Settings
| -> Open Habiter App info
| -> guide: More -> Allow restricted settings
| -> return -> reopen Usage Access
|
+-- generic denial / unknown OEM
-> show manual guide + settings shortcuts
Requirements
Re-check permission state every time the app resumes from Settings.
If the permission is granted, advance automatically without another redundant tap.
If it remains unavailable, do not immediately send the user back to the same screen.
Present a clear recovery card/page after the first unsuccessful return.
Explain that the extra Android restriction is related to how the app was installed, not that Habiter itself can override it.
Keep Maybe later / opt-out available.
If access is revoked later, reuse the same recovery system instead of creating a second flow.
2. Restricted Settings guide inside Habiter
Add an in-app page or bottom-sheet flow such as:
Android blocked this permission
Explain in plain language:
Habiter needs Usage Access only to read local app-usage timing used for distraction recommendations / App Block.
Android may require an extra confirmation for apps installed directly from an APK or another non-store source.
The user should only enable Restricted Settings if they trust the Habiter build/source they installed.
Habiter cannot and must not disable this Android protection automatically.
Primary action:
Open Habiter app info
Then show the expected steps:
1. Open the More / three-dot menu.
2. Tap "Allow restricted settings".
3. Confirm with the device security prompt if requested.
4. Return to Habiter.
5. Habiter reopens / offers Usage Access and verifies the result.
Secondary actions:
Show steps for my device
Try Usage Access again
Maybe later
Do not instruct users to disable Play Protect, globally disable security protections, enable Developer Options, or broadly allow unknown-source installs as a workaround.
3. Install-source detection is a hint, not the source of truth
Use public Android APIs where available to inspect install-source/package-source information and improve the initial guidance.
Possible signals include PackageManager install-source/package-source metadata on supported API levels.
Rules:
Treat the detected install source as a hint.
A Play/store install must not be assumed to be unrestricted.
A sideload install must not be assumed to be blocked on every device/version.
Actual permission state after returning from Settings remains authoritative.
Do not use hidden/private APIs merely to detect Restricted Settings.
If the platform offers a supported API to determine restricted-setting state on a given API level, isolate it behind the native capability layer and provide a safe fallback.
Unknown source/device states must degrade to a generic manual guide, never a broken flow.
4. Add a reusable system-permission guide model
Avoid hard-coding this as a one-off wall of text inside UsageAccessEducationPage.
Introduce a small typed permission/setup guide abstraction that can later be reused by:
SystemCapabilityGuide
├── capability
├── platform
├── Android API range
├── device/OEM family
├── current status
├── title / explanation
├── ordered manual steps
├── supported settings actions
└── fallback guidance
Do not duplicate permission semantics in every onboarding page.
5. Device / OS-specific in-app guide pages
The Restricted Settings mechanism is Android-specific, but the exact Settings navigation and labels vary across OEM skins and Android releases.
The first implementation does not need to perfectly fingerprint every OEM. Device/manufacturer/version detection can choose a likely guide, while the user can manually switch to another guide/fallback.
Each guide should contain:
tested Android / OEM version range;
exact path to Habiter App info;
where the More/overflow action is expected;
how to return to Usage Access;
screenshots or illustrations only when verified and maintainable;
fallback wording for devices where Allow restricted settings is absent or moved;
a note that work/school/parental/device-management policy may prevent the setting entirely.
Samsung and OnePlus community reports confirm that OEM paths often still use the App info overflow menu, but there are also version-specific reports where the option moves/disappears. Therefore do not assume one static OEM path forever; guides need version metadata and a generic fallback.
Desktop platforms
Usage Access / Android Restricted Settings are not desktop concepts. Windows, macOS, and Linux must not receive fake equivalent instructions.
If the reusable guide center is exposed outside App Block, it may include platform-specific setup pages only for capabilities that actually exist on those platforms. This issue's required first-class guide matrix is Android because that is where this permission flow exists.
6. Repository documentation as a maintained source
Add a dedicated permission/system-setup documentation section, for example:
The app's short in-flow guide and the long-form docs should share the same terminology and verified navigation paths. Avoid maintaining two contradictory sets of instructions.
Where practical, keep guide metadata/copy in a structured source that can feed both the app and documentation, or add tests that ensure the documented variants stay in sync with the app registry.
Update VitePress navigation if these become public docs pages.
7. Overlay permission must use the same recovery pattern
App Block also requests overlay/display-over-other-apps access. Android 17 explicitly treats this as another enforced restricted setting for covered/sideloaded apps.
Therefore overlay_education_page.dart must not remain a separate one-shot implementation that can reproduce the same dead end later.
request attempt count / last external-settings transition;
resume reconciliation;
current capability state;
selected guide variant;
retry/recovery behavior;
diagnostics safe enough for bug reports.
The native side should own only platform facts/actions, not product navigation.
10. Diagnostics
For troubleshooting, expose safe local diagnostics such as:
Android version / API
manufacturer / model
Habiter version
install-source category (store/downloaded/local/unknown where available)
Usage Access granted: yes/no
Overlay granted: yes/no
selected guide variant
last settings action opened
last reconcile result
Do not include browsing history, app usage records, habit content, tokens, or other unrelated private data in permission diagnostics.
This should integrate cleanly with the Developer Mode/debug-bundle work from #24 later.
11. Tests / QA matrix
Add automated tests for the state machine and targeted Android integration/manual QA for system UI behavior.
At least Pixel/AOSP and Samsung direct-APK flows are validated before considering the issue complete.
All user-facing copy is localized and accessible.
Definition of done
A user who installs Habiter directly from the official Habiter/GitHub distribution can enter App Block onboarding, understand why Usage Access is required, be guided through Android's extra Restricted Settings confirmation when necessary, return to Habiter, and continue automatically once the permission is actually granted.
The same permission flow remains understandable on common OEM Android variants and has a maintained generic fallback instead of relying on users to search the web for their device's Settings path.
Summary
Make the App Block permission setup resilient to Android's Restricted Settings model and provide an in-app, device-aware guide instead of sending users into system settings without enough context.
This matters especially for Habiter because Android builds are frequently installed directly as APKs / from GitHub rather than through Google Play. For covered apps installed from a downloaded or local file, modern Android can block sensitive special access until the user explicitly allows restricted settings from Habiter's App info page. Android 17 explicitly lists Usage Access (
OPSTR_GET_USAGE_STATS) and Display over other apps (OPSTR_SYSTEM_ALERT_WINDOW) among the restricted special permissions for covered/sideloaded apps.The app must not bypass or weaken Android security. It should explain the state, route the user to the correct supported settings surface, re-check on return, and provide verified instructions for common Android/OEM variants.
Related: #12, #10, #22, #38
Current problem
The existing App Block onboarding already has a dedicated Usage Access education screen:
apps/habiter/lib/features/app_lock/presentation/onboarding/usage_access_education_page.dartIts primary CTA currently calls:
controller.requestUsageAccess();The controller then opens the OS permission surface. On resume,
reconcilePermissions()only has a positive path:If access is still unavailable, the user is effectively left at the same onboarding step with no explanation of why Android refused the toggle and no recovery path.
A typical sideloaded install can therefore become:
That is not sufficient for a permission that is a prerequisite for App Block discovery.
Platform behavior that must be handled
Google documents Restricted Settings on Android 13+ and instructs users to enable them from:
The user then returns to the original special-access page and grants the requested access.
The Android 17 Compatibility Definition is even more explicit for sideloaded/downloaded-file installs:
References:
Goal
Turn permission setup into a stateful system-setup flow rather than a one-shot deep link.
Habiter should be able to distinguish at product level between:
The exact native implementation may use a richer typed model, but the UI must never collapse all non-granted states into "try the same button again".
1. Sideload-aware Usage Access flow
Recommended journey:
Requirements
Maybe later/ opt-out available.2. Restricted Settings guide inside Habiter
Add an in-app page or bottom-sheet flow such as:
Android blocked this permissionExplain in plain language:
Primary action:
Then show the expected steps:
Secondary actions:
Do not instruct users to disable Play Protect, globally disable security protections, enable Developer Options, or broadly allow unknown-source installs as a workaround.
3. Install-source detection is a hint, not the source of truth
Use public Android APIs where available to inspect install-source/package-source information and improve the initial guidance.
Possible signals include
PackageManagerinstall-source/package-source metadata on supported API levels.Rules:
4. Add a reusable system-permission guide model
Avoid hard-coding this as a one-off wall of text inside
UsageAccessEducationPage.Introduce a small typed permission/setup guide abstraction that can later be reused by:
Conceptual direction:
Do not duplicate permission semantics in every onboarding page.
5. Device / OS-specific in-app guide pages
The Restricted Settings mechanism is Android-specific, but the exact Settings navigation and labels vary across OEM skins and Android releases.
Create maintained guide variants for at least:
The first implementation does not need to perfectly fingerprint every OEM. Device/manufacturer/version detection can choose a likely guide, while the user can manually switch to another guide/fallback.
Each guide should contain:
Allow restricted settingsis absent or moved;Samsung and OnePlus community reports confirm that OEM paths often still use the App info overflow menu, but there are also version-specific reports where the option moves/disappears. Therefore do not assume one static OEM path forever; guides need version metadata and a generic fallback.
Desktop platforms
Usage Access / Android Restricted Settings are not desktop concepts. Windows, macOS, and Linux must not receive fake equivalent instructions.
If the reusable guide center is exposed outside App Block, it may include platform-specific setup pages only for capabilities that actually exist on those platforms. This issue's required first-class guide matrix is Android because that is where this permission flow exists.
6. Repository documentation as a maintained source
Add a dedicated permission/system-setup documentation section, for example:
The app's short in-flow guide and the long-form docs should share the same terminology and verified navigation paths. Avoid maintaining two contradictory sets of instructions.
Where practical, keep guide metadata/copy in a structured source that can feed both the app and documentation, or add tests that ensure the documented variants stay in sync with the app registry.
Update VitePress navigation if these become public docs pages.
7. Overlay permission must use the same recovery pattern
App Block also requests overlay/display-over-other-apps access. Android 17 explicitly treats this as another enforced restricted setting for covered/sideloaded apps.
Therefore
overlay_education_page.dartmust not remain a separate one-shot implementation that can reproduce the same dead end later.Apply the same lifecycle:
The copy must explain the actual purpose of the overlay and should not imply broader access than Habiter uses.
8. UX requirements
Suggested failure-state CTA hierarchy:
9. Native / architecture requirements
Keep OS knowledge inside the platform capability layer rather than scattering Android checks throughout Flutter widgets.
Suggested responsibilities:
The coordinator should own:
The native side should own only platform facts/actions, not product navigation.
10. Diagnostics
For troubleshooting, expose safe local diagnostics such as:
Do not include browsing history, app usage records, habit content, tokens, or other unrelated private data in permission diagnostics.
This should integrate cleanly with the Developer Mode/debug-bundle work from #24 later.
11. Tests / QA matrix
Add automated tests for the state machine and targeted Android integration/manual QA for system UI behavior.
Flutter/unit/widget
Maybe laterpersists the correct deferred state;Android/native
ACTION_USAGE_ACCESS_SETTINGSpath works where supported;Real-device / release QA
At minimum test a release APK installed from a downloaded/local file on:
Then expand the compatibility matrix for:
The QA case must use the same direct/sideload distribution path users actually receive, not only
flutter runor a Play-installed build.Acceptance criteria
More / three-dot menu -> Allow restricted settings -> return -> grant Usage Accessfor variants where that path is valid.Definition of done
A user who installs Habiter directly from the official Habiter/GitHub distribution can enter App Block onboarding, understand why Usage Access is required, be guided through Android's extra Restricted Settings confirmation when necessary, return to Habiter, and continue automatically once the permission is actually granted.
The same permission flow remains understandable on common OEM Android variants and has a maintained generic fallback instead of relying on users to search the web for their device's Settings path.