diff options
author | bunnei <[email protected]> | 2023-02-04 00:55:02 -0800 |
---|---|---|
committer | bunnei <[email protected]> | 2023-06-03 00:05:29 -0700 |
commit | 93cf8c3090889e1228b94df4d171601d8f7dd7f0 (patch) | |
tree | 50dea0d1b7e2c9ecd668ad3e33c912df5b3e5f0a | |
parent | 63a98e3e1cc9dfb306bb3f063a6c89acaf1f55ad (diff) | |
download | yuzu-android-93cf8c3090889e1228b94df4d171601d8f7dd7f0.tar.gz yuzu-android-93cf8c3090889e1228b94df4d171601d8f7dd7f0.zip |
android: frontend: Integrate key installation for SAF.
-rw-r--r-- | src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.java | 30 | ||||
-rw-r--r-- | src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainPresenter.java | 5 | ||||
-rw-r--r-- | src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileBrowserHelper.java | 9 | ||||
-rw-r--r-- | src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.java | 37 | ||||
-rw-r--r-- | src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/StartupHandler.java | 24 | ||||
-rw-r--r-- | src/android/app/src/main/jni/native.cpp | 2 | ||||
-rw-r--r-- | src/android/app/src/main/res/drawable-hdpi/ic_install.png (renamed from src/android/app/src/main/res/drawable-hdpi/ic_cia_install.png) | bin | 514 -> 514 bytes | |||
-rw-r--r-- | src/android/app/src/main/res/drawable-mdpi/ic_install.png (renamed from src/android/app/src/main/res/drawable-mdpi/ic_cia_install.png) | bin | 364 -> 364 bytes | |||
-rw-r--r-- | src/android/app/src/main/res/drawable-night-hdpi/ic_install.png (renamed from src/android/app/src/main/res/drawable-night-hdpi/ic_cia_install.png) | bin | 556 -> 556 bytes | |||
-rw-r--r-- | src/android/app/src/main/res/drawable-night-mdpi/ic_install.png (renamed from src/android/app/src/main/res/drawable-night-mdpi/ic_cia_install.png) | bin | 405 -> 405 bytes | |||
-rw-r--r-- | src/android/app/src/main/res/drawable-night-xhdpi/ic_install.png (renamed from src/android/app/src/main/res/drawable-night-xhdpi/ic_cia_install.png) | bin | 729 -> 729 bytes | |||
-rw-r--r-- | src/android/app/src/main/res/drawable-night-xxhdpi/ic_install.png (renamed from src/android/app/src/main/res/drawable-night-xxhdpi/ic_cia_install.png) | bin | 1168 -> 1168 bytes | |||
-rw-r--r-- | src/android/app/src/main/res/drawable-night-xxxhdpi/ic_install.png (renamed from src/android/app/src/main/res/drawable-night-xxxhdpi/ic_cia_install.png) | bin | 1433 -> 1433 bytes | |||
-rw-r--r-- | src/android/app/src/main/res/drawable-xhdpi/ic_install.png (renamed from src/android/app/src/main/res/drawable-xhdpi/ic_cia_install.png) | bin | 656 -> 656 bytes | |||
-rw-r--r-- | src/android/app/src/main/res/drawable-xxhdpi/ic_install.png (renamed from src/android/app/src/main/res/drawable-xxhdpi/ic_cia_install.png) | bin | 967 -> 967 bytes | |||
-rw-r--r-- | src/android/app/src/main/res/drawable-xxxhdpi/ic_install.png (renamed from src/android/app/src/main/res/drawable-xxxhdpi/ic_cia_install.png) | bin | 1244 -> 1244 bytes | |||
-rw-r--r-- | src/android/app/src/main/res/menu/menu_game_grid.xml | 6 | ||||
-rw-r--r-- | src/android/app/src/main/res/values/strings.xml | 6 | ||||
-rw-r--r-- | src/core/crypto/key_manager.cpp | 2 | ||||
-rw-r--r-- | src/core/crypto/key_manager.h | 2 |
20 files changed, 102 insertions, 21 deletions
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.java index 26ff14914..7fdd692c2 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.java +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.java @@ -6,18 +6,22 @@ import android.os.Bundle; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; +import android.widget.Toast; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; +import org.yuzu.yuzu_emu.NativeLibrary; import org.yuzu.yuzu_emu.R; import org.yuzu.yuzu_emu.activities.EmulationActivity; import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity; import org.yuzu.yuzu_emu.model.GameProvider; import org.yuzu.yuzu_emu.ui.platform.PlatformGamesFragment; import org.yuzu.yuzu_emu.utils.AddDirectoryHelper; +import org.yuzu.yuzu_emu.utils.DirectoryInitialization; import org.yuzu.yuzu_emu.utils.FileBrowserHelper; +import org.yuzu.yuzu_emu.utils.FileUtil; import org.yuzu.yuzu_emu.utils.PicassoUtils; import org.yuzu.yuzu_emu.utils.StartupHandler; import org.yuzu.yuzu_emu.utils.ThemeUtil; @@ -116,8 +120,13 @@ public final class MainActivity extends AppCompatActivity implements MainView { switch (request) { case MainPresenter.REQUEST_ADD_DIRECTORY: FileBrowserHelper.openDirectoryPicker(this, - MainPresenter.REQUEST_ADD_DIRECTORY, - R.string.select_game_folder); + MainPresenter.REQUEST_ADD_DIRECTORY, + R.string.select_game_folder); + break; + case MainPresenter.REQUEST_INSTALL_KEYS: + FileBrowserHelper.openFilePicker(this, + MainPresenter.REQUEST_INSTALL_KEYS, + R.string.install_keys); break; } } @@ -132,7 +141,6 @@ public final class MainActivity extends AppCompatActivity implements MainView { super.onActivityResult(requestCode, resultCode, result); switch (requestCode) { case MainPresenter.REQUEST_ADD_DIRECTORY: - // If the user picked a file, as opposed to just backing out. if (resultCode == MainActivity.RESULT_OK) { int takeFlags = (Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION); getContentResolver().takePersistableUriPermission(Uri.parse(result.getDataString()), takeFlags); @@ -144,6 +152,22 @@ public final class MainActivity extends AppCompatActivity implements MainView { mPresenter.onDirectorySelected(FileBrowserHelper.getSelectedDirectory(result)); } break; + + case MainPresenter.REQUEST_INSTALL_KEYS: + if (resultCode == MainActivity.RESULT_OK) { + int takeFlags = (Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION); + getContentResolver().takePersistableUriPermission(Uri.parse(result.getDataString()), takeFlags); + String dstPath = DirectoryInitialization.getUserDirectory() + "/keys/"; + if (FileUtil.copyUriToInternalStorage(this, result.getData(), dstPath, "prod.keys")) { + if (NativeLibrary.ReloadKeys()) { + Toast.makeText(this, R.string.install_keys_success, Toast.LENGTH_SHORT).show(); + } else { + Toast.makeText(this, R.string.install_keys_failure, Toast.LENGTH_SHORT).show(); + launchFileListActivity(MainPresenter.REQUEST_INSTALL_KEYS); + } + } + } + break; } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainPresenter.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainPresenter.java index 01f577600..82667a98f 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainPresenter.java +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainPresenter.java @@ -11,6 +11,7 @@ import org.yuzu.yuzu_emu.utils.AddDirectoryHelper; public final class MainPresenter { public static final int REQUEST_ADD_DIRECTORY = 1; + public static final int REQUEST_INSTALL_KEYS = 2; private final MainView mView; private String mDirToAdd; private long mLastClickTime = 0; @@ -46,6 +47,10 @@ public final class MainPresenter { case R.id.button_add_directory: launchFileListActivity(REQUEST_ADD_DIRECTORY); return true; + + case R.id.button_install_keys: + launchFileListActivity(REQUEST_INSTALL_KEYS); + return true; } return false; diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileBrowserHelper.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileBrowserHelper.java index 6175f39c4..4dab914c7 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileBrowserHelper.java +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileBrowserHelper.java @@ -10,6 +10,15 @@ public final class FileBrowserHelper { activity.startActivityForResult(i, requestCode); } + public static void openFilePicker(FragmentActivity activity, int requestCode, int title) { + Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION); + intent.putExtra(Intent.EXTRA_TITLE, title); + intent.setType("*/*"); + activity.startActivityForResult(intent, requestCode); + } + public static String getSelectedDirectory(Intent result) { return result.getDataString(); } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.java index 624fd4a88..8665704cc 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.java +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.java @@ -12,8 +12,9 @@ import androidx.documentfile.provider.DocumentFile; import org.yuzu.yuzu_emu.model.MinimalDocumentFile; +import java.io.FileOutputStream; +import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; import java.net.URLDecoder; import java.util.ArrayList; import java.util.List; @@ -243,6 +244,40 @@ public class FileUtil { return size; } + public static boolean copyUriToInternalStorage(Context context, Uri sourceUri, String destinationParentPath, String destinationFilename) { + InputStream input = null; + FileOutputStream output = null; + try { + input = context.getContentResolver().openInputStream(sourceUri); + output = new FileOutputStream(destinationParentPath + "/" + destinationFilename); + byte[] buffer = new byte[1024]; + int len; + while ((len = input.read(buffer)) != -1) { + output.write(buffer, 0, len); + } + output.flush(); + return true; + } catch (Exception e) { + Log.error("[FileUtil]: Cannot copy file, error: " + e.getMessage()); + } finally { + if (input != null) { + try { + input.close(); + } catch (IOException e) { + Log.error("[FileUtil]: Cannot close input file, error: " + e.getMessage()); + } + } + if (output != null) { + try { + output.close(); + } catch (IOException e) { + Log.error("[FileUtil]: Cannot close output file, error: " + e.getMessage()); + } + } + } + return false; + } + public static boolean isRootTreeUri(Uri uri) { final List<String> paths = uri.getPathSegments(); return paths.size() == 2 && PATH_TREE.equals(paths.get(0)); diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/StartupHandler.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/StartupHandler.java index 6d3e58e18..749a06b32 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/StartupHandler.java +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/StartupHandler.java @@ -2,6 +2,10 @@ package org.yuzu.yuzu_emu.utils; import android.content.SharedPreferences; import android.preference.PreferenceManager; +import android.text.Html; +import android.text.method.LinkMovementMethod; +import android.widget.TextView; + import androidx.appcompat.app.AlertDialog; import org.yuzu.yuzu_emu.R; @@ -13,7 +17,7 @@ public final class StartupHandler { private static SharedPreferences mPreferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.getAppContext()); private static void handleStartupPromptDismiss(MainActivity parent) { - parent.launchFileListActivity(MainPresenter.REQUEST_ADD_DIRECTORY); + parent.launchFileListActivity(MainPresenter.REQUEST_INSTALL_KEYS); } private static void markFirstBoot() { @@ -26,14 +30,16 @@ public final class StartupHandler { if (mPreferences.getBoolean("FirstApplicationLaunch", true)) { markFirstBoot(); - // Prompt user with standard first boot disclaimer - new AlertDialog.Builder(parent) - .setTitle(R.string.app_name) - .setIcon(R.mipmap.ic_launcher) - .setMessage(parent.getResources().getString(R.string.app_disclaimer)) - .setPositiveButton(android.R.string.ok, null) - .setOnDismissListener(dialogInterface -> handleStartupPromptDismiss(parent)) - .show(); + AlertDialog.Builder builder = new AlertDialog.Builder(parent); + builder.setMessage(Html.fromHtml(parent.getResources().getString(R.string.app_disclaimer))); + builder.setTitle(R.string.app_name); + builder.setIcon(R.mipmap.ic_launcher); + builder.setPositiveButton(android.R.string.ok, null); + builder.setOnDismissListener(dialogInterface -> handleStartupPromptDismiss(parent)); + + AlertDialog alert = builder.create(); + alert.show(); + ((TextView) alert.findViewById(android.R.id.message)).setMovementMethod(LinkMovementMethod.getInstance()); } } } diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 358316c48..6d1e75c40 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -271,7 +271,7 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_SetAppDirectory(JNIEnv* env, jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_ReloadKeys(JNIEnv* env, [[maybe_unused]] jclass clazz) { Core::Crypto::KeyManager::Instance().ReloadKeys(); - return static_cast<jboolean>(Core::Crypto::KeyManager::Instance().IsKeysLoaded()); + return static_cast<jboolean>(Core::Crypto::KeyManager::Instance().AreKeysLoaded()); } void Java_org_yuzu_yuzu_1emu_NativeLibrary_UnPauseEmulation([[maybe_unused]] JNIEnv* env, diff --git a/src/android/app/src/main/res/drawable-hdpi/ic_cia_install.png b/src/android/app/src/main/res/drawable-hdpi/ic_install.png Binary files differindex 8c00d8c34..8c00d8c34 100644 --- a/src/android/app/src/main/res/drawable-hdpi/ic_cia_install.png +++ b/src/android/app/src/main/res/drawable-hdpi/ic_install.png diff --git a/src/android/app/src/main/res/drawable-mdpi/ic_cia_install.png b/src/android/app/src/main/res/drawable-mdpi/ic_install.png Binary files differindex c6dc232b4..c6dc232b4 100644 --- a/src/android/app/src/main/res/drawable-mdpi/ic_cia_install.png +++ b/src/android/app/src/main/res/drawable-mdpi/ic_install.png diff --git a/src/android/app/src/main/res/drawable-night-hdpi/ic_cia_install.png b/src/android/app/src/main/res/drawable-night-hdpi/ic_install.png Binary files differindex cc986c8ac..cc986c8ac 100644 --- a/src/android/app/src/main/res/drawable-night-hdpi/ic_cia_install.png +++ b/src/android/app/src/main/res/drawable-night-hdpi/ic_install.png diff --git a/src/android/app/src/main/res/drawable-night-mdpi/ic_cia_install.png b/src/android/app/src/main/res/drawable-night-mdpi/ic_install.png Binary files differindex f61d84961..f61d84961 100644 --- a/src/android/app/src/main/res/drawable-night-mdpi/ic_cia_install.png +++ b/src/android/app/src/main/res/drawable-night-mdpi/ic_install.png diff --git a/src/android/app/src/main/res/drawable-night-xhdpi/ic_cia_install.png b/src/android/app/src/main/res/drawable-night-xhdpi/ic_install.png Binary files differindex 1eccbe68d..1eccbe68d 100644 --- a/src/android/app/src/main/res/drawable-night-xhdpi/ic_cia_install.png +++ b/src/android/app/src/main/res/drawable-night-xhdpi/ic_install.png diff --git a/src/android/app/src/main/res/drawable-night-xxhdpi/ic_cia_install.png b/src/android/app/src/main/res/drawable-night-xxhdpi/ic_install.png Binary files differindex fc3c434b0..fc3c434b0 100644 --- a/src/android/app/src/main/res/drawable-night-xxhdpi/ic_cia_install.png +++ b/src/android/app/src/main/res/drawable-night-xxhdpi/ic_install.png diff --git a/src/android/app/src/main/res/drawable-night-xxxhdpi/ic_cia_install.png b/src/android/app/src/main/res/drawable-night-xxxhdpi/ic_install.png Binary files differindex b4d1b92b7..b4d1b92b7 100644 --- a/src/android/app/src/main/res/drawable-night-xxxhdpi/ic_cia_install.png +++ b/src/android/app/src/main/res/drawable-night-xxxhdpi/ic_install.png diff --git a/src/android/app/src/main/res/drawable-xhdpi/ic_cia_install.png b/src/android/app/src/main/res/drawable-xhdpi/ic_install.png Binary files differindex 839869401..839869401 100644 --- a/src/android/app/src/main/res/drawable-xhdpi/ic_cia_install.png +++ b/src/android/app/src/main/res/drawable-xhdpi/ic_install.png diff --git a/src/android/app/src/main/res/drawable-xxhdpi/ic_cia_install.png b/src/android/app/src/main/res/drawable-xxhdpi/ic_install.png Binary files differindex e6812f0d4..e6812f0d4 100644 --- a/src/android/app/src/main/res/drawable-xxhdpi/ic_cia_install.png +++ b/src/android/app/src/main/res/drawable-xxhdpi/ic_install.png diff --git a/src/android/app/src/main/res/drawable-xxxhdpi/ic_cia_install.png b/src/android/app/src/main/res/drawable-xxxhdpi/ic_install.png Binary files differindex 69ae32dc3..69ae32dc3 100644 --- a/src/android/app/src/main/res/drawable-xxxhdpi/ic_cia_install.png +++ b/src/android/app/src/main/res/drawable-xxxhdpi/ic_install.png diff --git a/src/android/app/src/main/res/menu/menu_game_grid.xml b/src/android/app/src/main/res/menu/menu_game_grid.xml index cd515afbf..3eb8cf817 100644 --- a/src/android/app/src/main/res/menu/menu_game_grid.xml +++ b/src/android/app/src/main/res/menu/menu_game_grid.xml @@ -14,9 +14,9 @@ android:title="@string/select_game_folder" app:showAsAction="ifRoom" /> <item - android:id="@+id/button_install_cia" - android:icon="@drawable/ic_cia_install" - android:title="@string/install_cia_title" + android:id="@+id/button_install_keys" + android:icon="@drawable/ic_install" + android:title="@string/install_keys" app:showAsAction="ifRoom" /> </menu> </item> diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 893f6aa1a..1c6858a60 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -3,7 +3,7 @@ <!-- General application strings --> <string name="app_name" translatable="false">yuzu</string> - <string name="app_disclaimer">This software will run games for the Nintendo Switch game console. No game titles are included.\n\nBefore you run, please place your rightfully owned Switch game files onto your device storage.</string> + <string name="app_disclaimer">This software will run games for the Nintendo Switch game console. No game titles or keys are included.<br /><br />Before you begin, please locate your <![CDATA[<b> prod.keys </b>]]> file on your device storage.<br /><br /><![CDATA[<a href="https://yuzu-emu.org/wiki/dumping-decryption-keys-from-a-switch-console/">Learn more</a>]]></string> <string name="app_notification_channel_name" translatable="false">yuzu</string> <string name="app_notification_channel_id" translatable="false">yuzu</string> <string name="app_notification_channel_description">yuzu Switch emulator notifications</string> @@ -49,7 +49,9 @@ <!-- Add Directory Screen--> <string name="select_game_folder">Select game folder</string> - <string name="install_cia_title">Install CIA</string> + <string name="install_keys">Install keys</string> + <string name="install_keys_success">Keys successfully installed</string> + <string name="install_keys_failure">Keys file (prod.keys) is invalid</string> <!-- Preferences Screen --> <string name="preferences_settings">Settings</string> diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp index 0bd5859d0..4ff2c50e5 100644 --- a/src/core/crypto/key_manager.cpp +++ b/src/core/crypto/key_manager.cpp @@ -706,7 +706,7 @@ void KeyManager::LoadFromFile(const std::filesystem::path& file_path, bool is_ti } } -bool KeyManager::IsKeysLoaded() const { +bool KeyManager::AreKeysLoaded() const { return !s128_keys.empty() && !s256_keys.empty(); } diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h index fb991ae54..8c864503b 100644 --- a/src/core/crypto/key_manager.h +++ b/src/core/crypto/key_manager.h @@ -268,7 +268,7 @@ public: bool AddTicketPersonalized(Ticket raw); void ReloadKeys(); - bool IsKeysLoaded() const; + bool AreKeysLoaded() const; private: KeyManager(); |