mirror of
https://github.com/izzy2lost/PSX2.git
synced 2026-07-05 15:18:36 -07:00
fix some crashes better stability
This commit is contained in:
@@ -30,6 +30,13 @@ public class CheatsDialogFragment extends DialogFragment {
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
|
||||
// Notify MainActivity that this dialog is opening
|
||||
try {
|
||||
if (getActivity() instanceof MainActivity) {
|
||||
((MainActivity) getActivity()).onDialogOpened();
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
|
||||
Context ctx = requireContext();
|
||||
View view = LayoutInflater.from(ctx).inflate(R.layout.simple_list, null, false);
|
||||
ListView lv = view.findViewById(android.R.id.list);
|
||||
@@ -43,12 +50,23 @@ public class CheatsDialogFragment extends DialogFragment {
|
||||
|
||||
refreshList();
|
||||
|
||||
return new MaterialAlertDialogBuilder(ctx, com.google.android.material.R.style.ThemeOverlay_Material3_MaterialAlertDialog)
|
||||
Dialog dialog = new MaterialAlertDialogBuilder(ctx, com.google.android.material.R.style.ThemeOverlay_Material3_MaterialAlertDialog)
|
||||
.setCustomTitle(UiUtils.centeredDialogTitle(ctx, "Manage Cheats"))
|
||||
.setView(view)
|
||||
.setNegativeButton("Close", null)
|
||||
.setPositiveButton("Import For Game", (d, w) -> startImport())
|
||||
.create();
|
||||
|
||||
// Resume game when dialog is dismissed
|
||||
dialog.setOnDismissListener(d -> {
|
||||
android.util.Log.d("CheatsDialog", "Cheats dialog dismissed");
|
||||
// Use the global dialog tracking system
|
||||
if (getActivity() instanceof MainActivity) {
|
||||
((MainActivity) getActivity()).onDialogClosed();
|
||||
}
|
||||
});
|
||||
|
||||
return dialog;
|
||||
}
|
||||
|
||||
private void refreshList() {
|
||||
|
||||
@@ -30,6 +30,13 @@ public class ControllerTestDialogFragment extends DialogFragment implements Cont
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
|
||||
// Notify MainActivity that this dialog is opening
|
||||
try {
|
||||
if (getActivity() instanceof MainActivity) {
|
||||
((MainActivity) getActivity()).onDialogOpened();
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
|
||||
View view = getLayoutInflater().inflate(R.layout.dialog_controller_test, null);
|
||||
|
||||
mControllerListText = view.findViewById(R.id.tv_controller_list);
|
||||
@@ -41,11 +48,22 @@ public class ControllerTestDialogFragment extends DialogFragment implements Cont
|
||||
// Update controller list
|
||||
updateControllerList();
|
||||
|
||||
return new MaterialAlertDialogBuilder(requireContext())
|
||||
Dialog dialog = new MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle("Controller Test")
|
||||
.setView(view)
|
||||
.setPositiveButton("Close", null)
|
||||
.create();
|
||||
|
||||
// Resume game when dialog is dismissed
|
||||
dialog.setOnDismissListener(d -> {
|
||||
android.util.Log.d("ControllerTestDialog", "Controller test dialog dismissed");
|
||||
// Use the global dialog tracking system
|
||||
if (getActivity() instanceof MainActivity) {
|
||||
((MainActivity) getActivity()).onDialogClosed();
|
||||
}
|
||||
});
|
||||
|
||||
return dialog;
|
||||
}
|
||||
|
||||
private void updateControllerList() {
|
||||
|
||||
@@ -41,17 +41,26 @@ public class GameSettingsDialogFragment extends DialogFragment {
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
// Dialog is being destroyed - resume the game
|
||||
android.util.Log.d("GameSettingsDialog", "onDestroy called - resuming game");
|
||||
try {
|
||||
if (getActivity() instanceof MainActivity) {
|
||||
((MainActivity) getActivity()).onDialogClosed();
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
|
||||
// Pause game when game settings dialog is shown
|
||||
// Notify MainActivity that this dialog is opening
|
||||
try {
|
||||
if (getActivity() instanceof MainActivity) {
|
||||
MainActivity mainActivity = (MainActivity) getActivity();
|
||||
if (mainActivity.hasSelectedGame() && mainActivity.isEmulationThreadRunning() && !NativeApp.isPaused()) {
|
||||
NativeApp.pause();
|
||||
}
|
||||
((MainActivity) getActivity()).onDialogOpened();
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
|
||||
@@ -381,21 +390,7 @@ public class GameSettingsDialogFragment extends DialogFragment {
|
||||
});
|
||||
}
|
||||
|
||||
Dialog dialog = builder.create();
|
||||
|
||||
// Resume game when dialog is dismissed
|
||||
dialog.setOnDismissListener(d -> {
|
||||
try {
|
||||
if (getActivity() instanceof MainActivity) {
|
||||
MainActivity mainActivity = (MainActivity) getActivity();
|
||||
if (mainActivity.hasSelectedGame() && mainActivity.isEmulationThreadRunning() && NativeApp.isPaused()) {
|
||||
NativeApp.resume();
|
||||
}
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
});
|
||||
|
||||
return dialog;
|
||||
return builder.create();
|
||||
}
|
||||
|
||||
private void saveGameSettings(String gameSerial, String gameCrc,
|
||||
|
||||
@@ -98,6 +98,18 @@ public class GamesCoverDialogFragment extends DialogFragment {
|
||||
forceDialogImmersive();
|
||||
if (rv != null) applyCoverflowTransforms(rv);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
// Dialog is being destroyed - resume the game
|
||||
android.util.Log.d("GamesCoverDialog", "onDestroy called - resuming game");
|
||||
try {
|
||||
if (getActivity() instanceof MainActivity) {
|
||||
((MainActivity) getActivity()).onDialogClosed();
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(@NonNull android.content.res.Configuration newConfig) {
|
||||
@@ -117,13 +129,10 @@ public class GamesCoverDialogFragment extends DialogFragment {
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
|
||||
// Pause game when games cover dialog is shown
|
||||
// Notify MainActivity that this dialog is opening
|
||||
try {
|
||||
if (getActivity() instanceof MainActivity) {
|
||||
MainActivity mainActivity = (MainActivity) getActivity();
|
||||
if (mainActivity.hasSelectedGame() && mainActivity.isEmulationThreadRunning() && !NativeApp.isPaused()) {
|
||||
NativeApp.pause();
|
||||
}
|
||||
((MainActivity) getActivity()).onDialogOpened();
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
|
||||
@@ -132,18 +141,6 @@ public class GamesCoverDialogFragment extends DialogFragment {
|
||||
// Ensure immersive as soon as window exists
|
||||
try { applyImmersiveToWindow(d.getWindow()); } catch (Throwable ignored) {}
|
||||
|
||||
// Resume game when dialog is dismissed
|
||||
d.setOnDismissListener(dialog -> {
|
||||
try {
|
||||
if (getActivity() instanceof MainActivity) {
|
||||
MainActivity mainActivity = (MainActivity) getActivity();
|
||||
if (mainActivity.hasSelectedGame() && mainActivity.isEmulationThreadRunning() && NativeApp.isPaused()) {
|
||||
NativeApp.resume();
|
||||
}
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
});
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
@@ -375,6 +372,36 @@ public class GamesCoverDialogFragment extends DialogFragment {
|
||||
} catch (Throwable ignored) {}
|
||||
});
|
||||
|
||||
// Setup drawer listener for pause/resume tracking
|
||||
try {
|
||||
androidx.drawerlayout.widget.DrawerLayout dialogDrawer = root.findViewById(R.id.dlg_drawer_layout);
|
||||
if (dialogDrawer != null) {
|
||||
dialogDrawer.addDrawerListener(new androidx.drawerlayout.widget.DrawerLayout.DrawerListener() {
|
||||
@Override
|
||||
public void onDrawerSlide(@NonNull View drawerView, float slideOffset) {}
|
||||
|
||||
@Override
|
||||
public void onDrawerOpened(@NonNull View drawerView) {
|
||||
// Notify MainActivity that drawer opened (will pause game)
|
||||
if (getActivity() instanceof MainActivity) {
|
||||
((MainActivity) getActivity()).onDrawerOpened();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDrawerClosed(@NonNull View drawerView) {
|
||||
// Notify MainActivity that drawer closed (will resume if no other dialogs open)
|
||||
if (getActivity() instanceof MainActivity) {
|
||||
((MainActivity) getActivity()).onDrawerClosed();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDrawerStateChanged(int newState) {}
|
||||
});
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
|
||||
// Wire in-dialog navigation header actions to mirror main drawer
|
||||
try {
|
||||
com.google.android.material.navigation.NavigationView nav = root.findViewById(R.id.dialog_nav_view);
|
||||
|
||||
@@ -88,8 +88,12 @@ public class MainActivity extends AppCompatActivity implements GamesCoverDialogF
|
||||
private AlertDialog mBiosPromptDialog = null;
|
||||
private boolean mControllerHintShowing = false;
|
||||
private boolean mPreviousControllerState = false;
|
||||
|
||||
// Global dialog tracking for pause/resume
|
||||
private int mOpenDialogCount = 0;
|
||||
private boolean mDrawerOpen = false;
|
||||
|
||||
private boolean isThread() {
|
||||
public boolean isThread() {
|
||||
if (mEmulationThread != null) {
|
||||
Thread.State _thread_state = mEmulationThread.getState();
|
||||
return _thread_state == Thread.State.BLOCKED
|
||||
@@ -553,9 +557,9 @@ public class MainActivity extends AppCompatActivity implements GamesCoverDialogF
|
||||
if (btn_settings != null) {
|
||||
btn_settings.setOnClickListener(v -> {
|
||||
try {
|
||||
// Pause game when opening settings drawer
|
||||
// Pause game when opening settings drawer using the same logic as the pause/play button
|
||||
if (hasSelectedGame() && isThread() && !NativeApp.isPaused()) {
|
||||
NativeApp.pause();
|
||||
togglePauseState(); // This will pause the game and update button state
|
||||
}
|
||||
// Get drawer layout first
|
||||
DrawerLayout drawer = findViewById(R.id.drawer_layout);
|
||||
@@ -1336,19 +1340,59 @@ public class MainActivity extends AppCompatActivity implements GamesCoverDialogF
|
||||
|
||||
private void importSingleBiosUri(Uri uri, File biosDir) {
|
||||
if (uri == null) return;
|
||||
String displayName = getDisplayNameFromUri(this, uri);
|
||||
if (TextUtils.isEmpty(displayName)) displayName = "bios.bin";
|
||||
File outFile = new File(biosDir, displayName);
|
||||
copyUriToFile(this, uri, outFile);
|
||||
// Also mirror to SAF data root if set
|
||||
android.net.Uri dataRoot = SafManager.getDataRootUri(this);
|
||||
if (dataRoot != null) {
|
||||
androidx.documentfile.provider.DocumentFile target = SafManager.createChild(this, new String[]{"bios"}, displayName, "application/octet-stream");
|
||||
if (target != null) {
|
||||
try (java.io.InputStream in = getContentResolver().openInputStream(uri)) {
|
||||
SafManager.copyFromStream(this, in, target.getUri());
|
||||
} catch (Exception ignored) {}
|
||||
|
||||
try {
|
||||
String displayName = getDisplayNameFromUri(this, uri);
|
||||
if (TextUtils.isEmpty(displayName)) displayName = "bios.bin";
|
||||
File outFile = new File(biosDir, displayName);
|
||||
|
||||
// Check if file already exists and is valid
|
||||
if (outFile.exists() && outFile.length() > 0) {
|
||||
android.util.Log.d("MainActivity", "BIOS file already exists: " + displayName);
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure bios directory exists
|
||||
if (!biosDir.exists()) {
|
||||
if (!biosDir.mkdirs()) {
|
||||
android.util.Log.e("MainActivity", "Failed to create BIOS directory: " + biosDir.getAbsolutePath());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy the file with improved error handling
|
||||
boolean success = copyUriToFile(this, uri, outFile);
|
||||
if (!success) {
|
||||
android.util.Log.e("MainActivity", "Failed to copy BIOS file: " + displayName);
|
||||
return;
|
||||
}
|
||||
|
||||
// Also mirror to SAF data root if set (with error handling)
|
||||
android.net.Uri dataRoot = SafManager.getDataRootUri(this);
|
||||
if (dataRoot != null) {
|
||||
try {
|
||||
androidx.documentfile.provider.DocumentFile target = SafManager.createChild(this, new String[]{"bios"}, displayName, "application/octet-stream");
|
||||
if (target != null) {
|
||||
try (java.io.InputStream in = getContentResolver().openInputStream(uri)) {
|
||||
if (in != null) {
|
||||
SafManager.copyFromStream(this, in, target.getUri());
|
||||
android.util.Log.d("MainActivity", "BIOS file mirrored to SAF: " + displayName);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
android.util.Log.w("MainActivity", "Failed to mirror BIOS to SAF: " + e.getMessage());
|
||||
// Don't fail the import if SAF mirroring fails
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
android.util.Log.w("MainActivity", "Failed to create SAF target for BIOS: " + e.getMessage());
|
||||
// Don't fail the import if SAF mirroring fails
|
||||
}
|
||||
}
|
||||
|
||||
android.util.Log.d("MainActivity", "BIOS import completed successfully: " + displayName);
|
||||
|
||||
} catch (Exception e) {
|
||||
android.util.Log.e("MainActivity", "Error during BIOS import: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1397,14 +1441,49 @@ public class MainActivity extends AppCompatActivity implements GamesCoverDialogF
|
||||
is = context.getContentResolver().openInputStream(uri);
|
||||
if (is == null) return false;
|
||||
os = new FileOutputStream(destFile);
|
||||
byte[] buffer = new byte[8192];
|
||||
|
||||
// Use smaller buffer size for lower-end devices to reduce memory pressure
|
||||
byte[] buffer = new byte[4096]; // Reduced from 8192
|
||||
int read;
|
||||
long totalBytes = 0;
|
||||
long maxFileSize = 16 * 1024 * 1024; // 16MB max BIOS file size limit
|
||||
|
||||
while ((read = is.read(buffer)) != -1) {
|
||||
totalBytes += read;
|
||||
|
||||
// Check file size limit to prevent memory issues on lower-end devices
|
||||
if (totalBytes > maxFileSize) {
|
||||
android.util.Log.w("MainActivity", "BIOS file too large: " + totalBytes + " bytes");
|
||||
return false;
|
||||
}
|
||||
|
||||
os.write(buffer, 0, read);
|
||||
|
||||
// Force periodic flush to reduce memory pressure
|
||||
if (totalBytes % (64 * 1024) == 0) { // Flush every 64KB
|
||||
os.flush();
|
||||
}
|
||||
}
|
||||
os.flush();
|
||||
|
||||
// Verify the file was copied successfully
|
||||
if (destFile.length() == 0) {
|
||||
android.util.Log.e("MainActivity", "BIOS file copy failed: empty file");
|
||||
return false;
|
||||
}
|
||||
|
||||
android.util.Log.d("MainActivity", "BIOS file copied successfully: " + destFile.getName() +
|
||||
" (" + destFile.length() + " bytes)");
|
||||
return true;
|
||||
} catch (OutOfMemoryError e) {
|
||||
android.util.Log.e("MainActivity", "Out of memory during BIOS file copy: " + e.getMessage());
|
||||
// Try to clean up partial file
|
||||
try { if (destFile.exists()) destFile.delete(); } catch (Exception ignored) {}
|
||||
return false;
|
||||
} catch (Exception e) {
|
||||
android.util.Log.e("MainActivity", "Error copying BIOS file: " + e.getMessage());
|
||||
// Try to clean up partial file
|
||||
try { if (destFile.exists()) destFile.delete(); } catch (Exception ignored) {}
|
||||
return false;
|
||||
} finally {
|
||||
try { if (is != null) is.close(); } catch (Exception ignored) {}
|
||||
@@ -1802,8 +1881,15 @@ public class MainActivity extends AppCompatActivity implements GamesCoverDialogF
|
||||
android.util.Log.d("Controller", "Controller " + controllerId + " combo: " + comboName);
|
||||
|
||||
if ("select_start".equals(comboName)) {
|
||||
// Show quick actions dialog
|
||||
// Show quick actions dialog with proper dialog tracking
|
||||
runOnUiThread(() -> {
|
||||
// Pause the game immediately when controller combo is detected using the same logic as the pause/play button
|
||||
if (hasSelectedGame() && isThread() && !NativeApp.isPaused()) {
|
||||
try {
|
||||
togglePauseState(); // This will pause the game and update button state
|
||||
} catch (Throwable ignored) {}
|
||||
}
|
||||
|
||||
QuickActionsDialogFragment dialog = new QuickActionsDialogFragment();
|
||||
dialog.show(getSupportFragmentManager(), "quick_actions");
|
||||
});
|
||||
@@ -2215,16 +2301,27 @@ public class MainActivity extends AppCompatActivity implements GamesCoverDialogF
|
||||
public void togglePauseState() {
|
||||
try {
|
||||
boolean isPaused = NativeApp.isPaused();
|
||||
boolean hasGame = hasSelectedGame() && isThread();
|
||||
android.util.Log.d("TogglePause", "togglePauseState called. isPaused: " + isPaused + ", hasGame: " + hasGame);
|
||||
|
||||
if (isPaused) {
|
||||
android.util.Log.d("TogglePause", "Resuming game");
|
||||
NativeApp.resume();
|
||||
} else {
|
||||
android.util.Log.d("TogglePause", "Pausing game");
|
||||
NativeApp.pause();
|
||||
}
|
||||
updatePausePlayButton();
|
||||
} catch (Throwable ignored) {}
|
||||
|
||||
// Log the state after the change
|
||||
boolean newIsPaused = NativeApp.isPaused();
|
||||
android.util.Log.d("TogglePause", "After toggle. New isPaused: " + newIsPaused);
|
||||
} catch (Throwable e) {
|
||||
android.util.Log.e("TogglePause", "Error in togglePauseState: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void updatePausePlayButton() {
|
||||
public void updatePausePlayButton() {
|
||||
MaterialButton btn_pause_play = findViewById(R.id.btn_pause_play);
|
||||
if (btn_pause_play != null) {
|
||||
try {
|
||||
@@ -2297,6 +2394,65 @@ public class MainActivity extends AppCompatActivity implements GamesCoverDialogF
|
||||
.show();
|
||||
}
|
||||
|
||||
// Global dialog tracking methods
|
||||
public void onDialogOpened() {
|
||||
mOpenDialogCount++;
|
||||
android.util.Log.d("DialogTracking", "Dialog opened. Count: " + mOpenDialogCount);
|
||||
if (mOpenDialogCount == 1 && !mDrawerOpen) {
|
||||
// First dialog opened and no drawer is open, pause the game
|
||||
try {
|
||||
if (hasSelectedGame() && isThread() && !NativeApp.isPaused()) {
|
||||
android.util.Log.d("DialogTracking", "Pausing game on dialog open");
|
||||
NativeApp.pause();
|
||||
updatePausePlayButton();
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
}
|
||||
}
|
||||
|
||||
public void onDialogClosed() {
|
||||
mOpenDialogCount = Math.max(0, mOpenDialogCount - 1);
|
||||
android.util.Log.d("DialogTracking", "Dialog closed. Count: " + mOpenDialogCount + ", Drawer open: " + mDrawerOpen);
|
||||
if (mOpenDialogCount == 0 && !mDrawerOpen) {
|
||||
// All dialogs closed and no drawers open, resume the game
|
||||
try {
|
||||
if (hasSelectedGame() && isThread() && NativeApp.isPaused()) {
|
||||
android.util.Log.d("DialogTracking", "Resuming game on dialog close");
|
||||
NativeApp.resume();
|
||||
updatePausePlayButton();
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
}
|
||||
}
|
||||
|
||||
public void onDrawerOpened() {
|
||||
mDrawerOpen = true;
|
||||
android.util.Log.d("DrawerTracking", "Drawer opened");
|
||||
// Drawer opened, pause the game
|
||||
try {
|
||||
if (hasSelectedGame() && isThread() && !NativeApp.isPaused()) {
|
||||
android.util.Log.d("DrawerTracking", "Pausing game on drawer open");
|
||||
NativeApp.pause();
|
||||
updatePausePlayButton();
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
}
|
||||
|
||||
public void onDrawerClosed() {
|
||||
mDrawerOpen = false;
|
||||
android.util.Log.d("DrawerTracking", "Drawer closed. Dialog count: " + mOpenDialogCount);
|
||||
if (mOpenDialogCount == 0 && !mDrawerOpen) {
|
||||
// All dialogs closed and no drawers open, resume the game
|
||||
try {
|
||||
if (hasSelectedGame() && isThread() && NativeApp.isPaused()) {
|
||||
android.util.Log.d("DrawerTracking", "Resuming game on drawer close");
|
||||
NativeApp.resume();
|
||||
updatePausePlayButton();
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
}
|
||||
}
|
||||
|
||||
private void setupDrawerListeners() {
|
||||
try {
|
||||
DrawerLayout drawer = findViewById(R.id.drawer_layout);
|
||||
@@ -2307,22 +2463,12 @@ public class MainActivity extends AppCompatActivity implements GamesCoverDialogF
|
||||
|
||||
@Override
|
||||
public void onDrawerOpened(@NonNull View drawerView) {
|
||||
// Pause game when any drawer is opened
|
||||
try {
|
||||
if (hasSelectedGame() && isThread() && !NativeApp.isPaused()) {
|
||||
NativeApp.pause();
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
MainActivity.this.onDrawerOpened();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDrawerClosed(@NonNull View drawerView) {
|
||||
// Resume game when all drawers are closed
|
||||
try {
|
||||
if (hasSelectedGame() && isThread() && NativeApp.isPaused()) {
|
||||
NativeApp.resume();
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
MainActivity.this.onDrawerClosed();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -21,13 +21,10 @@ public class QuickActionsDialogFragment extends DialogFragment {
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
|
||||
// Pause game when quick actions dialog is shown
|
||||
// Notify MainActivity that this dialog is opening
|
||||
try {
|
||||
if (getActivity() instanceof MainActivity) {
|
||||
MainActivity mainActivity = (MainActivity) getActivity();
|
||||
if (mainActivity.hasSelectedGame() && mainActivity.isEmulationThreadRunning() && !NativeApp.isPaused()) {
|
||||
NativeApp.pause();
|
||||
}
|
||||
((MainActivity) getActivity()).onDialogOpened();
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
|
||||
@@ -228,7 +225,8 @@ public class QuickActionsDialogFragment extends DialogFragment {
|
||||
if (btnGames != null) {
|
||||
btnGames.setOnClickListener(v -> {
|
||||
if (requireActivity() instanceof MainActivity) {
|
||||
((MainActivity) requireActivity()).openGamesDialog();
|
||||
MainActivity mainActivity = (MainActivity) requireActivity();
|
||||
mainActivity.openGamesDialog();
|
||||
}
|
||||
dismissAllowingStateLoss();
|
||||
});
|
||||
@@ -259,7 +257,8 @@ public class QuickActionsDialogFragment extends DialogFragment {
|
||||
root.postDelayed(() -> {
|
||||
try {
|
||||
if (!activity.isFinishing()) {
|
||||
((MainActivity) activity).openGamesDialog();
|
||||
MainActivity mainActivity = (MainActivity) activity;
|
||||
mainActivity.openGamesDialog();
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
}, 300);
|
||||
@@ -272,26 +271,24 @@ public class QuickActionsDialogFragment extends DialogFragment {
|
||||
// Cancel button
|
||||
if (btnCancel != null) btnCancel.setOnClickListener(v -> dismissAllowingStateLoss());
|
||||
|
||||
Dialog dialog = new MaterialAlertDialogBuilder(requireContext(),
|
||||
return new MaterialAlertDialogBuilder(requireContext(),
|
||||
com.google.android.material.R.style.ThemeOverlay_Material3_MaterialAlertDialog)
|
||||
.setView(view)
|
||||
.create();
|
||||
|
||||
// Resume game when dialog is dismissed
|
||||
dialog.setOnDismissListener(d -> {
|
||||
try {
|
||||
if (getActivity() instanceof MainActivity) {
|
||||
MainActivity mainActivity = (MainActivity) getActivity();
|
||||
if (mainActivity.hasSelectedGame() && mainActivity.isEmulationThreadRunning() && NativeApp.isPaused()) {
|
||||
NativeApp.resume();
|
||||
}
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
});
|
||||
|
||||
return dialog;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
// Dialog is being destroyed - resume the game
|
||||
android.util.Log.d("QuickActionsDialog", "onDestroy called - resuming game");
|
||||
try {
|
||||
if (getActivity() instanceof MainActivity) {
|
||||
((MainActivity) getActivity()).onDialogClosed();
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
}
|
||||
|
||||
private void quitApp() {
|
||||
// Stop emulator first
|
||||
NativeApp.shutdown();
|
||||
|
||||
@@ -148,13 +148,10 @@ public class SavesDialogFragment extends DialogFragment {
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
|
||||
// Pause game when saves dialog is shown
|
||||
// Notify MainActivity that this dialog is opening
|
||||
try {
|
||||
if (getActivity() instanceof MainActivity) {
|
||||
MainActivity mainActivity = (MainActivity) getActivity();
|
||||
if (mainActivity.hasSelectedGame() && mainActivity.isEmulationThreadRunning() && !NativeApp.isPaused()) {
|
||||
NativeApp.pause();
|
||||
}
|
||||
((MainActivity) getActivity()).onDialogOpened();
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
|
||||
@@ -192,7 +189,6 @@ public class SavesDialogFragment extends DialogFragment {
|
||||
// Success - refresh the dialog or close it
|
||||
dismiss();
|
||||
}
|
||||
NativeApp.resume();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -201,7 +197,6 @@ public class SavesDialogFragment extends DialogFragment {
|
||||
// Success
|
||||
dismiss();
|
||||
}
|
||||
NativeApp.resume();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -213,20 +208,18 @@ public class SavesDialogFragment extends DialogFragment {
|
||||
.setView(view)
|
||||
.setNegativeButton("Cancel", (d, w) -> d.dismiss());
|
||||
|
||||
Dialog dialog = builder.create();
|
||||
|
||||
// Resume game when dialog is dismissed
|
||||
dialog.setOnDismissListener(d -> {
|
||||
try {
|
||||
if (getActivity() instanceof MainActivity) {
|
||||
MainActivity mainActivity = (MainActivity) getActivity();
|
||||
if (mainActivity.hasSelectedGame() && mainActivity.isEmulationThreadRunning() && NativeApp.isPaused()) {
|
||||
NativeApp.resume();
|
||||
}
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
});
|
||||
|
||||
return dialog;
|
||||
return builder.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
// Dialog is being destroyed - resume the game
|
||||
android.util.Log.d("SavesDialog", "onDestroy called - resuming game");
|
||||
try {
|
||||
if (getActivity() instanceof MainActivity) {
|
||||
((MainActivity) getActivity()).onDialogClosed();
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,15 +72,10 @@ public class SettingsDialogFragment extends DialogFragment {
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
|
||||
// Pause game when settings dialog is shown
|
||||
try {
|
||||
if (getActivity() instanceof MainActivity) {
|
||||
MainActivity mainActivity = (MainActivity) getActivity();
|
||||
if (mainActivity.hasSelectedGame() && mainActivity.isEmulationThreadRunning() && !NativeApp.isPaused()) {
|
||||
NativeApp.pause();
|
||||
}
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
// Notify main activity that dialog is opening
|
||||
if (getActivity() instanceof MainActivity) {
|
||||
((MainActivity) getActivity()).onDialogOpened();
|
||||
}
|
||||
|
||||
Context ctx = requireContext();
|
||||
View view = getLayoutInflater().inflate(R.layout.dialog_settings, null, false);
|
||||
@@ -294,14 +289,11 @@ public class SettingsDialogFragment extends DialogFragment {
|
||||
|
||||
// Resume game when dialog is dismissed
|
||||
dialog.setOnDismissListener(d -> {
|
||||
try {
|
||||
if (getActivity() instanceof MainActivity) {
|
||||
MainActivity mainActivity = (MainActivity) getActivity();
|
||||
if (mainActivity.hasSelectedGame() && mainActivity.isEmulationThreadRunning() && NativeApp.isPaused()) {
|
||||
NativeApp.resume();
|
||||
}
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
android.util.Log.d("SettingsDialog", "Settings dialog dismissed");
|
||||
// Use the global dialog tracking system
|
||||
if (getActivity() instanceof MainActivity) {
|
||||
((MainActivity) getActivity()).onDialogClosed();
|
||||
}
|
||||
});
|
||||
|
||||
return dialog;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.izzy2lost.psx2;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.Gravity;
|
||||
@@ -28,12 +29,66 @@ public class SetupWizardDialogFragment extends DialogFragment {
|
||||
private TextView hintView;
|
||||
private Runnable mPeriodicCheck;
|
||||
private boolean mHasAutoAdvanced = false;
|
||||
|
||||
// Method to detect if this is a lower-end device
|
||||
private boolean isLowerEndDevice() {
|
||||
try {
|
||||
// Check available memory
|
||||
Runtime runtime = Runtime.getRuntime();
|
||||
long maxMemory = runtime.maxMemory();
|
||||
long totalMemory = runtime.totalMemory();
|
||||
long freeMemory = runtime.freeMemory();
|
||||
long availableMemory = maxMemory - totalMemory + freeMemory;
|
||||
|
||||
// Consider device lower-end if it has less than 512MB available memory
|
||||
boolean lowMemory = availableMemory < 512 * 1024 * 1024; // 512MB
|
||||
|
||||
// Check Android version - older versions might be on older hardware
|
||||
boolean oldAndroid = Build.VERSION.SDK_INT < Build.VERSION_CODES.P; // Android 9+
|
||||
|
||||
// Check number of CPU cores
|
||||
int cpuCores = runtime.availableProcessors();
|
||||
boolean fewCores = cpuCores <= 2; // 2 or fewer cores
|
||||
|
||||
android.util.Log.d("SetupWizard", "Device specs - Available Memory: " +
|
||||
(availableMemory / 1024 / 1024) + "MB, " +
|
||||
"CPU Cores: " + cpuCores +
|
||||
", Android API: " + Build.VERSION.SDK_INT);
|
||||
|
||||
return lowMemory || (oldAndroid && fewCores);
|
||||
} catch (Exception e) {
|
||||
android.util.Log.w("SetupWizard", "Error detecting device capabilities: " + e.getMessage());
|
||||
return false; // Assume not lower-end if we can't detect
|
||||
}
|
||||
}
|
||||
|
||||
// Method to get appropriate timeout based on device capabilities
|
||||
private long getTimeoutForDevice(long baseTimeout, long lowerEndTimeout) {
|
||||
return isLowerEndDevice() ? lowerEndTimeout : baseTimeout;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
|
||||
// Notify MainActivity that this dialog is opening
|
||||
try {
|
||||
if (getActivity() instanceof MainActivity) {
|
||||
((MainActivity) getActivity()).onDialogOpened();
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
|
||||
Dialog d = new Dialog(requireContext(), R.style.PSX2_FullScreenDialog);
|
||||
d.setContentView(buildContent());
|
||||
|
||||
// Resume game when dialog is dismissed
|
||||
d.setOnDismissListener(dialog -> {
|
||||
android.util.Log.d("SetupWizardDialog", "Setup wizard dialog dismissed");
|
||||
// Use the global dialog tracking system
|
||||
if (getActivity() instanceof MainActivity) {
|
||||
((MainActivity) getActivity()).onDialogClosed();
|
||||
}
|
||||
});
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
@@ -141,13 +196,20 @@ public class SetupWizardDialogFragment extends DialogFragment {
|
||||
try { a = (MainActivity) requireActivity(); a.setSetupWizardActive(false); } catch (Throwable ignored) {}
|
||||
dismissAllowingStateLoss();
|
||||
if (a != null) {
|
||||
// Open the games dialog just like the old GAMES button
|
||||
// Use adaptive delay based on device capabilities
|
||||
long manualDelay = getTimeoutForDevice(2000, 4000); // 2s normal, 4s lower-end
|
||||
android.util.Log.d("SetupWizard", "Using manual delay: " + manualDelay + "ms");
|
||||
|
||||
final MainActivity act = a;
|
||||
View decor = act.getWindow() != null ? act.getWindow().getDecorView() : null;
|
||||
if (decor != null) {
|
||||
decor.postDelayed(act::openGamesDialog, 150);
|
||||
decor.postDelayed(() -> {
|
||||
act.openGamesDialog();
|
||||
}, manualDelay);
|
||||
} else {
|
||||
act.runOnUiThread(act::openGamesDialog);
|
||||
act.runOnUiThread(() -> {
|
||||
act.openGamesDialog();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -310,7 +372,14 @@ public class SetupWizardDialogFragment extends DialogFragment {
|
||||
mHasAutoAdvanced = true;
|
||||
stopPeriodicCheck();
|
||||
|
||||
// All steps complete, auto-advance with delay to let BIOS processing finish
|
||||
// Use adaptive timeouts based on device capabilities
|
||||
long autoAdvanceDelay = getTimeoutForDevice(2000, 4000); // 2s normal, 4s lower-end
|
||||
long gamesDialogDelay = getTimeoutForDevice(3000, 6000); // 3s normal, 6s lower-end
|
||||
|
||||
android.util.Log.d("SetupWizard", "Using timeouts - Auto-advance: " + autoAdvanceDelay +
|
||||
"ms, Games dialog: " + gamesDialogDelay + "ms");
|
||||
|
||||
// All steps complete, auto-advance with adaptive delay
|
||||
View decor = getDialog() != null && getDialog().getWindow() != null ? getDialog().getWindow().getDecorView() : null;
|
||||
if (decor != null) {
|
||||
decor.postDelayed(() -> {
|
||||
@@ -320,13 +389,15 @@ public class SetupWizardDialogFragment extends DialogFragment {
|
||||
MainActivity a = (MainActivity) requireActivity();
|
||||
a.setSetupWizardActive(false);
|
||||
dismissAllowingStateLoss();
|
||||
// Add extra delay before opening games dialog to avoid overload
|
||||
// Add adaptive delay before opening games dialog
|
||||
View mainDecor = a.getWindow() != null ? a.getWindow().getDecorView() : null;
|
||||
if (mainDecor != null) {
|
||||
mainDecor.postDelayed(a::openGamesDialog, 800);
|
||||
mainDecor.postDelayed(() -> {
|
||||
a.openGamesDialog();
|
||||
}, gamesDialogDelay);
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
}, 500);
|
||||
}, autoAdvanceDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user