From a824c5f4f5d7926a46e6f3a68b1dc68c4c98096d Mon Sep 17 00:00:00 2001 From: artdeell Date: Thu, 10 Nov 2022 17:22:16 +0300 Subject: [PATCH] VUI fixes & improvements pt.2 Added GameService for the time the game runs Added refcount to ProgressService (renamed from LazyService) Moved notification channel creation to Tools Now the game behaves better in the background Now the game fully exits on swipe-up --- .../src/main/AndroidManifest.xml | 4 +- .../net/kdt/pojavlaunch/MainActivity.java | 3 +- .../main/java/net/kdt/pojavlaunch/Tools.java | 10 +++ .../kdt/pojavlaunch/services/GameService.java | 69 +++++++++++++++ .../kdt/pojavlaunch/services/LazyService.java | 88 ------------------- .../pojavlaunch/services/ProgressService.java | 82 +++++++++++++++++ .../tasks/AsyncMinecraftDownloader.java | 13 +-- .../src/main/res/values/strings.xml | 1 + 8 files changed, 169 insertions(+), 101 deletions(-) create mode 100644 app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/services/GameService.java delete mode 100644 app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/services/LazyService.java create mode 100644 app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/services/ProgressService.java diff --git a/app_pojavlauncher/src/main/AndroidManifest.xml b/app_pojavlauncher/src/main/AndroidManifest.xml index 7f61197c2..a8467fef2 100644 --- a/app_pojavlauncher/src/main/AndroidManifest.xml +++ b/app_pojavlauncher/src/main/AndroidManifest.xml @@ -114,8 +114,8 @@ - - + + diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/MainActivity.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/MainActivity.java index 2fe3d526a..723a2a080 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/MainActivity.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/MainActivity.java @@ -32,6 +32,7 @@ import net.kdt.pojavlaunch.customcontrols.keyboard.TouchCharInput; import net.kdt.pojavlaunch.multirt.MultiRTUtils; import net.kdt.pojavlaunch.prefs.*; +import net.kdt.pojavlaunch.services.GameService; import net.kdt.pojavlaunch.utils.*; import net.kdt.pojavlaunch.value.*; import net.kdt.pojavlaunch.value.launcherprofiles.LauncherProfiles; @@ -77,7 +78,7 @@ public class MainActivity extends BaseActivity { if(LauncherProfiles.mainProfileJson == null) LauncherProfiles.update(); minecraftProfile = LauncherProfiles.mainProfileJson.profiles.get(LauncherPreferences.DEFAULT_PREF.getString(LauncherPreferences.PREF_KEY_CURRENT_PROFILE,"")); MCOptionUtils.load(Tools.getGameDirPath(minecraftProfile)); - + GameService.startService(this); initLayout(R.layout.activity_basemain); getWindow().setBackgroundDrawable(null); diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Tools.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Tools.java index 5762f08f2..a9302f142 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Tools.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Tools.java @@ -39,6 +39,7 @@ import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_NOTCH_SIZE; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.core.app.NotificationManagerCompat; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentTransaction; @@ -188,6 +189,15 @@ public final class Tools { return Tools.DIR_GAME_NEW; } + public static void buildNotificationChannel(Context context){ + if(Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return; + NotificationChannel channel = new NotificationChannel( + context.getString(R.string.notif_channel_id), + context.getString(R.string.notif_channel_name), NotificationManager.IMPORTANCE_HIGH); + NotificationManagerCompat manager = NotificationManagerCompat.from(context); + manager.createNotificationChannel(channel); + } + private static boolean mkdirs(String path) { File file = new File(path); return file.mkdirs(); diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/services/GameService.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/services/GameService.java new file mode 100644 index 000000000..a1d4c9bc6 --- /dev/null +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/services/GameService.java @@ -0,0 +1,69 @@ +package net.kdt.pojavlaunch.services; + +import android.app.PendingIntent; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Build; +import android.os.IBinder; +import android.os.Process; + +import androidx.annotation.Nullable; +import androidx.core.app.NotificationCompat; +import androidx.core.content.ContextCompat; + +import net.kdt.pojavlaunch.R; +import net.kdt.pojavlaunch.Tools; + +import java.lang.ref.WeakReference; + +public class GameService extends Service { + private static final WeakReference sGameService = new WeakReference<>(null); + public static void startService(Context context) { + Intent intent = new Intent(context, GameService.class); + ContextCompat.startForegroundService(context, intent); + } + + public static void stopService() { + Service gameService = sGameService.get(); + if(gameService != null) + gameService.stopSelf(); + } + + @Override + public void onCreate() { + Tools.buildNotificationChannel(getApplicationContext()); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + if(intent != null && intent.getBooleanExtra("kill", false)) { + stopSelf(); + Process.killProcess(Process.myPid()); + return super.onStartCommand(intent, flags, startId); + } + Intent killIntent = new Intent(getApplicationContext(), GameService.class); + killIntent.putExtra("kill", true); + PendingIntent pendingKillIntent = PendingIntent.getService(this, 0, killIntent, Build.VERSION.SDK_INT >=23 ? PendingIntent.FLAG_IMMUTABLE : 0); + NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, "channel_id") + .setContentTitle(getString(R.string.lazy_service_default_title)) + .setContentText(getString(R.string.notification_game_runs)) + .addAction(android.R.drawable.ic_menu_close_clear_cancel, getString(R.string.notification_terminate), pendingKillIntent) + .setSmallIcon(R.mipmap.ic_launcher_round); + startForeground(2, notificationBuilder.build()); + return START_NOT_STICKY; // non-sticky so android wont try restarting the game after the user uses the "Quit" button + } + + @Override + public void onTaskRemoved(Intent rootIntent) { + //At this point in time only the game runs and the user poofed the window, time to die + stopSelf(); + Process.killProcess(Process.myPid()); + } + + @Nullable + @Override + public IBinder onBind(Intent intent) { + return null; + } +} diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/services/LazyService.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/services/LazyService.java deleted file mode 100644 index f0347ee52..000000000 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/services/LazyService.java +++ /dev/null @@ -1,88 +0,0 @@ -package net.kdt.pojavlaunch.services; - -import android.app.Notification; -import android.app.NotificationChannel; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.app.Service; -import android.content.Context; -import android.content.Intent; -import android.os.Build; -import android.os.IBinder; -import android.os.Process; - -import androidx.annotation.Nullable; -import androidx.core.app.NotificationCompat; -import androidx.core.app.NotificationManagerCompat; -import androidx.core.content.ContextCompat; - -import net.kdt.pojavlaunch.R; - -import java.lang.ref.WeakReference; - -/** - * Lazy service which allows the process not to get killed. - * Can be created from context, can be killed statically - */ -public class LazyService extends Service { - private static final String NOTIF_TITLE = "notif_title"; - private static final String NOTIF_DESC = "notif_desc"; - - private static WeakReference sLazyService = new WeakReference<>(null); - - /** Simple wrapper to start the service */ - public static void startService(Context context){ - Intent intent = new Intent(context, LazyService.class); - ContextCompat.startForegroundService(context, intent); - } - - - /** Kill the service if it is still running */ - public static void killService(){ - Service service = sLazyService.get(); - if(service != null) - service.stopSelf(); - } - - public LazyService(){ - super(); - // TODO handle multiple service creation ? - sLazyService = new WeakReference<>(this); - } - - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - if(intent.getBooleanExtra("kill", false)) { - Process.killProcess(Process.myPid()); - return super.onStartCommand(intent, flags, startId); - } - buildNotificationChannel(); - Intent killIntent = new Intent(getApplicationContext(), LazyService.class); - killIntent.putExtra("kill", true); - NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "channel_id") - .setContentTitle(getString(R.string.lazy_service_default_title)) - .setContentText(getString(R.string.lazy_service_default_description)) - .addAction(android.R.drawable.ic_menu_close_clear_cancel, getString(R.string.notification_terminate), PendingIntent.getService(this, 0, killIntent, Build.VERSION.SDK_INT >=23 ? PendingIntent.FLAG_IMMUTABLE : 0)) - .setSmallIcon(R.mipmap.ic_launcher_round); - startForeground(1,builder.build()); - return super.onStartCommand(intent, flags, startId); - } - - @Nullable - @Override - public IBinder onBind(Intent intent) { - return null; - } - - private void buildNotificationChannel(){ - if(Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return; - - NotificationChannel channel = new NotificationChannel( - getResources().getString(R.string.notif_channel_id), - getResources().getString(R.string.notif_channel_name), NotificationManager.IMPORTANCE_HIGH); - NotificationManagerCompat manager = NotificationManagerCompat.from(getApplicationContext()); - - manager.createNotificationChannel(channel); - - } -} diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/services/ProgressService.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/services/ProgressService.java new file mode 100644 index 000000000..3b54f0aba --- /dev/null +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/services/ProgressService.java @@ -0,0 +1,82 @@ +package net.kdt.pojavlaunch.services; + +import android.app.PendingIntent; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Build; +import android.os.IBinder; +import android.os.Process; + +import androidx.annotation.Nullable; +import androidx.core.app.NotificationCompat; +import androidx.core.content.ContextCompat; + +import net.kdt.pojavlaunch.R; +import net.kdt.pojavlaunch.Tools; + +import java.lang.ref.WeakReference; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Lazy service which allows the process not to get killed. + * Can be created from context, can be killed statically + */ +public class ProgressService extends Service { + + private static WeakReference sProgressService = new WeakReference<>(null); + private static final AtomicInteger sReferenceCount = new AtomicInteger(0); + + /** Simple wrapper to start the service */ + public static void startService(Context context){ + Intent intent = new Intent(context, ProgressService.class); + if(sReferenceCount.get() < 0) sReferenceCount.set(0); + sReferenceCount.getAndIncrement(); + ContextCompat.startForegroundService(context, intent); + } + + /** Kill the service if it is still running */ + public static void killService(){ + Service service = sProgressService.get(); + int refcnt = sReferenceCount.decrementAndGet(); + if(service != null && refcnt <= 0) { + service.stopSelf(); + } + } + + private NotificationCompat.Builder mNotificationBuilder; + public ProgressService(){ + super(); + sProgressService = new WeakReference<>(this); + } + + @Override + public void onCreate() { + Tools.buildNotificationChannel(getApplicationContext()); + Intent killIntent = new Intent(getApplicationContext(), ProgressService.class); + killIntent.putExtra("kill", true); + PendingIntent pendingKillIntent = PendingIntent.getService(this, 0, killIntent, Build.VERSION.SDK_INT >=23 ? PendingIntent.FLAG_IMMUTABLE : 0); + mNotificationBuilder = new NotificationCompat.Builder(this, "channel_id") + .setContentTitle(getString(R.string.lazy_service_default_title)) + .addAction(android.R.drawable.ic_menu_close_clear_cancel, getString(R.string.notification_terminate), pendingKillIntent) + .setSmallIcon(R.mipmap.ic_launcher_round); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + if(intent != null && intent.getBooleanExtra("kill", false)) { + stopSelf(); // otherwise Android tries to restart the service since it "crashed" + Process.killProcess(Process.myPid()); + return super.onStartCommand(intent, flags, startId); + } + mNotificationBuilder.setContentText(getString(R.string.progresslayout_tasks_in_progress, sReferenceCount.get())); + startForeground(1,mNotificationBuilder.build()); + return super.onStartCommand(intent, flags, startId); + } + + @Nullable + @Override + public IBinder onBind(Intent intent) { + return null; + } +} diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/tasks/AsyncMinecraftDownloader.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/tasks/AsyncMinecraftDownloader.java index b6062036b..b8a5d2c51 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/tasks/AsyncMinecraftDownloader.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/tasks/AsyncMinecraftDownloader.java @@ -1,12 +1,9 @@ package net.kdt.pojavlaunch.tasks; import static net.kdt.pojavlaunch.PojavApplication.sExecutorService; -import static net.kdt.pojavlaunch.Tools.ENABLE_DEV_FEATURES; import static net.kdt.pojavlaunch.utils.DownloadUtils.downloadFileMonitored; import android.app.Activity; -import android.content.Context; -import android.util.ArrayMap; import android.util.Log; import androidx.annotation.NonNull; @@ -21,16 +18,12 @@ import net.kdt.pojavlaunch.R; import net.kdt.pojavlaunch.Tools; import net.kdt.pojavlaunch.extra.ExtraConstants; import net.kdt.pojavlaunch.extra.ExtraCore; -import net.kdt.pojavlaunch.multirt.MultiRTUtils; -import net.kdt.pojavlaunch.multirt.Runtime; import net.kdt.pojavlaunch.prefs.LauncherPreferences; -import net.kdt.pojavlaunch.services.LazyService; +import net.kdt.pojavlaunch.services.ProgressService; import net.kdt.pojavlaunch.utils.DownloadUtils; import net.kdt.pojavlaunch.value.DependentLibrary; import net.kdt.pojavlaunch.value.MinecraftClientInfo; import net.kdt.pojavlaunch.value.MinecraftLibraryArtifact; -import net.kdt.pojavlaunch.value.launcherprofiles.LauncherProfiles; -import net.kdt.pojavlaunch.value.launcherprofiles.MinecraftProfile; import org.apache.commons.io.IOUtils; @@ -54,13 +47,13 @@ public class AsyncMinecraftDownloader { public AsyncMinecraftDownloader(@NonNull Activity activity, JMinecraftVersionList.Version version, @NonNull DoneListener listener){ - LazyService.startService(activity); + ProgressService.startService(activity); sExecutorService.execute(() -> { if(downloadGame(activity, version, version.id)) listener.onDownloadDone(); - LazyService.killService(); + ProgressService.killService(); }); } diff --git a/app_pojavlauncher/src/main/res/values/strings.xml b/app_pojavlauncher/src/main/res/values/strings.xml index e47dda026..b6ff16e6f 100644 --- a/app_pojavlauncher/src/main/res/values/strings.xml +++ b/app_pojavlauncher/src/main/res/values/strings.xml @@ -311,4 +311,5 @@ Default control %d tasks in progress Terminate + The game is running!