Search code examples
androidxamarin.formsxamarin.essentials

Xamarin Forms - How to check setting of RequestIgnoreBatteryOptimizations permission on Android


(See UPDATE below)

I have a Xamarin Forms app on Android which uses the Xamarin.Essentials library.

The app requires to run in the background to be fed location data (not particularly relevant to the question in hand, but included for context), and so must not be put to sleep by any battery optimisations that the OS might attempt.

I know that the user can manually opt out specific apps from Battery Optimizations, but as it is so crucial to the successful operation of the app, I would like the app to be able to :

  1. check the Battery Optimization Opt-out permission status to ensure it is appropriately set,

and/or

  1. force Android to opt the app of any battery optimizations.

I have added an entry into AndroidManifest.xml, but it doesn't seem to help, with the newly-installed app defaulting to being Battery Optimized.

AndroidManifest.xml

The manifest contains the following entry:

<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />

Xamarin.Essentials

This library gives access to a number of permission-related info on the device, but Battery Optimizations doesn't seem to be one of them.

Device being used

I don't know if it's relevant, but I am testing on a Samsung Galaxy S20 Ultra.

Can anyone offer any advice?

UPDATE Aug 28 2021

Following the advice from contributors and with reference to the docs at https://learn.microsoft.com/en-us/xamarin/essentials/permissions?tabs=android#extending-permissions ...

In My Shared Code

public interface IRequestIgnoreBatteryOptimizationPermission
{
    Task<PermissionStatus> CheckStatusAsync();
    Task<PermissionStatus> RequestAsync();
}

In My Android Project

[assembly: Xamarin.Forms.Dependency(typeof(RequestIgnoreBatteryOptimizationPermission))]

namespace MyAndroidProject
{
    public class RequestIgnoreBatteryOptimizationPermission : Permissions.BasePlatformPermission, IRequestIgnoreBatteryOptimizationPermission
    {
        public override (string androidPermission, bool isRuntime)[] RequiredPermissions => new List<(string androidPermission, bool isRuntime)>
        {
            (Android.Manifest.Permission.RequestIgnoreBatteryOptimizations, true)
        }.ToArray();
    }
}

On Shared App Initialization

// Ensure Required Permissions have been granted
var requestIgnoreBatteryOptimizationPermission = DependencyService.Get<IRequestIgnoreBatteryOptimizationPermission>();
            
var status = requestIgnoreBatteryOptimizationPermission.CheckStatusAsync().Result;

if (status != PermissionStatus.Granted)
{
    status = requestIgnoreBatteryOptimizationPermission.RequestAsync().Result;
}

Result...

On calling CheckStatusAsync, the result comes back as Granted.

But the app settings still say otherwise...

enter image description here

enter image description here

I've tried it on both a physical device (Samsung Galaxy S20 Ultra) and on an Android Emulator (Pixel 2 API 28), with same result on both.


Solution

  • From this document, there are two ways to set Battery Optimization

    An app can fire the ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS intent to take the user directly to the Battery Optimization, where they can add the app.

    An app holding the REQUEST_IGNORE_BATTERY_OPTIMIZATIONS permission can trigger a system dialog to let the user add the app to the exemption list directly, without going to settings. The app fires a ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS Intent to trigger the dialog.

    I use PowserManager.isIgnoringBatteryOptimizations to check Battery Optimization.

    Firstly, add RequestIgnoreBatteryOptimizations in AndroidManifest.xml

    <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
    

    Then creating Interface in Shared code.

    public interface IBattery
    {
         void getbattery();     
    }
    

    Implementing this interface in Android platform.

    [assembly: Dependency(typeof(ImplementBattery))]
    namespace FormsSample.Droid
    {
    public class ImplementBattery : IBattery
    {
        public void getbattery()
        {
            Intent intent = new Intent();
            String packageName = MainActivity.mac.PackageName;
            PowerManager pm = (PowerManager)MainActivity.mac.GetSystemService(Context.PowerService);
            if (pm.IsIgnoringBatteryOptimizations(packageName))
                intent.SetAction(Android.Provider.Settings.ActionIgnoreBatteryOptimizationSettings);
            else
            {
                intent.SetAction(Android.Provider.Settings.ActionRequestIgnoreBatteryOptimizations);
                intent.SetData(Android.Net.Uri.Parse("package:" + packageName));
            }
            MainActivity.mac.StartActivity(intent);
        }
    }
    }
    

    Creating static Mainactivity field in Mainactivity.cs.

      public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
    {
        public static MainActivity mac;
        protected override void OnCreate(Bundle savedInstanceState)
        {
            initFontScale();
            TabLayoutResource = Resource.Layout.Tabbar;
            ToolbarResource = Resource.Layout.Toolbar;
            
            base.OnCreate(savedInstanceState);
           
            mac = this;
    

    Now, using DependencyService to fire.

       private void Button_Clicked(object sender, EventArgs e)
        {
            DependencyService.Get<IBattery>().getbattery();
        }
    

    enter image description here