ANDROID: Early fix for Android 9+ crash and config/save path issues

This commit is contained in:
Thanasis Antoniou 2020-01-31 17:22:28 +02:00
parent de6bde08a7
commit 3c9741a4c3
3 changed files with 184 additions and 33 deletions

View File

@ -313,8 +313,12 @@ void OSystem_Android::initBackend() {
ConfMan.setBool("FM_high_quality", false);
ConfMan.setBool("FM_medium_quality", true);
if (!ConfMan.hasKey("browser_lastpath") || (ConfMan.hasKey("browser_lastpath") && (ConfMan.get("browser_lastpath") == "/storage")))
ConfMan.set("browser_lastpath", getenv("SDCARD"));
if (!ConfMan.hasKey("browser_lastpath")) {
// TODO remove the debug message eventually
LOGD("Setting Browser Lastpath to root");
ConfMan.set("browser_lastpath", "/");
}
if (ConfMan.hasKey("touchpad_mouse_mode"))
_touchpad_mode = ConfMan.getBool("touchpad_mouse_mode");
@ -335,6 +339,9 @@ void OSystem_Android::initBackend() {
// screen. Passing the savepath in this way makes it stick
// (via ConfMan.registerDefault)
_savefileManager = new DefaultSaveFileManager(ConfMan.get("savepath"));
// TODO remove the debug message eventually
LOGD("Setting DefaultSaveFileManager path to: %s", ConfMan.get("savepath").c_str());
_mutexManager = new PthreadMutexManager();
_timerManager = new DefaultTimerManager();

View File

@ -27,6 +27,11 @@ import android.widget.ImageView;
import android.widget.Toast;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.util.List;
public class ScummVMActivity extends Activity {
@ -35,6 +40,10 @@ public class ScummVMActivity extends Activity {
private static boolean _hoverAvailable;
private ClipboardManager _clipboard;
private File _configScummvmFile;
private File _actualScummVMDataDir;
private File _defaultScummVMSavesDir;
/**
* Id to identify an external storage read request.
@ -206,39 +215,31 @@ public class ScummVMActivity extends Activity {
requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, MY_PERMISSIONS_REQUEST_READ_EXT_STORAGE);
}
// This is a common enough error that we should warn about it
// explicitly.
if (!Environment.getExternalStorageDirectory().canRead()) {
new AlertDialog.Builder(this)
.setTitle(R.string.no_sdcard_title)
.setIcon(android.R.drawable.ic_dialog_alert)
.setMessage(R.string.no_sdcard)
.setNegativeButton(R.string.quit,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int which) {
finish();
}
})
.show();
return;
}
// REMOVED: Dialogue prompt with only option to Quit the app if !Environment.getExternalStorageDirectory().canRead()
SurfaceView main_surface = (SurfaceView)findViewById(R.id.main_surface);
main_surface.requestFocus();
getFilesDir().mkdirs();
// REMOVED: Since getFilesDir() is guaranteed to exist, getFilesDir().mkdirs() might be related to crashes in Android version 9+ (Pie or above, API 28+)!
// Store savegames on external storage if we can, which means they're
// world-readable and don't get deleted on uninstall.
String savePath = Environment.getExternalStorageDirectory() + "/ScummVM/Saves/";
File saveDir = new File(savePath);
saveDir.mkdirs();
if (!saveDir.isDirectory()) {
// If it doesn't work, resort to the internal app path.
savePath = getDir("saves", Context.MODE_PRIVATE).getPath();
// REMOVED: Setting savePath to Environment.getExternalStorageDirectory() + "/ScummVM/Saves/"
// so that it will be in persistent external storage and not deleted on uninstall
// This has the issue for external storage being unavailable on some devices
// Is this persistence really important considering that Android does not really support it anymore
// Exceptions are:
// - shareable media files (images, audio, video)
// - files stored with Storage Access Framework (SAF) which requires user interaction with FilePicker)
// Original fallback was getDir()
// so app's internal space, which would be deleted on uninstall) set as WORLD_READABLE which is no longer supported in newer versions of Android API
// In newer APIs we can set that path as Context.MODE_PRIVATE which is the default - but will make the files inaccessible to other apps
//
// seekAndInitScummvmConfiguration() returns false if something went wrong when
// initializing configuration (or finding and using an old ini file) for ScummVM
if (!seekAndInitScummvmConfiguration()) {
Log.e(ScummVM.LOG_TAG, "Error while trying to find and/or initialize scummvm configuration file!");
// TODO error prompt (popup to user)
}
_clipboard = (ClipboardManager)getSystemService(CLIPBOARD_SERVICE);
@ -247,10 +248,10 @@ public class ScummVMActivity extends Activity {
_scummvm = new MyScummVM(main_surface.getHolder());
_scummvm.setArgs(new String[] {
"ScummVM",
"--config=" + getFileStreamPath("scummvmrc").getPath(),
"--path=" + Environment.getExternalStorageDirectory().getPath(),
"--savepath=" + savePath
"ScummVM",
"--config=" + _configScummvmFile.getPath(),
"--path=" + _actualScummVMDataDir.getPath(),
"--savepath=" + _defaultScummVMSavesDir.getPath()
});
Log.d(ScummVM.LOG_TAG, "Hover available: " + _hoverAvailable);
@ -419,4 +420,139 @@ public class ScummVMActivity extends Activity {
sendBroadcast(intent);
}
}
// Auxilliary function to overwrite a file (used for overwriting the scummvm.ini file with an existing other one)
private static void copyFileUsingStream(File source, File dest) throws IOException {
InputStream is = null;
OutputStream os = null;
try {
is = new FileInputStream(source);
os = new FileOutputStream(dest);
byte[] buffer = new byte[1024];
int length;
while ((length = is.read(buffer)) > 0) {
os.write(buffer, 0, length);
}
} finally {
is.close();
os.close();
}
}
private boolean seekAndInitScummvmConfiguration() {
boolean retVal = false;
_actualScummVMDataDir = getExternalFilesDir(null);
if (_actualScummVMDataDir == null || !_actualScummVMDataDir.canRead()) {
new AlertDialog.Builder(this)
.setTitle(R.string.no_external_files_dir_access_title)
.setIcon(android.R.drawable.ic_dialog_alert)
.setMessage(R.string.no_external_files_dir_access)
.setNegativeButton(R.string.quit,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
finish();
}
})
.show();
retVal = false;
return retVal;
}
Log.d(ScummVM.LOG_TAG, "Base ScummVM data folder is: " + _actualScummVMDataDir.getPath());
File[] extfiles = _actualScummVMDataDir.listFiles();
Log.d(ScummVM.LOG_TAG, "Size: "+ extfiles.length);
for (int i = 0; i < extfiles.length; i++) {
Log.d(ScummVM.LOG_TAG, "FileName:" + extfiles[i].getName());
}
File externalScummVMConfigDir = new File(_actualScummVMDataDir, ".config/scummvm");
if (externalScummVMConfigDir.mkdirs()) {
Log.d(ScummVM.LOG_TAG, "Created ScummVM Config path: " + externalScummVMConfigDir.getPath());
} else if (externalScummVMConfigDir.isDirectory()) {
Log.d(ScummVM.LOG_TAG, "ScummVM Config path already exists: " + externalScummVMConfigDir.getPath());
} else {
Log.e(ScummVM.LOG_TAG, "Could not create folder for ScummVM Config path: " + externalScummVMConfigDir.getPath());
new AlertDialog.Builder(this)
.setTitle(R.string.no_config_file_title)
.setIcon(android.R.drawable.ic_dialog_alert)
.setMessage(R.string.no_config_file)
.setNegativeButton(R.string.quit,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
finish();
}
})
.show();
}
_configScummvmFile = new File(_actualScummVMDataDir, "scummvm.ini");
try {
if (!_configScummvmFile.createNewFile()) {
Log.d(ScummVM.LOG_TAG, "ScummVM Config file already exists!");
Log.d(ScummVM.LOG_TAG, "Existing Scummvm INI: " + _configScummvmFile.getPath());
} else {
Log.d(ScummVM.LOG_TAG, "ScummVM Config file was created!");
Log.d(ScummVM.LOG_TAG, "New Scummvm INI: " + _configScummvmFile.getPath());
// if there was an old scummvmrc file (old config file), then copy that over the empty new scummvm.ini
File oldScummVMconfig = getFileStreamPath("scummvmrc");
if (!oldScummVMconfig.exists()) {
Log.d(ScummVM.LOG_TAG, "Old config Scummvm file was not found!");
} else {
Log.d(ScummVM.LOG_TAG, "Old config Scummvm file was found!");
copyFileUsingStream(oldScummVMconfig, _configScummvmFile);
Log.d(ScummVM.LOG_TAG, "Old config Scummvm file overwrites the new (empty) scummvm.ini");
}
}
} catch(Exception e) {
e.printStackTrace();
new AlertDialog.Builder(this)
.setTitle(R.string.no_config_file_title)
.setIcon(android.R.drawable.ic_dialog_alert)
.setMessage(R.string.no_config_file)
.setNegativeButton(R.string.quit,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
finish();
}
})
.show();
}
// Set global savepath
// TODO what if the old save-game path is no longer accessible (due to missing SD card, or newer Android API/OS version more strict restrictions)
// TODO changing the save path to this, app specific location we should consider:
// - moving or copying old save files from old location (if accessible)
// - allowing the user to set a user-defined location via Storage Access Framework, that would override this location!
// TODO This would override/ overwrite(?) actual old save-game path stored in the config file!
//
// By default shoose to store savegames on app's external storage, which means they're accessible by other apps BUT will get deleted on uninstall!
//
_defaultScummVMSavesDir = new File(_actualScummVMDataDir, "saves");
// TODO what about old save paths from plain android port? do we favor them?
if (_defaultScummVMSavesDir.mkdirs()) {
Log.d(ScummVM.LOG_TAG, "Created ScummVM saves path: " + _defaultScummVMSavesDir.getPath());
} else if (_defaultScummVMSavesDir.isDirectory()) {
Log.d(ScummVM.LOG_TAG, "ScummVM saves path already exists: " + _defaultScummVMSavesDir.getPath());
} else {
Log.e(ScummVM.LOG_TAG, "Could not create folder for ScummVM saves path: " + _defaultScummVMSavesDir.getPath());
new AlertDialog.Builder(this)
.setTitle(R.string.no_config_file_title)
.setIcon(android.R.drawable.ic_dialog_alert)
.setMessage(R.string.no_config_file)
.setNegativeButton(R.string.quit,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
finish();
}
})
.show();
}
retVal = true;
return retVal;
}
}

View File

@ -14,6 +14,14 @@
<string name="no_sdcard">Unable to read your SD card. This usually
means you still have it mounted on your PC. Unmount, reinsert,
whatever and then try again.</string>
<string name="no_external_files_dir_access_title">External storage error</string>
<string name="no_external_files_dir_access">Unable to access external storage
to retrieve ScummVM config info! Please grant storage access permissions to
the ScummVM app, in order to function properly!</string>
<string name="no_config_file_title">Config File Error</string>
<string name="no_config_file">Unable to read ScummVM config file or create a new one!</string>
<string name="no_save_path_title">Save Path Error</string>
<string name="no_save_path_configured">Unable to create or access default save path!</string>
<string name="no_plugins_title">No plugins found</string>
<string name="no_plugins_found">ScummVM requires at least one <i>game
engine</i> to be useful. Engines are available as separate plugin