From b1a5796bfea36befca80b82730efeb73ce555375 Mon Sep 17 00:00:00 2001 From: Benoit HERVIER Date: Fri, 5 Apr 2019 08:42:51 +0200 Subject: [PATCH] Start service with start foregroundService on Android Oreo, add a notification channel for Oreo permanent service notification #1785 --- .../java/org/kivy/android/PythonService.java | 49 ++++++++++++--- .../common/build/templates/Service.tmpl.java | 62 +++++++++++++++++-- 2 files changed, 100 insertions(+), 11 deletions(-) diff --git a/pythonforandroid/bootstraps/common/build/src/main/java/org/kivy/android/PythonService.java b/pythonforandroid/bootstraps/common/build/src/main/java/org/kivy/android/PythonService.java index 4f20fb7808..67798ffd51 100644 --- a/pythonforandroid/bootstraps/common/build/src/main/java/org/kivy/android/PythonService.java +++ b/pythonforandroid/bootstraps/common/build/src/main/java/org/kivy/android/PythonService.java @@ -18,6 +18,21 @@ import org.renpy.android.Hardware; +import android.content.pm.PackageManager; +import android.support.v4.app.ActivityCompat; +import android.Manifest; +import android.app.NotificationChannel; +import android.app.Notification; +import android.app.NotificationManager; +import android.content.Context; +import android.support.v4.app.NotificationCompat; +import android.graphics.Color; +import android.app.Service; +import android.content.ComponentName; +import android.app.PendingIntent; + +import android.graphics.drawable.Icon; +import android.provider.Settings; public class PythonService extends Service implements Runnable { @@ -35,6 +50,7 @@ public class PythonService extends Service implements Runnable { private String pythonServiceArgument; public static PythonService mService = null; private Intent startIntent = null; + private String NOTIFICATION_CHANNEL_ID = "BackgroundService"; private boolean autoRestartService = false; @@ -67,8 +83,13 @@ public int onStartCommand(Intent intent, int flags, int startId) { return START_NOT_STICKY; } - startIntent = intent; + startIntent = intent; Bundle extras = intent.getExtras(); + + if (canDisplayNotification()) { + doStartForeground(extras); + } + androidPrivate = extras.getString("androidPrivate"); androidArgument = extras.getString("androidArgument"); serviceEntrypoint = extras.getString("serviceEntrypoint"); @@ -80,10 +101,6 @@ public int onStartCommand(Intent intent, int flags, int startId) { pythonThread = new Thread(this); pythonThread.start(); - if (canDisplayNotification()) { - doStartForeground(extras); - } - return startType(); } @@ -96,6 +113,7 @@ protected void doStartForeground(Bundle extras) { Intent contextIntent = new Intent(context, PythonActivity.class); PendingIntent pIntent = PendingIntent.getActivity(context, 0, contextIntent, PendingIntent.FLAG_UPDATE_CURRENT); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { notification = new Notification( context.getApplicationInfo().icon, serviceTitle, System.currentTimeMillis()); @@ -108,6 +126,25 @@ protected void doStartForeground(Bundle extras) { } catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { } + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + String channelName = "Service"; + NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_MIN); + chan.setLightColor(Color.BLUE); + chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE); + + NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + assert manager != null; + manager.createNotificationChannel(chan); + + NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context, "persistent"); + notification = notificationBuilder.setOngoing(true) + .setSmallIcon(context.getApplicationInfo().icon) + .setContentTitle(serviceTitle) + .setContentIntent(pIntent) + .setPriority(Notification.PRIORITY_MIN) + .setShowWhen(false) + .setOnlyAlertOnce(true) + .build(); } else { Notification.Builder builder = new Notification.Builder(context); builder.setContentTitle(serviceTitle); @@ -127,7 +164,6 @@ public void onDestroy() { Log.v("python service", "service restart requested"); startService(startIntent); } - Process.killProcess(Process.myPid()); } /** @@ -137,7 +173,6 @@ public void onDestroy() { @Override public void onTaskRemoved(Intent rootIntent) { super.onTaskRemoved(rootIntent); - stopSelf(); } @Override diff --git a/pythonforandroid/bootstraps/common/build/templates/Service.tmpl.java b/pythonforandroid/bootstraps/common/build/templates/Service.tmpl.java index 3ed10c2690..3551ab1b33 100644 --- a/pythonforandroid/bootstraps/common/build/templates/Service.tmpl.java +++ b/pythonforandroid/bootstraps/common/build/templates/Service.tmpl.java @@ -11,19 +11,34 @@ import org.kivy.android.PythonService; import org.kivy.android.PythonActivity; +import android.content.pm.PackageManager; +import android.support.v4.app.ActivityCompat; +import android.Manifest; +import android.app.NotificationChannel; +import android.app.Notification; +import android.app.NotificationManager; +import android.content.Context; +import android.support.v4.app.NotificationCompat; +import android.graphics.Color; +import android.app.Service; +import android.content.ComponentName; +import android.app.PendingIntent; public class Service{{ name|capitalize }} extends PythonService { + + private boolean autoRestartService = true; + {% if sticky %} @Override public int startType() { - return START_STICKY; + return START_NOT_STICKY; } {% endif %} {% if not foreground %} @Override public boolean canDisplayNotification() { - return false; + return true; } {% endif %} @@ -46,6 +61,26 @@ protected void doStartForeground(Bundle extras) { } catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { } + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + String channelName = "{{ name| capitalize }}"; + NotificationChannel chan = new NotificationChannel("{{ args.name }}", channelName, NotificationManager.IMPORTANCE_MIN); + chan.setLightColor(Color.BLUE); + chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE); + + NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + assert manager != null; + manager.createNotificationChannel(chan); + + NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context, "{{ args.name }}"); + notification = notificationBuilder.setOngoing(true) + .setSmallIcon(context.getApplicationInfo().icon) + .setContentTitle(channelName) + .setContentIntent(pIntent) + .setPriority(Notification.PRIORITY_MIN) + .setShowWhen(false) + //.setCategory(Notification.CATEGORY_SERVICE) + .setOnlyAlertOnce(true) + .build(); } else { Notification.Builder builder = new Notification.Builder(context); builder.setContentTitle("{{ args.name }}"); @@ -53,7 +88,7 @@ protected void doStartForeground(Bundle extras) { builder.setContentIntent(pIntent); builder.setSmallIcon(context.getApplicationInfo().icon); notification = builder.build(); - } + } startForeground({{ service_id }}, notification); } @@ -67,7 +102,26 @@ static public void start(Context ctx, String pythonServiceArgument) { intent.putExtra("pythonHome", argument); intent.putExtra("pythonPath", argument + ":" + argument + "/lib"); intent.putExtra("pythonServiceArgument", pythonServiceArgument); - ctx.startService(intent); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + ctx.startForegroundService(intent); + } else { + ctx.startService(intent); + } + } + + + @Override + public void onDestroy() { + super.onDestroy(); + } + + /** + * Stops the task gracefully when killed. + * Calling stopSelf() will trigger a onDestroy() call from the system. + */ + @Override + public void onTaskRemoved(Intent rootIntent) { + super.onTaskRemoved(rootIntent); } static public void stop(Context ctx) {