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
This commit is contained in:
artdeell 2022-11-10 17:22:16 +03:00 committed by ArtDev
parent 93396eff44
commit a824c5f4f5
8 changed files with 169 additions and 101 deletions

View File

@ -114,8 +114,8 @@
</provider>
<service android:name=".services.LazyService">
</service>
<service android:name=".services.ProgressService"/>
<service android:name=".services.GameService" android:process=":game"/>
</application>

View File

@ -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);

View File

@ -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();

View File

@ -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<Service> 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;
}
}

View File

@ -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<Service> 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);
}
}

View File

@ -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<Service> 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;
}
}

View File

@ -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();
});
}

View File

@ -311,4 +311,5 @@
<string name="default_control">Default control</string>
<string name="progresslayout_tasks_in_progress">%d tasks in progress</string>
<string name="notification_terminate">Terminate</string>
<string name="notification_game_runs">The game is running!</string>
</resources>