mirror of
https://github.com/izzy2lost/PSX2.git
synced 2026-07-05 15:18:36 -07:00
Fixed crash when pressing menu/settings button and fixed crash on first boot.
This commit is contained in:
+2
-2
@@ -11,8 +11,8 @@ android {
|
||||
applicationId "com.izzy2lost.psx2"
|
||||
minSdk 26
|
||||
targetSdk 36
|
||||
versionCode 9
|
||||
versionName "1.0.7"
|
||||
versionCode 10
|
||||
versionName "1.0.8"
|
||||
// APK
|
||||
setProperty("archivesBaseName","PSX2_${versionCode}_${new Date().format('yyyyMMddHHmm')}")
|
||||
|
||||
|
||||
@@ -189,17 +189,25 @@ public class GamesCoverDialogFragment extends DialogFragment {
|
||||
coverUrls = new String[uris.length];
|
||||
localPaths = new String[uris.length];
|
||||
SharedPreferences prefs = requireContext().getSharedPreferences("app_prefs", Context.MODE_PRIVATE);
|
||||
boolean isFirstBoot = !prefs.getBoolean("has_resolved_titles_once", false);
|
||||
|
||||
for (int i = 0; i < uris.length; i++) {
|
||||
String saved = prefs.getString("serial:" + uris[i], null);
|
||||
String serial = saved;
|
||||
// On first boot, skip native serial extraction to avoid crashes
|
||||
// The background thread will handle it later with proper delays
|
||||
if (serial == null || serial.isEmpty()) {
|
||||
try {
|
||||
String nativeSerial = NativeApp.getGameSerialSafe(uris[i]);
|
||||
if (nativeSerial != null && !nativeSerial.isEmpty()) {
|
||||
serial = normalizeSerial(nativeSerial);
|
||||
prefs.edit().putString("serial:" + uris[i], serial).apply();
|
||||
if (!isFirstBoot) {
|
||||
try {
|
||||
String nativeSerial = NativeApp.getGameSerialSafe(uris[i]);
|
||||
if (nativeSerial != null && !nativeSerial.isEmpty()) {
|
||||
serial = normalizeSerial(nativeSerial);
|
||||
prefs.edit().putString("serial:" + uris[i], serial).apply();
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
android.util.Log.w("GamesCoverDialog", "Error getting serial for " + uris[i] + ": " + e.getMessage());
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
}
|
||||
}
|
||||
if (serial == null || serial.isEmpty()) {
|
||||
serial = buildSerialFromUri(uris[i]);
|
||||
@@ -295,25 +303,66 @@ public class GamesCoverDialogFragment extends DialogFragment {
|
||||
|
||||
// Resolve proper game titles using local YAML index if available (GameIndex/Redump).
|
||||
// Falls back to native URI API, then filename if needed.
|
||||
// Capture context early to avoid requireContext() crashes if fragment detaches
|
||||
final Context ctx = requireContext().getApplicationContext();
|
||||
|
||||
new Thread(() -> {
|
||||
boolean changed = false;
|
||||
for (int i = 0; i < uris.length; i++) {
|
||||
try {
|
||||
String t = TitleResolver.resolveTitleForUri(requireContext(), uris[i], titles[i]);
|
||||
if (t != null && !t.isEmpty() && i < titles.length && !t.equals(titles[i])) {
|
||||
titles[i] = t;
|
||||
if (origTitles != null && i < origTitles.length) origTitles[i] = t;
|
||||
changed = true;
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
}
|
||||
if (changed && isAdded()) requireActivity().runOnUiThread(() -> {
|
||||
if (sortMode != SORT_ALPHA || (query != null && !query.isEmpty())) {
|
||||
applyFilterAndSort();
|
||||
} else {
|
||||
adapter.notifyDataSetChanged();
|
||||
try {
|
||||
// On first boot, add a delay to let native library fully initialize
|
||||
if (isFirstBoot) {
|
||||
Thread.sleep(2000); // 2 second delay on first boot
|
||||
}
|
||||
});
|
||||
|
||||
// Check if fragment is still attached before proceeding
|
||||
if (!isAdded()) return;
|
||||
|
||||
boolean changed = false;
|
||||
for (int i = 0; i < uris.length; i++) {
|
||||
// Check if fragment is still attached on each iteration
|
||||
if (!isAdded()) break;
|
||||
|
||||
try {
|
||||
String t = TitleResolver.resolveTitleForUri(ctx, uris[i], titles[i]);
|
||||
if (t != null && !t.isEmpty() && i < titles.length && !t.equals(titles[i])) {
|
||||
titles[i] = t;
|
||||
if (origTitles != null && i < origTitles.length) origTitles[i] = t;
|
||||
changed = true;
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
android.util.Log.w("GamesCoverDialog", "Error resolving title for " + uris[i] + ": " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Mark that we've resolved titles at least once
|
||||
if (isFirstBoot && isAdded()) {
|
||||
try {
|
||||
ctx.getSharedPreferences("app_prefs", Context.MODE_PRIVATE)
|
||||
.edit().putBoolean("has_resolved_titles_once", true).apply();
|
||||
} catch (Throwable ignored) {}
|
||||
}
|
||||
|
||||
// Only update UI if fragment is still attached
|
||||
if (changed && isAdded()) {
|
||||
try {
|
||||
requireActivity().runOnUiThread(() -> {
|
||||
try {
|
||||
if (!isAdded()) return;
|
||||
if (sortMode != SORT_ALPHA || (query != null && !query.isEmpty())) {
|
||||
applyFilterAndSort();
|
||||
} else {
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
android.util.Log.w("GamesCoverDialog", "Error updating UI after title resolution: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
} catch (Throwable e) {
|
||||
android.util.Log.w("GamesCoverDialog", "Error posting to UI thread: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
android.util.Log.e("GamesCoverDialog", "Error in title resolution thread: " + e.getMessage());
|
||||
}
|
||||
}).start();
|
||||
|
||||
// Dynamically size items based on RecyclerView size and orientation
|
||||
|
||||
@@ -491,26 +491,30 @@ public class MainActivity extends AppCompatActivity implements GamesCoverDialogF
|
||||
}
|
||||
updateUiForControllerPresence();
|
||||
|
||||
// After initial BIOS auto-boot, gently open the Games dialog
|
||||
// Only when setup is complete and this is app launch (not rotation)
|
||||
if (getSharedPreferences("app_prefs", MODE_PRIVATE).getBoolean("first_run_done", false)) {
|
||||
try {
|
||||
final View decor = (getWindow() != null) ? getWindow().getDecorView() : null;
|
||||
if (decor != null) {
|
||||
decor.postDelayed(() -> {
|
||||
if (!isFinishing() && !mSetupWizardActive) {
|
||||
openGamesDialog();
|
||||
}
|
||||
}, 1600); // small delay to let BIOS boot briefly
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
}
|
||||
|
||||
// Show first-run setup wizard if needed
|
||||
if (!getSharedPreferences("app_prefs", MODE_PRIVATE).getBoolean("first_run_done", false)) {
|
||||
SharedPreferences prefs = getSharedPreferences("app_prefs", MODE_PRIVATE);
|
||||
boolean firstRunDone = prefs.getBoolean("first_run_done", false);
|
||||
|
||||
if (!firstRunDone) {
|
||||
SetupWizardDialogFragment f = SetupWizardDialogFragment.newInstance();
|
||||
f.setCancelable(false);
|
||||
f.show(getSupportFragmentManager(), "setup_wizard");
|
||||
} else {
|
||||
// Only auto-open games dialog if this is NOT the first boot after setup
|
||||
// (Setup wizard handles opening the games dialog on first completion)
|
||||
boolean hasOpenedGamesAfterSetup = prefs.getBoolean("has_opened_games_after_setup", false);
|
||||
if (hasOpenedGamesAfterSetup) {
|
||||
try {
|
||||
final View decor = (getWindow() != null) ? getWindow().getDecorView() : null;
|
||||
if (decor != null) {
|
||||
decor.postDelayed(() -> {
|
||||
if (!isFinishing() && !mSetupWizardActive) {
|
||||
openGamesDialog();
|
||||
}
|
||||
}, 1600); // small delay to let BIOS boot briefly
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
}
|
||||
}
|
||||
|
||||
// Setup right drawer quick actions
|
||||
@@ -541,6 +545,12 @@ public class MainActivity extends AppCompatActivity implements GamesCoverDialogF
|
||||
// Public method to open the games covers dialog via controller quick actions
|
||||
public void openGamesDialog() {
|
||||
SharedPreferences prefs = getSharedPreferences("app_prefs", MODE_PRIVATE);
|
||||
|
||||
// Mark that we've opened games dialog after setup (prevents double-open on first boot)
|
||||
if (!prefs.getBoolean("has_opened_games_after_setup", false)) {
|
||||
prefs.edit().putBoolean("has_opened_games_after_setup", true).apply();
|
||||
}
|
||||
|
||||
String folderUri = prefs.getString("games_folder_uri", null);
|
||||
if (TextUtils.isEmpty(folderUri)) {
|
||||
pickGamesFolder();
|
||||
@@ -568,33 +578,13 @@ public class MainActivity extends AppCompatActivity implements GamesCoverDialogF
|
||||
if (btn_settings != null) {
|
||||
btn_settings.setOnClickListener(v -> {
|
||||
try {
|
||||
// Pause game when opening settings drawer using the same logic as the pause/play button
|
||||
if (hasSelectedGame() && isThread() && !NativeApp.isPaused()) {
|
||||
togglePauseState(); // This will pause the game and update button state
|
||||
}
|
||||
// Get drawer layout first
|
||||
// Just open the drawer - let the drawer listener handle pausing
|
||||
DrawerLayout drawer = findViewById(R.id.drawer_layout);
|
||||
if (drawer != null) {
|
||||
// Refresh drawer settings before opening - add error handling
|
||||
try {
|
||||
refreshDrawerSettings();
|
||||
} catch (Exception e) {
|
||||
android.util.Log.e("MainActivity", "Error refreshing drawer settings: " + e.getMessage());
|
||||
// Continue anyway even if refresh fails
|
||||
}
|
||||
drawer.openDrawer(androidx.core.view.GravityCompat.START);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
android.util.Log.e("MainActivity", "Error opening settings drawer: " + t.getMessage());
|
||||
// Try to open drawer directly as fallback
|
||||
try {
|
||||
DrawerLayout drawer = findViewById(R.id.drawer_layout);
|
||||
if (drawer != null) {
|
||||
drawer.openDrawer(androidx.core.view.GravityCompat.START);
|
||||
}
|
||||
} catch (Throwable fallback) {
|
||||
android.util.Log.e("MainActivity", "Fallback drawer open also failed: " + fallback.getMessage());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -2449,6 +2439,14 @@ public class MainActivity extends AppCompatActivity implements GamesCoverDialogF
|
||||
public void onDrawerOpened() {
|
||||
mDrawerOpen = true;
|
||||
android.util.Log.d("DrawerTracking", "Drawer opened");
|
||||
|
||||
// Refresh drawer settings to reflect current state
|
||||
try {
|
||||
refreshDrawerSettings();
|
||||
} catch (Exception e) {
|
||||
android.util.Log.e("DrawerTracking", "Error refreshing drawer settings: " + e.getMessage());
|
||||
}
|
||||
|
||||
// Drawer opened, pause the game
|
||||
try {
|
||||
if (hasSelectedGame() && isThread() && !NativeApp.isPaused()) {
|
||||
|
||||
Reference in New Issue
Block a user