I am currently implement a feature where the users are requested to ignore battery optimisation for the application. The reason for doing so, is that the main functionality of the application is unfortunately drastically affected by power save mode.
To achieve my goal, I prompt the users by creating an Intent
and setting the Action
to ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
.
Although, before firing the Intent
, I both check for isPowerSaveMode()
and isIgnoringBatteryOptimizations()
to ensure that I don't prompt the users when power save mode is not enabled; which is a requirement for the feature. The way I do so is by:
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
boolean isPowerSaveMode = pm.isPowerSaveMode(); // always returns false for Huawei devices
This works fine for the most devices, but for Huawei devices, isPowerSaveMode()
always returns false
. Consequently, since the preconditions fail, the prompt is never shown.
Has anyone else possibly encountered this issue? If so, what did you do to solve it?
As a note, the same issue is also present in the Xamarin.Android
SDK.
Some Chinese ROM like Huawei
or Xiaomi
didn't implement the standard API for power save mode query. But like other system settings, a state flag will be saved to database when user turn power save mode on/off.
So we can utilize this state flag to solve the compatibility problem. Also a specific intent will send by system when toggle power save mode, we can listen this intent action to monitor power save mode changing.
Below is the detailed kotlin code implementation for Huawei
or Xiaomi
devices.
object PowerManagerCompat {
private const val TAG = "PowerManagerCompat"
interface PowerSaveModeChangeListener {
/**
* will be called when power save mode change, new state can be query via [PowerManagerCompat.isPowerSaveMode]
*/
fun onPowerSaveModeChanged()
}
private val POWER_SAVE_MODE_VALUES = mapOf(
"HUAWEI" to 4,
"XIAOMI" to 1
)
private val POWER_SAVE_MODE_SETTING_NAMES = arrayOf(
"SmartModeStatus", // huawei setting name
"POWER_SAVE_MODE_OPEN" // xiaomi setting name
)
private val POWER_SAVE_MODE_CHANGE_ACTIONS = arrayOf(
"huawei.intent.action.POWER_MODE_CHANGED_ACTION",
"miui.intent.action.POWER_SAVE_MODE_CHANGED"
)
private const val monitorViaBroadcast = true
/**
* Monitor power save mode change, only support following devices
* * Xiaomi
* * Huawei
*/
fun monitorPowerSaveModeChange(context: Context, powerSaveModeChangeListener: PowerSaveModeChangeListener) {
if (Build.MANUFACTURER.toUpperCase(Locale.getDefault()) !in POWER_SAVE_MODE_VALUES.keys) {
Log.w(TAG, "monitorPowerSaveModeChange: doesn't know how to monitor power save mode change for ${Build.MANUFACTURER}")
}
if (monitorViaBroadcast) {
context.registerReceiver(object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
powerSaveModeChangeListener.onPowerSaveModeChanged()
}
}, IntentFilter().also {
for (a in POWER_SAVE_MODE_CHANGE_ACTIONS) {
it.addAction(a)
}
})
} else {
val contentObserver = object : ContentObserver(null) {
override fun onChange(selfChange: Boolean) {
super.onChange(selfChange)
powerSaveModeChangeListener.onPowerSaveModeChanged()
}
}
for (name in POWER_SAVE_MODE_SETTING_NAMES) {
context.contentResolver.registerContentObserver(
Uri.parse("content://settings/system/${name}"), false, contentObserver)
}
}
}
/**
* Check the system is currently in power save mode
* @see [PowerManager.isPowerSaveMode]
*/
fun isPowerSaveMode(context: Context): Boolean {
if (Build.MANUFACTURER.toUpperCase(Locale.getDefault()) in POWER_SAVE_MODE_VALUES.keys) {
return isPowerSaveModeCompat(context)
}
val powerManager = context.getSystemService(Context.POWER_SERVICE) as? PowerManager
return powerManager?.isPowerSaveMode ?: false
}
private fun isPowerSaveModeCompat(context: Context): Boolean {
for (name in POWER_SAVE_MODE_SETTING_NAMES) {
val mode = Settings.System.getInt(context.contentResolver, name, -1)
if (mode != -1) {
return POWER_SAVE_MODE_VALUES[Build.MANUFACTURER.toUpperCase(Locale.getDefault())] == mode
}
}
return false
}
}
IntentFilter
:(Note tested only on Huawei P20 Lite (ANE-LX3) @ EMUI 8.0.0)
// Manually request Power Save Mode:
public Boolean isPowerSaveMode(Context context) {
if (Build.MANUFACTURER.equalsIgnoreCase("Huawei")) {
return isPowerSaveModeHuawei(context);
} else {
return isPowerSaveModeAndroid(context);
}
}
@TargetApi(21)
private Boolean isPowerSaveModeAndroid(Context context) {
boolean isPowerSaveMode = false;
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
if (pm != null) isPowerSaveMode = pm.isPowerSaveMode();
}
return isPowerSaveMode;
}
private Boolean isPowerSaveModeHuawei(Context context) {
try {
int value = android.provider.Settings.System.getInt(context.getContentResolver(), "SmartModeStatus");
return (value == 4);
} catch (Settings.SettingNotFoundException e) {
// Setting not found? Return standard android mechanism and hope for the best...
return isPowerSaveModeAndroid(context);
}
}
// Listening for changes in Power Save Mode
public void startMonitoringPowerSaveChanges(Context context) {
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (mPowerSaveChangeReceiver != null) {
return;
}
// Register for PowerSaver change updates.
mPowerSaveChangeReceiver = new PowerSaveChangeReceiver();
// Registering the receiver
IntentFilter filter = new IntentFilter();
filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
// Add custom huawei action
filter.addAction("huawei.intent.action.POWER_MODE_CHANGED_ACTION");
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
filter.addAction(android.provider.Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);
}
context.registerReceiver(mPowerSaveChangeReceiver, filter);
}
}
@TargetApi(21)
class PowerSaveChangeReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
boolean isPowerSaveMode = false;
// Oh, Huawei...why don't you play by the same rules as everyone else?
if (intent.getAction().equals("huawei.intent.action.POWER_MODE_CHANGED_ACTION")) {
Bundle extras = intent.getExtras();
if ((extras != null) && extras.containsKey("state")) {
int state = intent.getExtras().getInt("state");
isPowerSaveMode = (state == 1); // ON=1; OFF=2
}
} else {
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
isPowerSaveMode = pm.isPowerSaveMode();
}
Log.d("MyTag", "[powersavechange] isPowerSaveMode? " + isPowerSaveMode);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With