From 20bbf4a409c3b75edc04b42483355ad3a0f5c310 Mon Sep 17 00:00:00 2001 From: izzy2lost Date: Wed, 10 Dec 2025 17:31:17 -0500 Subject: [PATCH] Fix app breaking retroacheivements crashes --- .../psx2/AchievementsDialogFragment.java | 30 ++++-- .../java/com/izzy2lost/psx2/MainActivity.java | 93 ++++++++++++++++++- .../java/com/izzy2lost/psx2/NativeApp.java | 10 +- 3 files changed, 120 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/com/izzy2lost/psx2/AchievementsDialogFragment.java b/app/src/main/java/com/izzy2lost/psx2/AchievementsDialogFragment.java index 5d42d72..69f77c8 100644 --- a/app/src/main/java/com/izzy2lost/psx2/AchievementsDialogFragment.java +++ b/app/src/main/java/com/izzy2lost/psx2/AchievementsDialogFragment.java @@ -233,9 +233,20 @@ public class AchievementsDialogFragment extends DialogFragment { boolean enabled = mEnabledCheckbox.isChecked(); - // Check if we're logged in by checking if we have a saved username - String savedUsername = getPrefs().getString(PREF_USERNAME, ""); + // Check if we're logged in by looking at saved token/active client + SharedPreferences prefs = getPrefs(); + String savedUsername = prefs.getString(PREF_USERNAME, ""); + String savedToken = prefs.getString("token", ""); boolean hasCredentials = !savedUsername.isEmpty(); + boolean hasToken = savedToken != null && !savedToken.isEmpty(); + boolean isActive = false; + try { + isActive = NativeApp.achievementsIsActive(); + } catch (Throwable t) { + android.util.Log.w("Achievements", "Could not query active state: " + t.getMessage()); + } + // Treat as logged in only when we actually have a saved token AND the client reports active + boolean loggedIn = hasToken && isActive; if (mHardcoreModeCheckbox != null) mHardcoreModeCheckbox.setEnabled(enabled); if (mNotificationsCheckbox != null) mNotificationsCheckbox.setEnabled(enabled); @@ -243,12 +254,17 @@ public class AchievementsDialogFragment extends DialogFragment { if (mUsernameEdit != null) mUsernameEdit.setEnabled(enabled); if (mPasswordEdit != null) mPasswordEdit.setEnabled(enabled); - // Login button: enabled when achievements are enabled - mLoginButton.setEnabled(enabled); + // Login button: enabled when achievements are enabled and no active session/token + mLoginButton.setEnabled(enabled && !loggedIn); mLoginButton.setVisibility(View.VISIBLE); + if (loggedIn) { + mLoginButton.setText("Logged In"); + } else { + mLoginButton.setText("Login"); + } - // Logout button: enabled if we have saved credentials - mLogoutButton.setEnabled(enabled && hasCredentials); + // Logout button: enabled if we have anything to clear + mLogoutButton.setEnabled(enabled && (hasCredentials || hasToken || isActive)); mLogoutButton.setVisibility(View.VISIBLE); // Create account button: always enabled @@ -312,7 +328,7 @@ public class AchievementsDialogFragment extends DialogFragment { android.util.Log.d("Achievements", "After login - isActive: " + isActive); if (isActive) { - Toast.makeText(requireContext(), "Login successful! Token saved for auto-login.", Toast.LENGTH_SHORT).show(); + Toast.makeText(requireContext(), "Signed into RetroAchievements.", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(requireContext(), "Login may have failed - check logs", Toast.LENGTH_LONG).show(); } diff --git a/app/src/main/java/com/izzy2lost/psx2/MainActivity.java b/app/src/main/java/com/izzy2lost/psx2/MainActivity.java index 338ab17..8fffab1 100644 --- a/app/src/main/java/com/izzy2lost/psx2/MainActivity.java +++ b/app/src/main/java/com/izzy2lost/psx2/MainActivity.java @@ -77,6 +77,7 @@ public class MainActivity extends AppCompatActivity implements GamesCoverDialogF public static final int ORIENTATION_PORTRAIT = 2; private String m_szGamefile = ""; + private boolean mRaLoginPromptScheduled = false; private HIDDeviceManager mHIDDeviceManager; private ControllerInputHandler mControllerInputHandler; @@ -493,9 +494,6 @@ public class MainActivity extends AppCompatActivity implements GamesCoverDialogF // Initialize RetroAchievements RetroAchievementsManager.initialize(this); - // Load and auto-login with saved credentials if available - NativeApp.loadAndLoginAchievements(); - // Initialize controller input handler mControllerInputHandler = new ControllerInputHandler(this); @@ -560,6 +558,9 @@ public class MainActivity extends AppCompatActivity implements GamesCoverDialogF // Setup picture-in-picture support addPictureInPictureSupport(); + + // Offer RetroAchievements re-login after a short delay (if previously enabled) + scheduleRetroAchievementsReLoginPrompt(); } @Override @@ -716,6 +717,17 @@ public class MainActivity extends AppCompatActivity implements GamesCoverDialogF } catch (Throwable ignored) {} }); } + View btnAchievements = header.findViewById(R.id.drawer_btn_achievements); + if (btnAchievements != null) { + btnAchievements.setOnClickListener(v -> { + try { + AchievementsDialogFragment.newInstance() + .show(getSupportFragmentManager(), "achievements_dialog"); + } catch (Throwable t) { + android.util.Log.e("MainActivity", "Failed to open achievements dialog: " + t.getMessage()); + } + }); + } View btnAbout = header.findViewById(R.id.drawer_btn_about); if (btnAbout != null) { btnAbout.setOnClickListener(v -> { @@ -1556,6 +1568,12 @@ public class MainActivity extends AppCompatActivity implements GamesCoverDialogF @Override protected void onDestroy() { + try { + NativeApp.achievementsLogout(); + NativeApp.achievementsShutdown(); + } catch (Throwable t) { + android.util.Log.w("MainActivity", "Error shutting down achievements on exit: " + t.getMessage()); + } NativeApp.shutdown(); super.onDestroy(); //// @@ -2827,7 +2845,7 @@ public class MainActivity extends AppCompatActivity implements GamesCoverDialogF // Register broadcast receiver for PiP actions registerPiPBroadcastReceiver(); } - + private void registerPiPBroadcastReceiver() { // Register receiver for PiP remote actions if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { @@ -2879,4 +2897,71 @@ public class MainActivity extends AppCompatActivity implements GamesCoverDialogF } } + private void scheduleRetroAchievementsReLoginPrompt() { + if (mRaLoginPromptScheduled) return; + mRaLoginPromptScheduled = true; + + SharedPreferences prefs = getSharedPreferences("RetroAchievements", MODE_PRIVATE); + boolean enabled = prefs.getBoolean("enabled", false); + String username = prefs.getString("username", ""); + String token = prefs.getString("token", ""); + if (!enabled || TextUtils.isEmpty(username) || TextUtils.isEmpty(token)) { + return; + } + + View decor = (getWindow() != null) ? getWindow().getDecorView() : null; + if (decor == null) return; + + decor.postDelayed(() -> { + if (isFinishing() || isDestroyed()) return; + showRetroAchievementsReLoginDialog(username, token); + }, 5000); + } + + private void showRetroAchievementsReLoginDialog(String username, String token) { + try { + new MaterialAlertDialogBuilder(this, + com.google.android.material.R.style.ThemeOverlay_Material3_MaterialAlertDialog) + .setTitle("RetroAchievements") + .setMessage("Sign back in as " + username + "?") + .setNegativeButton("Not now", (d, w) -> clearRetroAchievementsSession()) + .setPositiveButton("Sign in", (d, w) -> startRetroAchievementsLogin(username, token)) + .show(); + } catch (Throwable t) { + android.util.Log.w("MainActivity", "Failed to show RetroAchievements prompt: " + t.getMessage()); + } + } + + private void startRetroAchievementsLogin(String username, String token) { + new Thread(() -> { + try { + NativeApp.achievementsInitialize(); + Thread.sleep(500); // let init settle before logging in + NativeApp.achievementsLoginWithToken(username, token); + android.util.Log.i("MainActivity", "RetroAchievements token login started for " + username); + } catch (Throwable t) { + android.util.Log.e("MainActivity", "RetroAchievements token login failed: " + t.getMessage()); + } + }).start(); + } + + private void clearRetroAchievementsSession() { + try { + NativeApp.achievementsLogout(); + } catch (Throwable t) { + android.util.Log.w("MainActivity", "RA logout error: " + t.getMessage()); + } + try { + SharedPreferences prefs = getSharedPreferences("RetroAchievements", MODE_PRIVATE); + prefs.edit() + .remove("token") + .remove("login_timestamp") + .remove("saved_password") + .putBoolean("remember_me", false) + .apply(); + } catch (Throwable t) { + android.util.Log.w("MainActivity", "RA prefs clear error: " + t.getMessage()); + } + } + } diff --git a/app/src/main/java/com/izzy2lost/psx2/NativeApp.java b/app/src/main/java/com/izzy2lost/psx2/NativeApp.java index 9e48045..ff18bdc 100644 --- a/app/src/main/java/com/izzy2lost/psx2/NativeApp.java +++ b/app/src/main/java/com/izzy2lost/psx2/NativeApp.java @@ -212,6 +212,10 @@ public class NativeApp { android.util.Log.e("Achievements", "Cannot load credentials: context is null"); return; } + if (hasNoNativeBinary) { + android.util.Log.w("Achievements", "Skipping auto-login: native core not loaded"); + return; + } android.content.SharedPreferences prefs = context.getSharedPreferences("RetroAchievements", Context.MODE_PRIVATE); boolean enabled = prefs.getBoolean("enabled", false); @@ -238,8 +242,10 @@ public class NativeApp { Thread.sleep(500); // Give it time to initialize achievementsLoginWithToken(username, token); android.util.Log.i("Achievements", "Auto-login initiated"); - } catch (Exception e) { - android.util.Log.e("Achievements", "Auto-login failed: " + e.getMessage()); + } catch (Throwable e) { + android.util.Log.e("Achievements", "Auto-login failed", e); + // Avoid startup crash loops by disabling auto-login until the user re-enables it manually + prefs.edit().putBoolean("enabled", false).apply(); } }).start(); }