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:
Michel Heily 2020-09-30 23:55:32 +03:00 committed by MishMish
parent 3c6628138c
commit d1bf01d562
6 changed files with 118 additions and 22 deletions

View file

@ -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'

View file

@ -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>

View file

@ -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);
@ -88,7 +100,7 @@ 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);
@ -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));
}
} }

View file

@ -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();
} }

View file

@ -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;
} }

View file

@ -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>