android: prepare transition to sdk 30+. cache last savestate path
Migrate home folder to externalFilesDir (/sdcard/Android/data/com.flycast.emulator/files) if running on android 10+. Use externalFilesDir for home by default. No more onboarding on first install. Use content uri instead of hacking out real path since the latter won't work on higher target sdk. Cache last readable savestate path in hostfs::getSavestatePath(). All content folders are scanned when not found, which is very slow with ASS
This commit is contained in:
parent
9b4667cb38
commit
aed4d21a4f
|
@ -128,10 +128,22 @@ std::string getSavestatePath(int index, bool writable)
|
|||
state_file = state_file + index_str + ".state";
|
||||
if (index == -1)
|
||||
state_file += ".net";
|
||||
if (writable)
|
||||
|
||||
static std::string lastFile;
|
||||
static std::string lastPath;
|
||||
|
||||
if (writable) {
|
||||
lastFile.clear();
|
||||
return get_writable_data_path(state_file);
|
||||
}
|
||||
else
|
||||
return get_readonly_data_path(state_file);
|
||||
{
|
||||
if (lastFile != state_file) {
|
||||
lastFile = state_file;
|
||||
lastPath = get_readonly_data_path(state_file);
|
||||
}
|
||||
return lastPath;
|
||||
}
|
||||
}
|
||||
|
||||
std::string getShaderCachePath(const std::string& filename)
|
||||
|
|
|
@ -71,11 +71,15 @@ public class AndroidStorage {
|
|||
else {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
|
||||
activity.getContentResolver().takePersistableUriPermission(uri, storageIntentPerms);
|
||||
/* Use the uri path now to avoid issues when targeting sdk 30+ in the future
|
||||
String realPath = getRealPath(uri);
|
||||
if (realPath != null)
|
||||
// when targeting sdk 30+ (android 11+) using the real path doesn't work (empty content) -> *must* use the uri
|
||||
int targetSdkVersion = activity.getApplication().getApplicationInfo().targetSdkVersion;
|
||||
if (realPath != null && targetSdkVersion <= Build.VERSION_CODES.Q)
|
||||
addStorageCallback(realPath);
|
||||
else
|
||||
addStorageCallback(uri.toString());
|
||||
*/
|
||||
addStorageCallback(uri.toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -160,7 +164,7 @@ public class AndroidStorage {
|
|||
intent = Intent.createChooser(intent, "Select a cheat file");
|
||||
}
|
||||
else {
|
||||
intent = Intent.createChooser(intent, "Select a directory");
|
||||
intent = Intent.createChooser(intent, "Select a content directory");
|
||||
}
|
||||
storageIntentPerms = Intent.FLAG_GRANT_READ_URI_PERMISSION | (writeAccess ? Intent.FLAG_GRANT_WRITE_URI_PERMISSION : 0);
|
||||
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION | storageIntentPerms);
|
||||
|
|
|
@ -3,6 +3,7 @@ package com.flycast.emulator;
|
|||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
|
@ -26,7 +27,6 @@ import androidx.annotation.NonNull;
|
|||
import androidx.annotation.Nullable;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
|
||||
import com.flycast.emulator.AndroidStorage;
|
||||
import com.flycast.emulator.config.Config;
|
||||
import com.flycast.emulator.emu.AudioBackend;
|
||||
import com.flycast.emulator.emu.HttpClient;
|
||||
|
@ -35,6 +35,9 @@ import com.flycast.emulator.periph.InputDeviceManager;
|
|||
import com.flycast.emulator.periph.SipEmulator;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
@ -91,8 +94,21 @@ public abstract class BaseGLActivity extends Activity implements ActivityCompat.
|
|||
OuyaController.init(this);
|
||||
new HttpClient().nativeInit();
|
||||
|
||||
String home_directory = prefs.getString(Config.pref_home, "");
|
||||
String result = JNIdc.initEnvironment((Emulator)getApplicationContext(), getFilesDir().getAbsolutePath(), home_directory,
|
||||
String homeDir = prefs.getString(Config.pref_home, "");
|
||||
// Check that home dir is valid, migrate if needed
|
||||
String newHome = checkHomeDirectory(homeDir);
|
||||
if (newHome != null) {
|
||||
if (!newHome.equals(homeDir))
|
||||
prefs.edit().putString(Config.pref_home, newHome).apply();
|
||||
finishCreation();
|
||||
}
|
||||
Log.i("flycast", "BaseGLActivity.onCreate done");
|
||||
}
|
||||
|
||||
protected void finishCreation()
|
||||
{
|
||||
String homeDir = prefs.getString(Config.pref_home, getDefaultHomeDir());
|
||||
String result = JNIdc.initEnvironment((Emulator)getApplicationContext(), getFilesDir().getAbsolutePath(), homeDir,
|
||||
Locale.getDefault().toString());
|
||||
if (result != null) {
|
||||
AlertDialog.Builder dlgAlert = new AlertDialog.Builder(this);
|
||||
|
@ -158,7 +174,7 @@ public abstract class BaseGLActivity extends Activity implements ActivityCompat.
|
|||
pendingIntentUrl = gameUri.toString();
|
||||
}
|
||||
}
|
||||
Log.i("flycast", "BaseGLActivity.onCreate done");
|
||||
Log.i("flycast", "BaseGLActivity.finishCreation done");
|
||||
}
|
||||
|
||||
private void setStorageDirectories()
|
||||
|
@ -395,6 +411,132 @@ public abstract class BaseGLActivity extends Activity implements ActivityCompat.
|
|||
|
||||
}
|
||||
|
||||
private String getDefaultHomeDir()
|
||||
{
|
||||
return getExternalFilesDir(null).getAbsolutePath();
|
||||
}
|
||||
|
||||
private String checkHomeDirectory(String homeDir)
|
||||
{
|
||||
if (homeDir.isEmpty())
|
||||
// home dir not set: use default
|
||||
return getDefaultHomeDir();
|
||||
if (homeDir.startsWith(getDefaultHomeDir()))
|
||||
// home dir is ok
|
||||
return homeDir;
|
||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P)
|
||||
// no need to migrate on Android 9 or earlier
|
||||
return homeDir;
|
||||
// Only ask to migrate once
|
||||
String migrationPref = "legacy-storage-migration-done";
|
||||
if (prefs.getBoolean(migrationPref, false))
|
||||
return homeDir;
|
||||
// Ask the user if he wants to migrate
|
||||
AlertDialog.Builder dlgAlert = new AlertDialog.Builder(this);
|
||||
dlgAlert.setMessage("The current Flycast home folder will be inaccessible in future versions.\n\n"
|
||||
+ "Do you want to move config and save files to a valid location?");
|
||||
dlgAlert.setTitle("Migrate Home");
|
||||
dlgAlert.setPositiveButton("Yes",
|
||||
(dialog, id) -> BaseGLActivity.this.migrateHome(homeDir));
|
||||
dlgAlert.setNegativeButton("No",
|
||||
(dialog, id) -> BaseGLActivity.this.finishCreation());
|
||||
dlgAlert.setIcon(android.R.drawable.ic_dialog_alert);
|
||||
dlgAlert.setCancelable(false);
|
||||
dlgAlert.create().show();
|
||||
// Don't ask again
|
||||
prefs.edit().putBoolean(migrationPref, true).apply();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean migrationThreadCancelled = false;
|
||||
private void migrateHome(String oldHome)
|
||||
{
|
||||
File source = new File(oldHome);
|
||||
File dest = new File(getDefaultHomeDir());
|
||||
ProgressDialog progress = ProgressDialog.show(this, "Migrating", "Moving files to their new home",
|
||||
true, true, dialogInterface -> migrationThreadCancelled = true);
|
||||
progress.show();
|
||||
|
||||
migrationThreadCancelled = false;
|
||||
Thread thread = new Thread(new Runnable() {
|
||||
private void moveFile(File file, File toDir)
|
||||
{
|
||||
//Log.d("flycast", "Moving " + file.getAbsolutePath() + " to " + toDir.getAbsolutePath());
|
||||
try {
|
||||
File dest = new File(toDir, file.getName());
|
||||
// file.renameTo(dest) doesn't seem to work
|
||||
FileInputStream in = new FileInputStream(file);
|
||||
FileOutputStream out = new FileOutputStream(dest);
|
||||
byte[] buf = new byte[8192];
|
||||
while (true) {
|
||||
int len = in.read(buf);
|
||||
if (len == -1)
|
||||
break;
|
||||
out.write(buf, 0, len);
|
||||
}
|
||||
out.close();
|
||||
in.close();
|
||||
file.delete();
|
||||
} catch (IOException e) {
|
||||
Log.e("flycast", "Error moving " + file.getAbsolutePath(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private void moveDir(File from, File to)
|
||||
{
|
||||
//Log.d("flycast", "Moving dir " + from.getAbsolutePath() + " to " + to.getAbsolutePath());
|
||||
if (!from.exists())
|
||||
return;
|
||||
File[] files = from.listFiles();
|
||||
if (files == null) {
|
||||
Log.e("flycast", "Can't list content of " + from.getAbsolutePath());
|
||||
return;
|
||||
}
|
||||
for (File file : files)
|
||||
{
|
||||
if (migrationThreadCancelled)
|
||||
break;
|
||||
if (file.isFile())
|
||||
moveFile(file, to);
|
||||
else if (file.isDirectory() && !file.getName().equals("boxart")) {
|
||||
File subDir = new File(to, file.getName());
|
||||
subDir.mkdir();
|
||||
moveDir(file, subDir);
|
||||
}
|
||||
}
|
||||
from.delete();
|
||||
}
|
||||
|
||||
private void migrate()
|
||||
{
|
||||
moveFile(new File(source, "emu.cfg"), dest);
|
||||
if (migrationThreadCancelled)
|
||||
return;
|
||||
File mappings = new File(dest, "mappings");
|
||||
mappings.mkdirs();
|
||||
moveDir(new File(source, "mappings"), mappings);
|
||||
if (migrationThreadCancelled)
|
||||
return;
|
||||
File data = new File(dest, "data");
|
||||
data.mkdirs();
|
||||
moveDir(new File(source, "data"), data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
migrate();
|
||||
runOnUiThread(() -> {
|
||||
prefs.edit().putString(Config.pref_home, getDefaultHomeDir()).apply();
|
||||
progress.dismiss();
|
||||
BaseGLActivity.this.finishCreation();
|
||||
});
|
||||
}
|
||||
});
|
||||
thread.start();
|
||||
}
|
||||
|
||||
// Called from native code
|
||||
public void onGameStateChange(boolean started) {
|
||||
runOnUiThread(new Runnable() {
|
||||
|
|
|
@ -35,28 +35,36 @@ public final class NativeGLActivity extends BaseGLActivity {
|
|||
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
Log.i("flycast", "NativeGLActivity.onCreate done");
|
||||
}
|
||||
|
||||
protected void finishCreation()
|
||||
{
|
||||
super.finishCreation();
|
||||
// Create the actual GL view
|
||||
mView = new NativeGLView(this);
|
||||
mLayout = new RelativeLayout(this);
|
||||
mLayout.addView(mView);
|
||||
|
||||
setContentView(mLayout);
|
||||
Log.i("flycast", "NativeGLActivity.onCreate done");
|
||||
Log.i("flycast", "NativeGLActivity.finishCreation done");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPause() {
|
||||
mView.pause();
|
||||
if (mView != null)
|
||||
mView.pause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doResume() {
|
||||
mView.resume();
|
||||
if (mView != null)
|
||||
mView.resume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSurfaceReady() {
|
||||
return mView.isSurfaceReady();
|
||||
return mView != null && mView.isSurfaceReady();
|
||||
}
|
||||
|
||||
// Called from native code
|
||||
|
|
|
@ -2,5 +2,4 @@ package com.flycast.emulator.config;
|
|||
|
||||
public class Config {
|
||||
public static final String pref_home = "home_directory";
|
||||
public static String git_issues = "https://github.com/flyinghead/flycast/issues/";
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue