I create background service with is started after user signed in and should work even after activity was 'throw to trash'. Service show notifications that it's running ,register two broadcast receivers for monitoring wifi and phone state and should keep running until token isn't expired or user logout.
Everything works but service is killed by android.The only solution that REALLY WORKS is instruction from this article http://nine-faq.9folders.com/articles/37422-stop-your-huawei-smartphone-from-closing-apps-when-you-lock-the-screen Unfortunate this solution isn't acceptable because it's dependence on user.
Code created by C# using Xamarin , but If any one know how to implement solution from article programically I'll be glad for useful advice even in other languages (java ,kotlin)
Manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="1"
android:versionName="1.0"
package="com.companyname.com.bgapptest">
<uses-sdk android:minSdkVersion="25" android:targetSdkVersion="28" />
<application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme">
<service
android:name="com.BGAppTest.BackgroundService"
android:enabled="true"
android:exported="false"/>
</application>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
</manifest>
Start service from activity
Activity
using System;
using System.Linq;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.OS;
using Android.Provider;
using Android.Runtime;
using Android.Support.Design.Widget;
using Android.Support.V7.App;
using Android.Views;
using Android.Widget;
using AlertDialog = Android.Support.V7.App.AlertDialog;
namespace com.BGAppTest
{
[Activity(Label = "@string/app_name", Theme = "@style/AppTheme.NoActionBar", MainLauncher = true)]
public class MainActivity : AppCompatActivity
{
public static string HOPE = "Nothing";
string[] perms = new string[] { "android.permission.ACCESS_FINE_LOCATION", "android.permission.ACCESS_COARSE_LOCATION", "android.permission.ACCESS_NETWORK_STATE", "android.permission.READ_PHONE_STATE","android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"};
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.activity_main);
Android.Support.V7.Widget.Toolbar toolbar = FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.toolbar);
SetSupportActionBar(toolbar);
FloatingActionButton fab = FindViewById<FloatingActionButton>(Resource.Id.fab);
fab.Click += FabOnClick;
HOPE = DateTime.Now.ToString();
StartService();
}
public override bool OnCreateOptionsMenu(IMenu menu)
{
MenuInflater.Inflate(Resource.Menu.menu_main, menu);
return true;
}
public override bool OnOptionsItemSelected(IMenuItem item)
{
int id = item.ItemId;
if (id == Resource.Id.action_settings)
{
return true;
}
return base.OnOptionsItemSelected(item);
}
private void FabOnClick(object sender, EventArgs eventArgs)
{
View view = (View)sender;
Intent service = new Intent(this, typeof(BackgroundService));
StopService(service);
}
protected void IGnoreBatteryActivity()
{
PowerManager m = GetSystemService(PowerService) as PowerManager;
Intent intent = new Intent();
if (m.IsIgnoringBatteryOptimizations(this.PackageName))
{
//intent.SetAction(Settings.ActionIgnoreBatteryOptimizationSettings);
}
else
{
intent.SetAction(Settings.ActionRequestIgnoreBatteryOptimizations);
intent.SetData(Android.Net.Uri.Parse("package:" + PackageName));
StartActivity(intent);
}
}
void StartService()
{
foreach (var p in perms)
{
if (CheckSelfPermission(p) == Permission.Denied)
{
RequestPermissions(perms, 2);
return;
}
}
IGnoreBatteryActivity();
Intent service = new Intent(this, typeof(BackgroundService));
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
StartForegroundService(service);
else
StartService(service);
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Permission[] grantResults)
{
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
if (grantResults.Any(x => x == Permission.Denied))
{
RunOnUiThread(() =>
{
new AlertDialog.Builder(this)
.SetMessage("Uprawnienia są wymagane.Chcesz nadać uprawnienia?")
.SetNegativeButton("Nie", delegate
{
this.FinishAffinity();
})
.SetPositiveButton("Tak", delegate
{
RequestPermissions(perms, requestCode);
})
.SetCancelable(false)
.Create()
.Show();
});
}
else
StartService();
}
}
}
Service
[Service(Name = "com.BGAppTest.BackgroundService")]
public class BackgroundService : Service
{
NetworkChangeReceiver networkReceiver;
PhoneCallsReceiver receiver;
const int Service_Running_Notification_ID = 937;
public bool isStarted = false;
PowerManager.WakeLock wakeLock;
public BackgroundService()
{
}
[return: GeneratedEnum]
public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
{
if (isStarted)
return StartCommandResult.Sticky;
isStarted = true;
PowerManager m = GetSystemService(Context.PowerService) as PowerManager;
wakeLock = m.NewWakeLock(WakeLockFlags.Partial, "MYWeakLock");
wakeLock.Acquire();
return StartCommandResult.Sticky;
}
public override void OnCreate()
{
base.OnCreate();
RegisterForegroundService();
RegisterWifiReceiver();
RegisterPhoneReceiver();
}
public void RegisterForegroundService()
{
Notification notification = BuildNotification("Title","Text");
StartForeground(Service_Running_Notification_ID, notification);
}
Notification BuildNotification(string title, string message)
{
NotificationCompat.Builder notificationBuilder = null;
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
String NOTIFICATION_CHANNEL_ID = "com.BGAppTest";
NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "MY_Foreground", NotificationImportance.High);
NotificationManager manager = (NotificationManager)GetSystemService(Context.NotificationService);
manager.CreateNotificationChannel(chan);
notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID);
}
else
notificationBuilder = new NotificationCompat.Builder(this);
return notificationBuilder
.SetSmallIcon(Resource.Drawable.ic_mtrl_chip_checked_black)
.SetContentTitle(Resources.GetString(Resource.String.app_name))
.SetContentIntent(BuildIntentToShowMainActivity())
.SetContentTitle(title)
.SetStyle(new NotificationCompat.BigTextStyle().BigText(message))
.SetOngoing(true)
.Build();
}
private PendingIntent BuildIntentToShowMainActivity()
{
var intent = this.PackageManager.GetLaunchIntentForPackage(this.PackageName);
intent.AddFlags(ActivityFlags.ClearTop);
var pendingIntent = PendingIntent.GetActivity(this, 0, intent, PendingIntentFlags.UpdateCurrent);
return pendingIntent;
}
public void RegisterWifiReceiver()
{
var networkReceiver = new NetworkChangeReceiver();
IntentFilter intentFilters = new IntentFilter();
intentFilters.AddAction("android.net.conn.CONNECTIVITY_CHANGE");
RegisterReceiver(networkReceiver, intentFilters);
}
public void RegisterPhoneReceiver()
{
var receiver = new PhoneCallsReceiver();
IntentFilter intentFilters = new IntentFilter();
intentFilters.AddAction("android.intent.action.PHONE_STATE");
RegisterReceiver(receiver, intentFilters);
}
public override IBinder OnBind(Intent intent)
{
return null;
}
#region Session checker
//TODO:przenieść tą fukcionalność do odzielnego serwisu
System.Timers.Timer timers = new System.Timers.Timer();
private void StartTokenExpiredTimer()
{
}
#endregion
public override void OnDestroy()
{
base.OnDestroy();
try
{
UnregisterReceiver(receiver);
UnregisterReceiver(networkReceiver);
}
catch { }
}
}
Receivers
using Android.Content;
using Android.Widget;
namespace com.BGAppTestReceivers
{
public class NetworkChangeReceiver: BroadcastReceiver
{
static NetworkChangeReceiver()
{
}
public NetworkChangeReceiver()
{
}
public override async void OnReceive(Context context, Intent intent)
{
Toast.MakeText(context, "BGNetworkChange", ToastLength.Short).Show();
}
}
public class PhoneCallsReceiver : BroadcastReceiver
{
static PhoneCallsReceiver()
{
}
public PhoneCallsReceiver()
{
}
public override async void OnReceive(Context context, Intent intent)
{
Toast.MakeText(context, "BGPhoneChange", ToastLength.Short).Show();
}
}
}
UPDATE 26.09.2020
I add full code of my example application, made it as simple as I could
UPDATE 30.09.2020
Add android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
and ActionRequestIgnoreBatteryOptimizations