华为设备上的isPowerSaveMode()方法是否总是返回false?

16
我正在实现一个功能,要求用户忽略应用的电池优化。这样做的原因是,应用程序的主要功能受到节能模式的严重影响。
为了达到我的目标,我通过创建一个 Intent 并将 Action 设置为 ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS 来提示用户。
不过,在触发 Intent 之前,我会检查 isPowerSaveMode() 和 isIgnoringBatteryOptimizations(),以确保在未启用节能模式时不提示用户;这是该功能的要求。我检查的方式是:
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
boolean isPowerSaveMode = pm.isPowerSaveMode(); // always returns false for Huawei devices

这对大多数设备都有效,但对于华为设备,isPowerSaveMode()总是返回false。因此,由于前提条件失败,提示永远不会显示。
是否有其他人可能遇到了这个问题?如果是,你是如何解决的?
请注意,Xamarin.Android SDK中也存在同样的问题。

有一个解决方法,在这里已经被描述。 - Ch4t4r
@Ch4t4r 感谢提供链接。虽然它并没有解决我遇到的问题。 - Demitrian
你确定这与手机上安装的Android版本无关吗?如果是的话,你可以尝试这段代码:PowerManager powerManager = (PowerManager) getActivity().getSystemService(Context.POWER_SERVICE);if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && powerManager.isPowerSaveMode()) {} - hugo
@hugorgor 我确定这与操作系统版本无关。我在几台华为设备上尝试过,包括运行Android 6和7的设备。在这两种情况下,即使启用了省电模式,设备请求isPowerSaveMode()时也会返回“false”。我的想法是,与其他制造商(如三星)相比,该标志以不同的方式公开。但这只是一种理论。 - Demitrian
也许你应该尝试一下 Xamarin Test Cloud。 - hugo
到目前为止有任何解决方案吗? - Ton
7个回答

5

一些中文ROM,如HuaweiXiaomi,没有实施标准API以查询省电模式。但是像其他系统设置一样,当用户打开/关闭省电模式时,状态标志将保存到数据库中。

因此,我们可以利用这个状态标志来解决兼容性问题。此外,系统在切换省电模式时会发送一个特定的意图,我们可以监听此意图动作以监视省电模式的更改。

以下是适用于HuaweiXiaomi设备的详细kotlin代码实现。

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
    }
}

1
每个 OEM 都会修改 SDK 以适应其需求。华为设备不使用默认的省电功能,而是使用称为“受保护的应用”的东西。受保护的应用是一组允许在屏幕关闭时运行的应用程序。这就是它总是返回 false 的原因。最好将意图抛到受保护的应用程序屏幕,但无法知道您的应用程序是否已添加到受保护的应用程序列表中。 什么是受保护的应用?

1
  • 我已经找到了一种手动请求当前华为电源模式状态并通过向IntentFilter添加自定义操作来接收更改事件的方法:

(注意仅在华为 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);
    }
}

0

在实现手持设备和可穿戴设备时,我遇到了相同的新问题。 我找到的唯一解决方案是为所有应用程序禁用省电模式。 我建议在禁用所有应用程序的省电模式后检测您方法的结果。这个错误只出现在华为上。糟糕的供应商。


0
对于华为vtr-al00手机,SmartModeStatus 1可以是超级省电模式或普通模式。我已经使用反射来处理它。
    final int _HX =  Build.MANUFACTURER.equalsIgnoreCase("Xiaomi")?2
            :Build.MANUFACTURER.equalsIgnoreCase("Huawei")?1
            :0;
    // “No Kotlin”
    private boolean isPowerSaveModeCompat(){
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
                && powerManager.isPowerSaveMode()) { // hopefully...
            return true;
        }
        if (_HX==0) {
            return false;
        }
        else if (_HX==1) {
            try {
                int value = Settings.System.getInt(getContentResolver(), "SmartModeStatus");
                CMN.debug("isPowerSaveModeCompat::huawei::"+value);
                // value 4==Save Mode; 1==Ultra Save Mode==Normal Mode;
                //  ( tested on my huawei vtr-al00 )
                if(value==4) {
                    return true;
                }
                if(value==1) {
                    // what if Ultra save mode???
                    // https://github.com/huaweigerrit
                    // https://github.com/SivanLiu/HwFrameWorkSource
                    
                    // https://dev59.com/LHE85IYBdhLWcg3wvGAR
//                  Class sysProp= Class.forName("android.os.SystemProperties");
//                  Method sysProp_getBool = sysProp.getMethod("getBoolean", new Class[]{String.class, boolean.class});
//                  Object[] parms = new Object[]{"sys.super_power_save", false};
//                  CMN.debug("huawei::UltraPowerSave::", sysProp_getBool.invoke(null, parms));
//                  CMN.debug("huawei::UltraPowerSave::", getSystemProperty("sys.super_power_save"));
                    return "true".equals(getSystemProperty("sys.super_power_save"));
                }
            } catch (Exception e) {
                CMN.debug(e);
            }
        }
        else if (_HX==2){
            try {
                int value = Settings.System.getInt(getContentResolver(), "POWER_SAVE_MODE_OPEN");
                CMN.debug("isPowerSaveModeCompat::xiaomi::"+value);
                // dont have xiaomi. not tested.
                return value==1;
            } catch (Exception e) {
                CMN.debug(e);
            }
        }
        // else if...
        return false;
    }
    
    // https://dev59.com/VmLVa4cB1Zd3GeqPtSGR
    public String getSystemProperty(String key) {
        String value = null;
        
        try {
            value = (String) Class.forName("android.os.SystemProperties")
                    .getMethod("get", String.class).invoke(null, key);
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        return value;
    }

Java只是比Kotlin短一些,即使有很多注释和脏测试!:)


0
在新的华为设备上,例如华为P30 lite,目前(2021年12月27日)对于这个问题的解决方案尚不清楚。使用键“SmartModeStatus”调用getInt将抛出一个未知的键异常。因此,我们能做的最好的办法是以下内容。
private string HuaweiPowerSaveModeSettingsName = "SmartModeStatus";
private int HuaweiPowerSaveModeValue = 4;

public bool IsBatterySaverEnabled
    => Build.Manufacturer?.ToUpper() == "HUAWEI" ? GetIsBatterySaverEnabledHuawei() : GetIsBatterySaverEnabledAllDevicesExceptHuawei();

private bool GetIsBatterySaverEnabledAllDevicesExceptHuawei()
{
    return PowerManager.FromContext(Application.Context)?.IsPowerSaveMode ?? false;
}

private bool GetIsBatterySaverEnabledHuawei()
{
    try
    {
        var mode = Settings.System.GetInt(Application.Context.ContentResolver, HuaweiPowerSaveModeSettingsName);
        return HuaweiPowerSaveModeValue == mode;
    } catch (Exception e)
    {
        return GetIsBatterySaverEnabledAllDevicesExceptHuawei();
    }
}

0
private void isPowerSaveModeHuaweiXiaomi(){
  if (Build.MANUFACTURER.equalsIgnoreCase("Xiaomi")) {
    try {
       int value = android.provider.Settings.System.getInt(getContext().getContentResolver(), "POWER_SAVE_MODE_OPEN");

            } catch (Settings.SettingNotFoundException e) {
                Log.d("Valor modo bateria:", "Error");
            }
        }else if (Build.MANUFACTURER.equalsIgnoreCase("Huawei")){
            try {
                int value = android.provider.Settings.System.getInt(getContext().getContentResolver(), "SmartModeStatus");

            } catch (Settings.SettingNotFoundException e) {
                Log.d("Valor modo bateria:", "Error");
            }
        }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接