-
-
Notifications
You must be signed in to change notification settings - Fork 870
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[android] Request to disable power saving mode and Battery optimisation before Navigation #8399
base: master
Are you sure you want to change the base?
[android] Request to disable power saving mode and Battery optimisation before Navigation #8399
Conversation
Signed-off-by: kavikhalique <[email protected]>
@@ -2004,6 +2064,61 @@ public void onSearchRoutePoint(@RoutePointInfo.RouteMarkType int pointType) | |||
@Override | |||
public void onRoutingStart() | |||
{ | |||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be better to extract this section into a separate function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Motivation: it will be called from Trace Path feature.
} | ||
|
||
// Check for device battery saver mode | ||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be better to extract this section into a separate function.
private final ArrayList<String> POWER_SAVE_MODE_CHANGE_ACTIONS = new ArrayList<String>() | ||
{ | ||
{ | ||
add("huawei.intent.action.POWER_MODE_CHANGED_ACTION"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we just use static final String HUAWEI_POWER_MODE_INTENT instead?
@@ -2200,4 +2200,12 @@ | |||
<string name="type.amenity.events_venue">Events Venue</string> | |||
<string name="type.shop.auction">Auction</string> | |||
<string name="type.shop.collector">Collectables</string> | |||
<string name="battery_optimisation_dialog_title">Stop optimizing battery usage?</string> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file is auto-regenerated from https://github.com/organicmaps/organicmaps/blob/master/data/strings/strings.txt. Please take a look at https://github.com/organicmaps/organicmaps/blob/master/docs/TRANSLATIONS.md#translations. Please complain in https://github.com/orgs/organicmaps/discussions/4515 if you think switching to native XML translations would be better.
Let's keep it as is until implemention is finalized. I will help you with strings.txt and translations later.
|
||
private boolean isPowerSaveModeCompat(Context context) | ||
{ | ||
for (String name : POWER_SAVE_MODE_SETTINGS_NAMES) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't it check for the vendor instead?
switch(Build.MANUFACTURER.toUpperCase(Locale.getDefault()))
case "HUAWEI" -> // huawei-specific stuff.
case "SAMSUNG" -> // samsung-specific stuff.
...
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are manufacturers guaranteed to be equal to these short strings? Would it be safer to check if a string contains the keyword?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are manufacturers guaranteed to be equal to these short strings? Would it be safer to check if a string contains the keyword?
For huawei and xiaomi i guess there is no issue. It will always be this.
{ | ||
if (POWER_SAVE_MODE_VALUES.containsKey(Build.MANUFACTURER.toUpperCase(Locale.getDefault()))) | ||
{ | ||
context.registerReceiver(new BroadcastReceiver() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where this BroadcastReceiver is unregistered? It probably will leak MwmActivity class because of using such closure.
Do we need this event at all? Isn't it enough to check for the power save when routing is started? What do we want to do on this POWER_SAVE_MODE_CHANGED event? I found only one usage where the log message is printed. Probably it is not a big deal to remove?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes for now its of no use. should i remove the implementation of the same from PowerManagerCompat class? All the broadcast receivers and everything except the method for checking the status?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This version gets crashed in the background periodically:
Fatal Exception: java.lang.RuntimeException: Unable to destroy activity {app.organicmaps.beta/app.organicmaps.MwmActivity}: java.lang.IllegalArgumentException: Receiver not registered: app.organicmaps.util.PowerManagerCompat$7@dc149b8
at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:5827)
at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:5859)
at android.app.servertransaction.DestroyActivityItem.execute(DestroyActivityItem.java:47)
at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:60)
at android.app.servertransaction.TransactionExecutor.executeLifecycleItem(TransactionExecutor.java:254)
at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:228)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:91)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2544)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loopOnce(Looper.java:232)
at android.os.Looper.loop(Looper.java:317)
at android.app.ActivityThread.main(ActivityThread.java:8501)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:878)
Caused by java.lang.IllegalArgumentException: Receiver not registered: app.organicmaps.util.PowerManagerCompat$7@dc149b8
at android.app.LoadedApk.forgetReceiverDispatcher(LoadedApk.java:1670)
at android.app.ContextImpl.unregisterReceiver(ContextImpl.java:1873)
at android.content.ContextWrapper.unregisterReceiver(ContextWrapper.java:821)
at android.content.ContextWrapper.unregisterReceiver(ContextWrapper.java:821)
at app.organicmaps.util.PowerManagerCompat.unregister(PowerManagerCompat.java:75)
at app.organicmaps.MwmActivity.onSafeDestroy(MwmActivity.java:1185)
at app.organicmaps.base.BaseMwmFragmentActivity.onDestroy(BaseMwmFragmentActivity.java:105)
at android.app.Activity.performDestroy(Activity.java:9048)
at android.app.Instrumentation.callActivityOnDestroy(Instrumentation.java:1554)
at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:5814)
at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:5859)
at android.app.servertransaction.DestroyActivityItem.execute(DestroyActivityItem.java:47)
at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:60)
at android.app.servertransaction.TransactionExecutor.executeLifecycleItem(TransactionExecutor.java:254)
at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:228)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:91)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2544)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loopOnce(Looper.java:232)
at android.os.Looper.loop(Looper.java:317)
at android.app.ActivityThread.main(ActivityThread.java:8501)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:878)
It is probably caused by this BroadcastReceiver.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the feedback. This issue is probably because broadcast reciever unregister is called when no broadcast receiver was registered. I guess i placed unregister call in wrong method of mwmActivty.
I will check it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The issue was with unregister method actually i was not unregistering the broadcast reciever properly. Now i have created a variable and saved the instance into it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't really understand the need for this entire PowerManagerCompat.
mDeviceBatterySaverPermission.launch(intent); | ||
}); | ||
} | ||
builder.show(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please save the instance into mAlertDialog. Otherwise, dismissAlertDialog() will not work. There was a bug that rotation of the screen causes a crash if mAlertDialog is on the screen.
builder.show(); | |
mAlertDialog = builder.show(); |
Logger.w(TAG, "Checking for App battery optimisation permission"); | ||
mNavigationBatterySaverPermission = true; | ||
dismissAlertDialog(); | ||
final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this, R.style.MwmTheme_AlertDialog) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This dialog is displayed every time even if the use pressed DENY. Let's be less annoying and save the rejection resolution into Config to avoid showing this dialog one more time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-
if user denies, in which condition we gonna again ask this permission?
-
if user allowed and after navigation started he reverted the permissions. Will we be asking him instantly or on every new navigation start?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kavikhalique How do other apps behave in such situations?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tested starva and mapmyride
-
For battery optimisation and battery saver they asked only on first start after that they didn't asked even if the feature is turned on.
-
For location permission they ask every time if it is disabled
-
For post notification permission i am not sure because i do not have a device which have android 13 : )
But here the issue is if power saving mode is turned on we don't get satellite fix that means no location update at all which makes trace path feature useless.
I do not have proper knowledge how these apps deal with this problem in power saving mode but probably since they are not offline apps they may acquire location from other sources (network, wifi etc). But this might not be case for us since many users might be using it because of its total offline functionality.
.setNegativeButton(R.string.deny, (dialog, which) -> onRoutingStart()) | ||
.setOnDismissListener(dialog -> mAlertDialog = null); | ||
|
||
final Intent intent = Utils.makeAppBatteryOptimizationIntent(this); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please verify that MANUFACTURER is supported first before showing the dialog. It is just useless without intent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wouldn't it be beneficial if we inform the user to disable it atleast? I guess atleast informing them would be the better option even if we cant route them there to turn it off.
else | ||
Logger.w(TAG, "Power Save mode is disabled on the device"); | ||
|
||
if (mNavigationDeviceBatterySaverPermission) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the Trace Path we will need to distinguish between Routing vs Trace Path case. Please consider adding an enum instead of boolean flag.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How can I check what intent is needed on each particular device? I have Samsung, Huawei, vanilla Android.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I used an app called "Activity Launcher" to get the package name and activity name to start any activity from our app. I used it to start battery saver activity of xiaomi device (haven't pushed i yet).
you can download it from play store in your device and lookout for the required activity and there you will get package name and activity name to start it from your app : )
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean that you just check the list of available activities of the Settings app?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here is what I can see on my Samsung when MapMyRide opens "Power saving" screen:
06-08 08:49:38.566 976 3537 I ActivityTaskManager: START u0 {act=android.settings.BATTERY_SAVER_SETTINGS cmp=com.android.settings/.Settings$BatterySaverSettingsActivity} from uid 10334
06-08 08:49:38.567 976 3537 D ActivityTaskManager: TaskLaunchParamsModifier:task=null activity=ActivityRecord{ac371b u0 com.android.settings/.Settings$BatterySaverSettingsActivity display-area-from-source=DefaultTaskDisplayArea@93219380 display-id=0 display-windowing-mode=1 suggested-display-area=DefaultTaskDisplayArea@93219380
06-08 08:49:38.567 976 3537 D ActivityTaskManager: TaskLaunchParamsModifier:task=Task{13fba47 #811 type=standard A=10334:com.mapmyride.android2 U=0 visible=true mode=fullscreen translucent=false sz=1} activity=ActivityRecord{ac371b u0 com.android.settings/.Settings$BatterySaverSettingsActivity t-1} display-area-from-source=DefaultTaskDisplayArea@93219380 display-id=0 display-windowing-mode=1 suggested-display-area=DefaultTaskDisplayArea@93219380 inherit-from-source=fullscreen activity-options-fullscreen=Rect(0, 0 - 0, 0) non-freeform-display display-area=DefaultTaskDisplayArea@93219380 maximized-bounds
06-08 08:49:38.569 626 656 I SurfaceFlinger: id=279 createSurf (0x0),-1 flag=80004, ActivityRecord{ac371b u0 com.android.settings/.Settings$BatterySaverSettingsActivity t811}#0
![](https://private-user-images.githubusercontent.com/1799054/335305154-5e1c9046-a719-46a4-9925-d56179096d26.jpg?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MTk0MDA4NzAsIm5iZiI6MTcxOTQwMDU3MCwicGF0aCI6Ii8xNzk5MDU0LzMzNTMwNTE1NC01ZTFjOTA0Ni1hNzE5LTQ2YTQtOTkyNS1kNTYxNzkwOTZkMjYuanBnP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI0MDYyNiUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNDA2MjZUMTExNjEwWiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9Njk5ZTAxMWQ3YzcwZGI2MzVkZDU1ZTQ5NTE2YmFmY2RjYTBiM2FlNWFjZTczNGVkYmM0MzVjNWIyNGE2Njc2YyZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QmYWN0b3JfaWQ9MCZrZXlfaWQ9MCZyZXBvX2lkPTAifQ.zFVLn9Mbqqo7qpN-ZsiCGGWZ9S6gYd6OgHlccRyXEWI)
I guess that it is "act=android.settings.BATTERY_SAVER_SETTINGS"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Android provides an intent action to access those activities but in few chinese vendors (i.e huawei, xiaomi) they do not use it. I searched for it on internet and got solution on stack overflow : )
For any other devices like samsung and all a default intent action provided by android is used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean that you just check the list of available activities of the Settings app?
I did that to open battery saver activity from the app for xiaomi devices because from android provided intent action it was not starting (not pushed yet)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here is what I can see on my Samsung when MapMyRide opens "Power saving" screen:
I guess that it is "act=android.settings.BATTERY_SAVER_SETTINGS"
Is default intent not working on your samsung device to open power saver setting?
The intent action which you provided is already there in code it it the default one.
public static final String ACTION_BATTERY_SAVER_SETTINGS
= "android.settings.BATTERY_SAVER_SETTINGS";
This is declared in android Settings file.
Signed-off-by: kavikhalique <[email protected]>
All changes done. Please have a look @rtsisyk String generation is pending and content for dialog box is required. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please see minified version in #8499
|
||
private PowerManager mPowerManager; | ||
|
||
private RequestedBy currRequest; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's make it less generic. Please also use m
prefix for members.
private RequestedBy currRequest; | |
@Nullable | |
private BatterySaverRequestedBy mBatterySaverRequestedBy; |
@@ -225,6 +237,10 @@ public interface LeftAnimationTrackListener | |||
void onTrackLeftAnimation(float offset); | |||
} | |||
|
|||
public enum RequestedBy |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's use less generic name.
public enum RequestedBy | |
public enum BatterySaverRequestedBy |
@SuppressWarnings("NotNullFieldNotInitialized") | ||
@NonNull | ||
private DisplayManager mDisplayManager; | ||
|
||
private boolean mRemoveDisplayListener = true; | ||
private int mLastUiMode = Configuration.UI_MODE_TYPE_UNDEFINED; | ||
|
||
private PowerManagerCompat mPowerManagerCompat; | ||
|
||
private PowerManager mPowerManager; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
private PowerManager mPowerManager; | |
@SuppressWarnings("NotNullFieldNotInitialized") | |
@NonNull | |
private PowerManager mPowerManager; |
@@ -209,13 +211,23 @@ public class MwmActivity extends BaseMwmFragmentActivity | |||
@NonNull | |||
private ActivityResultLauncher<IntentSenderRequest> mLocationResolutionRequest; | |||
|
|||
private ActivityResultLauncher<Intent> mBatterySaverPermission; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
private ActivityResultLauncher<Intent> mBatterySaverPermission; | |
@Nullable | |
private ActivityResultLauncher<Intent> mBatterySaverPermission; |
@@ -225,6 +237,10 @@ public interface LeftAnimationTrackListener | |||
void onTrackLeftAnimation(float offset); | |||
} | |||
|
|||
public enum RequestedBy | |||
{ | |||
NAVIGATION |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please see public @interface ChoosePositionMode
return true; | ||
|
||
currRequest = requestedBy; | ||
Config.setNeedToReuestBatterySaverPermission(requestedBy, false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be after getting the negative result of the dialog. Currently, we don't show the dialog at all if the device wasn't in powersave mode on the first call.
mAlertDialog = builder.show(); | ||
} | ||
} | ||
return false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return false; | |
return true; |
mBatterySaverPermission.launch(intent); | ||
}); | ||
} | ||
mAlertDialog = builder.show(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mAlertDialog = builder.show(); | |
mAlertDialog = builder.show(); | |
return false; |
mAlertDialog = builder.show(); | ||
} | ||
} | ||
return false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return false; | |
return true; |
mDeviceBatterySaverPermission.launch(intent); | ||
}); | ||
} | ||
mAlertDialog = builder.show(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mAlertDialog = builder.show(); | |
currRequest = requestedBy; | |
mAlertDialog = builder.show(); | |
return false; |
Originally posted by @rtsisyk in #8499 (comment)
What is about Xiaomi, Huawei and others? What information do we have? |
Yes, I have stated this in my past comments too, actually system is not hindering our app to access gps locations but it hinders the gnss hardware's power supply in power saving mode which results in not fixing of satellite. Power optimisation was just an additional feature, it was not going to help in satellite fix in any ways. |
Our main focus is disabling power saving mode or at least warning the user to disable it otherwise they will not be able to record properly. |
Yes. Let's remove ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS and focus on detecting power-save mode and opening system settings to disable it. |
#8336
Implements functionality to request users to disable Power saving mode and battery optimisation if it is enabled before the start of the navigation.
It is a cold request type i.e even if user denies the process continues.
Discussion Required -