diff options
| author | 2023-07-01 15:01:11 -0700 | |
|---|---|---|
| committer | 2023-07-01 15:01:11 -0700 | |
| commit | 98685d48e3cb9f25f6919f004ec62cadf33afad2 (patch) | |
| tree | 9df2ce7f57370641589bfae7196c77b090bcbe0f /src/android | |
| parent | PR feedback + constification (diff) | |
| parent | Update translations (2023-07-01) (#10972) (diff) | |
| download | yuzu-98685d48e3cb9f25f6919f004ec62cadf33afad2.tar.gz yuzu-98685d48e3cb9f25f6919f004ec62cadf33afad2.tar.xz yuzu-98685d48e3cb9f25f6919f004ec62cadf33afad2.zip | |
Merge remote-tracking branch 'origin/master' into ssl
Diffstat (limited to 'src/android')
21 files changed, 578 insertions, 456 deletions
diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts index bab4f4d0f..9a47e2bd8 100644 --- a/src/android/app/build.gradle.kts +++ b/src/android/app/build.gradle.kts | |||
| @@ -26,7 +26,7 @@ val autoVersion = (((System.currentTimeMillis() / 1000) - 1451606400) / 10).toIn | |||
| 26 | android { | 26 | android { |
| 27 | namespace = "org.yuzu.yuzu_emu" | 27 | namespace = "org.yuzu.yuzu_emu" |
| 28 | 28 | ||
| 29 | compileSdkVersion = "android-33" | 29 | compileSdkVersion = "android-34" |
| 30 | ndkVersion = "25.2.9519653" | 30 | ndkVersion = "25.2.9519653" |
| 31 | 31 | ||
| 32 | buildFeatures { | 32 | buildFeatures { |
| @@ -51,7 +51,7 @@ android { | |||
| 51 | // TODO If this is ever modified, change application_id in strings.xml | 51 | // TODO If this is ever modified, change application_id in strings.xml |
| 52 | applicationId = "org.yuzu.yuzu_emu" | 52 | applicationId = "org.yuzu.yuzu_emu" |
| 53 | minSdk = 30 | 53 | minSdk = 30 |
| 54 | targetSdk = 33 | 54 | targetSdk = 34 |
| 55 | versionName = getGitVersion() | 55 | versionName = getGitVersion() |
| 56 | 56 | ||
| 57 | // If you want to use autoVersion for the versionCode, create a property in local.properties | 57 | // If you want to use autoVersion for the versionCode, create a property in local.properties |
diff --git a/src/android/app/src/main/AndroidManifest.xml b/src/android/app/src/main/AndroidManifest.xml index e31ad69e2..51d949d65 100644 --- a/src/android/app/src/main/AndroidManifest.xml +++ b/src/android/app/src/main/AndroidManifest.xml | |||
| @@ -13,6 +13,7 @@ SPDX-License-Identifier: GPL-3.0-or-later | |||
| 13 | 13 | ||
| 14 | <uses-permission android:name="android.permission.INTERNET" /> | 14 | <uses-permission android:name="android.permission.INTERNET" /> |
| 15 | <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> | 15 | <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> |
| 16 | <uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" /> | ||
| 16 | <uses-permission android:name="android.permission.NFC" /> | 17 | <uses-permission android:name="android.permission.NFC" /> |
| 17 | <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> | 18 | <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> |
| 18 | 19 | ||
| @@ -69,7 +70,9 @@ SPDX-License-Identifier: GPL-3.0-or-later | |||
| 69 | android:resource="@xml/nfc_tech_filter" /> | 70 | android:resource="@xml/nfc_tech_filter" /> |
| 70 | </activity> | 71 | </activity> |
| 71 | 72 | ||
| 72 | <service android:name="org.yuzu.yuzu_emu.utils.ForegroundService"/> | 73 | <service android:name="org.yuzu.yuzu_emu.utils.ForegroundService" android:foregroundServiceType="specialUse"> |
| 74 | <property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE" android:value="Keep emulation running in background"/> | ||
| 75 | </service> | ||
| 73 | 76 | ||
| 74 | <provider | 77 | <provider |
| 75 | android:name=".features.DocumentProvider" | 78 | android:name=".features.DocumentProvider" |
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 f860cdd4b..9c32e044c 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 | |||
| @@ -286,7 +286,7 @@ object NativeLibrary { | |||
| 286 | /** | 286 | /** |
| 287 | * Unpauses emulation from a paused state. | 287 | * Unpauses emulation from a paused state. |
| 288 | */ | 288 | */ |
| 289 | external fun unPauseEmulation() | 289 | external fun unpauseEmulation() |
| 290 | 290 | ||
| 291 | /** | 291 | /** |
| 292 | * Pauses emulation. | 292 | * Pauses emulation. |
| @@ -314,6 +314,21 @@ object NativeLibrary { | |||
| 314 | external fun isPaused(): Boolean | 314 | external fun isPaused(): Boolean |
| 315 | 315 | ||
| 316 | /** | 316 | /** |
| 317 | * Mutes emulation sound | ||
| 318 | */ | ||
| 319 | external fun muteAudio(): Boolean | ||
| 320 | |||
| 321 | /** | ||
| 322 | * Unmutes emulation sound | ||
| 323 | */ | ||
| 324 | external fun unmuteAudio(): Boolean | ||
| 325 | |||
| 326 | /** | ||
| 327 | * Returns true if emulation audio is muted. | ||
| 328 | */ | ||
| 329 | external fun isMuted(): Boolean | ||
| 330 | |||
| 331 | /** | ||
| 317 | * Returns the performance stats for the current game | 332 | * Returns the performance stats for the current game |
| 318 | */ | 333 | */ |
| 319 | external fun getPerfStats(): DoubleArray | 334 | external fun getPerfStats(): DoubleArray |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt index f0a6753a9..ae665ed2e 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt | |||
| @@ -27,13 +27,13 @@ import android.view.MotionEvent | |||
| 27 | import android.view.Surface | 27 | import android.view.Surface |
| 28 | import android.view.View | 28 | import android.view.View |
| 29 | import android.view.inputmethod.InputMethodManager | 29 | import android.view.inputmethod.InputMethodManager |
| 30 | import android.widget.Toast | ||
| 30 | import androidx.activity.viewModels | 31 | import androidx.activity.viewModels |
| 31 | import androidx.appcompat.app.AppCompatActivity | 32 | import androidx.appcompat.app.AppCompatActivity |
| 32 | import androidx.core.view.WindowCompat | 33 | import androidx.core.view.WindowCompat |
| 33 | import androidx.core.view.WindowInsetsCompat | 34 | import androidx.core.view.WindowInsetsCompat |
| 34 | import androidx.core.view.WindowInsetsControllerCompat | 35 | import androidx.core.view.WindowInsetsControllerCompat |
| 35 | import androidx.navigation.fragment.NavHostFragment | 36 | import androidx.navigation.fragment.NavHostFragment |
| 36 | import kotlin.math.roundToInt | ||
| 37 | import org.yuzu.yuzu_emu.NativeLibrary | 37 | import org.yuzu.yuzu_emu.NativeLibrary |
| 38 | import org.yuzu.yuzu_emu.R | 38 | import org.yuzu.yuzu_emu.R |
| 39 | import org.yuzu.yuzu_emu.databinding.ActivityEmulationBinding | 39 | import org.yuzu.yuzu_emu.databinding.ActivityEmulationBinding |
| @@ -44,8 +44,10 @@ import org.yuzu.yuzu_emu.model.Game | |||
| 44 | import org.yuzu.yuzu_emu.utils.ControllerMappingHelper | 44 | import org.yuzu.yuzu_emu.utils.ControllerMappingHelper |
| 45 | import org.yuzu.yuzu_emu.utils.ForegroundService | 45 | import org.yuzu.yuzu_emu.utils.ForegroundService |
| 46 | import org.yuzu.yuzu_emu.utils.InputHandler | 46 | import org.yuzu.yuzu_emu.utils.InputHandler |
| 47 | import org.yuzu.yuzu_emu.utils.MemoryUtil | ||
| 47 | import org.yuzu.yuzu_emu.utils.NfcReader | 48 | import org.yuzu.yuzu_emu.utils.NfcReader |
| 48 | import org.yuzu.yuzu_emu.utils.ThemeHelper | 49 | import org.yuzu.yuzu_emu.utils.ThemeHelper |
| 50 | import kotlin.math.roundToInt | ||
| 49 | 51 | ||
| 50 | class EmulationActivity : AppCompatActivity(), SensorEventListener { | 52 | class EmulationActivity : AppCompatActivity(), SensorEventListener { |
| 51 | private lateinit var binding: ActivityEmulationBinding | 53 | private lateinit var binding: ActivityEmulationBinding |
| @@ -63,6 +65,8 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { | |||
| 63 | 65 | ||
| 64 | private val actionPause = "ACTION_EMULATOR_PAUSE" | 66 | private val actionPause = "ACTION_EMULATOR_PAUSE" |
| 65 | private val actionPlay = "ACTION_EMULATOR_PLAY" | 67 | private val actionPlay = "ACTION_EMULATOR_PLAY" |
| 68 | private val actionMute = "ACTION_EMULATOR_MUTE" | ||
| 69 | private val actionUnmute = "ACTION_EMULATOR_UNMUTE" | ||
| 66 | 70 | ||
| 67 | private val settingsViewModel: SettingsViewModel by viewModels() | 71 | private val settingsViewModel: SettingsViewModel by viewModels() |
| 68 | 72 | ||
| @@ -102,6 +106,19 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { | |||
| 102 | inputHandler = InputHandler() | 106 | inputHandler = InputHandler() |
| 103 | inputHandler.initialize() | 107 | inputHandler.initialize() |
| 104 | 108 | ||
| 109 | val memoryUtil = MemoryUtil(this) | ||
| 110 | if (memoryUtil.isLessThan(8, MemoryUtil.Gb)) { | ||
| 111 | Toast.makeText( | ||
| 112 | this, | ||
| 113 | getString( | ||
| 114 | R.string.device_memory_inadequate, | ||
| 115 | memoryUtil.getDeviceRAM(), | ||
| 116 | "8 ${getString(R.string.memory_gigabyte)}" | ||
| 117 | ), | ||
| 118 | Toast.LENGTH_LONG | ||
| 119 | ).show() | ||
| 120 | } | ||
| 121 | |||
| 105 | // Start a foreground service to prevent the app from getting killed in the background | 122 | // Start a foreground service to prevent the app from getting killed in the background |
| 106 | val startIntent = Intent(this, ForegroundService::class.java) | 123 | val startIntent = Intent(this, ForegroundService::class.java) |
| 107 | startForegroundService(startIntent) | 124 | startForegroundService(startIntent) |
| @@ -305,6 +322,41 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { | |||
| 305 | pictureInPictureActions.add(pauseRemoteAction) | 322 | pictureInPictureActions.add(pauseRemoteAction) |
| 306 | } | 323 | } |
| 307 | 324 | ||
| 325 | if (NativeLibrary.isMuted()) { | ||
| 326 | val unmuteIcon = Icon.createWithResource( | ||
| 327 | this@EmulationActivity, | ||
| 328 | R.drawable.ic_pip_unmute | ||
| 329 | ) | ||
| 330 | val unmutePendingIntent = PendingIntent.getBroadcast( | ||
| 331 | this@EmulationActivity, | ||
| 332 | R.drawable.ic_pip_unmute, | ||
| 333 | Intent(actionUnmute), | ||
| 334 | pendingFlags | ||
| 335 | ) | ||
| 336 | val unmuteRemoteAction = RemoteAction( | ||
| 337 | unmuteIcon, | ||
| 338 | getString(R.string.unmute), | ||
| 339 | getString(R.string.unmute), | ||
| 340 | unmutePendingIntent | ||
| 341 | ) | ||
| 342 | pictureInPictureActions.add(unmuteRemoteAction) | ||
| 343 | } else { | ||
| 344 | val muteIcon = Icon.createWithResource(this@EmulationActivity, R.drawable.ic_pip_mute) | ||
| 345 | val mutePendingIntent = PendingIntent.getBroadcast( | ||
| 346 | this@EmulationActivity, | ||
| 347 | R.drawable.ic_pip_mute, | ||
| 348 | Intent(actionMute), | ||
| 349 | pendingFlags | ||
| 350 | ) | ||
| 351 | val muteRemoteAction = RemoteAction( | ||
| 352 | muteIcon, | ||
| 353 | getString(R.string.mute), | ||
| 354 | getString(R.string.mute), | ||
| 355 | mutePendingIntent | ||
| 356 | ) | ||
| 357 | pictureInPictureActions.add(muteRemoteAction) | ||
| 358 | } | ||
| 359 | |||
| 308 | return this.apply { setActions(pictureInPictureActions) } | 360 | return this.apply { setActions(pictureInPictureActions) } |
| 309 | } | 361 | } |
| 310 | 362 | ||
| @@ -322,10 +374,15 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { | |||
| 322 | private var pictureInPictureReceiver = object : BroadcastReceiver() { | 374 | private var pictureInPictureReceiver = object : BroadcastReceiver() { |
| 323 | override fun onReceive(context: Context?, intent: Intent) { | 375 | override fun onReceive(context: Context?, intent: Intent) { |
| 324 | if (intent.action == actionPlay) { | 376 | if (intent.action == actionPlay) { |
| 325 | if (NativeLibrary.isPaused()) NativeLibrary.unPauseEmulation() | 377 | if (NativeLibrary.isPaused()) NativeLibrary.unpauseEmulation() |
| 326 | } else if (intent.action == actionPause) { | 378 | } else if (intent.action == actionPause) { |
| 327 | if (!NativeLibrary.isPaused()) NativeLibrary.pauseEmulation() | 379 | if (!NativeLibrary.isPaused()) NativeLibrary.pauseEmulation() |
| 328 | } | 380 | } |
| 381 | if (intent.action == actionUnmute) { | ||
| 382 | if (NativeLibrary.isMuted()) NativeLibrary.unmuteAudio() | ||
| 383 | } else if (intent.action == actionMute) { | ||
| 384 | if (!NativeLibrary.isMuted()) NativeLibrary.muteAudio() | ||
| 385 | } | ||
| 329 | buildPictureInPictureParams() | 386 | buildPictureInPictureParams() |
| 330 | } | 387 | } |
| 331 | } | 388 | } |
| @@ -339,6 +396,8 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { | |||
| 339 | IntentFilter().apply { | 396 | IntentFilter().apply { |
| 340 | addAction(actionPause) | 397 | addAction(actionPause) |
| 341 | addAction(actionPlay) | 398 | addAction(actionPlay) |
| 399 | addAction(actionMute) | ||
| 400 | addAction(actionUnmute) | ||
| 342 | }.also { | 401 | }.also { |
| 343 | registerReceiver(pictureInPictureReceiver, it) | 402 | registerReceiver(pictureInPictureReceiver, it) |
| 344 | } | 403 | } |
| @@ -347,6 +406,8 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { | |||
| 347 | unregisterReceiver(pictureInPictureReceiver) | 406 | unregisterReceiver(pictureInPictureReceiver) |
| 348 | } catch (ignored: Exception) { | 407 | } catch (ignored: Exception) { |
| 349 | } | 408 | } |
| 409 | // Always resume audio, since there is no UI button | ||
| 410 | if (NativeLibrary.isMuted()) NativeLibrary.unmuteAudio() | ||
| 350 | } | 411 | } |
| 351 | } | 412 | } |
| 352 | 413 | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt index 4643418c1..09976db62 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt | |||
| @@ -714,7 +714,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 714 | State.PAUSED -> { | 714 | State.PAUSED -> { |
| 715 | Log.debug("[EmulationFragment] Resuming emulation.") | 715 | Log.debug("[EmulationFragment] Resuming emulation.") |
| 716 | NativeLibrary.surfaceChanged(surface) | 716 | NativeLibrary.surfaceChanged(surface) |
| 717 | NativeLibrary.unPauseEmulation() | 717 | NativeLibrary.unpauseEmulation() |
| 718 | } | 718 | } |
| 719 | 719 | ||
| 720 | else -> Log.debug("[EmulationFragment] Bug, run called while already running.") | 720 | else -> Log.debug("[EmulationFragment] Bug, run called while already running.") |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt index 6f8adbba5..5a36ffad4 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt | |||
| @@ -68,79 +68,109 @@ class HomeSettingsFragment : Fragment() { | |||
| 68 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | 68 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { |
| 69 | mainActivity = requireActivity() as MainActivity | 69 | mainActivity = requireActivity() as MainActivity |
| 70 | 70 | ||
| 71 | val optionsList: MutableList<HomeSetting> = mutableListOf( | 71 | val optionsList: MutableList<HomeSetting> = mutableListOf<HomeSetting>().apply { |
| 72 | HomeSetting( | 72 | add( |
| 73 | R.string.advanced_settings, | 73 | HomeSetting( |
| 74 | R.string.settings_description, | 74 | R.string.advanced_settings, |
| 75 | R.drawable.ic_settings | 75 | R.string.settings_description, |
| 76 | ) { SettingsActivity.launch(requireContext(), SettingsFile.FILE_NAME_CONFIG, "") }, | 76 | R.drawable.ic_settings |
| 77 | HomeSetting( | 77 | ) { SettingsActivity.launch(requireContext(), SettingsFile.FILE_NAME_CONFIG, "") } |
| 78 | R.string.open_user_folder, | 78 | ) |
| 79 | R.string.open_user_folder_description, | 79 | add( |
| 80 | R.drawable.ic_folder_open | 80 | HomeSetting( |
| 81 | ) { openFileManager() }, | 81 | R.string.open_user_folder, |
| 82 | HomeSetting( | 82 | R.string.open_user_folder_description, |
| 83 | R.string.preferences_theme, | 83 | R.drawable.ic_folder_open |
| 84 | R.string.theme_and_color_description, | 84 | ) { openFileManager() } |
| 85 | R.drawable.ic_palette | 85 | ) |
| 86 | ) { SettingsActivity.launch(requireContext(), Settings.SECTION_THEME, "") }, | 86 | add( |
| 87 | HomeSetting( | 87 | HomeSetting( |
| 88 | R.string.install_gpu_driver, | 88 | R.string.preferences_theme, |
| 89 | R.string.install_gpu_driver_description, | 89 | R.string.theme_and_color_description, |
| 90 | R.drawable.ic_exit | 90 | R.drawable.ic_palette |
| 91 | ) { driverInstaller() }, | 91 | ) { SettingsActivity.launch(requireContext(), Settings.SECTION_THEME, "") } |
| 92 | HomeSetting( | 92 | ) |
| 93 | R.string.install_amiibo_keys, | 93 | |
| 94 | R.string.install_amiibo_keys_description, | 94 | if (GpuDriverHelper.supportsCustomDriverLoading()) { |
| 95 | R.drawable.ic_nfc | 95 | add( |
| 96 | ) { mainActivity.getAmiiboKey.launch(arrayOf("*/*")) }, | 96 | HomeSetting( |
| 97 | HomeSetting( | 97 | R.string.install_gpu_driver, |
| 98 | R.string.install_game_content, | 98 | R.string.install_gpu_driver_description, |
| 99 | R.string.install_game_content_description, | 99 | R.drawable.ic_exit |
| 100 | R.drawable.ic_system_update_alt | 100 | ) { driverInstaller() } |
| 101 | ) { mainActivity.installGameUpdate.launch(arrayOf("*/*")) }, | ||
| 102 | HomeSetting( | ||
| 103 | R.string.select_games_folder, | ||
| 104 | R.string.select_games_folder_description, | ||
| 105 | R.drawable.ic_add | ||
| 106 | ) { | ||
| 107 | mainActivity.getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data) | ||
| 108 | }, | ||
| 109 | HomeSetting( | ||
| 110 | R.string.manage_save_data, | ||
| 111 | R.string.import_export_saves_description, | ||
| 112 | R.drawable.ic_save | ||
| 113 | ) { | ||
| 114 | ImportExportSavesFragment().show( | ||
| 115 | parentFragmentManager, | ||
| 116 | ImportExportSavesFragment.TAG | ||
| 117 | ) | 101 | ) |
| 118 | }, | ||
| 119 | HomeSetting( | ||
| 120 | R.string.install_prod_keys, | ||
| 121 | R.string.install_prod_keys_description, | ||
| 122 | R.drawable.ic_unlock | ||
| 123 | ) { mainActivity.getProdKey.launch(arrayOf("*/*")) }, | ||
| 124 | HomeSetting( | ||
| 125 | R.string.install_firmware, | ||
| 126 | R.string.install_firmware_description, | ||
| 127 | R.drawable.ic_firmware | ||
| 128 | ) { mainActivity.getFirmware.launch(arrayOf("application/zip")) }, | ||
| 129 | HomeSetting( | ||
| 130 | R.string.share_log, | ||
| 131 | R.string.share_log_description, | ||
| 132 | R.drawable.ic_log | ||
| 133 | ) { shareLog() }, | ||
| 134 | HomeSetting( | ||
| 135 | R.string.about, | ||
| 136 | R.string.about_description, | ||
| 137 | R.drawable.ic_info_outline | ||
| 138 | ) { | ||
| 139 | exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true) | ||
| 140 | parentFragmentManager.primaryNavigationFragment?.findNavController() | ||
| 141 | ?.navigate(R.id.action_homeSettingsFragment_to_aboutFragment) | ||
| 142 | } | 102 | } |
| 143 | ) | 103 | |
| 104 | add( | ||
| 105 | HomeSetting( | ||
| 106 | R.string.install_amiibo_keys, | ||
| 107 | R.string.install_amiibo_keys_description, | ||
| 108 | R.drawable.ic_nfc | ||
| 109 | ) { mainActivity.getAmiiboKey.launch(arrayOf("*/*")) } | ||
| 110 | ) | ||
| 111 | add( | ||
| 112 | HomeSetting( | ||
| 113 | R.string.install_game_content, | ||
| 114 | R.string.install_game_content_description, | ||
| 115 | R.drawable.ic_system_update_alt | ||
| 116 | ) { mainActivity.installGameUpdate.launch(arrayOf("*/*")) } | ||
| 117 | ) | ||
| 118 | add( | ||
| 119 | HomeSetting( | ||
| 120 | R.string.select_games_folder, | ||
| 121 | R.string.select_games_folder_description, | ||
| 122 | R.drawable.ic_add | ||
| 123 | ) { | ||
| 124 | mainActivity.getGamesDirectory.launch( | ||
| 125 | Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data | ||
| 126 | ) | ||
| 127 | } | ||
| 128 | ) | ||
| 129 | add( | ||
| 130 | HomeSetting( | ||
| 131 | R.string.manage_save_data, | ||
| 132 | R.string.import_export_saves_description, | ||
| 133 | R.drawable.ic_save | ||
| 134 | ) { | ||
| 135 | ImportExportSavesFragment().show( | ||
| 136 | parentFragmentManager, | ||
| 137 | ImportExportSavesFragment.TAG | ||
| 138 | ) | ||
| 139 | } | ||
| 140 | ) | ||
| 141 | add( | ||
| 142 | HomeSetting( | ||
| 143 | R.string.install_prod_keys, | ||
| 144 | R.string.install_prod_keys_description, | ||
| 145 | R.drawable.ic_unlock | ||
| 146 | ) { mainActivity.getProdKey.launch(arrayOf("*/*")) } | ||
| 147 | ) | ||
| 148 | add( | ||
| 149 | HomeSetting( | ||
| 150 | R.string.install_firmware, | ||
| 151 | R.string.install_firmware_description, | ||
| 152 | R.drawable.ic_firmware | ||
| 153 | ) { mainActivity.getFirmware.launch(arrayOf("application/zip")) } | ||
| 154 | ) | ||
| 155 | add( | ||
| 156 | HomeSetting( | ||
| 157 | R.string.share_log, | ||
| 158 | R.string.share_log_description, | ||
| 159 | R.drawable.ic_log | ||
| 160 | ) { shareLog() } | ||
| 161 | ) | ||
| 162 | add( | ||
| 163 | HomeSetting( | ||
| 164 | R.string.about, | ||
| 165 | R.string.about_description, | ||
| 166 | R.drawable.ic_info_outline | ||
| 167 | ) { | ||
| 168 | exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true) | ||
| 169 | parentFragmentManager.primaryNavigationFragment?.findNavController() | ||
| 170 | ?.navigate(R.id.action_homeSettingsFragment_to_aboutFragment) | ||
| 171 | } | ||
| 172 | ) | ||
| 173 | } | ||
| 144 | 174 | ||
| 145 | if (!BuildConfig.PREMIUM) { | 175 | if (!BuildConfig.PREMIUM) { |
| 146 | optionsList.add( | 176 | optionsList.add( |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LongMessageDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LongMessageDialogFragment.kt new file mode 100644 index 000000000..b29b627e9 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LongMessageDialogFragment.kt | |||
| @@ -0,0 +1,62 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | package org.yuzu.yuzu_emu.fragments | ||
| 5 | |||
| 6 | import android.app.Dialog | ||
| 7 | import android.content.Intent | ||
| 8 | import android.net.Uri | ||
| 9 | import android.os.Bundle | ||
| 10 | import androidx.fragment.app.DialogFragment | ||
| 11 | import com.google.android.material.dialog.MaterialAlertDialogBuilder | ||
| 12 | import org.yuzu.yuzu_emu.R | ||
| 13 | |||
| 14 | class LongMessageDialogFragment : DialogFragment() { | ||
| 15 | override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { | ||
| 16 | val titleId = requireArguments().getInt(TITLE) | ||
| 17 | val description = requireArguments().getString(DESCRIPTION) | ||
| 18 | val helpLinkId = requireArguments().getInt(HELP_LINK) | ||
| 19 | |||
| 20 | val dialog = MaterialAlertDialogBuilder(requireContext()) | ||
| 21 | .setPositiveButton(R.string.close, null) | ||
| 22 | .setTitle(titleId) | ||
| 23 | .setMessage(description) | ||
| 24 | |||
| 25 | if (helpLinkId != 0) { | ||
| 26 | dialog.setNeutralButton(R.string.learn_more) { _, _ -> | ||
| 27 | openLink(getString(helpLinkId)) | ||
| 28 | } | ||
| 29 | } | ||
| 30 | |||
| 31 | return dialog.show() | ||
| 32 | } | ||
| 33 | |||
| 34 | private fun openLink(link: String) { | ||
| 35 | val intent = Intent(Intent.ACTION_VIEW, Uri.parse(link)) | ||
| 36 | startActivity(intent) | ||
| 37 | } | ||
| 38 | |||
| 39 | companion object { | ||
| 40 | const val TAG = "LongMessageDialogFragment" | ||
| 41 | |||
| 42 | private const val TITLE = "Title" | ||
| 43 | private const val DESCRIPTION = "Description" | ||
| 44 | private const val HELP_LINK = "Link" | ||
| 45 | |||
| 46 | fun newInstance( | ||
| 47 | titleId: Int, | ||
| 48 | description: String, | ||
| 49 | helpLinkId: Int = 0 | ||
| 50 | ): LongMessageDialogFragment { | ||
| 51 | val dialog = LongMessageDialogFragment() | ||
| 52 | val bundle = Bundle() | ||
| 53 | bundle.apply { | ||
| 54 | putInt(TITLE, titleId) | ||
| 55 | putString(DESCRIPTION, description) | ||
| 56 | putInt(HELP_LINK, helpLinkId) | ||
| 57 | } | ||
| 58 | dialog.arguments = bundle | ||
| 59 | return dialog | ||
| 60 | } | ||
| 61 | } | ||
| 62 | } | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt index dd6c895fd..f54dccc69 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt | |||
| @@ -29,7 +29,6 @@ import org.yuzu.yuzu_emu.layout.AutofitGridLayoutManager | |||
| 29 | import org.yuzu.yuzu_emu.model.Game | 29 | import org.yuzu.yuzu_emu.model.Game |
| 30 | import org.yuzu.yuzu_emu.model.GamesViewModel | 30 | import org.yuzu.yuzu_emu.model.GamesViewModel |
| 31 | import org.yuzu.yuzu_emu.model.HomeViewModel | 31 | import org.yuzu.yuzu_emu.model.HomeViewModel |
| 32 | import org.yuzu.yuzu_emu.utils.FileUtil | ||
| 33 | 32 | ||
| 34 | class SearchFragment : Fragment() { | 33 | class SearchFragment : Fragment() { |
| 35 | private var _binding: FragmentSearchBinding? = null | 34 | private var _binding: FragmentSearchBinding? = null |
| @@ -128,10 +127,7 @@ class SearchFragment : Fragment() { | |||
| 128 | 127 | ||
| 129 | R.id.chip_homebrew -> baseList.filter { it.isHomebrew } | 128 | R.id.chip_homebrew -> baseList.filter { it.isHomebrew } |
| 130 | 129 | ||
| 131 | R.id.chip_retail -> baseList.filter { | 130 | R.id.chip_retail -> baseList.filter { !it.isHomebrew } |
| 132 | FileUtil.hasExtension(it.path, "xci") || | ||
| 133 | FileUtil.hasExtension(it.path, "nsp") | ||
| 134 | } | ||
| 135 | 131 | ||
| 136 | else -> baseList | 132 | else -> baseList |
| 137 | } | 133 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt index 6a048e39f..6527c64ab 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt | |||
| @@ -43,7 +43,7 @@ class Game( | |||
| 43 | 43 | ||
| 44 | companion object { | 44 | companion object { |
| 45 | val extensions: Set<String> = HashSet( | 45 | val extensions: Set<String> = HashSet( |
| 46 | listOf(".xci", ".nsp", ".nca", ".nro") | 46 | listOf("xci", "nsp", "nca", "nro") |
| 47 | ) | 47 | ) |
| 48 | } | 48 | } |
| 49 | } | 49 | } |
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 cc1d87f1b..f7d7aed1e 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 | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | package org.yuzu.yuzu_emu.ui.main | 4 | package org.yuzu.yuzu_emu.ui.main |
| 5 | 5 | ||
| 6 | import android.content.Intent | 6 | import android.content.Intent |
| 7 | import android.net.Uri | ||
| 7 | import android.os.Bundle | 8 | import android.os.Bundle |
| 8 | import android.view.View | 9 | import android.view.View |
| 9 | import android.view.ViewGroup.MarginLayoutParams | 10 | import android.view.ViewGroup.MarginLayoutParams |
| @@ -42,6 +43,7 @@ import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel | |||
| 42 | import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity | 43 | import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity |
| 43 | import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile | 44 | import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile |
| 44 | import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment | 45 | import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment |
| 46 | import org.yuzu.yuzu_emu.fragments.LongMessageDialogFragment | ||
| 45 | import org.yuzu.yuzu_emu.fragments.MessageDialogFragment | 47 | import org.yuzu.yuzu_emu.fragments.MessageDialogFragment |
| 46 | import org.yuzu.yuzu_emu.model.GamesViewModel | 48 | import org.yuzu.yuzu_emu.model.GamesViewModel |
| 47 | import org.yuzu.yuzu_emu.model.HomeViewModel | 49 | import org.yuzu.yuzu_emu.model.HomeViewModel |
| @@ -294,7 +296,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { | |||
| 294 | return@registerForActivityResult | 296 | return@registerForActivityResult |
| 295 | } | 297 | } |
| 296 | 298 | ||
| 297 | if (!FileUtil.hasExtension(result, "keys")) { | 299 | if (FileUtil.getExtension(result) != "keys") { |
| 298 | MessageDialogFragment.newInstance( | 300 | MessageDialogFragment.newInstance( |
| 299 | R.string.reading_keys_failure, | 301 | R.string.reading_keys_failure, |
| 300 | R.string.install_prod_keys_failure_extension_description | 302 | R.string.install_prod_keys_failure_extension_description |
| @@ -391,7 +393,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { | |||
| 391 | return@registerForActivityResult | 393 | return@registerForActivityResult |
| 392 | } | 394 | } |
| 393 | 395 | ||
| 394 | if (!FileUtil.hasExtension(result, "bin")) { | 396 | if (FileUtil.getExtension(result) != "bin") { |
| 395 | MessageDialogFragment.newInstance( | 397 | MessageDialogFragment.newInstance( |
| 396 | R.string.reading_keys_failure, | 398 | R.string.reading_keys_failure, |
| 397 | R.string.install_amiibo_keys_failure_extension_description | 399 | R.string.install_amiibo_keys_failure_extension_description |
| @@ -481,62 +483,110 @@ class MainActivity : AppCompatActivity(), ThemeProvider { | |||
| 481 | } | 483 | } |
| 482 | } | 484 | } |
| 483 | 485 | ||
| 484 | val installGameUpdate = | 486 | val installGameUpdate = registerForActivityResult( |
| 485 | registerForActivityResult(ActivityResultContracts.OpenDocument()) { | 487 | ActivityResultContracts.OpenMultipleDocuments() |
| 486 | if (it == null) { | 488 | ) { documents: List<Uri> -> |
| 487 | return@registerForActivityResult | 489 | if (documents.isNotEmpty()) { |
| 488 | } | ||
| 489 | |||
| 490 | IndeterminateProgressDialogFragment.newInstance( | 490 | IndeterminateProgressDialogFragment.newInstance( |
| 491 | this@MainActivity, | 491 | this@MainActivity, |
| 492 | R.string.install_game_content | 492 | R.string.install_game_content |
| 493 | ) { | 493 | ) { |
| 494 | val result = NativeLibrary.installFileToNand(it.toString()) | 494 | var installSuccess = 0 |
| 495 | var installOverwrite = 0 | ||
| 496 | var errorBaseGame = 0 | ||
| 497 | var errorExtension = 0 | ||
| 498 | var errorOther = 0 | ||
| 499 | var errorTotal = 0 | ||
| 495 | lifecycleScope.launch { | 500 | lifecycleScope.launch { |
| 496 | withContext(Dispatchers.Main) { | 501 | documents.forEach { |
| 497 | when (result) { | 502 | when (NativeLibrary.installFileToNand(it.toString())) { |
| 498 | NativeLibrary.InstallFileToNandResult.Success -> { | 503 | NativeLibrary.InstallFileToNandResult.Success -> { |
| 499 | Toast.makeText( | 504 | installSuccess += 1 |
| 500 | applicationContext, | ||
| 501 | R.string.install_game_content_success, | ||
| 502 | Toast.LENGTH_SHORT | ||
| 503 | ).show() | ||
| 504 | } | 505 | } |
| 505 | 506 | ||
| 506 | NativeLibrary.InstallFileToNandResult.SuccessFileOverwritten -> { | 507 | NativeLibrary.InstallFileToNandResult.SuccessFileOverwritten -> { |
| 507 | Toast.makeText( | 508 | installOverwrite += 1 |
| 508 | applicationContext, | ||
| 509 | R.string.install_game_content_success_overwrite, | ||
| 510 | Toast.LENGTH_SHORT | ||
| 511 | ).show() | ||
| 512 | } | 509 | } |
| 513 | 510 | ||
| 514 | NativeLibrary.InstallFileToNandResult.ErrorBaseGame -> { | 511 | NativeLibrary.InstallFileToNandResult.ErrorBaseGame -> { |
| 515 | MessageDialogFragment.newInstance( | 512 | errorBaseGame += 1 |
| 516 | R.string.install_game_content_failure, | ||
| 517 | R.string.install_game_content_failure_base | ||
| 518 | ).show(supportFragmentManager, MessageDialogFragment.TAG) | ||
| 519 | } | 513 | } |
| 520 | 514 | ||
| 521 | NativeLibrary.InstallFileToNandResult.ErrorFilenameExtension -> { | 515 | NativeLibrary.InstallFileToNandResult.ErrorFilenameExtension -> { |
| 522 | MessageDialogFragment.newInstance( | 516 | errorExtension += 1 |
| 523 | R.string.install_game_content_failure, | ||
| 524 | R.string.install_game_content_failure_file_extension, | ||
| 525 | R.string.install_game_content_help_link | ||
| 526 | ).show(supportFragmentManager, MessageDialogFragment.TAG) | ||
| 527 | } | 517 | } |
| 528 | 518 | ||
| 529 | else -> { | 519 | else -> { |
| 530 | MessageDialogFragment.newInstance( | 520 | errorOther += 1 |
| 531 | R.string.install_game_content_failure, | ||
| 532 | R.string.install_game_content_failure_description, | ||
| 533 | R.string.install_game_content_help_link | ||
| 534 | ).show(supportFragmentManager, MessageDialogFragment.TAG) | ||
| 535 | } | 521 | } |
| 536 | } | 522 | } |
| 537 | } | 523 | } |
| 524 | withContext(Dispatchers.Main) { | ||
| 525 | val separator = System.getProperty("line.separator") ?: "\n" | ||
| 526 | val installResult = StringBuilder() | ||
| 527 | if (installSuccess > 0) { | ||
| 528 | installResult.append( | ||
| 529 | getString( | ||
| 530 | R.string.install_game_content_success_install, | ||
| 531 | installSuccess | ||
| 532 | ) | ||
| 533 | ) | ||
| 534 | installResult.append(separator) | ||
| 535 | } | ||
| 536 | if (installOverwrite > 0) { | ||
| 537 | installResult.append( | ||
| 538 | getString( | ||
| 539 | R.string.install_game_content_success_overwrite, | ||
| 540 | installOverwrite | ||
| 541 | ) | ||
| 542 | ) | ||
| 543 | installResult.append(separator) | ||
| 544 | } | ||
| 545 | errorTotal = errorBaseGame + errorExtension + errorOther | ||
| 546 | if (errorTotal > 0) { | ||
| 547 | installResult.append(separator) | ||
| 548 | installResult.append( | ||
| 549 | getString( | ||
| 550 | R.string.install_game_content_failed_count, | ||
| 551 | errorTotal | ||
| 552 | ) | ||
| 553 | ) | ||
| 554 | installResult.append(separator) | ||
| 555 | if (errorBaseGame > 0) { | ||
| 556 | installResult.append(separator) | ||
| 557 | installResult.append( | ||
| 558 | getString(R.string.install_game_content_failure_base) | ||
| 559 | ) | ||
| 560 | installResult.append(separator) | ||
| 561 | } | ||
| 562 | if (errorExtension > 0) { | ||
| 563 | installResult.append(separator) | ||
| 564 | installResult.append( | ||
| 565 | getString(R.string.install_game_content_failure_file_extension) | ||
| 566 | ) | ||
| 567 | installResult.append(separator) | ||
| 568 | } | ||
| 569 | if (errorOther > 0) { | ||
| 570 | installResult.append( | ||
| 571 | getString(R.string.install_game_content_failure_description) | ||
| 572 | ) | ||
| 573 | installResult.append(separator) | ||
| 574 | } | ||
| 575 | LongMessageDialogFragment.newInstance( | ||
| 576 | R.string.install_game_content_failure, | ||
| 577 | installResult.toString().trim(), | ||
| 578 | R.string.install_game_content_help_link | ||
| 579 | ).show(supportFragmentManager, LongMessageDialogFragment.TAG) | ||
| 580 | } else { | ||
| 581 | LongMessageDialogFragment.newInstance( | ||
| 582 | R.string.install_game_content_success, | ||
| 583 | installResult.toString().trim() | ||
| 584 | ).show(supportFragmentManager, LongMessageDialogFragment.TAG) | ||
| 585 | } | ||
| 586 | } | ||
| 538 | } | 587 | } |
| 539 | return@newInstance result | 588 | return@newInstance installSuccess + installOverwrite + errorTotal |
| 540 | }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG) | 589 | }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG) |
| 541 | } | 590 | } |
| 591 | } | ||
| 542 | } | 592 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt index 9f3bbe56f..142af5f26 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt | |||
| @@ -7,7 +7,6 @@ import android.content.Context | |||
| 7 | import android.database.Cursor | 7 | import android.database.Cursor |
| 8 | import android.net.Uri | 8 | import android.net.Uri |
| 9 | import android.provider.DocumentsContract | 9 | import android.provider.DocumentsContract |
| 10 | import android.provider.OpenableColumns | ||
| 11 | import androidx.documentfile.provider.DocumentFile | 10 | import androidx.documentfile.provider.DocumentFile |
| 12 | import java.io.BufferedInputStream | 11 | import java.io.BufferedInputStream |
| 13 | import java.io.File | 12 | import java.io.File |
| @@ -185,19 +184,18 @@ object FileUtil { | |||
| 185 | 184 | ||
| 186 | /** | 185 | /** |
| 187 | * Get file display name from given path | 186 | * Get file display name from given path |
| 188 | * @param path content uri path | 187 | * @param uri content uri |
| 189 | * @return String display name | 188 | * @return String display name |
| 190 | */ | 189 | */ |
| 191 | fun getFilename(context: Context, path: String): String { | 190 | fun getFilename(uri: Uri): String { |
| 192 | val resolver = context.contentResolver | 191 | val resolver = YuzuApplication.appContext.contentResolver |
| 193 | val columns = arrayOf( | 192 | val columns = arrayOf( |
| 194 | DocumentsContract.Document.COLUMN_DISPLAY_NAME | 193 | DocumentsContract.Document.COLUMN_DISPLAY_NAME |
| 195 | ) | 194 | ) |
| 196 | var filename = "" | 195 | var filename = "" |
| 197 | var c: Cursor? = null | 196 | var c: Cursor? = null |
| 198 | try { | 197 | try { |
| 199 | val mUri = Uri.parse(path) | 198 | c = resolver.query(uri, columns, null, null, null) |
| 200 | c = resolver.query(mUri, columns, null, null, null) | ||
| 201 | c!!.moveToNext() | 199 | c!!.moveToNext() |
| 202 | filename = c.getString(0) | 200 | filename = c.getString(0) |
| 203 | } catch (e: Exception) { | 201 | } catch (e: Exception) { |
| @@ -326,25 +324,9 @@ object FileUtil { | |||
| 326 | } | 324 | } |
| 327 | } | 325 | } |
| 328 | 326 | ||
| 329 | fun hasExtension(path: String, extension: String): Boolean = | 327 | fun getExtension(uri: Uri): String { |
| 330 | path.substring(path.lastIndexOf(".") + 1).contains(extension) | 328 | val fileName = getFilename(uri) |
| 331 | 329 | return fileName.substring(fileName.lastIndexOf(".") + 1) | |
| 332 | fun hasExtension(uri: Uri, extension: String): Boolean { | 330 | .lowercase() |
| 333 | val fileName: String? | ||
| 334 | val cursor = YuzuApplication.appContext.contentResolver.query(uri, null, null, null, null) | ||
| 335 | val nameIndex = cursor?.getColumnIndex(OpenableColumns.DISPLAY_NAME) | ||
| 336 | cursor?.moveToFirst() | ||
| 337 | |||
| 338 | if (nameIndex == null) { | ||
| 339 | return false | ||
| 340 | } | ||
| 341 | |||
| 342 | fileName = cursor.getString(nameIndex) | ||
| 343 | cursor.close() | ||
| 344 | |||
| 345 | if (fileName == null) { | ||
| 346 | return false | ||
| 347 | } | ||
| 348 | return fileName.substring(fileName.lastIndexOf(".") + 1).contains(extension) | ||
| 349 | } | 331 | } |
| 350 | } | 332 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt index ee9f3e570..f8e7eeca7 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt | |||
| @@ -6,7 +6,6 @@ package org.yuzu.yuzu_emu.utils | |||
| 6 | import android.content.SharedPreferences | 6 | import android.content.SharedPreferences |
| 7 | import android.net.Uri | 7 | import android.net.Uri |
| 8 | import androidx.preference.PreferenceManager | 8 | import androidx.preference.PreferenceManager |
| 9 | import java.util.* | ||
| 10 | import kotlinx.serialization.encodeToString | 9 | import kotlinx.serialization.encodeToString |
| 11 | import kotlinx.serialization.json.Json | 10 | import kotlinx.serialization.json.Json |
| 12 | import org.yuzu.yuzu_emu.NativeLibrary | 11 | import org.yuzu.yuzu_emu.NativeLibrary |
| @@ -33,15 +32,9 @@ object GameHelper { | |||
| 33 | val children = FileUtil.listFiles(context, gamesUri) | 32 | val children = FileUtil.listFiles(context, gamesUri) |
| 34 | for (file in children) { | 33 | for (file in children) { |
| 35 | if (!file.isDirectory) { | 34 | if (!file.isDirectory) { |
| 36 | val filename = file.uri.toString() | 35 | // Check that the file has an extension we care about before trying to read out of it. |
| 37 | val extensionStart = filename.lastIndexOf('.') | 36 | if (Game.extensions.contains(FileUtil.getExtension(file.uri))) { |
| 38 | if (extensionStart > 0) { | 37 | games.add(getGame(file.uri)) |
| 39 | val fileExtension = filename.substring(extensionStart) | ||
| 40 | |||
| 41 | // Check that the file has an extension we care about before trying to read out of it. | ||
| 42 | if (Game.extensions.contains(fileExtension.lowercase(Locale.getDefault()))) { | ||
| 43 | games.add(getGame(filename)) | ||
| 44 | } | ||
| 45 | } | 38 | } |
| 46 | } | 39 | } |
| 47 | } | 40 | } |
| @@ -59,21 +52,19 @@ object GameHelper { | |||
| 59 | return games.toList() | 52 | return games.toList() |
| 60 | } | 53 | } |
| 61 | 54 | ||
| 62 | private fun getGame(filePath: String): Game { | 55 | private fun getGame(uri: Uri): Game { |
| 56 | val filePath = uri.toString() | ||
| 63 | var name = NativeLibrary.getTitle(filePath) | 57 | var name = NativeLibrary.getTitle(filePath) |
| 64 | 58 | ||
| 65 | // If the game's title field is empty, use the filename. | 59 | // If the game's title field is empty, use the filename. |
| 66 | if (name.isEmpty()) { | 60 | if (name.isEmpty()) { |
| 67 | name = filePath.substring(filePath.lastIndexOf("/") + 1) | 61 | name = FileUtil.getFilename(uri) |
| 68 | } | 62 | } |
| 69 | var gameId = NativeLibrary.getGameId(filePath) | 63 | var gameId = NativeLibrary.getGameId(filePath) |
| 70 | 64 | ||
| 71 | // If the game's ID field is empty, use the filename without extension. | 65 | // If the game's ID field is empty, use the filename without extension. |
| 72 | if (gameId.isEmpty()) { | 66 | if (gameId.isEmpty()) { |
| 73 | gameId = filePath.substring( | 67 | gameId = name.substring(0, name.lastIndexOf(".")) |
| 74 | filePath.lastIndexOf("/") + 1, | ||
| 75 | filePath.lastIndexOf(".") | ||
| 76 | ) | ||
| 77 | } | 68 | } |
| 78 | 69 | ||
| 79 | val newGame = Game( | 70 | val newGame = Game( |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt index dad159481..1d4695a2a 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt | |||
| @@ -113,6 +113,8 @@ object GpuDriverHelper { | |||
| 113 | initializeDriverParameters(context) | 113 | initializeDriverParameters(context) |
| 114 | } | 114 | } |
| 115 | 115 | ||
| 116 | external fun supportsCustomDriverLoading(): Boolean | ||
| 117 | |||
| 116 | // Parse the custom driver metadata to retrieve the name. | 118 | // Parse the custom driver metadata to retrieve the name. |
| 117 | val customDriverName: String? | 119 | val customDriverName: String? |
| 118 | get() { | 120 | get() { |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt new file mode 100644 index 000000000..18e5fa0b0 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt | |||
| @@ -0,0 +1,59 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | package org.yuzu.yuzu_emu.utils | ||
| 5 | |||
| 6 | import android.app.ActivityManager | ||
| 7 | import android.content.Context | ||
| 8 | import org.yuzu.yuzu_emu.R | ||
| 9 | import java.util.Locale | ||
| 10 | |||
| 11 | class MemoryUtil(val context: Context) { | ||
| 12 | |||
| 13 | private val Long.floatForm: String | ||
| 14 | get() = String.format(Locale.ROOT, "%.2f", this.toDouble()) | ||
| 15 | |||
| 16 | private fun bytesToSizeUnit(size: Long): String { | ||
| 17 | return when { | ||
| 18 | size < Kb -> "${size.floatForm} ${context.getString(R.string.memory_byte)}" | ||
| 19 | size < Mb -> "${(size / Kb).floatForm} ${context.getString(R.string.memory_kilobyte)}" | ||
| 20 | size < Gb -> "${(size / Mb).floatForm} ${context.getString(R.string.memory_megabyte)}" | ||
| 21 | size < Tb -> "${(size / Gb).floatForm} ${context.getString(R.string.memory_gigabyte)}" | ||
| 22 | size < Pb -> "${(size / Tb).floatForm} ${context.getString(R.string.memory_terabyte)}" | ||
| 23 | size < Eb -> "${(size / Pb).floatForm} ${context.getString(R.string.memory_petabyte)}" | ||
| 24 | else -> "${(size / Eb).floatForm} ${context.getString(R.string.memory_exabyte)}" | ||
| 25 | } | ||
| 26 | } | ||
| 27 | |||
| 28 | private val totalMemory = | ||
| 29 | with(context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager) { | ||
| 30 | val memInfo = ActivityManager.MemoryInfo() | ||
| 31 | getMemoryInfo(memInfo) | ||
| 32 | memInfo.totalMem | ||
| 33 | } | ||
| 34 | |||
| 35 | fun isLessThan(minimum: Int, size: Long): Boolean { | ||
| 36 | return when (size) { | ||
| 37 | Kb -> totalMemory < Mb && totalMemory < minimum | ||
| 38 | Mb -> totalMemory < Gb && (totalMemory / Mb) < minimum | ||
| 39 | Gb -> totalMemory < Tb && (totalMemory / Gb) < minimum | ||
| 40 | Tb -> totalMemory < Pb && (totalMemory / Tb) < minimum | ||
| 41 | Pb -> totalMemory < Eb && (totalMemory / Pb) < minimum | ||
| 42 | Eb -> totalMemory / Eb < minimum | ||
| 43 | else -> totalMemory < Kb && totalMemory < minimum | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | fun getDeviceRAM(): String { | ||
| 48 | return bytesToSizeUnit(totalMemory) | ||
| 49 | } | ||
| 50 | |||
| 51 | companion object { | ||
| 52 | const val Kb: Long = 1024 | ||
| 53 | const val Mb = Kb * 1024 | ||
| 54 | const val Gb = Mb * 1024 | ||
| 55 | const val Tb = Gb * 1024 | ||
| 56 | const val Pb = Tb * 1024 | ||
| 57 | const val Eb = Pb * 1024 | ||
| 58 | } | ||
| 59 | } | ||
diff --git a/src/android/app/src/main/jni/CMakeLists.txt b/src/android/app/src/main/jni/CMakeLists.txt index 041781577..e2ed08e9f 100644 --- a/src/android/app/src/main/jni/CMakeLists.txt +++ b/src/android/app/src/main/jni/CMakeLists.txt | |||
| @@ -14,7 +14,6 @@ add_library(yuzu-android SHARED | |||
| 14 | id_cache.cpp | 14 | id_cache.cpp |
| 15 | id_cache.h | 15 | id_cache.h |
| 16 | native.cpp | 16 | native.cpp |
| 17 | native.h | ||
| 18 | ) | 17 | ) |
| 19 | 18 | ||
| 20 | set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR}) | 19 | set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR}) |
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index f9617202b..8bc6a4a04 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | #include <android/api-level.h> | 14 | #include <android/api-level.h> |
| 15 | #include <android/native_window_jni.h> | 15 | #include <android/native_window_jni.h> |
| 16 | #include <core/loader/nro.h> | 16 | #include <core/loader/nro.h> |
| 17 | #include <jni.h> | ||
| 17 | 18 | ||
| 18 | #include "common/detached_tasks.h" | 19 | #include "common/detached_tasks.h" |
| 19 | #include "common/dynamic_library.h" | 20 | #include "common/dynamic_library.h" |
| @@ -59,6 +60,9 @@ | |||
| 59 | #include "video_core/rasterizer_interface.h" | 60 | #include "video_core/rasterizer_interface.h" |
| 60 | #include "video_core/renderer_base.h" | 61 | #include "video_core/renderer_base.h" |
| 61 | 62 | ||
| 63 | #define jconst [[maybe_unused]] const auto | ||
| 64 | #define jauto [[maybe_unused]] auto | ||
| 65 | |||
| 62 | namespace { | 66 | namespace { |
| 63 | 67 | ||
| 64 | class EmulationSession final { | 68 | class EmulationSession final { |
| @@ -98,8 +102,8 @@ public: | |||
| 98 | } | 102 | } |
| 99 | 103 | ||
| 100 | int InstallFileToNand(std::string filename) { | 104 | int InstallFileToNand(std::string filename) { |
| 101 | const auto copy_func = [](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest, | 105 | jconst copy_func = [](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest, |
| 102 | std::size_t block_size) { | 106 | std::size_t block_size) { |
| 103 | if (src == nullptr || dest == nullptr) { | 107 | if (src == nullptr || dest == nullptr) { |
| 104 | return false; | 108 | return false; |
| 105 | } | 109 | } |
| @@ -108,10 +112,10 @@ public: | |||
| 108 | } | 112 | } |
| 109 | 113 | ||
| 110 | using namespace Common::Literals; | 114 | using namespace Common::Literals; |
| 111 | std::vector<u8> buffer(1_MiB); | 115 | [[maybe_unused]] std::vector<u8> buffer(1_MiB); |
| 112 | 116 | ||
| 113 | for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) { | 117 | for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) { |
| 114 | const auto read = src->Read(buffer.data(), buffer.size(), i); | 118 | jconst read = src->Read(buffer.data(), buffer.size(), i); |
| 115 | dest->Write(buffer.data(), read, i); | 119 | dest->Write(buffer.data(), read, i); |
| 116 | } | 120 | } |
| 117 | return true; | 121 | return true; |
| @@ -128,14 +132,14 @@ public: | |||
| 128 | m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); | 132 | m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); |
| 129 | m_system.GetFileSystemController().CreateFactories(*m_vfs); | 133 | m_system.GetFileSystemController().CreateFactories(*m_vfs); |
| 130 | 134 | ||
| 131 | std::shared_ptr<FileSys::NSP> nsp; | 135 | [[maybe_unused]] std::shared_ptr<FileSys::NSP> nsp; |
| 132 | if (filename.ends_with("nsp")) { | 136 | if (filename.ends_with("nsp")) { |
| 133 | nsp = std::make_shared<FileSys::NSP>(m_vfs->OpenFile(filename, FileSys::Mode::Read)); | 137 | nsp = std::make_shared<FileSys::NSP>(m_vfs->OpenFile(filename, FileSys::Mode::Read)); |
| 134 | if (nsp->IsExtractedType()) { | 138 | if (nsp->IsExtractedType()) { |
| 135 | return InstallError; | 139 | return InstallError; |
| 136 | } | 140 | } |
| 137 | } else if (filename.ends_with("xci")) { | 141 | } else if (filename.ends_with("xci")) { |
| 138 | const auto xci = | 142 | jconst xci = |
| 139 | std::make_shared<FileSys::XCI>(m_vfs->OpenFile(filename, FileSys::Mode::Read)); | 143 | std::make_shared<FileSys::XCI>(m_vfs->OpenFile(filename, FileSys::Mode::Read)); |
| 140 | nsp = xci->GetSecurePartitionNSP(); | 144 | nsp = xci->GetSecurePartitionNSP(); |
| 141 | } else { | 145 | } else { |
| @@ -150,7 +154,7 @@ public: | |||
| 150 | return InstallError; | 154 | return InstallError; |
| 151 | } | 155 | } |
| 152 | 156 | ||
| 153 | const auto res = m_system.GetFileSystemController().GetUserNANDContents()->InstallEntry( | 157 | jconst res = m_system.GetFileSystemController().GetUserNANDContents()->InstallEntry( |
| 154 | *nsp, true, copy_func); | 158 | *nsp, true, copy_func); |
| 155 | 159 | ||
| 156 | switch (res) { | 160 | switch (res) { |
| @@ -233,10 +237,11 @@ public: | |||
| 233 | m_system.SetFilesystem(m_vfs); | 237 | m_system.SetFilesystem(m_vfs); |
| 234 | 238 | ||
| 235 | // Initialize system. | 239 | // Initialize system. |
| 236 | auto android_keyboard = std::make_unique<SoftwareKeyboard::AndroidKeyboard>(); | 240 | jauto android_keyboard = std::make_unique<SoftwareKeyboard::AndroidKeyboard>(); |
| 237 | m_software_keyboard = android_keyboard.get(); | 241 | m_software_keyboard = android_keyboard.get(); |
| 238 | m_system.SetShuttingDown(false); | 242 | m_system.SetShuttingDown(false); |
| 239 | m_system.ApplySettings(); | 243 | m_system.ApplySettings(); |
| 244 | Settings::LogSettings(); | ||
| 240 | m_system.HIDCore().ReloadInputDevices(); | 245 | m_system.HIDCore().ReloadInputDevices(); |
| 241 | m_system.SetAppletFrontendSet({ | 246 | m_system.SetAppletFrontendSet({ |
| 242 | nullptr, // Amiibo Settings | 247 | nullptr, // Amiibo Settings |
| @@ -330,7 +335,7 @@ public: | |||
| 330 | 335 | ||
| 331 | while (true) { | 336 | while (true) { |
| 332 | { | 337 | { |
| 333 | std::unique_lock lock(m_mutex); | 338 | [[maybe_unused]] std::unique_lock lock(m_mutex); |
| 334 | if (m_cv.wait_for(lock, std::chrono::milliseconds(800), | 339 | if (m_cv.wait_for(lock, std::chrono::milliseconds(800), |
| 335 | [&]() { return !m_is_running; })) { | 340 | [&]() { return !m_is_running; })) { |
| 336 | // Emulation halted. | 341 | // Emulation halted. |
| @@ -362,7 +367,7 @@ public: | |||
| 362 | } | 367 | } |
| 363 | 368 | ||
| 364 | bool IsHandheldOnly() { | 369 | bool IsHandheldOnly() { |
| 365 | const auto npad_style_set = m_system.HIDCore().GetSupportedStyleTag(); | 370 | jconst npad_style_set = m_system.HIDCore().GetSupportedStyleTag(); |
| 366 | 371 | ||
| 367 | if (npad_style_set.fullkey == 1) { | 372 | if (npad_style_set.fullkey == 1) { |
| 368 | return false; | 373 | return false; |
| @@ -375,17 +380,17 @@ public: | |||
| 375 | return !Settings::values.use_docked_mode.GetValue(); | 380 | return !Settings::values.use_docked_mode.GetValue(); |
| 376 | } | 381 | } |
| 377 | 382 | ||
| 378 | void SetDeviceType(int index, int type) { | 383 | void SetDeviceType([[maybe_unused]] int index, int type) { |
| 379 | auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); | 384 | jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); |
| 380 | controller->SetNpadStyleIndex(static_cast<Core::HID::NpadStyleIndex>(type)); | 385 | controller->SetNpadStyleIndex(static_cast<Core::HID::NpadStyleIndex>(type)); |
| 381 | } | 386 | } |
| 382 | 387 | ||
| 383 | void OnGamepadConnectEvent(int index) { | 388 | void OnGamepadConnectEvent([[maybe_unused]] int index) { |
| 384 | auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); | 389 | jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); |
| 385 | 390 | ||
| 386 | // Ensure that player1 is configured correctly and handheld disconnected | 391 | // Ensure that player1 is configured correctly and handheld disconnected |
| 387 | if (controller->GetNpadIdType() == Core::HID::NpadIdType::Player1) { | 392 | if (controller->GetNpadIdType() == Core::HID::NpadIdType::Player1) { |
| 388 | auto handheld = | 393 | jauto handheld = |
| 389 | m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); | 394 | m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); |
| 390 | 395 | ||
| 391 | if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) { | 396 | if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) { |
| @@ -397,7 +402,8 @@ public: | |||
| 397 | 402 | ||
| 398 | // Ensure that handheld is configured correctly and player 1 disconnected | 403 | // Ensure that handheld is configured correctly and player 1 disconnected |
| 399 | if (controller->GetNpadIdType() == Core::HID::NpadIdType::Handheld) { | 404 | if (controller->GetNpadIdType() == Core::HID::NpadIdType::Handheld) { |
| 400 | auto player1 = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); | 405 | jauto player1 = |
| 406 | m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); | ||
| 401 | 407 | ||
| 402 | if (controller->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::Handheld) { | 408 | if (controller->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::Handheld) { |
| 403 | player1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld); | 409 | player1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld); |
| @@ -411,8 +417,8 @@ public: | |||
| 411 | } | 417 | } |
| 412 | } | 418 | } |
| 413 | 419 | ||
| 414 | void OnGamepadDisconnectEvent(int index) { | 420 | void OnGamepadDisconnectEvent([[maybe_unused]] int index) { |
| 415 | auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); | 421 | jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); |
| 416 | controller->Disconnect(); | 422 | controller->Disconnect(); |
| 417 | } | 423 | } |
| 418 | 424 | ||
| @@ -428,7 +434,7 @@ private: | |||
| 428 | }; | 434 | }; |
| 429 | 435 | ||
| 430 | RomMetadata GetRomMetadata(const std::string& path) { | 436 | RomMetadata GetRomMetadata(const std::string& path) { |
| 431 | if (auto search = m_rom_metadata_cache.find(path); search != m_rom_metadata_cache.end()) { | 437 | if (jauto search = m_rom_metadata_cache.find(path); search != m_rom_metadata_cache.end()) { |
| 432 | return search->second; | 438 | return search->second; |
| 433 | } | 439 | } |
| 434 | 440 | ||
| @@ -436,14 +442,14 @@ private: | |||
| 436 | } | 442 | } |
| 437 | 443 | ||
| 438 | RomMetadata CacheRomMetadata(const std::string& path) { | 444 | RomMetadata CacheRomMetadata(const std::string& path) { |
| 439 | const auto file = Core::GetGameFileFromPath(m_vfs, path); | 445 | jconst file = Core::GetGameFileFromPath(m_vfs, path); |
| 440 | auto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0); | 446 | jauto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0); |
| 441 | 447 | ||
| 442 | RomMetadata entry; | 448 | RomMetadata entry; |
| 443 | loader->ReadTitle(entry.title); | 449 | loader->ReadTitle(entry.title); |
| 444 | loader->ReadIcon(entry.icon); | 450 | loader->ReadIcon(entry.icon); |
| 445 | if (loader->GetFileType() == Loader::FileType::NRO) { | 451 | if (loader->GetFileType() == Loader::FileType::NRO) { |
| 446 | auto loader_nro = dynamic_cast<Loader::AppLoader_NRO*>(loader.get()); | 452 | jauto loader_nro = dynamic_cast<Loader::AppLoader_NRO*>(loader.get()); |
| 447 | entry.isHomebrew = loader_nro->IsHomebrew(); | 453 | entry.isHomebrew = loader_nro->IsHomebrew(); |
| 448 | } else { | 454 | } else { |
| 449 | entry.isHomebrew = false; | 455 | entry.isHomebrew = false; |
| @@ -514,7 +520,7 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath) { | |||
| 514 | 520 | ||
| 515 | SCOPE_EXIT({ EmulationSession::GetInstance().ShutdownEmulation(); }); | 521 | SCOPE_EXIT({ EmulationSession::GetInstance().ShutdownEmulation(); }); |
| 516 | 522 | ||
| 517 | const auto result = EmulationSession::GetInstance().InitializeEmulation(filepath); | 523 | jconst result = EmulationSession::GetInstance().InitializeEmulation(filepath); |
| 518 | if (result != Core::SystemResultStatus::Success) { | 524 | if (result != Core::SystemResultStatus::Success) { |
| 519 | return result; | 525 | return result; |
| 520 | } | 526 | } |
| @@ -526,83 +532,104 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath) { | |||
| 526 | 532 | ||
| 527 | extern "C" { | 533 | extern "C" { |
| 528 | 534 | ||
| 529 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceChanged(JNIEnv* env, | 535 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceChanged(JNIEnv* env, jobject instance, |
| 530 | [[maybe_unused]] jclass clazz, | 536 | [[maybe_unused]] jobject surf) { |
| 531 | jobject surf) { | ||
| 532 | EmulationSession::GetInstance().SetNativeWindow(ANativeWindow_fromSurface(env, surf)); | 537 | EmulationSession::GetInstance().SetNativeWindow(ANativeWindow_fromSurface(env, surf)); |
| 533 | EmulationSession::GetInstance().SurfaceChanged(); | 538 | EmulationSession::GetInstance().SurfaceChanged(); |
| 534 | } | 539 | } |
| 535 | 540 | ||
| 536 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceDestroyed(JNIEnv* env, | 541 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceDestroyed(JNIEnv* env, jobject instance) { |
| 537 | [[maybe_unused]] jclass clazz) { | ||
| 538 | ANativeWindow_release(EmulationSession::GetInstance().NativeWindow()); | 542 | ANativeWindow_release(EmulationSession::GetInstance().NativeWindow()); |
| 539 | EmulationSession::GetInstance().SetNativeWindow(nullptr); | 543 | EmulationSession::GetInstance().SetNativeWindow(nullptr); |
| 540 | EmulationSession::GetInstance().SurfaceChanged(); | 544 | EmulationSession::GetInstance().SurfaceChanged(); |
| 541 | } | 545 | } |
| 542 | 546 | ||
| 543 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env, | 547 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env, jobject instance, |
| 544 | [[maybe_unused]] jclass clazz, | 548 | [[maybe_unused]] jstring j_directory) { |
| 545 | jstring j_directory) { | ||
| 546 | Common::FS::SetAppDirectory(GetJString(env, j_directory)); | 549 | Common::FS::SetAppDirectory(GetJString(env, j_directory)); |
| 547 | } | 550 | } |
| 548 | 551 | ||
| 549 | int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, | 552 | int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject instance, |
| 550 | [[maybe_unused]] jclass clazz, | 553 | [[maybe_unused]] jstring j_file) { |
| 551 | jstring j_file) { | ||
| 552 | return EmulationSession::GetInstance().InstallFileToNand(GetJString(env, j_file)); | 554 | return EmulationSession::GetInstance().InstallFileToNand(GetJString(env, j_file)); |
| 553 | } | 555 | } |
| 554 | 556 | ||
| 555 | void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeGpuDriver( | 557 | void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeGpuDriver(JNIEnv* env, jclass clazz, |
| 556 | JNIEnv* env, [[maybe_unused]] jclass clazz, jstring hook_lib_dir, jstring custom_driver_dir, | 558 | jstring hook_lib_dir, |
| 557 | jstring custom_driver_name, jstring file_redirect_dir) { | 559 | jstring custom_driver_dir, |
| 560 | jstring custom_driver_name, | ||
| 561 | jstring file_redirect_dir) { | ||
| 558 | EmulationSession::GetInstance().InitializeGpuDriver( | 562 | EmulationSession::GetInstance().InitializeGpuDriver( |
| 559 | GetJString(env, hook_lib_dir), GetJString(env, custom_driver_dir), | 563 | GetJString(env, hook_lib_dir), GetJString(env, custom_driver_dir), |
| 560 | GetJString(env, custom_driver_name), GetJString(env, file_redirect_dir)); | 564 | GetJString(env, custom_driver_name), GetJString(env, file_redirect_dir)); |
| 561 | } | 565 | } |
| 562 | 566 | ||
| 563 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadKeys(JNIEnv* env, | 567 | [[maybe_unused]] static bool CheckKgslPresent() { |
| 564 | [[maybe_unused]] jclass clazz) { | 568 | constexpr auto KgslPath{"/dev/kgsl-3d0"}; |
| 569 | |||
| 570 | return access(KgslPath, F_OK) == 0; | ||
| 571 | } | ||
| 572 | |||
| 573 | [[maybe_unused]] bool SupportsCustomDriver() { | ||
| 574 | return android_get_device_api_level() >= 28 && CheckKgslPresent(); | ||
| 575 | } | ||
| 576 | |||
| 577 | jboolean JNICALL Java_org_yuzu_yuzu_1emu_utils_GpuDriverHelper_supportsCustomDriverLoading( | ||
| 578 | JNIEnv* env, jobject instance) { | ||
| 579 | #ifdef ARCHITECTURE_arm64 | ||
| 580 | // If the KGSL device exists custom drivers can be loaded using adrenotools | ||
| 581 | return SupportsCustomDriver(); | ||
| 582 | #else | ||
| 583 | return false; | ||
| 584 | #endif | ||
| 585 | } | ||
| 586 | |||
| 587 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadKeys(JNIEnv* env, jclass clazz) { | ||
| 565 | Core::Crypto::KeyManager::Instance().ReloadKeys(); | 588 | Core::Crypto::KeyManager::Instance().ReloadKeys(); |
| 566 | return static_cast<jboolean>(Core::Crypto::KeyManager::Instance().AreKeysLoaded()); | 589 | return static_cast<jboolean>(Core::Crypto::KeyManager::Instance().AreKeysLoaded()); |
| 567 | } | 590 | } |
| 568 | 591 | ||
| 569 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_unPauseEmulation([[maybe_unused]] JNIEnv* env, | 592 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_unpauseEmulation(JNIEnv* env, jclass clazz) { |
| 570 | [[maybe_unused]] jclass clazz) { | ||
| 571 | EmulationSession::GetInstance().UnPauseEmulation(); | 593 | EmulationSession::GetInstance().UnPauseEmulation(); |
| 572 | } | 594 | } |
| 573 | 595 | ||
| 574 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_pauseEmulation([[maybe_unused]] JNIEnv* env, | 596 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_pauseEmulation(JNIEnv* env, jclass clazz) { |
| 575 | [[maybe_unused]] jclass clazz) { | ||
| 576 | EmulationSession::GetInstance().PauseEmulation(); | 597 | EmulationSession::GetInstance().PauseEmulation(); |
| 577 | } | 598 | } |
| 578 | 599 | ||
| 579 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_stopEmulation([[maybe_unused]] JNIEnv* env, | 600 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_stopEmulation(JNIEnv* env, jclass clazz) { |
| 580 | [[maybe_unused]] jclass clazz) { | ||
| 581 | EmulationSession::GetInstance().HaltEmulation(); | 601 | EmulationSession::GetInstance().HaltEmulation(); |
| 582 | } | 602 | } |
| 583 | 603 | ||
| 584 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_resetRomMetadata([[maybe_unused]] JNIEnv* env, | 604 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_resetRomMetadata(JNIEnv* env, jclass clazz) { |
| 585 | [[maybe_unused]] jclass clazz) { | ||
| 586 | EmulationSession::GetInstance().ResetRomMetadata(); | 605 | EmulationSession::GetInstance().ResetRomMetadata(); |
| 587 | } | 606 | } |
| 588 | 607 | ||
| 589 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isRunning([[maybe_unused]] JNIEnv* env, | 608 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isRunning(JNIEnv* env, jclass clazz) { |
| 590 | [[maybe_unused]] jclass clazz) { | ||
| 591 | return static_cast<jboolean>(EmulationSession::GetInstance().IsRunning()); | 609 | return static_cast<jboolean>(EmulationSession::GetInstance().IsRunning()); |
| 592 | } | 610 | } |
| 593 | 611 | ||
| 594 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isPaused([[maybe_unused]] JNIEnv* env, | 612 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isPaused(JNIEnv* env, jclass clazz) { |
| 595 | [[maybe_unused]] jclass clazz) { | ||
| 596 | return static_cast<jboolean>(EmulationSession::GetInstance().IsPaused()); | 613 | return static_cast<jboolean>(EmulationSession::GetInstance().IsPaused()); |
| 597 | } | 614 | } |
| 598 | 615 | ||
| 599 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHandheldOnly([[maybe_unused]] JNIEnv* env, | 616 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_muteAduio(JNIEnv* env, jclass clazz) { |
| 600 | [[maybe_unused]] jclass clazz) { | 617 | Settings::values.audio_muted = true; |
| 618 | } | ||
| 619 | |||
| 620 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_unmuteAudio(JNIEnv* env, jclass clazz) { | ||
| 621 | Settings::values.audio_muted = false; | ||
| 622 | } | ||
| 623 | |||
| 624 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isMuted(JNIEnv* env, jclass clazz) { | ||
| 625 | return static_cast<jboolean>(Settings::values.audio_muted.GetValue()); | ||
| 626 | } | ||
| 627 | |||
| 628 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHandheldOnly(JNIEnv* env, jclass clazz) { | ||
| 601 | return EmulationSession::GetInstance().IsHandheldOnly(); | 629 | return EmulationSession::GetInstance().IsHandheldOnly(); |
| 602 | } | 630 | } |
| 603 | 631 | ||
| 604 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_setDeviceType([[maybe_unused]] JNIEnv* env, | 632 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_setDeviceType(JNIEnv* env, jclass clazz, |
| 605 | [[maybe_unused]] jclass clazz, | ||
| 606 | jint j_device, jint j_type) { | 633 | jint j_device, jint j_type) { |
| 607 | if (EmulationSession::GetInstance().IsRunning()) { | 634 | if (EmulationSession::GetInstance().IsRunning()) { |
| 608 | EmulationSession::GetInstance().SetDeviceType(j_device, j_type); | 635 | EmulationSession::GetInstance().SetDeviceType(j_device, j_type); |
| @@ -610,8 +637,7 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_setDeviceType([[maybe_unused]] JN | |||
| 610 | return static_cast<jboolean>(true); | 637 | return static_cast<jboolean>(true); |
| 611 | } | 638 | } |
| 612 | 639 | ||
| 613 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadConnectEvent([[maybe_unused]] JNIEnv* env, | 640 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadConnectEvent(JNIEnv* env, jclass clazz, |
| 614 | [[maybe_unused]] jclass clazz, | ||
| 615 | jint j_device) { | 641 | jint j_device) { |
| 616 | if (EmulationSession::GetInstance().IsRunning()) { | 642 | if (EmulationSession::GetInstance().IsRunning()) { |
| 617 | EmulationSession::GetInstance().OnGamepadConnectEvent(j_device); | 643 | EmulationSession::GetInstance().OnGamepadConnectEvent(j_device); |
| @@ -619,17 +645,16 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadConnectEvent([[maybe_unu | |||
| 619 | return static_cast<jboolean>(true); | 645 | return static_cast<jboolean>(true); |
| 620 | } | 646 | } |
| 621 | 647 | ||
| 622 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadDisconnectEvent( | 648 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadDisconnectEvent(JNIEnv* env, jclass clazz, |
| 623 | [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint j_device) { | 649 | jint j_device) { |
| 624 | if (EmulationSession::GetInstance().IsRunning()) { | 650 | if (EmulationSession::GetInstance().IsRunning()) { |
| 625 | EmulationSession::GetInstance().OnGamepadDisconnectEvent(j_device); | 651 | EmulationSession::GetInstance().OnGamepadDisconnectEvent(j_device); |
| 626 | } | 652 | } |
| 627 | return static_cast<jboolean>(true); | 653 | return static_cast<jboolean>(true); |
| 628 | } | 654 | } |
| 629 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadButtonEvent([[maybe_unused]] JNIEnv* env, | 655 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadButtonEvent(JNIEnv* env, jclass clazz, |
| 630 | [[maybe_unused]] jclass clazz, | 656 | jint j_device, jint j_button, |
| 631 | [[maybe_unused]] jint j_device, | 657 | jint action) { |
| 632 | jint j_button, jint action) { | ||
| 633 | if (EmulationSession::GetInstance().IsRunning()) { | 658 | if (EmulationSession::GetInstance().IsRunning()) { |
| 634 | // Ensure gamepad is connected | 659 | // Ensure gamepad is connected |
| 635 | EmulationSession::GetInstance().OnGamepadConnectEvent(j_device); | 660 | EmulationSession::GetInstance().OnGamepadConnectEvent(j_device); |
| @@ -639,8 +664,7 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadButtonEvent([[maybe_unus | |||
| 639 | return static_cast<jboolean>(true); | 664 | return static_cast<jboolean>(true); |
| 640 | } | 665 | } |
| 641 | 666 | ||
| 642 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadJoystickEvent([[maybe_unused]] JNIEnv* env, | 667 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadJoystickEvent(JNIEnv* env, jclass clazz, |
| 643 | [[maybe_unused]] jclass clazz, | ||
| 644 | jint j_device, jint stick_id, | 668 | jint j_device, jint stick_id, |
| 645 | jfloat x, jfloat y) { | 669 | jfloat x, jfloat y) { |
| 646 | if (EmulationSession::GetInstance().IsRunning()) { | 670 | if (EmulationSession::GetInstance().IsRunning()) { |
| @@ -650,9 +674,8 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadJoystickEvent([[maybe_un | |||
| 650 | } | 674 | } |
| 651 | 675 | ||
| 652 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadMotionEvent( | 676 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadMotionEvent( |
| 653 | [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint j_device, | 677 | JNIEnv* env, jclass clazz, jint j_device, jlong delta_timestamp, jfloat gyro_x, jfloat gyro_y, |
| 654 | jlong delta_timestamp, jfloat gyro_x, jfloat gyro_y, jfloat gyro_z, jfloat accel_x, | 678 | jfloat gyro_z, jfloat accel_x, jfloat accel_y, jfloat accel_z) { |
| 655 | jfloat accel_y, jfloat accel_z) { | ||
| 656 | if (EmulationSession::GetInstance().IsRunning()) { | 679 | if (EmulationSession::GetInstance().IsRunning()) { |
| 657 | EmulationSession::GetInstance().Window().OnGamepadMotionEvent( | 680 | EmulationSession::GetInstance().Window().OnGamepadMotionEvent( |
| 658 | j_device, delta_timestamp, gyro_x, gyro_y, gyro_z, accel_x, accel_y, accel_z); | 681 | j_device, delta_timestamp, gyro_x, gyro_y, gyro_z, accel_x, accel_y, accel_z); |
| @@ -660,8 +683,7 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadMotionEvent( | |||
| 660 | return static_cast<jboolean>(true); | 683 | return static_cast<jboolean>(true); |
| 661 | } | 684 | } |
| 662 | 685 | ||
| 663 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onReadNfcTag([[maybe_unused]] JNIEnv* env, | 686 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onReadNfcTag(JNIEnv* env, jclass clazz, |
| 664 | [[maybe_unused]] jclass clazz, | ||
| 665 | jbyteArray j_data) { | 687 | jbyteArray j_data) { |
| 666 | jboolean isCopy{false}; | 688 | jboolean isCopy{false}; |
| 667 | std::span<u8> data(reinterpret_cast<u8*>(env->GetByteArrayElements(j_data, &isCopy)), | 689 | std::span<u8> data(reinterpret_cast<u8*>(env->GetByteArrayElements(j_data, &isCopy)), |
| @@ -673,108 +695,92 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onReadNfcTag([[maybe_unused]] JNI | |||
| 673 | return static_cast<jboolean>(true); | 695 | return static_cast<jboolean>(true); |
| 674 | } | 696 | } |
| 675 | 697 | ||
| 676 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onRemoveNfcTag([[maybe_unused]] JNIEnv* env, | 698 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onRemoveNfcTag(JNIEnv* env, jclass clazz) { |
| 677 | [[maybe_unused]] jclass clazz) { | ||
| 678 | if (EmulationSession::GetInstance().IsRunning()) { | 699 | if (EmulationSession::GetInstance().IsRunning()) { |
| 679 | EmulationSession::GetInstance().Window().OnRemoveNfcTag(); | 700 | EmulationSession::GetInstance().Window().OnRemoveNfcTag(); |
| 680 | } | 701 | } |
| 681 | return static_cast<jboolean>(true); | 702 | return static_cast<jboolean>(true); |
| 682 | } | 703 | } |
| 683 | 704 | ||
| 684 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchPressed([[maybe_unused]] JNIEnv* env, | 705 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchPressed(JNIEnv* env, jclass clazz, jint id, |
| 685 | [[maybe_unused]] jclass clazz, jint id, | ||
| 686 | jfloat x, jfloat y) { | 706 | jfloat x, jfloat y) { |
| 687 | if (EmulationSession::GetInstance().IsRunning()) { | 707 | if (EmulationSession::GetInstance().IsRunning()) { |
| 688 | EmulationSession::GetInstance().Window().OnTouchPressed(id, x, y); | 708 | EmulationSession::GetInstance().Window().OnTouchPressed(id, x, y); |
| 689 | } | 709 | } |
| 690 | } | 710 | } |
| 691 | 711 | ||
| 692 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchMoved([[maybe_unused]] JNIEnv* env, | 712 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchMoved(JNIEnv* env, jclass clazz, jint id, |
| 693 | [[maybe_unused]] jclass clazz, jint id, | ||
| 694 | jfloat x, jfloat y) { | 713 | jfloat x, jfloat y) { |
| 695 | if (EmulationSession::GetInstance().IsRunning()) { | 714 | if (EmulationSession::GetInstance().IsRunning()) { |
| 696 | EmulationSession::GetInstance().Window().OnTouchMoved(id, x, y); | 715 | EmulationSession::GetInstance().Window().OnTouchMoved(id, x, y); |
| 697 | } | 716 | } |
| 698 | } | 717 | } |
| 699 | 718 | ||
| 700 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased([[maybe_unused]] JNIEnv* env, | 719 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased(JNIEnv* env, jclass clazz, jint id) { |
| 701 | [[maybe_unused]] jclass clazz, jint id) { | ||
| 702 | if (EmulationSession::GetInstance().IsRunning()) { | 720 | if (EmulationSession::GetInstance().IsRunning()) { |
| 703 | EmulationSession::GetInstance().Window().OnTouchReleased(id); | 721 | EmulationSession::GetInstance().Window().OnTouchReleased(id); |
| 704 | } | 722 | } |
| 705 | } | 723 | } |
| 706 | 724 | ||
| 707 | jbyteArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getIcon([[maybe_unused]] JNIEnv* env, | 725 | jbyteArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getIcon(JNIEnv* env, jclass clazz, |
| 708 | [[maybe_unused]] jclass clazz, | 726 | jstring j_filename) { |
| 709 | [[maybe_unused]] jstring j_filename) { | 727 | jauto icon_data = EmulationSession::GetInstance().GetRomIcon(GetJString(env, j_filename)); |
| 710 | auto icon_data = EmulationSession::GetInstance().GetRomIcon(GetJString(env, j_filename)); | ||
| 711 | jbyteArray icon = env->NewByteArray(static_cast<jsize>(icon_data.size())); | 728 | jbyteArray icon = env->NewByteArray(static_cast<jsize>(icon_data.size())); |
| 712 | env->SetByteArrayRegion(icon, 0, env->GetArrayLength(icon), | 729 | env->SetByteArrayRegion(icon, 0, env->GetArrayLength(icon), |
| 713 | reinterpret_cast<jbyte*>(icon_data.data())); | 730 | reinterpret_cast<jbyte*>(icon_data.data())); |
| 714 | return icon; | 731 | return icon; |
| 715 | } | 732 | } |
| 716 | 733 | ||
| 717 | jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getTitle([[maybe_unused]] JNIEnv* env, | 734 | jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getTitle(JNIEnv* env, jclass clazz, |
| 718 | [[maybe_unused]] jclass clazz, | 735 | jstring j_filename) { |
| 719 | [[maybe_unused]] jstring j_filename) { | 736 | jauto title = EmulationSession::GetInstance().GetRomTitle(GetJString(env, j_filename)); |
| 720 | auto title = EmulationSession::GetInstance().GetRomTitle(GetJString(env, j_filename)); | ||
| 721 | return env->NewStringUTF(title.c_str()); | 737 | return env->NewStringUTF(title.c_str()); |
| 722 | } | 738 | } |
| 723 | 739 | ||
| 724 | jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getDescription([[maybe_unused]] JNIEnv* env, | 740 | jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getDescription(JNIEnv* env, jclass clazz, |
| 725 | [[maybe_unused]] jclass clazz, | ||
| 726 | jstring j_filename) { | 741 | jstring j_filename) { |
| 727 | return j_filename; | 742 | return j_filename; |
| 728 | } | 743 | } |
| 729 | 744 | ||
| 730 | jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGameId([[maybe_unused]] JNIEnv* env, | 745 | jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGameId(JNIEnv* env, jclass clazz, |
| 731 | [[maybe_unused]] jclass clazz, | ||
| 732 | jstring j_filename) { | 746 | jstring j_filename) { |
| 733 | return j_filename; | 747 | return j_filename; |
| 734 | } | 748 | } |
| 735 | 749 | ||
| 736 | jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getRegions([[maybe_unused]] JNIEnv* env, | 750 | jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getRegions(JNIEnv* env, jclass clazz, |
| 737 | [[maybe_unused]] jclass clazz, | 751 | jstring j_filename) { |
| 738 | [[maybe_unused]] jstring j_filename) { | ||
| 739 | return env->NewStringUTF(""); | 752 | return env->NewStringUTF(""); |
| 740 | } | 753 | } |
| 741 | 754 | ||
| 742 | jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCompany([[maybe_unused]] JNIEnv* env, | 755 | jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCompany(JNIEnv* env, jclass clazz, |
| 743 | [[maybe_unused]] jclass clazz, | 756 | jstring j_filename) { |
| 744 | [[maybe_unused]] jstring j_filename) { | ||
| 745 | return env->NewStringUTF(""); | 757 | return env->NewStringUTF(""); |
| 746 | } | 758 | } |
| 747 | 759 | ||
| 748 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHomebrew([[maybe_unused]] JNIEnv* env, | 760 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHomebrew(JNIEnv* env, jclass clazz, |
| 749 | [[maybe_unused]] jclass clazz, | 761 | jstring j_filename) { |
| 750 | [[maybe_unused]] jstring j_filename) { | ||
| 751 | return EmulationSession::GetInstance().GetIsHomebrew(GetJString(env, j_filename)); | 762 | return EmulationSession::GetInstance().GetIsHomebrew(GetJString(env, j_filename)); |
| 752 | } | 763 | } |
| 753 | 764 | ||
| 754 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmulation | 765 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmulation(JNIEnv* env, jclass clazz) { |
| 755 | [[maybe_unused]] (JNIEnv* env, [[maybe_unused]] jclass clazz) { | ||
| 756 | // Create the default config.ini. | 766 | // Create the default config.ini. |
| 757 | Config{}; | 767 | Config{}; |
| 758 | // Initialize the emulated system. | 768 | // Initialize the emulated system. |
| 759 | EmulationSession::GetInstance().System().Initialize(); | 769 | EmulationSession::GetInstance().System().Initialize(); |
| 760 | } | 770 | } |
| 761 | 771 | ||
| 762 | jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore([[maybe_unused]] JNIEnv* env, | 772 | jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore(JNIEnv* env, jclass clazz) { |
| 763 | [[maybe_unused]] jclass clazz) { | ||
| 764 | return {}; | 773 | return {}; |
| 765 | } | 774 | } |
| 766 | 775 | ||
| 767 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2Ljava_lang_String_2Z( | 776 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2Ljava_lang_String_2Z( |
| 768 | [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, [[maybe_unused]] jstring j_file, | 777 | JNIEnv* env, jclass clazz, jstring j_file, jstring j_savestate, jboolean j_delete_savestate) {} |
| 769 | [[maybe_unused]] jstring j_savestate, [[maybe_unused]] jboolean j_delete_savestate) {} | ||
| 770 | 778 | ||
| 771 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadSettings([[maybe_unused]] JNIEnv* env, | 779 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadSettings(JNIEnv* env, jclass clazz) { |
| 772 | [[maybe_unused]] jclass clazz) { | ||
| 773 | Config{}; | 780 | Config{}; |
| 774 | } | 781 | } |
| 775 | 782 | ||
| 776 | jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getUserSetting([[maybe_unused]] JNIEnv* env, | 783 | jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getUserSetting(JNIEnv* env, jclass clazz, |
| 777 | [[maybe_unused]] jclass clazz, | ||
| 778 | jstring j_game_id, jstring j_section, | 784 | jstring j_game_id, jstring j_section, |
| 779 | jstring j_key) { | 785 | jstring j_key) { |
| 780 | std::string_view game_id = env->GetStringUTFChars(j_game_id, 0); | 786 | std::string_view game_id = env->GetStringUTFChars(j_game_id, 0); |
| @@ -788,8 +794,7 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getUserSetting([[maybe_unused]] JN | |||
| 788 | return env->NewStringUTF(""); | 794 | return env->NewStringUTF(""); |
| 789 | } | 795 | } |
| 790 | 796 | ||
| 791 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_setUserSetting([[maybe_unused]] JNIEnv* env, | 797 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_setUserSetting(JNIEnv* env, jclass clazz, |
| 792 | [[maybe_unused]] jclass clazz, | ||
| 793 | jstring j_game_id, jstring j_section, | 798 | jstring j_game_id, jstring j_section, |
| 794 | jstring j_key, jstring j_value) { | 799 | jstring j_key, jstring j_value) { |
| 795 | std::string_view game_id = env->GetStringUTFChars(j_game_id, 0); | 800 | std::string_view game_id = env->GetStringUTFChars(j_game_id, 0); |
| @@ -803,20 +808,18 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_setUserSetting([[maybe_unused]] JNIEn | |||
| 803 | env->ReleaseStringUTFChars(j_value, value.data()); | 808 | env->ReleaseStringUTFChars(j_value, value.data()); |
| 804 | } | 809 | } |
| 805 | 810 | ||
| 806 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_initGameIni([[maybe_unused]] JNIEnv* env, | 811 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_initGameIni(JNIEnv* env, jclass clazz, |
| 807 | [[maybe_unused]] jclass clazz, | ||
| 808 | jstring j_game_id) { | 812 | jstring j_game_id) { |
| 809 | std::string_view game_id = env->GetStringUTFChars(j_game_id, 0); | 813 | std::string_view game_id = env->GetStringUTFChars(j_game_id, 0); |
| 810 | 814 | ||
| 811 | env->ReleaseStringUTFChars(j_game_id, game_id.data()); | 815 | env->ReleaseStringUTFChars(j_game_id, game_id.data()); |
| 812 | } | 816 | } |
| 813 | 817 | ||
| 814 | jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats([[maybe_unused]] JNIEnv* env, | 818 | jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats(JNIEnv* env, jclass clazz) { |
| 815 | [[maybe_unused]] jclass clazz) { | ||
| 816 | jdoubleArray j_stats = env->NewDoubleArray(4); | 819 | jdoubleArray j_stats = env->NewDoubleArray(4); |
| 817 | 820 | ||
| 818 | if (EmulationSession::GetInstance().IsRunning()) { | 821 | if (EmulationSession::GetInstance().IsRunning()) { |
| 819 | const auto results = EmulationSession::GetInstance().PerfStats(); | 822 | jconst results = EmulationSession::GetInstance().PerfStats(); |
| 820 | 823 | ||
| 821 | // Converting the structure into an array makes it easier to pass it to the frontend | 824 | // Converting the structure into an array makes it easier to pass it to the frontend |
| 822 | double stats[4] = {results.system_fps, results.average_game_fps, results.frametime, | 825 | double stats[4] = {results.system_fps, results.average_game_fps, results.frametime, |
| @@ -828,11 +831,11 @@ jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats([[maybe_unused]] | |||
| 828 | return j_stats; | 831 | return j_stats; |
| 829 | } | 832 | } |
| 830 | 833 | ||
| 831 | void Java_org_yuzu_yuzu_1emu_utils_DirectoryInitialization_setSysDirectory( | 834 | void Java_org_yuzu_yuzu_1emu_utils_DirectoryInitialization_setSysDirectory(JNIEnv* env, |
| 832 | [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jstring j_path) {} | 835 | jclass clazz, |
| 836 | jstring j_path) {} | ||
| 833 | 837 | ||
| 834 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2([[maybe_unused]] JNIEnv* env, | 838 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2(JNIEnv* env, jclass clazz, |
| 835 | [[maybe_unused]] jclass clazz, | ||
| 836 | jstring j_path) { | 839 | jstring j_path) { |
| 837 | const std::string path = GetJString(env, j_path); | 840 | const std::string path = GetJString(env, j_path); |
| 838 | 841 | ||
| @@ -843,8 +846,7 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2([[maybe_unus | |||
| 843 | } | 846 | } |
| 844 | } | 847 | } |
| 845 | 848 | ||
| 846 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_logDeviceInfo([[maybe_unused]] JNIEnv* env, | 849 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_logDeviceInfo(JNIEnv* env, jclass clazz) { |
| 847 | [[maybe_unused]] jclass clazz) { | ||
| 848 | LOG_INFO(Frontend, "yuzu Version: {}-{}", Common::g_scm_branch, Common::g_scm_desc); | 850 | LOG_INFO(Frontend, "yuzu Version: {}-{}", Common::g_scm_branch, Common::g_scm_desc); |
| 849 | LOG_INFO(Frontend, "Host OS: Android API level {}", android_get_device_api_level()); | 851 | LOG_INFO(Frontend, "Host OS: Android API level {}", android_get_device_api_level()); |
| 850 | } | 852 | } |
diff --git a/src/android/app/src/main/jni/native.h b/src/android/app/src/main/jni/native.h deleted file mode 100644 index 24dcbbcb8..000000000 --- a/src/android/app/src/main/jni/native.h +++ /dev/null | |||
| @@ -1,165 +0,0 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <jni.h> | ||
| 7 | |||
| 8 | // Function calls from the Java side | ||
| 9 | #ifdef __cplusplus | ||
| 10 | extern "C" { | ||
| 11 | #endif | ||
| 12 | |||
| 13 | JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_UnPauseEmulation(JNIEnv* env, | ||
| 14 | jclass clazz); | ||
| 15 | |||
| 16 | JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_PauseEmulation(JNIEnv* env, | ||
| 17 | jclass clazz); | ||
| 18 | |||
| 19 | JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_StopEmulation(JNIEnv* env, | ||
| 20 | jclass clazz); | ||
| 21 | |||
| 22 | JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_ResetRomMetadata(JNIEnv* env, | ||
| 23 | jclass clazz); | ||
| 24 | |||
| 25 | JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_IsRunning(JNIEnv* env, | ||
| 26 | jclass clazz); | ||
| 27 | |||
| 28 | JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_isHandheldOnly(JNIEnv* env, | ||
| 29 | jclass clazz); | ||
| 30 | |||
| 31 | JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_setDeviceType(JNIEnv* env, | ||
| 32 | jclass clazz, | ||
| 33 | jstring j_device, | ||
| 34 | jstring j_type); | ||
| 35 | |||
| 36 | JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadConnectEvent( | ||
| 37 | JNIEnv* env, jclass clazz, jstring j_device); | ||
| 38 | |||
| 39 | JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadDisconnectEvent( | ||
| 40 | JNIEnv* env, jclass clazz, jstring j_device); | ||
| 41 | |||
| 42 | JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadEvent( | ||
| 43 | JNIEnv* env, jclass clazz, jstring j_device, jint j_button, jint action); | ||
| 44 | |||
| 45 | JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadMoveEvent( | ||
| 46 | JNIEnv* env, jclass clazz, jstring j_device, jint axis, jfloat x, jfloat y); | ||
| 47 | |||
| 48 | JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadAxisEvent( | ||
| 49 | JNIEnv* env, jclass clazz, jstring j_device, jint axis_id, jfloat axis_val); | ||
| 50 | |||
| 51 | JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onReadNfcTag(JNIEnv* env, | ||
| 52 | jclass clazz, | ||
| 53 | jbyteArray j_data); | ||
| 54 | |||
| 55 | JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onRemoveNfcTag(JNIEnv* env, | ||
| 56 | jclass clazz); | ||
| 57 | |||
| 58 | JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchEvent(JNIEnv* env, | ||
| 59 | jclass clazz, | ||
| 60 | jfloat x, jfloat y, | ||
| 61 | jboolean pressed); | ||
| 62 | |||
| 63 | JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchMoved(JNIEnv* env, jclass clazz, | ||
| 64 | jfloat x, jfloat y); | ||
| 65 | |||
| 66 | JNIEXPORT jbyteArray JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetIcon(JNIEnv* env, | ||
| 67 | jclass clazz, | ||
| 68 | jstring j_file); | ||
| 69 | |||
| 70 | JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetTitle(JNIEnv* env, jclass clazz, | ||
| 71 | jstring j_filename); | ||
| 72 | |||
| 73 | JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetDescription(JNIEnv* env, | ||
| 74 | jclass clazz, | ||
| 75 | jstring j_filename); | ||
| 76 | |||
| 77 | JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetGameId(JNIEnv* env, jclass clazz, | ||
| 78 | jstring j_filename); | ||
| 79 | |||
| 80 | JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetRegions(JNIEnv* env, | ||
| 81 | jclass clazz, | ||
| 82 | jstring j_filename); | ||
| 83 | |||
| 84 | JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetCompany(JNIEnv* env, | ||
| 85 | jclass clazz, | ||
| 86 | jstring j_filename); | ||
| 87 | |||
| 88 | JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetGitRevision(JNIEnv* env, | ||
| 89 | jclass clazz); | ||
| 90 | |||
| 91 | JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SetAppDirectory(JNIEnv* env, | ||
| 92 | jclass clazz, | ||
| 93 | jstring j_directory); | ||
| 94 | |||
| 95 | JNIEXPORT void JNICALL | ||
| 96 | Java_org_yuzu_yuzu_1emu_NativeLibrary_Java_org_yuzu_yuzu_1emu_NativeLibrary_InitializeGpuDriver( | ||
| 97 | JNIEnv* env, jclass clazz, jstring hook_lib_dir, jstring custom_driver_dir, | ||
| 98 | jstring custom_driver_name, jstring file_redirect_dir); | ||
| 99 | |||
| 100 | JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_ReloadKeys(JNIEnv* env, | ||
| 101 | jclass clazz); | ||
| 102 | |||
| 103 | JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_utils_DirectoryInitialization_SetSysDirectory( | ||
| 104 | JNIEnv* env, jclass clazz, jstring path_); | ||
| 105 | |||
| 106 | JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SetSysDirectory(JNIEnv* env, | ||
| 107 | jclass clazz, | ||
| 108 | jstring path); | ||
| 109 | |||
| 110 | JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_InitializeEmulation(JNIEnv* env, | ||
| 111 | jclass clazz); | ||
| 112 | |||
| 113 | JNIEXPORT jint JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_DefaultCPUCore(JNIEnv* env, | ||
| 114 | jclass clazz); | ||
| 115 | JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SetProfiling(JNIEnv* env, jclass clazz, | ||
| 116 | jboolean enable); | ||
| 117 | |||
| 118 | JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_WriteProfileResults(JNIEnv* env, | ||
| 119 | jclass clazz); | ||
| 120 | |||
| 121 | JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_NotifyOrientationChange( | ||
| 122 | JNIEnv* env, jclass clazz, jint layout_option, jint rotation); | ||
| 123 | |||
| 124 | JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_Run__Ljava_lang_String_2( | ||
| 125 | JNIEnv* env, jclass clazz, jstring j_path); | ||
| 126 | |||
| 127 | JNIEXPORT void JNICALL | ||
| 128 | Java_org_yuzu_yuzu_1emu_NativeLibrary_Run__Ljava_lang_String_2Ljava_lang_String_2Z( | ||
| 129 | JNIEnv* env, jclass clazz, jstring j_file, jstring j_savestate, jboolean j_delete_savestate); | ||
| 130 | |||
| 131 | JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SurfaceChanged(JNIEnv* env, | ||
| 132 | jclass clazz, | ||
| 133 | jobject surf); | ||
| 134 | |||
| 135 | JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SurfaceDestroyed(JNIEnv* env, | ||
| 136 | jclass clazz); | ||
| 137 | |||
| 138 | JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_InitGameIni(JNIEnv* env, jclass clazz, | ||
| 139 | jstring j_game_id); | ||
| 140 | |||
| 141 | JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_ReloadSettings(JNIEnv* env, | ||
| 142 | jclass clazz); | ||
| 143 | |||
| 144 | JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SetUserSetting( | ||
| 145 | JNIEnv* env, jclass clazz, jstring j_game_id, jstring j_section, jstring j_key, | ||
| 146 | jstring j_value); | ||
| 147 | |||
| 148 | JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetUserSetting( | ||
| 149 | JNIEnv* env, jclass clazz, jstring game_id, jstring section, jstring key); | ||
| 150 | |||
| 151 | JNIEXPORT jdoubleArray JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetPerfStats(JNIEnv* env, | ||
| 152 | jclass clazz); | ||
| 153 | |||
| 154 | JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_LogDeviceInfo(JNIEnv* env, | ||
| 155 | jclass clazz); | ||
| 156 | |||
| 157 | JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SubmitInlineKeyboardText( | ||
| 158 | JNIEnv* env, jclass clazz, jstring j_text); | ||
| 159 | |||
| 160 | JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SubmitInlineKeyboardInput( | ||
| 161 | JNIEnv* env, jclass clazz, jint j_key_code); | ||
| 162 | |||
| 163 | #ifdef __cplusplus | ||
| 164 | } | ||
| 165 | #endif | ||
diff --git a/src/android/app/src/main/res/drawable/ic_pip_mute.xml b/src/android/app/src/main/res/drawable/ic_pip_mute.xml new file mode 100644 index 000000000..a271c5fe8 --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_pip_mute.xml | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android" | ||
| 2 | android:width="24dp" | ||
| 3 | android:height="24dp" | ||
| 4 | android:viewportHeight="24" | ||
| 5 | android:viewportWidth="24"> | ||
| 6 | <path | ||
| 7 | android:fillColor="@android:color/white" | ||
| 8 | android:pathData="M7,9v6h4l5,5V4l-5,5H7z" /> | ||
| 9 | </vector> | ||
diff --git a/src/android/app/src/main/res/drawable/ic_pip_unmute.xml b/src/android/app/src/main/res/drawable/ic_pip_unmute.xml new file mode 100644 index 000000000..f7ed0862e --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_pip_unmute.xml | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android" | ||
| 2 | android:width="24dp" | ||
| 3 | android:height="24dp" | ||
| 4 | android:viewportHeight="24" | ||
| 5 | android:viewportWidth="24"> | ||
| 6 | <path | ||
| 7 | android:fillColor="@android:color/white" | ||
| 8 | android:pathData="M3,9v6h4l5,5L12,4L7,9L3,9zM16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM14,3.23v2.06c2.89,0.86 5,3.54 5,6.71s-2.11,5.85 -5,6.71v2.06c4.01,-0.91 7,-4.49 7,-8.77s-2.99,-7.86 -7,-8.77z" /> | ||
| 9 | </vector> | ||
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index cc1d8c39d..af7450619 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml | |||
| @@ -104,12 +104,14 @@ | |||
| 104 | <string name="share_log_missing">No log file found</string> | 104 | <string name="share_log_missing">No log file found</string> |
| 105 | <string name="install_game_content">Install game content</string> | 105 | <string name="install_game_content">Install game content</string> |
| 106 | <string name="install_game_content_description">Install game updates or DLC</string> | 106 | <string name="install_game_content_description">Install game updates or DLC</string> |
| 107 | <string name="install_game_content_failure">Error installing file to NAND</string> | 107 | <string name="install_game_content_failure">Error installing file(s) to NAND</string> |
| 108 | <string name="install_game_content_failure_description">Game content installation failed. Please ensure content is valid and that the prod.keys file is installed.</string> | 108 | <string name="install_game_content_failure_description">Please ensure content(s) are valid and that the prod.keys file is installed.</string> |
| 109 | <string name="install_game_content_failure_base">Installation of base games isn\'t permitted in order to avoid possible conflicts. Please select an update or DLC instead.</string> | 109 | <string name="install_game_content_failure_base">Installation of base games isn\'t permitted in order to avoid possible conflicts.</string> |
| 110 | <string name="install_game_content_failure_file_extension">The selected file type is not supported. Only NSP and XCI content is supported for this action. Please verify the game content is valid.</string> | 110 | <string name="install_game_content_failure_file_extension">Only NSP and XCI content is supported. Please verify the game content(s) are valid.</string> |
| 111 | <string name="install_game_content_success">Game content installed successfully</string> | 111 | <string name="install_game_content_failed_count">%1$d installation error(s)</string> |
| 112 | <string name="install_game_content_success_overwrite">Game content was overwritten successfully</string> | 112 | <string name="install_game_content_success">Game content(s) installed successfully</string> |
| 113 | <string name="install_game_content_success_install">%1$d installed successfully</string> | ||
| 114 | <string name="install_game_content_success_overwrite">%1$d overwritten successfully</string> | ||
| 113 | <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string> | 115 | <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string> |
| 114 | 116 | ||
| 115 | <!-- About screen strings --> | 117 | <!-- About screen strings --> |
| @@ -270,6 +272,7 @@ | |||
| 270 | <string name="fatal_error">Fatal Error</string> | 272 | <string name="fatal_error">Fatal Error</string> |
| 271 | <string name="fatal_error_message">A fatal error occurred. Check the log for details.\nContinuing emulation may result in crashes and bugs.</string> | 273 | <string name="fatal_error_message">A fatal error occurred. Check the log for details.\nContinuing emulation may result in crashes and bugs.</string> |
| 272 | <string name="performance_warning">Turning off this setting will significantly reduce emulation performance! For the best experience, it is recommended that you leave this setting enabled.</string> | 274 | <string name="performance_warning">Turning off this setting will significantly reduce emulation performance! For the best experience, it is recommended that you leave this setting enabled.</string> |
| 275 | <string name="device_memory_inadequate">Device RAM: %1$s\nRecommended: %2$s</string> | ||
| 273 | 276 | ||
| 274 | <!-- Region Names --> | 277 | <!-- Region Names --> |
| 275 | <string name="region_japan">Japan</string> | 278 | <string name="region_japan">Japan</string> |
| @@ -300,6 +303,15 @@ | |||
| 300 | <string name="language_traditional_chinese">Traditional Chinese (正體中文)</string> | 303 | <string name="language_traditional_chinese">Traditional Chinese (正體中文)</string> |
| 301 | <string name="language_brazilian_portuguese">Brazilian Portuguese (Português do Brasil)</string> | 304 | <string name="language_brazilian_portuguese">Brazilian Portuguese (Português do Brasil)</string> |
| 302 | 305 | ||
| 306 | <!-- Memory Sizes --> | ||
| 307 | <string name="memory_byte">Byte</string> | ||
| 308 | <string name="memory_kilobyte">KB</string> | ||
| 309 | <string name="memory_megabyte">MB</string> | ||
| 310 | <string name="memory_gigabyte">GB</string> | ||
| 311 | <string name="memory_terabyte">TB</string> | ||
| 312 | <string name="memory_petabyte">PB</string> | ||
| 313 | <string name="memory_exabyte">EB</string> | ||
| 314 | |||
| 303 | <!-- Renderer APIs --> | 315 | <!-- Renderer APIs --> |
| 304 | <string name="renderer_vulkan">Vulkan</string> | 316 | <string name="renderer_vulkan">Vulkan</string> |
| 305 | <string name="renderer_none">None</string> | 317 | <string name="renderer_none">None</string> |
| @@ -387,6 +399,8 @@ | |||
| 387 | <string name="picture_in_picture_description">Minimize window when placed in the background</string> | 399 | <string name="picture_in_picture_description">Minimize window when placed in the background</string> |
| 388 | <string name="pause">Pause</string> | 400 | <string name="pause">Pause</string> |
| 389 | <string name="play">Play</string> | 401 | <string name="play">Play</string> |
| 402 | <string name="mute">Mute</string> | ||
| 403 | <string name="unmute">Unmute</string> | ||
| 390 | 404 | ||
| 391 | <!-- Licenses screen strings --> | 405 | <!-- Licenses screen strings --> |
| 392 | <string name="licenses">Licenses</string> | 406 | <string name="licenses">Licenses</string> |
diff --git a/src/android/gradle.properties b/src/android/gradle.properties index e2f278f33..4fca1b576 100644 --- a/src/android/gradle.properties +++ b/src/android/gradle.properties | |||
| @@ -15,3 +15,6 @@ android.useAndroidX=true | |||
| 15 | kotlin.code.style=official | 15 | kotlin.code.style=official |
| 16 | kotlin.parallel.tasks.in.project=true | 16 | kotlin.parallel.tasks.in.project=true |
| 17 | android.defaults.buildfeatures.buildconfig=true | 17 | android.defaults.buildfeatures.buildconfig=true |
| 18 | |||
| 19 | # Android Gradle plugin 8.0.2 | ||
| 20 | android.suppressUnsupportedCompileSdk=34 | ||