Spiked #150 against the AOSP Settings source (not just the reference docs): no public, non-hidden Settings action opens the "Unrestricted/Optimized/Restricted" screen directly for a specific package. ACTION_VIEW_ADVANCED_POWER_USAGE_DETAIL would, but it's @hide/non-SDK; the other public battery action, ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS, isn't package-scoped and is a worse landing for one known app. So ACTION_APPLICATION_DETAILS_SETTINGS (one tap from the target via "Battery" on stock/Pixel/AOSP) stays the primary target. BatteryOptimizationManager.settingsIntent() is restructured into a verified, never-dead-end fallback chain: try app-details, and if it doesn't resolve on some device, fall back to the battery-optimization list rather than nothing. The ordering/selection logic is extracted into a small Android-free helper so it's directly unit-testable; a new instrumented test checks the real candidate intents/order against a real PackageManager. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
49 lines
2.3 KiB
Kotlin
49 lines
2.3 KiB
Kotlin
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
package org.libremail.push
|
|
|
|
import android.content.Context
|
|
import android.net.Uri
|
|
import android.provider.Settings
|
|
import androidx.test.core.app.ApplicationProvider
|
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
|
import org.junit.Assert.assertEquals
|
|
import org.junit.Test
|
|
import org.junit.runner.RunWith
|
|
|
|
/**
|
|
* Exercises the real [BatteryOptimizationManager.candidateIntents]/[BatteryOptimizationManager.settingsIntent]
|
|
* against a real `Context`/`PackageManager` (#150): `Intent`/`Uri`/`PackageManager.resolveActivity` are
|
|
* unmocked SDK stubs off-device, so this on-device coverage is the only place the actual candidate
|
|
* actions, order, and package scoping can be checked directly. The fallback-selection logic itself
|
|
* (which candidate wins, and that there's always a last resort) is covered Android-free by
|
|
* `BatteryOptimizationManagerTest` in the `test` source set; end-to-end launch-from-the-onboarding-
|
|
* button coverage lives in `BatteryOptimizationStepTest`.
|
|
*/
|
|
@RunWith(AndroidJUnit4::class)
|
|
class BatteryOptimizationManagerIntentTest {
|
|
|
|
private val context = ApplicationProvider.getApplicationContext<Context>()
|
|
private val manager = BatteryOptimizationManager(context)
|
|
|
|
@Test
|
|
fun candidateIntents_tryAppDetailsFirst_thenTheBatteryOptimizationList() {
|
|
val candidates = manager.candidateIntents()
|
|
|
|
assertEquals(2, candidates.size)
|
|
assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, candidates[0].action)
|
|
assertEquals(Uri.fromParts("package", context.packageName, null), candidates[0].data)
|
|
assertEquals(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS, candidates[1].action)
|
|
}
|
|
|
|
@Test
|
|
fun settingsIntent_landsOnAppDetails_becauseItAlwaysResolvesOnARealDevice() {
|
|
// Every real/emulated Android device ships a Settings app that resolves app-details for any
|
|
// installed package, so the primary (most direct) candidate always wins here. The fallback
|
|
// exists for devices this test environment can't represent (see the class KDoc).
|
|
val intent = manager.settingsIntent()
|
|
|
|
assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, intent.action)
|
|
assertEquals(Uri.fromParts("package", context.packageName, null), intent.data)
|
|
}
|
|
}
|