Fix[storage]: better ensure mounted storage

Avoids several crashes caused by Pojav running on devices without always-mounted external storage.
This commit is contained in:
artdeell 2025-01-15 17:55:03 +03:00 committed by Maksim Belov
parent b8e5f08719
commit 9c83fc677b
8 changed files with 58 additions and 21 deletions

View File

@ -37,10 +37,7 @@ public abstract class BaseActivity extends AppCompatActivity {
@Override @Override
protected void onResume() { protected void onResume() {
super.onResume(); super.onResume();
if(!Tools.checkStorageRoot(this)) { Tools.checkStorageInteractive(this);
startActivity(new Intent(this, MissingStorageActivity.class));
finish();
}
} }
@Override @Override

View File

@ -39,7 +39,12 @@ public class ImportControlActivity extends Activity {
@Override @Override
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(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); setContentView(R.layout.activity_import_control);
mEditText = findViewById(R.id.editText_import_control_file_name); mEditText = findViewById(R.id.editText_import_control_file_name);
@ -61,6 +66,12 @@ public class ImportControlActivity extends Activity {
@Override @Override
protected void onPostResume() { protected void onPostResume() {
super.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; if(!mHasIntentChanged) return;
mIsFileVerified = false; mIsFileVerified = false;
getUriData(); getUriData();

View File

@ -334,6 +334,9 @@ public class MainActivity extends BaseActivity implements ControlButtonMenuListe
if (requestCode == 1 && resultCode == Activity.RESULT_OK) { if (requestCode == 1 && resultCode == Activity.RESULT_OK) {
// Reload PREF_DEFAULTCTRL_PATH // 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()); LauncherPreferences.loadPreferences(getApplicationContext());
try { try {
mControlLayout.loadLayout(LauncherPreferences.PREF_DEFAULTCTRL_PATH); mControlLayout.loadLayout(LauncherPreferences.PREF_DEFAULTCTRL_PATH);

View File

@ -57,10 +57,7 @@ public class PojavApplication extends Application {
try { try {
super.onCreate(); super.onCreate();
Tools.initEarlyConstants(this);
Tools.DIR_DATA = getDir("files", MODE_PRIVATE).getParent();
Tools.DIR_CACHE = getCacheDir();
Tools.DIR_ACCOUNT_NEW = Tools.DIR_DATA + "/accounts";
Tools.DEVICE_ARCHITECTURE = Architecture.getDeviceArchitecture(); Tools.DEVICE_ARCHITECTURE = Architecture.getDeviceArchitecture();
//Force x86 lib directory for Asus x86 based zenfones //Force x86 lib directory for Asus x86 based zenfones
if(Architecture.isx86Device() && Architecture.is32BitsDevice()){ if(Architecture.isx86Device() && Architecture.is32BitsDevice()){

View File

@ -60,7 +60,8 @@ public class TestStorageActivity extends Activity {
startActivity(new Intent(this, MissingStorageActivity.class)); startActivity(new Intent(this, MissingStorageActivity.class));
return; 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.unpackComponents(this);
AsyncAssetManager.unpackSingleFiles(this); AsyncAssetManager.unpackSingleFiles(this);

View File

@ -32,7 +32,6 @@ import android.os.Bundle;
import android.os.Environment; import android.os.Environment;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.os.Process;
import android.provider.DocumentsContract; import android.provider.DocumentsContract;
import android.provider.OpenableColumns; import android.provider.OpenableColumns;
import android.util.ArrayMap; import android.util.ArrayMap;
@ -164,14 +163,42 @@ public final class Tools {
} }
/** /**
* Since some constant requires the use of the Context object * Checks if the Pojav's storage root is accessible and read-writable. If it's not, starts
* You can call this function to initialize them. * the MissingStorageActivity and finishes the supplied activity.
* Any value (in)directly dependant on DIR_DATA should be set only here. * @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_CACHE = ctx.getCacheDir();
DIR_DATA = ctx.getFilesDir().getParent(); 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_HOME = getPojavStorageRoot(ctx).getAbsolutePath();
DIR_GAME_NEW = DIR_GAME_HOME + "/.minecraft"; DIR_GAME_NEW = DIR_GAME_HOME + "/.minecraft";
DIR_HOME_VERSION = DIR_GAME_NEW + "/versions"; DIR_HOME_VERSION = DIR_GAME_NEW + "/versions";
@ -181,7 +208,6 @@ public final class Tools {
OBSOLETE_RESOURCES_PATH = DIR_GAME_NEW + "/resources"; OBSOLETE_RESOURCES_PATH = DIR_GAME_NEW + "/resources";
CTRLMAP_PATH = DIR_GAME_HOME + "/controlmap"; CTRLMAP_PATH = DIR_GAME_HOME + "/controlmap";
CTRLDEF_FILE = DIR_GAME_HOME + "/controlmap/default.json"; CTRLDEF_FILE = DIR_GAME_HOME + "/controlmap/default.json";
NATIVE_LIB_DIR = ctx.getApplicationInfo().nativeLibraryDir;
} }
/** /**

View File

@ -70,8 +70,8 @@ public class LauncherPreferences {
public static void loadPreferences(Context ctx) { public static void loadPreferences(Context ctx) {
//Required for the data folder. //Required for CTRLDEF_FILE and MultiRT
Tools.initContextConstants(ctx); Tools.initStorageConstants(ctx);
boolean isDevicePowerful = isDevicePowerful(ctx); boolean isDevicePowerful = isDevicePowerful(ctx);
PREF_RENDERER = DEFAULT_PREF.getString("renderer", "opengles2"); PREF_RENDERER = DEFAULT_PREF.getString("renderer", "opengles2");

View File

@ -11,7 +11,6 @@ import android.os.LocaleList;
import androidx.preference.*; import androidx.preference.*;
import java.util.*; import java.util.*;
import net.kdt.pojavlaunch.prefs.*;
public class LocaleUtils extends ContextWrapper { public class LocaleUtils extends ContextWrapper {
@ -22,7 +21,10 @@ public class LocaleUtils extends ContextWrapper {
public static ContextWrapper setLocale(Context context) { public static ContextWrapper setLocale(Context context) {
if (DEFAULT_PREF == null) { if (DEFAULT_PREF == null) {
DEFAULT_PREF = PreferenceManager.getDefaultSharedPreferences(context); 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){ if(PREF_FORCE_ENGLISH){