diff options
| author | 2024-01-25 16:21:29 -0500 | |
|---|---|---|
| committer | 2024-01-25 16:21:29 -0500 | |
| commit | 3e2d3548f2bf90f823fb9c565ca2b97be3d048b7 (patch) | |
| tree | 74137247efc83f233a4d81260de41eeb8aae1d8d /src/android/app | |
| parent | Merge pull request #12783 from liamwhite/cmif-generation (diff) | |
| parent | android: Add key check (diff) | |
| download | yuzu-3e2d3548f2bf90f823fb9c565ca2b97be3d048b7.tar.gz yuzu-3e2d3548f2bf90f823fb9c565ca2b97be3d048b7.tar.xz yuzu-3e2d3548f2bf90f823fb9c565ca2b97be3d048b7.zip | |
Merge pull request #12777 from t895/firmware-warning
android: Add key warning
Diffstat (limited to 'src/android/app')
7 files changed, 99 insertions, 17 deletions
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt index 5b9f553f7..55abba093 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt | |||
| @@ -620,6 +620,11 @@ object NativeLibrary { | |||
| 620 | external fun clearFilesystemProvider() | 620 | external fun clearFilesystemProvider() |
| 621 | 621 | ||
| 622 | /** | 622 | /** |
| 623 | * Checks if all necessary keys are present for decryption | ||
| 624 | */ | ||
| 625 | external fun areKeysPresent(): Boolean | ||
| 626 | |||
| 627 | /** | ||
| 623 | * Button type for use in onTouchEvent | 628 | * Button type for use in onTouchEvent |
| 624 | */ | 629 | */ |
| 625 | object ButtonType { | 630 | object ButtonType { |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt index 620d8db7c..22b084b9a 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt | |||
| @@ -26,9 +26,15 @@ class MessageDialogFragment : DialogFragment() { | |||
| 26 | val descriptionId = requireArguments().getInt(DESCRIPTION_ID) | 26 | val descriptionId = requireArguments().getInt(DESCRIPTION_ID) |
| 27 | val descriptionString = requireArguments().getString(DESCRIPTION_STRING)!! | 27 | val descriptionString = requireArguments().getString(DESCRIPTION_STRING)!! |
| 28 | val helpLinkId = requireArguments().getInt(HELP_LINK) | 28 | val helpLinkId = requireArguments().getInt(HELP_LINK) |
| 29 | val dismissible = requireArguments().getBoolean(DISMISSIBLE) | ||
| 30 | val clearPositiveAction = requireArguments().getBoolean(CLEAR_POSITIVE_ACTION) | ||
| 29 | 31 | ||
| 30 | val builder = MaterialAlertDialogBuilder(requireContext()) | 32 | val builder = MaterialAlertDialogBuilder(requireContext()) |
| 31 | 33 | ||
| 34 | if (clearPositiveAction) { | ||
| 35 | messageDialogViewModel.positiveAction = null | ||
| 36 | } | ||
| 37 | |||
| 32 | if (messageDialogViewModel.positiveAction == null) { | 38 | if (messageDialogViewModel.positiveAction == null) { |
| 33 | builder.setPositiveButton(R.string.close, null) | 39 | builder.setPositiveButton(R.string.close, null) |
| 34 | } else { | 40 | } else { |
| @@ -51,6 +57,8 @@ class MessageDialogFragment : DialogFragment() { | |||
| 51 | } | 57 | } |
| 52 | } | 58 | } |
| 53 | 59 | ||
| 60 | isCancelable = dismissible | ||
| 61 | |||
| 54 | return builder.show() | 62 | return builder.show() |
| 55 | } | 63 | } |
| 56 | 64 | ||
| @@ -67,6 +75,8 @@ class MessageDialogFragment : DialogFragment() { | |||
| 67 | private const val DESCRIPTION_ID = "DescriptionId" | 75 | private const val DESCRIPTION_ID = "DescriptionId" |
| 68 | private const val DESCRIPTION_STRING = "DescriptionString" | 76 | private const val DESCRIPTION_STRING = "DescriptionString" |
| 69 | private const val HELP_LINK = "Link" | 77 | private const val HELP_LINK = "Link" |
| 78 | private const val DISMISSIBLE = "Dismissible" | ||
| 79 | private const val CLEAR_POSITIVE_ACTION = "ClearPositiveAction" | ||
| 70 | 80 | ||
| 71 | fun newInstance( | 81 | fun newInstance( |
| 72 | activity: FragmentActivity? = null, | 82 | activity: FragmentActivity? = null, |
| @@ -75,22 +85,28 @@ class MessageDialogFragment : DialogFragment() { | |||
| 75 | descriptionId: Int = 0, | 85 | descriptionId: Int = 0, |
| 76 | descriptionString: String = "", | 86 | descriptionString: String = "", |
| 77 | helpLinkId: Int = 0, | 87 | helpLinkId: Int = 0, |
| 88 | dismissible: Boolean = true, | ||
| 78 | positiveAction: (() -> Unit)? = null | 89 | positiveAction: (() -> Unit)? = null |
| 79 | ): MessageDialogFragment { | 90 | ): MessageDialogFragment { |
| 91 | var clearPositiveAction = false | ||
| 92 | if (activity != null) { | ||
| 93 | ViewModelProvider(activity)[MessageDialogViewModel::class.java].apply { | ||
| 94 | clear() | ||
| 95 | this.positiveAction = positiveAction | ||
| 96 | } | ||
| 97 | } else { | ||
| 98 | clearPositiveAction = true | ||
| 99 | } | ||
| 100 | |||
| 80 | val dialog = MessageDialogFragment() | 101 | val dialog = MessageDialogFragment() |
| 81 | val bundle = Bundle() | 102 | val bundle = Bundle().apply { |
| 82 | bundle.apply { | ||
| 83 | putInt(TITLE_ID, titleId) | 103 | putInt(TITLE_ID, titleId) |
| 84 | putString(TITLE_STRING, titleString) | 104 | putString(TITLE_STRING, titleString) |
| 85 | putInt(DESCRIPTION_ID, descriptionId) | 105 | putInt(DESCRIPTION_ID, descriptionId) |
| 86 | putString(DESCRIPTION_STRING, descriptionString) | 106 | putString(DESCRIPTION_STRING, descriptionString) |
| 87 | putInt(HELP_LINK, helpLinkId) | 107 | putInt(HELP_LINK, helpLinkId) |
| 88 | } | 108 | putBoolean(DISMISSIBLE, dismissible) |
| 89 | if (activity != null) { | 109 | putBoolean(CLEAR_POSITIVE_ACTION, clearPositiveAction) |
| 90 | ViewModelProvider(activity)[MessageDialogViewModel::class.java].apply { | ||
| 91 | clear() | ||
| 92 | this.positiveAction = positiveAction | ||
| 93 | } | ||
| 94 | } | 110 | } |
| 95 | dialog.arguments = bundle | 111 | dialog.arguments = bundle |
| 96 | return dialog | 112 | return dialog |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt index 064342cdd..ebf41a639 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt | |||
| @@ -31,6 +31,7 @@ import androidx.preference.PreferenceManager | |||
| 31 | import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback | 31 | import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback |
| 32 | import com.google.android.material.transition.MaterialFadeThrough | 32 | import com.google.android.material.transition.MaterialFadeThrough |
| 33 | import kotlinx.coroutines.launch | 33 | import kotlinx.coroutines.launch |
| 34 | import org.yuzu.yuzu_emu.NativeLibrary | ||
| 34 | import java.io.File | 35 | import java.io.File |
| 35 | import org.yuzu.yuzu_emu.R | 36 | import org.yuzu.yuzu_emu.R |
| 36 | import org.yuzu.yuzu_emu.YuzuApplication | 37 | import org.yuzu.yuzu_emu.YuzuApplication |
| @@ -162,7 +163,7 @@ class SetupFragment : Fragment() { | |||
| 162 | R.string.install_prod_keys_warning_help, | 163 | R.string.install_prod_keys_warning_help, |
| 163 | { | 164 | { |
| 164 | val file = File(DirectoryInitialization.userDirectory + "/keys/prod.keys") | 165 | val file = File(DirectoryInitialization.userDirectory + "/keys/prod.keys") |
| 165 | if (file.exists()) { | 166 | if (file.exists() && NativeLibrary.areKeysPresent()) { |
| 166 | StepState.COMPLETE | 167 | StepState.COMPLETE |
| 167 | } else { | 168 | } else { |
| 168 | StepState.INCOMPLETE | 169 | StepState.INCOMPLETE |
| @@ -347,7 +348,8 @@ class SetupFragment : Fragment() { | |||
| 347 | val getProdKey = | 348 | val getProdKey = |
| 348 | registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> | 349 | registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> |
| 349 | if (result != null) { | 350 | if (result != null) { |
| 350 | if (mainActivity.processKey(result)) { | 351 | mainActivity.processKey(result) |
| 352 | if (NativeLibrary.areKeysPresent()) { | ||
| 351 | keyCallback.onStepCompleted() | 353 | keyCallback.onStepCompleted() |
| 352 | } | 354 | } |
| 353 | } | 355 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt index 513ac2fc5..cfc777b81 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt | |||
| @@ -31,6 +31,9 @@ class HomeViewModel : ViewModel() { | |||
| 31 | private val _reloadPropertiesList = MutableStateFlow(false) | 31 | private val _reloadPropertiesList = MutableStateFlow(false) |
| 32 | val reloadPropertiesList get() = _reloadPropertiesList.asStateFlow() | 32 | val reloadPropertiesList get() = _reloadPropertiesList.asStateFlow() |
| 33 | 33 | ||
| 34 | private val _checkKeys = MutableStateFlow(false) | ||
| 35 | val checkKeys = _checkKeys.asStateFlow() | ||
| 36 | |||
| 34 | var navigatedToSetup = false | 37 | var navigatedToSetup = false |
| 35 | 38 | ||
| 36 | fun setNavigationVisibility(visible: Boolean, animated: Boolean) { | 39 | fun setNavigationVisibility(visible: Boolean, animated: Boolean) { |
| @@ -66,4 +69,8 @@ class HomeViewModel : ViewModel() { | |||
| 66 | fun reloadPropertiesList(reload: Boolean) { | 69 | fun reloadPropertiesList(reload: Boolean) { |
| 67 | _reloadPropertiesList.value = reload | 70 | _reloadPropertiesList.value = reload |
| 68 | } | 71 | } |
| 72 | |||
| 73 | fun setCheckKeys(value: Boolean) { | ||
| 74 | _checkKeys.value = value | ||
| 75 | } | ||
| 69 | } | 76 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt index c2cc29961..b3967d294 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt | |||
| @@ -64,6 +64,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider { | |||
| 64 | 64 | ||
| 65 | override var themeId: Int = 0 | 65 | override var themeId: Int = 0 |
| 66 | 66 | ||
| 67 | private val CHECKED_DECRYPTION = "CheckedDecryption" | ||
| 68 | private var checkedDecryption = false | ||
| 69 | |||
| 67 | override fun onCreate(savedInstanceState: Bundle?) { | 70 | override fun onCreate(savedInstanceState: Bundle?) { |
| 68 | val splashScreen = installSplashScreen() | 71 | val splashScreen = installSplashScreen() |
| 69 | splashScreen.setKeepOnScreenCondition { !DirectoryInitialization.areDirectoriesReady } | 72 | splashScreen.setKeepOnScreenCondition { !DirectoryInitialization.areDirectoriesReady } |
| @@ -75,6 +78,18 @@ class MainActivity : AppCompatActivity(), ThemeProvider { | |||
| 75 | binding = ActivityMainBinding.inflate(layoutInflater) | 78 | binding = ActivityMainBinding.inflate(layoutInflater) |
| 76 | setContentView(binding.root) | 79 | setContentView(binding.root) |
| 77 | 80 | ||
| 81 | if (savedInstanceState != null) { | ||
| 82 | checkedDecryption = savedInstanceState.getBoolean(CHECKED_DECRYPTION) | ||
| 83 | } | ||
| 84 | if (!checkedDecryption) { | ||
| 85 | val firstTimeSetup = PreferenceManager.getDefaultSharedPreferences(applicationContext) | ||
| 86 | .getBoolean(Settings.PREF_FIRST_APP_LAUNCH, true) | ||
| 87 | if (!firstTimeSetup) { | ||
| 88 | checkKeys() | ||
| 89 | } | ||
| 90 | checkedDecryption = true | ||
| 91 | } | ||
| 92 | |||
| 78 | WindowCompat.setDecorFitsSystemWindows(window, false) | 93 | WindowCompat.setDecorFitsSystemWindows(window, false) |
| 79 | window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING) | 94 | window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING) |
| 80 | 95 | ||
| @@ -150,6 +165,16 @@ class MainActivity : AppCompatActivity(), ThemeProvider { | |||
| 150 | } | 165 | } |
| 151 | } | 166 | } |
| 152 | } | 167 | } |
| 168 | launch { | ||
| 169 | repeatOnLifecycle(Lifecycle.State.CREATED) { | ||
| 170 | homeViewModel.checkKeys.collect { | ||
| 171 | if (it) { | ||
| 172 | checkKeys() | ||
| 173 | homeViewModel.setCheckKeys(false) | ||
| 174 | } | ||
| 175 | } | ||
| 176 | } | ||
| 177 | } | ||
| 153 | } | 178 | } |
| 154 | 179 | ||
| 155 | // Dismiss previous notifications (should not happen unless a crash occurred) | 180 | // Dismiss previous notifications (should not happen unless a crash occurred) |
| @@ -158,6 +183,21 @@ class MainActivity : AppCompatActivity(), ThemeProvider { | |||
| 158 | setInsets() | 183 | setInsets() |
| 159 | } | 184 | } |
| 160 | 185 | ||
| 186 | private fun checkKeys() { | ||
| 187 | if (!NativeLibrary.areKeysPresent()) { | ||
| 188 | MessageDialogFragment.newInstance( | ||
| 189 | titleId = R.string.keys_missing, | ||
| 190 | descriptionId = R.string.keys_missing_description, | ||
| 191 | helpLinkId = R.string.keys_missing_help | ||
| 192 | ).show(supportFragmentManager, MessageDialogFragment.TAG) | ||
| 193 | } | ||
| 194 | } | ||
| 195 | |||
| 196 | override fun onSaveInstanceState(outState: Bundle) { | ||
| 197 | super.onSaveInstanceState(outState) | ||
| 198 | outState.putBoolean(CHECKED_DECRYPTION, checkedDecryption) | ||
| 199 | } | ||
| 200 | |||
| 161 | fun finishSetup(navController: NavController) { | 201 | fun finishSetup(navController: NavController) { |
| 162 | navController.navigate(R.id.action_firstTimeSetupFragment_to_gamesFragment) | 202 | navController.navigate(R.id.action_firstTimeSetupFragment_to_gamesFragment) |
| 163 | (binding.navigationView as NavigationBarView).setupWithNavController(navController) | 203 | (binding.navigationView as NavigationBarView).setupWithNavController(navController) |
| @@ -349,6 +389,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { | |||
| 349 | R.string.install_keys_success, | 389 | R.string.install_keys_success, |
| 350 | Toast.LENGTH_SHORT | 390 | Toast.LENGTH_SHORT |
| 351 | ).show() | 391 | ).show() |
| 392 | homeViewModel.setCheckKeys(true) | ||
| 352 | gamesViewModel.reloadGames(true) | 393 | gamesViewModel.reloadGames(true) |
| 353 | return true | 394 | return true |
| 354 | } else { | 395 | } else { |
| @@ -399,6 +440,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { | |||
| 399 | firmwarePath.deleteRecursively() | 440 | firmwarePath.deleteRecursively() |
| 400 | cacheFirmwareDir.copyRecursively(firmwarePath, true) | 441 | cacheFirmwareDir.copyRecursively(firmwarePath, true) |
| 401 | NativeLibrary.initializeSystem(true) | 442 | NativeLibrary.initializeSystem(true) |
| 443 | homeViewModel.setCheckKeys(true) | ||
| 402 | getString(R.string.save_file_imported_success) | 444 | getString(R.string.save_file_imported_success) |
| 403 | } | 445 | } |
| 404 | } catch (e: Exception) { | 446 | } catch (e: Exception) { |
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index c20c2d2b8..247f2c2b3 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp | |||
| @@ -464,8 +464,8 @@ int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject | |||
| 464 | }; | 464 | }; |
| 465 | 465 | ||
| 466 | return static_cast<int>( | 466 | return static_cast<int>( |
| 467 | ContentManager::InstallNSP(&EmulationSession::GetInstance().System(), | 467 | ContentManager::InstallNSP(EmulationSession::GetInstance().System(), |
| 468 | EmulationSession::GetInstance().System().GetFilesystem().get(), | 468 | *EmulationSession::GetInstance().System().GetFilesystem(), |
| 469 | GetJString(env, j_file), callback)); | 469 | GetJString(env, j_file), callback)); |
| 470 | } | 470 | } |
| 471 | 471 | ||
| @@ -825,7 +825,7 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeUpdate(JNIEnv* env, jobject job | |||
| 825 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeDLC(JNIEnv* env, jobject jobj, | 825 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeDLC(JNIEnv* env, jobject jobj, |
| 826 | jstring jprogramId) { | 826 | jstring jprogramId) { |
| 827 | auto program_id = EmulationSession::GetProgramId(env, jprogramId); | 827 | auto program_id = EmulationSession::GetProgramId(env, jprogramId); |
| 828 | ContentManager::RemoveAllDLC(&EmulationSession::GetInstance().System(), program_id); | 828 | ContentManager::RemoveAllDLC(EmulationSession::GetInstance().System(), program_id); |
| 829 | } | 829 | } |
| 830 | 830 | ||
| 831 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeMod(JNIEnv* env, jobject jobj, jstring jprogramId, | 831 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeMod(JNIEnv* env, jobject jobj, jstring jprogramId, |
| @@ -835,8 +835,9 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeMod(JNIEnv* env, jobject jobj, | |||
| 835 | program_id, GetJString(env, jname)); | 835 | program_id, GetJString(env, jname)); |
| 836 | } | 836 | } |
| 837 | 837 | ||
| 838 | jobject Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEnv* env, jobject jobj, | 838 | jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEnv* env, |
| 839 | jobject jcallback) { | 839 | jobject jobj, |
| 840 | jobject jcallback) { | ||
| 840 | auto jlambdaClass = env->GetObjectClass(jcallback); | 841 | auto jlambdaClass = env->GetObjectClass(jcallback); |
| 841 | auto jlambdaInvokeMethod = env->GetMethodID( | 842 | auto jlambdaInvokeMethod = env->GetMethodID( |
| 842 | jlambdaClass, "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); | 843 | jlambdaClass, "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); |
| @@ -848,7 +849,7 @@ jobject Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEnv* en | |||
| 848 | 849 | ||
| 849 | auto& session = EmulationSession::GetInstance(); | 850 | auto& session = EmulationSession::GetInstance(); |
| 850 | std::vector<std::string> result = ContentManager::VerifyInstalledContents( | 851 | std::vector<std::string> result = ContentManager::VerifyInstalledContents( |
| 851 | &session.System(), session.GetContentProvider(), callback); | 852 | session.System(), *session.GetContentProvider(), callback); |
| 852 | jobjectArray jresult = | 853 | jobjectArray jresult = |
| 853 | env->NewObjectArray(result.size(), IDCache::GetStringClass(), ToJString(env, "")); | 854 | env->NewObjectArray(result.size(), IDCache::GetStringClass(), ToJString(env, "")); |
| 854 | for (size_t i = 0; i < result.size(); ++i) { | 855 | for (size_t i = 0; i < result.size(); ++i) { |
| @@ -869,7 +870,7 @@ jint Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyGameContents(JNIEnv* env, jobje | |||
| 869 | }; | 870 | }; |
| 870 | auto& session = EmulationSession::GetInstance(); | 871 | auto& session = EmulationSession::GetInstance(); |
| 871 | return static_cast<jint>( | 872 | return static_cast<jint>( |
| 872 | ContentManager::VerifyGameContents(&session.System(), GetJString(env, jpath), callback)); | 873 | ContentManager::VerifyGameContents(session.System(), GetJString(env, jpath), callback)); |
| 873 | } | 874 | } |
| 874 | 875 | ||
| 875 | jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject jobj, | 876 | jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject jobj, |
| @@ -918,4 +919,10 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_clearFilesystemProvider(JNIEnv* env, | |||
| 918 | EmulationSession::GetInstance().GetContentProvider()->ClearAllEntries(); | 919 | EmulationSession::GetInstance().GetContentProvider()->ClearAllEntries(); |
| 919 | } | 920 | } |
| 920 | 921 | ||
| 922 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_areKeysPresent(JNIEnv* env, jobject jobj) { | ||
| 923 | auto& system = EmulationSession::GetInstance().System(); | ||
| 924 | system.GetFileSystemController().CreateFactories(*system.GetFilesystem()); | ||
| 925 | return ContentManager::AreKeysPresent(); | ||
| 926 | } | ||
| 927 | |||
| 921 | } // extern "C" | 928 | } // extern "C" |
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 779eb36a8..3cd1586fd 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml | |||
| @@ -144,6 +144,9 @@ | |||
| 144 | <string name="no_save_data_found">No save data found</string> | 144 | <string name="no_save_data_found">No save data found</string> |
| 145 | <string name="verify_installed_content">Verify installed content</string> | 145 | <string name="verify_installed_content">Verify installed content</string> |
| 146 | <string name="verify_installed_content_description">Checks all installed content for corruption</string> | 146 | <string name="verify_installed_content_description">Checks all installed content for corruption</string> |
| 147 | <string name="keys_missing">Encryption keys are missing</string> | ||
| 148 | <string name="keys_missing_description">Firmware and retail games cannot be decrypted</string> | ||
| 149 | <string name="keys_missing_help">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> | ||
| 147 | 150 | ||
| 148 | <!-- Applet launcher strings --> | 151 | <!-- Applet launcher strings --> |
| 149 | <string name="applets">Applet launcher</string> | 152 | <string name="applets">Applet launcher</string> |