platform/android: Add option to import/export save files
Also some typos and reformatting parts of the code Former-commit-id: 0f1de44fab44a09c49d08421a3c2032123d3023b Former-commit-id: 810b6b1bce1f7feeee7c33bec5ff16915367db17
This commit is contained in:
parent
3c6628138c
commit
d1bf01d562
|
@ -51,6 +51,7 @@ dependencies {
|
||||||
implementation 'com.google.android.material:material:1.1.0'
|
implementation 'com.google.android.material:material:1.1.0'
|
||||||
implementation "androidx.documentfile:documentfile:1.0.1"
|
implementation "androidx.documentfile:documentfile:1.0.1"
|
||||||
implementation 'androidx.preference:preference:1.1.0'
|
implementation 'androidx.preference:preference:1.1.0'
|
||||||
|
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||||
implementation 'commons-io:commons-io:2.6'
|
implementation 'commons-io:commons-io:2.6'
|
||||||
testImplementation 'junit:junit:4.12'
|
testImplementation 'junit:junit:4.12'
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.0'
|
androidTestImplementation 'androidx.test.ext:junit:1.1.0'
|
||||||
|
|
|
@ -11,11 +11,25 @@
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
|
android:hardwareAccelerated="true"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:isGame="true"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/AppTheme">
|
android:theme="@style/AppTheme">
|
||||||
|
|
||||||
|
<!-- Needed for Android >= Nougat for file access -->
|
||||||
|
<provider
|
||||||
|
android:name="androidx.core.content.FileProvider"
|
||||||
|
android:authorities="com.mrmichel.rustdroid_emu.provider"
|
||||||
|
android:exported="false"
|
||||||
|
android:grantUriPermissions="true">
|
||||||
|
<meta-data
|
||||||
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
|
android:resource="@xml/provider_paths" />
|
||||||
|
</provider>
|
||||||
|
|
||||||
<activity android:name=".ui.snapshots.SnapshotPickerActivity" />
|
<activity android:name=".ui.snapshots.SnapshotPickerActivity" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.snapshots.SnapshotListFragment"
|
android:name=".ui.snapshots.SnapshotListFragment"
|
||||||
|
@ -26,7 +40,8 @@
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.EmulatorActivity"
|
android:name=".ui.EmulatorActivity"
|
||||||
android:label="@string/title_activity_emulator" />
|
android:label="@string/title_activity_emulator" />
|
||||||
<activity android:name=".ui.SettingsActivity"
|
<activity
|
||||||
|
android:name=".ui.SettingsActivity"
|
||||||
android:label="Settings" />
|
android:label="Settings" />
|
||||||
<activity android:name=".ui.SplashActivity">
|
<activity android:name=".ui.SplashActivity">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
|
|
|
@ -5,9 +5,12 @@ import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.core.content.FileProvider;
|
||||||
|
|
||||||
import com.mrmichel.rustdroid_emu.ui.EmulatorActivity;
|
import com.mrmichel.rustdroid_emu.ui.EmulatorActivity;
|
||||||
|
|
||||||
|
@ -35,7 +38,7 @@ public class Util {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void showAlertDiaglogAndExit(final Activity activity, Exception e) {
|
public static void showAlertDialogAndExit(final Activity activity, Exception e) {
|
||||||
new AlertDialog.Builder(activity)
|
new AlertDialog.Builder(activity)
|
||||||
.setTitle(e.toString())
|
.setTitle(e.toString())
|
||||||
.setMessage(e.getMessage())
|
.setMessage(e.getMessage())
|
||||||
|
@ -50,6 +53,15 @@ public class Util {
|
||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void showAlertDialog(final Activity activity, Exception e) {
|
||||||
|
new AlertDialog.Builder(activity)
|
||||||
|
.setTitle(e.toString())
|
||||||
|
.setMessage(e.getMessage())
|
||||||
|
.setIcon(android.R.drawable.ic_dialog_alert)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static byte[] compressBitmapToByteArray(Bitmap bitmap) {
|
public static byte[] compressBitmapToByteArray(Bitmap bitmap) {
|
||||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||||
bitmap.compress(Bitmap.CompressFormat.PNG, 10, byteArrayOutputStream);
|
bitmap.compress(Bitmap.CompressFormat.PNG, 10, byteArrayOutputStream);
|
||||||
|
@ -77,7 +89,7 @@ public class Util {
|
||||||
|
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
while ( (len = gis.read(buffer, 0, 8192)) != -1) {
|
while ((len = gis.read(buffer, 0, 8192)) != -1) {
|
||||||
outputStream.write(buffer, 0, len);
|
outputStream.write(buffer, 0, len);
|
||||||
}
|
}
|
||||||
gis.close();
|
gis.close();
|
||||||
|
@ -88,14 +100,14 @@ public class Util {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] readFile(File file) throws FileNotFoundException, IOException {
|
public static byte[] readFile(File file) throws IOException {
|
||||||
byte[] buffer = new byte[8192];
|
byte[] buffer = new byte[8192];
|
||||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
FileInputStream fis = new FileInputStream(file);
|
FileInputStream fis = new FileInputStream(file);
|
||||||
|
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
while ( (len = fis.read(buffer, 0, 8192)) != -1) {
|
while ((len = fis.read(buffer, 0, 8192)) != -1) {
|
||||||
outputStream.write(buffer, 0, len);
|
outputStream.write(buffer, 0, len);
|
||||||
}
|
}
|
||||||
fis.close();
|
fis.close();
|
||||||
|
@ -123,4 +135,30 @@ public class Util {
|
||||||
md.update(bytes);
|
md.update(bytes);
|
||||||
return byteArrayToHexString(md.digest());
|
return byteArrayToHexString(md.digest());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void shareFile(Context context, File file, String message) throws FileNotFoundException {
|
||||||
|
if (!file.exists()) {
|
||||||
|
throw new FileNotFoundException("file does not exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
final Uri uri;
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
uri = Uri.fromFile(file);
|
||||||
|
} else {
|
||||||
|
uri = FileProvider.getUriForFile(context, context.getPackageName() + ".provider", file);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uri == null) {
|
||||||
|
throw new FileNotFoundException("could not find file to share");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Intent intentShareFile = new Intent(Intent.ACTION_SEND);
|
||||||
|
intentShareFile.setType("*/*");
|
||||||
|
intentShareFile.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||||
|
intentShareFile.putExtra(Intent.EXTRA_STREAM, uri);
|
||||||
|
intentShareFile.putExtra(Intent.EXTRA_TEXT, message);
|
||||||
|
|
||||||
|
context.startActivity(Intent.createChooser(intentShareFile, message));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,10 +4,6 @@ import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.media.AudioFormat;
|
|
||||||
import android.media.AudioManager;
|
|
||||||
import android.media.AudioTrack;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
|
@ -210,7 +206,7 @@ public class EmulatorActivity extends AppCompatActivity implements View.OnClickL
|
||||||
// onRomLoaded(rom, savePath);
|
// onRomLoaded(rom, savePath);
|
||||||
// } catch (Exception e) {
|
// } catch (Exception e) {
|
||||||
// Log.e(TAG, "got error while reading rom file");
|
// Log.e(TAG, "got error while reading rom file");
|
||||||
// Util.showAlertDiaglogAndExit(this, e);
|
// Util.showAlertDialogAndExit(this, e);
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
if (requestCode == LOAD_SNAPSHOT_REQUESTCODE) {
|
if (requestCode == LOAD_SNAPSHOT_REQUESTCODE) {
|
||||||
|
@ -224,7 +220,7 @@ public class EmulatorActivity extends AppCompatActivity implements View.OnClickL
|
||||||
try {
|
try {
|
||||||
emulator.loadState(pickedSnapshot.load());
|
emulator.loadState(pickedSnapshot.load());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Util.showAlertDiaglogAndExit(this, e);
|
Util.showAlertDialogAndExit(this, e);
|
||||||
}
|
}
|
||||||
resumeEmulation();
|
resumeEmulation();
|
||||||
|
|
||||||
|
@ -261,7 +257,7 @@ public class EmulatorActivity extends AppCompatActivity implements View.OnClickL
|
||||||
// try {
|
// try {
|
||||||
// emulator.open(bios, rom, savePath);
|
// emulator.open(bios, rom, savePath);
|
||||||
// } catch (EmulatorBindings.NativeBindingException e) {
|
// } catch (EmulatorBindings.NativeBindingException e) {
|
||||||
// Util.showAlertDiaglogAndExit(this, e);
|
// Util.showAlertDialogAndExit(this, e);
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// createThreads();
|
// createThreads();
|
||||||
|
@ -297,7 +293,7 @@ public class EmulatorActivity extends AppCompatActivity implements View.OnClickL
|
||||||
outState.putBoolean("turbo", false);
|
outState.putBoolean("turbo", false);
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Util.showAlertDiaglogAndExit(this, e);
|
Util.showAlertDialogAndExit(this, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -367,7 +363,7 @@ public class EmulatorActivity extends AppCompatActivity implements View.OnClickL
|
||||||
emulator.setTurbo(turbo);
|
emulator.setTurbo(turbo);
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Util.showAlertDiaglogAndExit(thisActivity, e);
|
Util.showAlertDialogAndExit(thisActivity, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -380,7 +376,7 @@ public class EmulatorActivity extends AppCompatActivity implements View.OnClickL
|
||||||
romData = Util.readFile(romMetadata.getRomFile());
|
romData = Util.readFile(romMetadata.getRomFile());
|
||||||
this.emulator.open(bios, romData, romMetadata.getBackupFile().getAbsolutePath(), skipBios);
|
this.emulator.open(bios, romData, romMetadata.getBackupFile().getAbsolutePath(), skipBios);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Util.showAlertDiaglogAndExit(this, e);
|
Util.showAlertDialogAndExit(this, e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -478,7 +474,7 @@ public class EmulatorActivity extends AppCompatActivity implements View.OnClickL
|
||||||
|
|
||||||
} catch (EmulatorBindings.NativeBindingException e) {
|
} catch (EmulatorBindings.NativeBindingException e) {
|
||||||
Log.e(TAG, e.toString());
|
Log.e(TAG, e.toString());
|
||||||
Util.showAlertDiaglogAndExit(this, e);
|
Util.showAlertDialogAndExit(this, e);
|
||||||
} finally {
|
} finally {
|
||||||
resumeEmulation();
|
resumeEmulation();
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,9 +25,11 @@ import com.mrmichel.rustdroid_emu.R;
|
||||||
import com.mrmichel.rustdroid_emu.Util;
|
import com.mrmichel.rustdroid_emu.Util;
|
||||||
import com.mrmichel.rustdroid_emu.core.RomManager;
|
import com.mrmichel.rustdroid_emu.core.RomManager;
|
||||||
import com.mrmichel.rustdroid_emu.ui.SettingsActivity;
|
import com.mrmichel.rustdroid_emu.ui.SettingsActivity;
|
||||||
import com.mrmichel.rustdroid_emu.ui.SettingsFragment;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
@ -38,6 +40,7 @@ public class RomListActivity extends AppCompatActivity {
|
||||||
private static final int REQUEST_IMPORT_ROM = 100;
|
private static final int REQUEST_IMPORT_ROM = 100;
|
||||||
private static final int REQUEST_IMPORT_DIR = 101;
|
private static final int REQUEST_IMPORT_DIR = 101;
|
||||||
private static final int REQUEST_SET_IMAGE = 102;
|
private static final int REQUEST_SET_IMAGE = 102;
|
||||||
|
private static final int REQUEST_IMPORT_SAVE = 103;
|
||||||
|
|
||||||
private static String[] ALLOWED_EXTENSIONS = {"gba", "zip", "bin"};
|
private static String[] ALLOWED_EXTENSIONS = {"gba", "zip", "bin"};
|
||||||
|
|
||||||
|
@ -111,6 +114,20 @@ public class RomListActivity extends AppCompatActivity {
|
||||||
intent.setType("image/*");
|
intent.setType("image/*");
|
||||||
intent.putExtra("romId", entry.getId());
|
intent.putExtra("romId", entry.getId());
|
||||||
startActivityForResult(intent, REQUEST_SET_IMAGE);
|
startActivityForResult(intent, REQUEST_SET_IMAGE);
|
||||||
|
return true;
|
||||||
|
case R.id.action_export_save_file:
|
||||||
|
File backupFile = entry.getBackupFile();
|
||||||
|
try {
|
||||||
|
Util.shareFile(this, backupFile, "Sending " + backupFile.getName());
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
Util.showAlertDialog(this, e);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
case R.id.action_import_save_file:
|
||||||
|
intent = new Intent(Intent.ACTION_GET_CONTENT);
|
||||||
|
intent.setType("*/*");
|
||||||
|
startActivityForResult(intent, REQUEST_IMPORT_SAVE);
|
||||||
|
return true;
|
||||||
default:
|
default:
|
||||||
return super.onContextItemSelected(item);
|
return super.onContextItemSelected(item);
|
||||||
}
|
}
|
||||||
|
@ -128,12 +145,14 @@ public class RomListActivity extends AppCompatActivity {
|
||||||
switch (item.getItemId()) {
|
switch (item.getItemId()) {
|
||||||
case R.id.action_import_rom:
|
case R.id.action_import_rom:
|
||||||
doImportRom();
|
doImportRom();
|
||||||
|
return true;
|
||||||
case R.id.action_import_directory:
|
case R.id.action_import_directory:
|
||||||
doImportDirectory();
|
doImportDirectory();
|
||||||
return true;
|
return true;
|
||||||
case R.id.action_settings:
|
case R.id.action_settings:
|
||||||
Intent intent = new Intent(this, SettingsActivity.class);
|
Intent intent = new Intent(this, SettingsActivity.class);
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
|
return true;
|
||||||
default:
|
default:
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
|
@ -189,12 +208,29 @@ public class RomListActivity extends AppCompatActivity {
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
Util.showAlertDiaglogAndExit(this, e);
|
Util.showAlertDialogAndExit(this, e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.d(TAG, "found bitmap");
|
Log.d(TAG, "found bitmap");
|
||||||
romManager.updateScreenshot(romId, bitmap);
|
romManager.updateScreenshot(romId, bitmap);
|
||||||
|
break;
|
||||||
|
case REQUEST_IMPORT_SAVE:
|
||||||
|
try {
|
||||||
|
InputStream inputStream = getContentResolver().openInputStream(data.getData());
|
||||||
|
byte[] saveData = new byte[inputStream.available()];
|
||||||
|
inputStream.read(saveData);
|
||||||
|
inputStream.close();
|
||||||
|
|
||||||
|
File file = selectedEntry.getBackupFile();
|
||||||
|
Log.d(TAG, "Saving imported save to " + file.getAbsolutePath());
|
||||||
|
FileOutputStream fos = new FileOutputStream(file);
|
||||||
|
fos.write(saveData);
|
||||||
|
fos.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Util.showAlertDialogAndExit(this, e);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,13 +14,12 @@
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_set_screenshot"
|
android:id="@+id/action_set_screenshot"
|
||||||
android:title="@string/action_set_screenshot"
|
android:title="@string/action_set_screenshot" />
|
||||||
/>
|
|
||||||
|
|
||||||
<item
|
<item
|
||||||
|
android:id="@+id/action_view_snapshots"
|
||||||
android:title="@string/action_view_snapshot"
|
android:title="@string/action_view_snapshot"
|
||||||
app:showAsAction="withText"
|
app:showAsAction="withText" />
|
||||||
android:id="@+id/action_view_snapshots" />
|
|
||||||
|
|
||||||
|
|
||||||
<item
|
<item
|
||||||
|
@ -28,4 +27,15 @@
|
||||||
android:icon="@android:drawable/ic_menu_delete"
|
android:icon="@android:drawable/ic_menu_delete"
|
||||||
android:title="@string/action_delete"
|
android:title="@string/action_delete"
|
||||||
app:showAsAction="ifRoom|withText" />
|
app:showAsAction="ifRoom|withText" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_import_save_file"
|
||||||
|
android:icon="@android:drawable/ic_menu_save"
|
||||||
|
android:title="Import Save File" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_export_save_file"
|
||||||
|
android:icon="@android:drawable/ic_menu_share"
|
||||||
|
android:title="Export Save File" />
|
||||||
|
|
||||||
</menu>
|
</menu>
|
||||||
|
|
Reference in a new issue