Skip to content

fix: use permission_handler for battery optimization checks - #3227

Merged
zlshames merged 1 commit into
masterfrom
claude/battery-optimization-permission-handler
Aug 15, 2026
Merged

fix: use permission_handler for battery optimization checks#3227
zlshames merged 1 commit into
masterfrom
claude/battery-optimization-permission-handler

Conversation

@zlshames

Copy link
Copy Markdown
Member

Replaces the forked disable_battery_optimization plugin with permission_handler's Permission.ignoreBatteryOptimizations, which was already a dependency and calls the same PowerManager.isIgnoringBatteryOptimizations() / ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS under the hood.

Split out of the Android build work so the Gradle/AGP upgrade can be reviewed separately.

Why

The plugin was 2,633 lines of Java across 26 files; the app used two methods. The rest is OEM autostart handling reachable only through methods we never call.

The part we did use was wrong. isAllBatteryOptimizationDisabled ANDed the real OS check with two SharedPreferences flags that cached !KillerManager.isActionAvailable(...) on first read and were only ever set by dialog flows the app never invoked. On any device whose OEM ships an autostart settings screen — Samsung, Xiaomi, Oppo, Huawei — that cached false permanently, so the check reported "not disabled" even after the user granted the exemption. Those are exactly the manufacturers whose battery management makes the check matter.

Changes

  • Manifest: declares REQUEST_IGNORE_BATTERY_OPTIMIZATIONS directly. It used to arrive via the plugin's manifest merger, and permission_handler_android ships an empty manifest by design — without this the permission silently disappears and request() never opens the system prompt. Also drops the plugin's stray oppo.permission.OPPO_COMPONENT_SAFE.
  • Jetifier off: the plugin's material-dialogs:0.9.6.0 pulled com.android.support and was the only thing keeping android.enableJetifier on. With it gone, ./gradlew checkJetifier reports 57/57 projects clean. AGP 9.1+ rejects the flag outright, so this had to happen regardless.
  • Live status: both surfaces re-read on AppLifecycleState.resumed. The exemption is granted in a system dialog, so returning to the foreground is the only signal it changed; the setup page previously checked once and never updated.
  • Setup page now shows status in both states — it rendered nothing at all when optimizations were still enabled.
  • Developer Tools tile routes to openAppSettings() when already exempt. Android has no revoke API, so undoing this has to be a trip to system settings rather than the "Already Disabled" dead end it was.

Testing

Verified on an Android 16 (API 36) emulator, prod flavor:

  • Setup page correctly shows red "Enabled" + button when not exempt
  • Tapping through launches the real com.android.settings/.fuelgauge.RequestIgnoreBatteryOptimizations dialog
  • Allow adds the app to the deviceidle whitelist and the page flips to a green check with no restart
  • Revoking via adb and relaunching shows the "Enabled" state again
  • Debug + release APKs build; app launches with no NoClassDefFoundError

Not verified: the Developer Tools tile itself — it sits behind a configured server I don't have. It shares the same helpers as the setup page, which are verified, but the tile UI is untested.

🤖 Generated with Claude Code

Replaces the forked disable_battery_optimization plugin with
permission_handler's Permission.ignoreBatteryOptimizations, which was already
a dependency and calls the same PowerManager.isIgnoringBatteryOptimizations()
and ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS under the hood.

The plugin was 2,633 lines of Java across 26 files, of which the app used two
methods. The rest is OEM autostart handling reachable only through methods the
app never calls.

More importantly, the part the app did use was wrong. Its
isAllBatteryOptimizationDisabled ANDed the real OS check with two
SharedPreferences flags that cached !KillerManager.isActionAvailable(...) on
first read and were only ever set by dialog flows the app never invoked. On any
device whose OEM ships an autostart settings screen — Samsung, Xiaomi, Oppo,
Huawei — that cached false permanently, so the check reported "not disabled"
even after the user granted the exemption. Those are the manufacturers whose
battery management makes the check matter most.

Also:

- Declares REQUEST_IGNORE_BATTERY_OPTIMIZATIONS in the app manifest. It used to
  arrive via the plugin's manifest merger; permission_handler_android ships an
  empty manifest by design, so without this the permission silently disappears
  and request() never opens the system prompt. Drops the plugin's stray
  oppo.permission.OPPO_COMPONENT_SAFE at the same time.
- Removes android.enableJetifier. The plugin's material-dialogs 0.9.6.0 pulled
  com.android.support and was the only thing keeping Jetifier on; with it gone
  ./gradlew checkJetifier reports all 57 projects clean. AGP 9.1+ rejects the
  flag outright, so this had to happen eventually.
- Both surfaces now re-read status on AppLifecycleState.resumed. The exemption
  is granted in a system dialog, so returning to the foreground is the only
  signal that it changed; previously the setup page checked once and never
  updated.
- The setup page shows status in both states — it previously rendered nothing
  at all when optimizations were still enabled.
- The Developer Tools tile routes to openAppSettings() when already exempt.
  Android has no API to revoke the exemption, so undoing it has to be a trip to
  system settings rather than the "Already Disabled" dead end it was before.

Verified on an Android 16 emulator: the prompt opens, Allow adds the app to the
deviceidle whitelist, and the status flips to a green check without a restart.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@zlshames
zlshames merged commit d80118a into master Aug 15, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant