From f446e11bd700c001e14bb4e4e8834c6e5f865208 Mon Sep 17 00:00:00 2001 From: artdeell Date: Wed, 15 Jan 2025 17:55:03 +0300 Subject: [PATCH] Fix[storage]: better ensure mounted storage Avoids several crashes caused by Pojav running on devices without always-mounted external storage. --- .../net/kdt/pojavlaunch/BaseActivity.java | 5 +-- .../pojavlaunch/ImportControlActivity.java | 13 +++++- .../net/kdt/pojavlaunch/MainActivity.java | 3 ++ .../net/kdt/pojavlaunch/PojavApplication.java | 5 +-- .../kdt/pojavlaunch/TestStorageActivity.java | 3 +- .../main/java/net/kdt/pojavlaunch/Tools.java | 40 +++++++++++++++---- .../prefs/LauncherPreferences.java | 4 +- .../kdt/pojavlaunch/utils/LocaleUtils.java | 6 ++- 8 files changed, 58 insertions(+), 21 deletions(-) diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/BaseActivity.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/BaseActivity.java index 9fe3c09708..a0e92e2bf2 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/BaseActivity.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/BaseActivity.java @@ -37,10 +37,7 @@ public void startActivity(Intent i) { @Override protected void onResume() { super.onResume(); - if(!Tools.checkStorageRoot(this)) { - startActivity(new Intent(this, MissingStorageActivity.class)); - finish(); - } + Tools.checkStorageInteractive(this); } @Override diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/ImportControlActivity.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/ImportControlActivity.java index a3739dbd50..3c013ce5d6 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/ImportControlActivity.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/ImportControlActivity.java @@ -39,7 +39,12 @@ public class ImportControlActivity extends Activity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - Tools.initContextConstants(getApplicationContext()); + if(Tools.checkStorageInteractive(this)) { + Tools.initStorageConstants(getApplicationContext()); + }else { + // Return early, no initialization needed. + return; + } setContentView(R.layout.activity_import_control); mEditText = findViewById(R.id.editText_import_control_file_name); @@ -61,6 +66,12 @@ protected void onNewIntent(Intent intent) { @Override protected void onPostResume() { super.onPostResume(); + if(!Tools.checkStorageInteractive(this)) { + // Don't try to read the file as when this check fails, external storage paths + // are no longer valid (likely unmounted). + // checkStorageInteractive() will finish this activity for us. + return; + } if(!mHasIntentChanged) return; mIsFileVerified = false; getUriData(); diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/MainActivity.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/MainActivity.java index bfabd8f368..42052ce201 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/MainActivity.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/MainActivity.java @@ -334,6 +334,9 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == 1 && resultCode == Activity.RESULT_OK) { // Reload PREF_DEFAULTCTRL_PATH + // If the storage root got unmounted/unreadable we won't be able to load the file anyway, + // and MissingStorageActivity will be started. + if(!Tools.checkStorageRoot(this)) return; LauncherPreferences.loadPreferences(getApplicationContext()); try { mControlLayout.loadLayout(LauncherPreferences.PREF_DEFAULTCTRL_PATH); diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/PojavApplication.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/PojavApplication.java index e8f280cfe7..5e07928e14 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/PojavApplication.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/PojavApplication.java @@ -57,10 +57,7 @@ public void onCreate() { try { super.onCreate(); - - Tools.DIR_DATA = getDir("files", MODE_PRIVATE).getParent(); - Tools.DIR_CACHE = getCacheDir(); - Tools.DIR_ACCOUNT_NEW = Tools.DIR_DATA + "/accounts"; + Tools.initEarlyConstants(this); Tools.DEVICE_ARCHITECTURE = Architecture.getDeviceArchitecture(); //Force x86 lib directory for Asus x86 based zenfones if(Architecture.isx86Device() && Architecture.is32BitsDevice()){ diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/TestStorageActivity.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/TestStorageActivity.java index 1d413d54ac..54726f42f3 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/TestStorageActivity.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/TestStorageActivity.java @@ -60,7 +60,8 @@ private void exit() { startActivity(new Intent(this, MissingStorageActivity.class)); return; } - //Only run them once we get a definitive green light to use storage + //Initialize constants after we confirm that we have storage. + Tools.initStorageConstants(getApplicationContext()); AsyncAssetManager.unpackComponents(this); AsyncAssetManager.unpackSingleFiles(this); diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Tools.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Tools.java index 18aef85a5f..a599bb3ef7 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Tools.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Tools.java @@ -32,7 +32,6 @@ import android.os.Environment; import android.os.Handler; import android.os.Looper; -import android.os.Process; import android.provider.DocumentsContract; import android.provider.OpenableColumns; import android.util.ArrayMap; @@ -164,14 +163,42 @@ public static boolean checkStorageRoot(Context context) { } /** - * Since some constant requires the use of the Context object - * You can call this function to initialize them. - * Any value (in)directly dependant on DIR_DATA should be set only here. + * Checks if the Pojav's storage root is accessible and read-writable. If it's not, starts + * the MissingStorageActivity and finishes the supplied activity. + * @param context the Activity that checks for storage availability + * @return whether the storage is available or not. */ - public static void initContextConstants(Context ctx){ + public static boolean checkStorageInteractive(Activity context) { + if(!Tools.checkStorageRoot(context)) { + context.startActivity(new Intent(context, MissingStorageActivity.class)); + context.finish(); + return false; + } + return true; + } + + /** + * Initialize context constants most necessary for launcher's early startup phase + * that are not dependent on user storage. + * All values that depend on DIR_DATA and are not dependent on DIR_GAME_HOME must + * be initialized here. + * @param ctx the context for initialization. + */ + public static void initEarlyConstants(Context ctx) { DIR_CACHE = ctx.getCacheDir(); DIR_DATA = ctx.getFilesDir().getParent(); - MULTIRT_HOME = DIR_DATA+"/runtimes"; + MULTIRT_HOME = DIR_DATA + "/runtimes"; + DIR_ACCOUNT_NEW = DIR_DATA + "/accounts"; + NATIVE_LIB_DIR = ctx.getApplicationInfo().nativeLibraryDir; + } + + /** + * Initialize context constants that depend on user storage. + * Any value (in)directly dependent on DIR_GAME_HOME should be set only here. + * You ABSOLUTELY MUST check for storage presence using checkStorageRoot() before calling this. + */ + public static void initStorageConstants(Context ctx){ + initEarlyConstants(ctx); DIR_GAME_HOME = getPojavStorageRoot(ctx).getAbsolutePath(); DIR_GAME_NEW = DIR_GAME_HOME + "/.minecraft"; DIR_HOME_VERSION = DIR_GAME_NEW + "/versions"; @@ -181,7 +208,6 @@ public static void initContextConstants(Context ctx){ OBSOLETE_RESOURCES_PATH = DIR_GAME_NEW + "/resources"; CTRLMAP_PATH = DIR_GAME_HOME + "/controlmap"; CTRLDEF_FILE = DIR_GAME_HOME + "/controlmap/default.json"; - NATIVE_LIB_DIR = ctx.getApplicationInfo().nativeLibraryDir; } /** diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/LauncherPreferences.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/LauncherPreferences.java index b5480596f7..e61f6c8df5 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/LauncherPreferences.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/LauncherPreferences.java @@ -70,8 +70,8 @@ public class LauncherPreferences { public static void loadPreferences(Context ctx) { - //Required for the data folder. - Tools.initContextConstants(ctx); + //Required for CTRLDEF_FILE and MultiRT + Tools.initStorageConstants(ctx); boolean isDevicePowerful = isDevicePowerful(ctx); PREF_RENDERER = DEFAULT_PREF.getString("renderer", "opengles2"); diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/LocaleUtils.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/LocaleUtils.java index fb7b53d44d..523a0bb5ae 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/LocaleUtils.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/LocaleUtils.java @@ -11,7 +11,6 @@ import androidx.preference.*; import java.util.*; -import net.kdt.pojavlaunch.prefs.*; public class LocaleUtils extends ContextWrapper { @@ -22,7 +21,10 @@ public LocaleUtils(Context base) { public static ContextWrapper setLocale(Context context) { if (DEFAULT_PREF == null) { DEFAULT_PREF = PreferenceManager.getDefaultSharedPreferences(context); - LauncherPreferences.loadPreferences(context); + // Too early to initialize all prefs here, as this is called by PojavApplication + // before storage checks are done and before the storage paths are initialized. + // So only initialize PREF_FORCE_ENGLISH for the check below. + PREF_FORCE_ENGLISH = DEFAULT_PREF.getBoolean("force_english", false); } if(PREF_FORCE_ENGLISH){