mirror of
https://github.com/AngelAuraMC/Amethyst-Android.git
synced 2025-09-13 06:39:54 -04:00
Feat: Handle MainActivity destruction (#4817)
* Feat[launcher]: begin implementing MainActivity destruction handling * Feat[lifecycle]: finalize MainActivity lifecycle awareness implementation * Clean[dialog]: unified halting LifecycleAwareAlertDialog implementation * Fix[mainactivity]: comment truncated
This commit is contained in:
parent
682fe04ef6
commit
d5f74af94a
@ -1,7 +1,5 @@
|
||||
package net.kdt.pojavlaunch;
|
||||
|
||||
import static net.kdt.pojavlaunch.MainActivity.fullyExit;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.ClipboardManager;
|
||||
import android.os.Bundle;
|
||||
@ -173,14 +171,10 @@ public class JavaGUILauncherActivity extends BaseActivity implements View.OnTouc
|
||||
openLogOutput(null);
|
||||
new Thread(() -> {
|
||||
try {
|
||||
final int exit = doCustomInstall(runtime, modFile, javaArgs);
|
||||
Logger.appendToLog(getString(R.string.toast_optifine_success));
|
||||
if (exit != 0) return;
|
||||
runOnUiThread(() -> {
|
||||
Toast.makeText(JavaGUILauncherActivity.this, R.string.toast_optifine_success, Toast.LENGTH_SHORT).show();
|
||||
fullyExit();
|
||||
});
|
||||
|
||||
// Due to time, the code here became, like, actually useless
|
||||
// So it was removed
|
||||
// Tbh this whole class needs a refactor...
|
||||
doCustomInstall(runtime, modFile, javaArgs);
|
||||
} catch (Throwable e) {
|
||||
Logger.appendToLog("Install failed:");
|
||||
Logger.appendToLog(Log.getStackTraceString(e));
|
||||
@ -287,7 +281,7 @@ public class JavaGUILauncherActivity extends BaseActivity implements View.OnTouc
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
public int launchJavaRuntime(Runtime runtime, File modFile, String javaArgs) {
|
||||
public void launchJavaRuntime(Runtime runtime, File modFile, String javaArgs) {
|
||||
JREUtils.redirectAndPrintJRELog();
|
||||
try {
|
||||
List<String> javaArgList = new ArrayList<>();
|
||||
@ -313,18 +307,17 @@ public class JavaGUILauncherActivity extends BaseActivity implements View.OnTouc
|
||||
|
||||
Logger.appendToLog("Info: Java arguments: " + Arrays.toString(javaArgList.toArray(new String[0])));
|
||||
|
||||
return JREUtils.launchJavaVM(this, runtime,null,javaArgList, LauncherPreferences.PREF_CUSTOM_JAVA_ARGS);
|
||||
JREUtils.launchJavaVM(this, runtime,null,javaArgList, LauncherPreferences.PREF_CUSTOM_JAVA_ARGS);
|
||||
} catch (Throwable th) {
|
||||
Tools.showError(this, th, true);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private int doCustomInstall(Runtime runtime, File modFile, String javaArgs) {
|
||||
private void doCustomInstall(Runtime runtime, File modFile, String javaArgs) {
|
||||
mSkipDetectMod = true;
|
||||
return launchJavaRuntime(runtime, modFile, javaArgs);
|
||||
launchJavaRuntime(runtime, modFile, javaArgs);
|
||||
}
|
||||
|
||||
public void toggleKeyboard(View view) {
|
||||
|
@ -23,7 +23,7 @@ import androidx.fragment.app.FragmentManager;
|
||||
import com.kdt.mcgui.ProgressLayout;
|
||||
import com.kdt.mcgui.mcAccountSpinner;
|
||||
|
||||
import net.kdt.pojavlaunch.contextexecutor.ContextExecutor;
|
||||
import net.kdt.pojavlaunch.lifecycle.ContextExecutor;
|
||||
import net.kdt.pojavlaunch.contracts.OpenDocumentWithExtension;
|
||||
import net.kdt.pojavlaunch.extra.ExtraConstants;
|
||||
import net.kdt.pojavlaunch.extra.ExtraCore;
|
||||
@ -40,7 +40,7 @@ import net.kdt.pojavlaunch.progresskeeper.TaskCountListener;
|
||||
import net.kdt.pojavlaunch.services.ProgressServiceKeeper;
|
||||
import net.kdt.pojavlaunch.tasks.AsyncMinecraftDownloader;
|
||||
import net.kdt.pojavlaunch.tasks.AsyncVersionList;
|
||||
import net.kdt.pojavlaunch.tasks.ContextAwareDoneListener;
|
||||
import net.kdt.pojavlaunch.lifecycle.ContextAwareDoneListener;
|
||||
import net.kdt.pojavlaunch.utils.NotificationUtils;
|
||||
import net.kdt.pojavlaunch.value.launcherprofiles.LauncherProfiles;
|
||||
import net.kdt.pojavlaunch.value.launcherprofiles.MinecraftProfile;
|
||||
|
@ -13,8 +13,10 @@ import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Color;
|
||||
@ -22,6 +24,7 @@ import android.graphics.drawable.ColorDrawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
@ -36,10 +39,12 @@ import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.drawerlayout.widget.DrawerLayout;
|
||||
|
||||
import com.kdt.LoggerView;
|
||||
|
||||
import net.kdt.pojavlaunch.lifecycle.ContextExecutor;
|
||||
import net.kdt.pojavlaunch.customcontrols.ControlButtonMenuListener;
|
||||
import net.kdt.pojavlaunch.customcontrols.ControlData;
|
||||
import net.kdt.pojavlaunch.customcontrols.ControlDrawerData;
|
||||
@ -62,7 +67,7 @@ import org.lwjgl.glfw.CallbackBridge;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public class MainActivity extends BaseActivity implements ControlButtonMenuListener, EditorExitable {
|
||||
public class MainActivity extends BaseActivity implements ControlButtonMenuListener, EditorExitable, ServiceConnection {
|
||||
public static volatile ClipboardManager GLOBAL_CLIPBOARD;
|
||||
public static final String INTENT_MINECRAFT_VERSION = "intent_version";
|
||||
|
||||
@ -84,13 +89,17 @@ public class MainActivity extends BaseActivity implements ControlButtonMenuListe
|
||||
private AdapterView.OnItemClickListener gameActionClickListener;
|
||||
public ArrayAdapter<String> ingameControlsEditorArrayAdapter;
|
||||
public AdapterView.OnItemClickListener ingameControlsEditorListener;
|
||||
private GameService.LocalBinder mServiceBinder;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
minecraftProfile = LauncherProfiles.getCurrentProfile();
|
||||
MCOptionUtils.load(Tools.getGameDirPath(minecraftProfile).getAbsolutePath());
|
||||
GameService.startService(this);
|
||||
|
||||
Intent gameServiceIntent = new Intent(this, GameService.class);
|
||||
// Start the service a bit early
|
||||
ContextCompat.startForegroundService(this, gameServiceIntent);
|
||||
initLayout(R.layout.activity_basemain);
|
||||
CallbackBridge.addGrabListener(touchpad);
|
||||
CallbackBridge.addGrabListener(minecraftGLView);
|
||||
@ -122,6 +131,12 @@ public class MainActivity extends BaseActivity implements ControlButtonMenuListe
|
||||
MCOptionUtils.MCOptionListener optionListener = MCOptionUtils::getMcScale;
|
||||
MCOptionUtils.addMCOptionListener(optionListener);
|
||||
mControlLayout.setModifiable(false);
|
||||
|
||||
// Set the activity for the executor. Must do this here, or else Tools.showErrorRemote() may not
|
||||
// execute the correct method
|
||||
ContextExecutor.setActivity(this);
|
||||
//Now, attach to the service. The game will only start when this happens, to make sure that we know the right state.
|
||||
bindService(gameServiceIntent, this, 0);
|
||||
}
|
||||
|
||||
protected void initLayout(int resId) {
|
||||
@ -191,11 +206,9 @@ public class MainActivity extends BaseActivity implements ControlButtonMenuListe
|
||||
|
||||
runCraft(finalVersion, mVersionInfo);
|
||||
}catch (Throwable e){
|
||||
Tools.showError(getApplicationContext(), e, true);
|
||||
Tools.showErrorRemote(e);
|
||||
}
|
||||
});
|
||||
|
||||
minecraftGLView.start();
|
||||
} catch (Throwable e) {
|
||||
Tools.showError(this, e, true);
|
||||
}
|
||||
@ -275,6 +288,7 @@ public class MainActivity extends BaseActivity implements ControlButtonMenuListe
|
||||
super.onDestroy();
|
||||
CallbackBridge.removeGrabListener(touchpad);
|
||||
CallbackBridge.removeGrabListener(minecraftGLView);
|
||||
ContextExecutor.clearActivity();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -336,6 +350,8 @@ public class MainActivity extends BaseActivity implements ControlButtonMenuListe
|
||||
int requiredJavaVersion = 8;
|
||||
if(version.javaVersion != null) requiredJavaVersion = version.javaVersion.majorVersion;
|
||||
Tools.launchMinecraft(this, minecraftAccount, minecraftProfile, versionId, requiredJavaVersion);
|
||||
//Note that we actually stall in the above function, even if the game crashes. But let's be safe.
|
||||
Tools.runOnUiThread(()-> mServiceBinder.isActive = false);
|
||||
}
|
||||
|
||||
private void printLauncherInfo(String gameVersion, String javaArguments) {
|
||||
@ -609,4 +625,17 @@ public class MainActivity extends BaseActivity implements ControlButtonMenuListe
|
||||
navDrawer.setOnItemClickListener(gameActionClickListener);
|
||||
isInEditor = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
GameService.LocalBinder localBinder = (GameService.LocalBinder) service;
|
||||
mServiceBinder = localBinder;
|
||||
minecraftGLView.start(localBinder.isActive);
|
||||
localBinder.isActive = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -144,14 +144,17 @@ public class MinecraftGLSurface extends View implements GrabListener {
|
||||
MCOptionUtils.addMCOptionListener(mGuiScaleListener);
|
||||
}
|
||||
|
||||
/** Initialize the view and all its settings */
|
||||
public void start(){
|
||||
/** Initialize the view and all its settings
|
||||
* @param isAlreadyRunning set to true to tell the view that the game is already running
|
||||
* (only updates the window without calling the start listener)
|
||||
*/
|
||||
public void start(boolean isAlreadyRunning){
|
||||
if(LauncherPreferences.PREF_USE_ALTERNATE_SURFACE){
|
||||
SurfaceView surfaceView = new SurfaceView(getContext());
|
||||
mSurface = surfaceView;
|
||||
|
||||
surfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
|
||||
private boolean isCalled = false;
|
||||
private boolean isCalled = isAlreadyRunning;
|
||||
@Override
|
||||
public void surfaceCreated(@NonNull SurfaceHolder holder) {
|
||||
if(isCalled) {
|
||||
@ -180,7 +183,7 @@ public class MinecraftGLSurface extends View implements GrabListener {
|
||||
mSurface = textureView;
|
||||
|
||||
textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
|
||||
private boolean isCalled = false;
|
||||
private boolean isCalled = isAlreadyRunning;
|
||||
@Override
|
||||
public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height) {
|
||||
Surface tSurface = new Surface(surface);
|
||||
|
@ -18,7 +18,7 @@ import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import net.kdt.pojavlaunch.contextexecutor.ContextExecutor;
|
||||
import net.kdt.pojavlaunch.lifecycle.ContextExecutor;
|
||||
import net.kdt.pojavlaunch.tasks.AsyncAssetManager;
|
||||
import net.kdt.pojavlaunch.utils.*;
|
||||
|
||||
|
@ -9,7 +9,7 @@ import android.os.Bundle;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import net.kdt.pojavlaunch.contextexecutor.ContextExecutorTask;
|
||||
import net.kdt.pojavlaunch.lifecycle.ContextExecutorTask;
|
||||
import net.kdt.pojavlaunch.utils.NotificationUtils;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
@ -8,7 +8,6 @@ import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_NOTCH_SIZE;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.ProgressDialog;
|
||||
@ -39,6 +38,8 @@ import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
@ -47,8 +48,9 @@ import androidx.fragment.app.FragmentTransaction;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
import net.kdt.pojavlaunch.contextexecutor.ContextExecutor;
|
||||
import net.kdt.pojavlaunch.contextexecutor.ContextExecutorTask;
|
||||
import net.kdt.pojavlaunch.lifecycle.ContextExecutor;
|
||||
import net.kdt.pojavlaunch.lifecycle.ContextExecutorTask;
|
||||
import net.kdt.pojavlaunch.lifecycle.LifecycleAwareAlertDialog;
|
||||
import net.kdt.pojavlaunch.multirt.MultiRTUtils;
|
||||
import net.kdt.pojavlaunch.multirt.Runtime;
|
||||
import net.kdt.pojavlaunch.plugins.FFmpegPlugin;
|
||||
@ -161,21 +163,18 @@ public final class Tools {
|
||||
NATIVE_LIB_DIR = ctx.getApplicationInfo().nativeLibraryDir;
|
||||
}
|
||||
|
||||
|
||||
public static void launchMinecraft(final Activity activity, MinecraftAccount minecraftAccount,
|
||||
public static void launchMinecraft(final AppCompatActivity activity, MinecraftAccount minecraftAccount,
|
||||
MinecraftProfile minecraftProfile, String versionId, int versionJavaRequirement) throws Throwable {
|
||||
int freeDeviceMemory = getFreeDeviceMemory(activity);
|
||||
if(LauncherPreferences.PREF_RAM_ALLOCATION > freeDeviceMemory) {
|
||||
Object memoryErrorLock = new Object();
|
||||
activity.runOnUiThread(() -> {
|
||||
androidx.appcompat.app.AlertDialog.Builder b = new androidx.appcompat.app.AlertDialog.Builder(activity)
|
||||
.setMessage(activity.getString(R.string.memory_warning_msg, freeDeviceMemory ,LauncherPreferences.PREF_RAM_ALLOCATION))
|
||||
.setPositiveButton(android.R.string.ok, (dialogInterface, i) -> {synchronized(memoryErrorLock){memoryErrorLock.notifyAll();}})
|
||||
.setOnCancelListener((i) -> {synchronized(memoryErrorLock){memoryErrorLock.notifyAll();}});
|
||||
b.show();
|
||||
});
|
||||
synchronized (memoryErrorLock) {
|
||||
memoryErrorLock.wait();
|
||||
LifecycleAwareAlertDialog.DialogCreator dialogCreator = (dialog, builder) ->
|
||||
builder.setMessage(activity.getString(R.string.memory_warning_msg, freeDeviceMemory, LauncherPreferences.PREF_RAM_ALLOCATION))
|
||||
.setPositiveButton(android.R.string.ok, (d, w)->{});
|
||||
|
||||
if(LifecycleAwareAlertDialog.haltOnDialog(activity.getLifecycle(), activity, dialogCreator)) {
|
||||
return; // If the dialog's lifecycle has ended, return without
|
||||
// actually launching the game, thus giving us the opportunity
|
||||
// to start after the activity is shown again
|
||||
}
|
||||
}
|
||||
Runtime runtime = MultiRTUtils.forceReread(Tools.pickRuntime(minecraftProfile, versionJavaRequirement));
|
||||
@ -216,6 +215,8 @@ public final class Tools {
|
||||
if(Tools.isValidString(minecraftProfile.javaArgs)) args = minecraftProfile.javaArgs;
|
||||
FFmpegPlugin.discover(activity);
|
||||
JREUtils.launchJavaVM(activity, runtime, gamedir, javaArgList, args);
|
||||
// If we returned, this means that the JVM exit dialog has been shown and we don't need to be active anymore.
|
||||
// We never return otherwise. The process will be killed anyway, and thus we will become inactive
|
||||
}
|
||||
|
||||
public static File getGameDirPath(@NonNull MinecraftProfile minecraftProfile){
|
||||
|
@ -1,4 +1,4 @@
|
||||
package net.kdt.pojavlaunch.tasks;
|
||||
package net.kdt.pojavlaunch.lifecycle;
|
||||
|
||||
import static net.kdt.pojavlaunch.MainActivity.INTENT_MINECRAFT_VERSION;
|
||||
|
||||
@ -9,9 +9,10 @@ import android.content.Intent;
|
||||
import net.kdt.pojavlaunch.MainActivity;
|
||||
import net.kdt.pojavlaunch.R;
|
||||
import net.kdt.pojavlaunch.Tools;
|
||||
import net.kdt.pojavlaunch.contextexecutor.ContextExecutor;
|
||||
import net.kdt.pojavlaunch.contextexecutor.ContextExecutorTask;
|
||||
import net.kdt.pojavlaunch.lifecycle.ContextExecutor;
|
||||
import net.kdt.pojavlaunch.lifecycle.ContextExecutorTask;
|
||||
import net.kdt.pojavlaunch.progresskeeper.ProgressKeeper;
|
||||
import net.kdt.pojavlaunch.tasks.AsyncMinecraftDownloader;
|
||||
import net.kdt.pojavlaunch.utils.NotificationUtils;
|
||||
|
||||
public class ContextAwareDoneListener implements AsyncMinecraftDownloader.DoneListener, ContextExecutorTask {
|
@ -1,4 +1,4 @@
|
||||
package net.kdt.pojavlaunch.contextexecutor;
|
||||
package net.kdt.pojavlaunch.lifecycle;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
@ -1,4 +1,4 @@
|
||||
package net.kdt.pojavlaunch.contextexecutor;
|
||||
package net.kdt.pojavlaunch.lifecycle;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
@ -0,0 +1,125 @@
|
||||
package net.kdt.pojavlaunch.lifecycle;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.lifecycle.Lifecycle;
|
||||
import androidx.lifecycle.LifecycleEventObserver;
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
|
||||
import net.kdt.pojavlaunch.Tools;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* A class that implements a form of lifecycle awareness for AlertDialog
|
||||
*/
|
||||
public abstract class LifecycleAwareAlertDialog implements LifecycleEventObserver {
|
||||
private Lifecycle mLifecycle;
|
||||
private AlertDialog mDialog;
|
||||
private boolean mLifecycleEnded = false;
|
||||
|
||||
/**
|
||||
* Show the lifecycle-aware dialog.
|
||||
* Note that the DialogCreator may not be always invoked.
|
||||
* @param lifecycle the lifecycle to follow
|
||||
* @param context the context for the dialog
|
||||
* @param dialogCreator an interface used to create the dialog.
|
||||
* Note that any dismiss listeners added to the dialog must be wrapped
|
||||
* with wrapDismissListener().
|
||||
*/
|
||||
public void show(Lifecycle lifecycle, Context context, DialogCreator dialogCreator) {
|
||||
this.mLifecycleEnded = false;
|
||||
this.mLifecycle = lifecycle;
|
||||
if(mLifecycle.getCurrentState().equals(Lifecycle.State.DESTROYED)) {
|
||||
this.mLifecycleEnded = true;
|
||||
dialogHidden(mLifecycleEnded);
|
||||
return;
|
||||
}
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(context);
|
||||
// Install the default cancel/dismiss handling
|
||||
builder.setOnDismissListener(wrapDismissListener(null));
|
||||
dialogCreator.createDialog(this, builder);
|
||||
mLifecycle.addObserver(this);
|
||||
mDialog = builder.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when the dialog gets hidden either by cancel()/dismiss(), or if a lifecycle event
|
||||
* happens.
|
||||
* @param lifecycleEnded if the dialog was hidden due to a lifecycle event
|
||||
*/
|
||||
abstract protected void dialogHidden(boolean lifecycleEnded);
|
||||
|
||||
protected void dispatchDialogHidden() {
|
||||
new Exception().printStackTrace();
|
||||
dialogHidden(mLifecycleEnded);
|
||||
mLifecycle.removeObserver(this);
|
||||
}
|
||||
|
||||
public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {
|
||||
if(event.equals(Lifecycle.Event.ON_DESTROY)) {
|
||||
mDialog.dismiss();
|
||||
mLifecycleEnded = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap an OnDismissListener for use with this LifecycleAwareAlertDialog. Pass null to only invoke the
|
||||
* default dialog hidden handling.
|
||||
* @param listener your listener
|
||||
* @return the wrapped listener
|
||||
*/
|
||||
public DialogInterface.OnDismissListener wrapDismissListener(DialogInterface.OnCancelListener listener) {
|
||||
return dialog -> {
|
||||
dispatchDialogHidden();
|
||||
if(listener != null) listener.onCancel(dialog);
|
||||
};
|
||||
}
|
||||
|
||||
public interface DialogCreator {
|
||||
/**
|
||||
* This methods is called when the LifecycleAwareAlertDialog needs to set up its dialog.
|
||||
* @param alertDialog an instance of LifecycleAwareAlertDialog for wrapping listeners
|
||||
* @param dialogBuilder the AlertDialog builder
|
||||
*/
|
||||
void createDialog(LifecycleAwareAlertDialog alertDialog, AlertDialog.Builder dialogBuilder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a dialog and halt the current thread until the dialog gets closed either due to user action or a lifecycle event.
|
||||
* @param lifecycle the Lifecycle object that this dialog will track to automatically close upon destruction
|
||||
* @param context the context used to show the dialog
|
||||
* @param dialogCreator a DialogCreator that creates the dialog
|
||||
* @return true if the dialog was automatically dismissed due to a lifecycle event. This may happen
|
||||
* before the dialog creator is used, so make sure to to handle the return value of the function.
|
||||
* false otherwise
|
||||
* @throws InterruptedException if the thread was interrupted while waiting for the dialog
|
||||
*/
|
||||
|
||||
public static boolean haltOnDialog(Lifecycle lifecycle, Context context, DialogCreator dialogCreator) throws InterruptedException {
|
||||
Object waitLock = new Object();
|
||||
AtomicBoolean hasLifecycleEnded = new AtomicBoolean(false);
|
||||
// This runnable is moved here in order to reduce bracket/lambda hell
|
||||
Runnable showDialogRunnable = () -> {
|
||||
LifecycleAwareAlertDialog lifecycleAwareDialog = new LifecycleAwareAlertDialog() {
|
||||
@Override
|
||||
protected void dialogHidden(boolean lifecycleEnded) {
|
||||
hasLifecycleEnded.set(lifecycleEnded);
|
||||
synchronized(waitLock){waitLock.notifyAll();}
|
||||
}
|
||||
};
|
||||
lifecycleAwareDialog.show(lifecycle, context, dialogCreator);
|
||||
};
|
||||
synchronized (waitLock) {
|
||||
Tools.runOnUiThread(showDialogRunnable);
|
||||
// the wait() method makes the thread wait on the end of the synchronized block.
|
||||
// so we put it here to make sure that the thread won't get notified before wait()
|
||||
// is called
|
||||
waitLock.wait();
|
||||
}
|
||||
return hasLifecycleEnded.get();
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@ import android.text.Html;
|
||||
|
||||
import net.kdt.pojavlaunch.R;
|
||||
import net.kdt.pojavlaunch.ShowErrorActivity;
|
||||
import net.kdt.pojavlaunch.contextexecutor.ContextExecutorTask;
|
||||
import net.kdt.pojavlaunch.lifecycle.ContextExecutorTask;
|
||||
import net.kdt.pojavlaunch.prefs.LauncherPreferences;
|
||||
|
||||
public class MirrorTamperedException extends Exception implements ContextExecutorTask {
|
||||
|
@ -2,15 +2,14 @@ package net.kdt.pojavlaunch.services;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Binder;
|
||||
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;
|
||||
@ -20,10 +19,7 @@ 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);
|
||||
}
|
||||
private final LocalBinder mLocalBinder = new LocalBinder();
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
@ -47,7 +43,7 @@ public class GameService extends Service {
|
||||
.addAction(android.R.drawable.ic_menu_close_clear_cancel, getString(R.string.notification_terminate), pendingKillIntent)
|
||||
.setSmallIcon(R.drawable.notif_icon)
|
||||
.setNotificationSilent();
|
||||
startForeground(NotificationUtils.NOTIFICATION_ID_GAME_SERVICE, notificationBuilder.build());
|
||||
startForeground(NotificationUtils.NOTIFICATION_ID_GAME_SERVICE, notificationBuilder.build());
|
||||
return START_NOT_STICKY; // non-sticky so android wont try restarting the game after the user uses the "Quit" button
|
||||
}
|
||||
|
||||
@ -61,6 +57,10 @@ public class GameService extends Service {
|
||||
@Nullable
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
return mLocalBinder;
|
||||
}
|
||||
|
||||
public static class LocalBinder extends Binder {
|
||||
public boolean isActive;
|
||||
}
|
||||
}
|
||||
|
@ -16,12 +16,15 @@ import android.system.*;
|
||||
import android.util.*;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.oracle.dalvik.*;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import net.kdt.pojavlaunch.*;
|
||||
import net.kdt.pojavlaunch.extra.ExtraConstants;
|
||||
import net.kdt.pojavlaunch.extra.ExtraCore;
|
||||
import net.kdt.pojavlaunch.lifecycle.LifecycleAwareAlertDialog;
|
||||
import net.kdt.pojavlaunch.multirt.MultiRTUtils;
|
||||
import net.kdt.pojavlaunch.multirt.Runtime;
|
||||
import net.kdt.pojavlaunch.plugins.FFmpegPlugin;
|
||||
@ -265,7 +268,7 @@ public class JREUtils {
|
||||
// return ldLibraryPath;
|
||||
}
|
||||
|
||||
public static int launchJavaVM(final Activity activity, final Runtime runtime, File gameDirectory, final List<String> JVMArgs, final String userArgsString) throws Throwable {
|
||||
public static void launchJavaVM(final AppCompatActivity activity, final Runtime runtime, File gameDirectory, final List<String> JVMArgs, final String userArgsString) throws Throwable {
|
||||
String runtimeHome = MultiRTUtils.getRuntimeHome(runtime.name).getAbsolutePath();
|
||||
|
||||
JREUtils.relocateLibPath(runtime, runtimeHome);
|
||||
@ -303,18 +306,13 @@ public class JREUtils {
|
||||
final int exitCode = VMLauncher.launchJVM(userArgs.toArray(new String[0]));
|
||||
Logger.appendToLog("Java Exit code: " + exitCode);
|
||||
if (exitCode != 0) {
|
||||
activity.runOnUiThread(() -> {
|
||||
AlertDialog.Builder dialog = new AlertDialog.Builder(activity);
|
||||
dialog.setMessage(activity.getString(R.string.mcn_exit_title, exitCode));
|
||||
LifecycleAwareAlertDialog.DialogCreator dialogCreator = (dialog, builder)->
|
||||
builder.setMessage(activity.getString(R.string.mcn_exit_title, exitCode))
|
||||
.setPositiveButton(R.string.main_share_logs, (dialogInterface, which)-> shareLog(activity));
|
||||
|
||||
dialog.setPositiveButton(R.string.main_share_logs, (p1, p2) -> {
|
||||
shareLog(activity);
|
||||
MainActivity.fullyExit();
|
||||
});
|
||||
dialog.show();
|
||||
});
|
||||
LifecycleAwareAlertDialog.haltOnDialog(activity.getLifecycle(), activity, dialogCreator);
|
||||
}
|
||||
return exitCode;
|
||||
MainActivity.fullyExit();
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user