From 7dba27e6d10fe44f9fcc980bd833657d1084e30b Mon Sep 17 00:00:00 2001 From: ardocrat Date: Tue, 20 Jun 2023 00:39:19 +0300 Subject: [PATCH] android: optimize notification removal on app exit, add app restart method --- .../mw/gri/android/BackgroundService.java | 38 ++++++++++++++----- .../java/mw/gri/android/MainActivity.java | 25 +++++++++--- 2 files changed, 47 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/mw/gri/android/BackgroundService.java b/app/src/main/java/mw/gri/android/BackgroundService.java index 19ec258..ddcafbc 100644 --- a/app/src/main/java/mw/gri/android/BackgroundService.java +++ b/app/src/main/java/mw/gri/android/BackgroundService.java @@ -24,27 +24,32 @@ public class BackgroundService extends Service { private final Runnable mUpdateSyncStatus = new Runnable() { @Override public void run() { + if (mStopped) { + return; + } + // Update sync status at notification. mNotificationBuilder.setContentText(getSyncStatusText()); NotificationManager manager = getSystemService(NotificationManager.class); manager.notify(SYNC_STATUS_NOTIFICATION_ID, mNotificationBuilder.build()); - + // Send broadcast to MainActivity if app exit is needed after node stop. if (exitAppAfterNodeStop()) { sendBroadcast(new Intent(MainActivity.FINISH_ACTIVITY_ACTION)); mStopped = true; } - + // Repeat notification update if service is not stopped. if (!mStopped) { - mHandler.postDelayed(this, 300); + mHandler.postDelayed(this, 500); } } }; @Override public void onCreate() { + // Prevent CPU to sleep at background. PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); mWakeLock.acquire(); - + // Create channel to show notifications. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel notificationChannel = new NotificationChannel( TAG, TAG, NotificationManager.IMPORTANCE_LOW @@ -53,7 +58,7 @@ public class BackgroundService extends Service { NotificationManager manager = getSystemService(NotificationManager.class); manager.createNotificationChannel(notificationChannel); } - + // Show notification with sync status. Intent i = getPackageManager().getLaunchIntentForPackage(this.getPackageName()); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, i, PendingIntent.FLAG_IMMUTABLE); mNotificationBuilder = new NotificationCompat.Builder(this, TAG) @@ -62,8 +67,9 @@ public class BackgroundService extends Service { .setSmallIcon(R.drawable.ic_stat_name) .setContentIntent(pendingIntent); Notification notification = mNotificationBuilder.build(); + // Start service at foreground state to prevent killing by system. startForeground(SYNC_STATUS_NOTIFICATION_ID, notification); - + // Update sync status at notification. mHandler.post(mUpdateSyncStatus); } @@ -92,17 +98,24 @@ public class BackgroundService extends Service { public void onStop() { mStopped = true; - + // Stop updating the notification. + mHandler.removeCallbacks(mUpdateSyncStatus); + // Remove service from foreground state. stopForeground(Service.STOP_FOREGROUND_REMOVE); - + // Remove notification. + NotificationManager notificationManager = getSystemService(NotificationManager.class); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + notificationManager.deleteNotificationChannel(TAG); + } + notificationManager.cancel(SYNC_STATUS_NOTIFICATION_ID); + // Release wake lock to allow CPU to sleep at background. if (mWakeLock.isHeld()) { mWakeLock.release(); mWakeLock = null; } - - mHandler.removeCallbacks(mUpdateSyncStatus); } + // Start the service. public static void start(Context context) { if (!isServiceRunning(context)) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { @@ -113,10 +126,12 @@ public class BackgroundService extends Service { } } + // Stop the service. public static void stop(Context context) { context.stopService(new Intent(context, BackgroundService.class)); } + // Check if service is running. private static boolean isServiceRunning(Context context) { ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); List services = activityManager.getRunningServices(Integer.MAX_VALUE); @@ -130,7 +145,10 @@ public class BackgroundService extends Service { return false; } + // Get sync status text for notification from native code. private native String getSyncStatusText(); + // Get sync title text for notification from native code. private native String getSyncTitle(); + // Check if exit app is needed after node stop from native code. private native boolean exitAppAfterNodeStop(); } diff --git a/app/src/main/java/mw/gri/android/MainActivity.java b/app/src/main/java/mw/gri/android/MainActivity.java index ebc8037..5091c04 100644 --- a/app/src/main/java/mw/gri/android/MainActivity.java +++ b/app/src/main/java/mw/gri/android/MainActivity.java @@ -1,9 +1,7 @@ package mw.gri.android; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; +import android.content.*; +import android.content.pm.PackageManager; import android.hardware.SensorManager; import android.os.Bundle; import android.os.Process; @@ -27,6 +25,7 @@ public class MainActivity extends GameActivity { @Override public void onReceive(Context ctx, Intent i) { if (i.getAction().equals(FINISH_ACTIVITY_ACTION)) { + unregisterReceiver(this); onExit(); } } @@ -86,11 +85,11 @@ public class MainActivity extends GameActivity { @Override protected void onDestroy() { - unregisterReceiver(mBroadcastReceiver); - if (!mManualExit) { + unregisterReceiver(mBroadcastReceiver); onTermination(); } + // Temp fix: kill process after 3 seconds to prevent app hanging at next launch. new Thread(() -> { try { @@ -117,5 +116,19 @@ public class MainActivity extends GameActivity { finish(); } + // Called from native code to restart the app. + public void onAppRestart() { + BackgroundService.stop(this); + + // Restart Activity. + Intent intent = getPackageManager().getLaunchIntentForPackage(getPackageName()); + ComponentName componentName = intent.getComponent(); + Intent mainIntent = Intent.makeRestartActivityTask(componentName); + startActivity(mainIntent); + + // Kill old process. + Process.killProcess(Process.myPid()); + } + public native void onTermination(); } \ No newline at end of file