I have a Huawei P8 with Android 5.0 that I'm using for testing an app. The app needs to be running in the background, as it tracks BLE regions.
I've discovered that Huawei has built in a "feature" called Protected Apps, that can be accessed from the phone settings (Battery Manager > Protected Apps). This allows elected apps to keep running after the screen is turned off.
Sensibly for Huawei, but unfortunately for me, it looks like it's opt-in, i.e. apps are out by default, and you have to manually put them in. This isn't a showstopper, as I can advise users in an FAQ or printed documentation about the fix, but I recently installed Tinder (for research purposes!), and noticed that it was put in the protected list automatically.
Does anyone know how I can do this for my app? Is it a setting in the manifest? Is it something Huawei has enabled for Tinder because it's a popular app?
if("huawei".equalsIgnoreCase(android.os.Build.MANUFACTURER) && !sp.getBoolean("protected",false)) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(R.string.huawei_headline).setMessage(R.string.huawei_text) .setPositiveButton(R.string.go_to_protected, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { Intent intent = new Intent(); intent.setComponent(new ComponentName("com.huawei.systemmanager", "com.huawei.systemmanager.optimize.process.ProtectActivity")); startActivity(intent); sp.edit().putBoolean("protected",true).commit(); } }).create().show(); }
There isn't a setting in the manifest, and Huawei has enabled Tinder because it's a popular app. There isn't a way to know if apps are protected.
Anyway I used "ifHuaweiAlert()" in "onCreate" to show an AlertDialog:
private void ifHuaweiAlert() { final SharedPreferences settings = getSharedPreferences("ProtectedApps", MODE_PRIVATE); final String saveIfSkip = "skipProtectedAppsMessage"; boolean skipMessage = settings.getBoolean(saveIfSkip, false); if (!skipMessage) { final SharedPreferences.Editor editor = settings.edit(); Intent intent = new Intent(); intent.setClassName("com.huawei.systemmanager", "com.huawei.systemmanager.optimize.process.ProtectActivity"); if (isCallable(intent)) { final AppCompatCheckBox dontShowAgain = new AppCompatCheckBox(this); dontShowAgain.setText("Do not show again"); dontShowAgain.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { editor.putBoolean(saveIfSkip, isChecked); editor.apply(); } }); new AlertDialog.Builder(this) .setIcon(android.R.drawable.ic_dialog_alert) .setTitle("Huawei Protected Apps") .setMessage(String.format("%s requires to be enabled in 'Protected Apps' to function properly.%n", getString(R.string.app_name))) .setView(dontShowAgain) .setPositiveButton("Protected Apps", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { huaweiProtectedApps(); } }) .setNegativeButton(android.R.string.cancel, null) .show(); } else { editor.putBoolean(saveIfSkip, true); editor.apply(); } } } private boolean isCallable(Intent intent) { List list = getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); return list.size() > 0; } private void huaweiProtectedApps() { try { String cmd = "am start -n com.huawei.systemmanager/.optimize.process.ProtectActivity"; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { cmd += " --user " + getUserSerial(); } Runtime.getRuntime().exec(cmd); } catch (IOException ignored) { } } private String getUserSerial() { //noinspection ResourceType Object userManager = getSystemService("user"); if (null == userManager) return ""; try { Method myUserHandleMethod = android.os.Process.class.getMethod("myUserHandle", (Class>[]) null); Object myUserHandle = myUserHandleMethod.invoke(android.os.Process.class, (Object[]) null); Method getSerialNumberForUser = userManager.getClass().getMethod("getSerialNumberForUser", myUserHandle.getClass()); Long userSerial = (Long) getSerialNumberForUser.invoke(userManager, myUserHandle); if (userSerial != null) { return String.valueOf(userSerial); } else { return ""; } } catch (NoSuchMethodException | IllegalArgumentException | InvocationTargetException | IllegalAccessException ignored) { } return ""; }
I'm using @Aiuspaktyn solution which is missing the part of how to detect when stop show the dialog after the user set the app as protected. I'm use a Service to check if the app was terminated or not, checking if it exists.
+1 Aiuspaktyn for the java solution
Xamarin Solution
Usage:
MainActivity => protected override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); MyUtils.IfHuaweiAlert(this); }
public static void IfHuaweiAlert(Context context) { ISharedPreferences settings = context.GetSharedPreferences("ProtectedApps", FileCreationMode.Private); string saveIfSkip = "skipProtectedAppsMessage"; bool skipMessage = settings.GetBoolean(saveIfSkip, false); if (!skipMessage) { ISharedPreferencesEditor editor = settings.Edit(); Intent intent = new Intent(); intent.SetClassName("com.huawei.systemmanager", "com.huawei.systemmanager.optimize.process.ProtectActivity"); if (context.PackageManager.QueryIntentActivities(intent, PackageInfoFlags.MatchDefaultOnly).Count > 0) { var dontShowAgain = new Android.Support.V7.Widget.AppCompatCheckBox(context); dontShowAgain.Text = "Do not show again"; dontShowAgain.CheckedChange += (object sender, CompoundButton.CheckedChangeEventArgs e) => { editor.PutBoolean(saveIfSkip, e.IsChecked); editor.Apply(); }; new AlertDialog.Builder(context) .SetIcon(Android.Resource.Drawable.IcDialogAlert) .SetTitle("Huawei Protected Apps") .SetMessage(string.Format("{0} requires to be enabled in 'Protected Apps' to function properly.\n", context.GetString(Resource.String.app_name))) .SetView(dontShowAgain) .SetPositiveButton("Protected Apps", (o, d) => { try { string cmd = "am start -n com.huawei.systemmanager/.optimize.process.ProtectActivity"; if (Build.VERSION.SdkInt >= BuildVersionCodes.JellyBeanMr1) { try { UserManager um = (UserManager)context.GetSystemService(Context.UserService); cmd += " --user " + um.GetSerialNumberForUser(Process.MyUserHandle()); } catch { } } Java.Lang.Runtime.GetRuntime().Exec(cmd); } catch (Exception ignored) { } }) .SetNegativeButton(Android.Resource.String.Cancel, (o, d) => { }) .Show(); } else { editor.PutBoolean(saveIfSkip, true); editor.Apply(); } } }
I still want to figure it out for the following (from here):
private static List POWERMANAGER_INTENTS = new List() { new Intent().SetComponent(new ComponentName("com.miui.securitycenter", "com.miui.permcenter.autostart.AutoStartManagementActivity")), new Intent().SetComponent(new ComponentName("com.letv.android.letvsafe", "com.letv.android.letvsafe.AutobootManageActivity")), new Intent().SetComponent(new ComponentName("com.huawei.systemmanager", "com.huawei.systemmanager.optimize.process.ProtectActivity")), new Intent().SetComponent(new ComponentName("com.coloros.safecenter", "com.coloros.safecenter.permission.startup.StartupAppListActivity")), new Intent().SetComponent(new ComponentName("com.coloros.safecenter", "com.coloros.safecenter.startupapp.StartupAppListActivity")), new Intent().SetComponent(new ComponentName("com.oppo.safe", "com.oppo.safe.permission.startup.StartupAppListActivity")), new Intent().SetComponent(new ComponentName("com.iqoo.secure", "com.iqoo.secure.ui.phoneoptimize.AddWhiteListActivity")), new Intent().SetComponent(new ComponentName("com.iqoo.secure", "com.iqoo.secure.ui.phoneoptimize.BgStartUpManager")), new Intent().SetComponent(new ComponentName("com.vivo.permissionmanager", "com.vivo.permissionmanager.activity.BgStartUpManagerActivity")), new Intent().SetComponent(new ComponentName("com.asus.mobilemanager", "com.asus.mobilemanager.entry.FunctionActivity")).SetData(Android.Net.Uri.Parse("mobilemanager://function/entry/AutoStart")) }; /* Should this method also run the command for the listed devices? I do not have devices to test it with. */ public static void StartPowerSaverIntent(Context context) { foreach (Intent intent in POWERMANAGER_INTENTS) { if (context.PackageManager.ResolveActivity(intent, PackageInfoFlags.MatchDefaultOnly) != null) { context.StartActivity(intent); break; } } }
It would be nice to just run the following command, and everything is covered:
MyUtils.StartPowerSaverIntent(this);
EDIT:
This should do:
public class MyUtils { private static List POWERMANAGER_INTENTS = new List() { new Intent().SetComponent(new ComponentName("com.miui.securitycenter", "com.miui.permcenter.autostart.AutoStartManagementActivity")), new Intent().SetComponent(new ComponentName("com.letv.android.letvsafe", "com.letv.android.letvsafe.AutobootManageActivity")), new Intent().SetComponent(new ComponentName("com.huawei.systemmanager", "com.huawei.systemmanager.optimize.process.ProtectActivity")), new Intent().SetComponent(new ComponentName("com.coloros.safecenter", "com.coloros.safecenter.permission.startup.StartupAppListActivity")), new Intent().SetComponent(new ComponentName("com.coloros.safecenter", "com.coloros.safecenter.startupapp.StartupAppListActivity")), new Intent().SetComponent(new ComponentName("com.oppo.safe", "com.oppo.safe.permission.startup.StartupAppListActivity")), new Intent().SetComponent(new ComponentName("com.iqoo.secure", "com.iqoo.secure.ui.phoneoptimize.AddWhiteListActivity")), new Intent().SetComponent(new ComponentName("com.iqoo.secure", "com.iqoo.secure.ui.phoneoptimize.BgStartUpManager")), new Intent().SetComponent(new ComponentName("com.vivo.permissionmanager", "com.vivo.permissionmanager.activity.BgStartUpManagerActivity")), new Intent().SetComponent(new ComponentName("com.asus.mobilemanager", "com.asus.mobilemanager.entry.FunctionActivity")).SetData(Android.Net.Uri.Parse("mobilemanager://function/entry/AutoStart")) }; public static void StartPowerSaverIntent(Context context) { ISharedPreferences settings = context.GetSharedPreferences("ProtectedApps", FileCreationMode.Private); bool skipMessage = settings.GetBoolean("skipAppListMessage", false); if (!skipMessage) { ISharedPreferencesEditor editor = settings.Edit(); foreach (Intent intent in POWERMANAGER_INTENTS) { if (context.PackageManager.ResolveActivity(intent, PackageInfoFlags.MatchDefaultOnly) != null) { var dontShowAgain = new Android.Support.V7.Widget.AppCompatCheckBox(context); dontShowAgain.Text = "Do not show again"; dontShowAgain.CheckedChange += (object sender, CompoundButton.CheckedChangeEventArgs e) => { editor.PutBoolean("skipAppListMessage", e.IsChecked); editor.Apply(); }; new AlertDialog.Builder(context) .SetIcon(Android.Resource.Drawable.IcDialogAlert) .SetTitle(string.Format("Add {0} to list", context.GetString(Resource.String.app_name))) .SetMessage(string.Format("{0} requires to be enabled/added in the list to function properly.\n", context.GetString(Resource.String.app_name))) .SetView(dontShowAgain) .SetPositiveButton("Go to settings", (o, d) => context.StartActivity(intent)) .SetNegativeButton(Android.Resource.String.Cancel, (o, d) => { }) .Show(); break; } } } } }