summaryrefslogtreecommitdiff
path: root/src/android
diff options
context:
space:
mode:
authorGravatar comex2023-07-01 15:01:11 -0700
committerGravatar comex2023-07-01 15:01:11 -0700
commit98685d48e3cb9f25f6919f004ec62cadf33afad2 (patch)
tree9df2ce7f57370641589bfae7196c77b090bcbe0f /src/android
parentPR feedback + constification (diff)
parentUpdate translations (2023-07-01) (#10972) (diff)
downloadyuzu-98685d48e3cb9f25f6919f004ec62cadf33afad2.tar.gz
yuzu-98685d48e3cb9f25f6919f004ec62cadf33afad2.tar.xz
yuzu-98685d48e3cb9f25f6919f004ec62cadf33afad2.zip
Merge remote-tracking branch 'origin/master' into ssl
Diffstat (limited to 'src/android')
-rw-r--r--src/android/app/build.gradle.kts4
-rw-r--r--src/android/app/src/main/AndroidManifest.xml5
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt17
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt65
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt172
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LongMessageDialogFragment.kt62
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt6
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt122
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt34
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt23
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt59
-rw-r--r--src/android/app/src/main/jni/CMakeLists.txt1
-rw-r--r--src/android/app/src/main/jni/native.cpp246
-rw-r--r--src/android/app/src/main/jni/native.h165
-rw-r--r--src/android/app/src/main/res/drawable/ic_pip_mute.xml9
-rw-r--r--src/android/app/src/main/res/drawable/ic_pip_unmute.xml9
-rw-r--r--src/android/app/src/main/res/values/strings.xml26
-rw-r--r--src/android/gradle.properties3
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
26android { 26android {
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
27import android.view.Surface 27import android.view.Surface
28import android.view.View 28import android.view.View
29import android.view.inputmethod.InputMethodManager 29import android.view.inputmethod.InputMethodManager
30import android.widget.Toast
30import androidx.activity.viewModels 31import androidx.activity.viewModels
31import androidx.appcompat.app.AppCompatActivity 32import androidx.appcompat.app.AppCompatActivity
32import androidx.core.view.WindowCompat 33import androidx.core.view.WindowCompat
33import androidx.core.view.WindowInsetsCompat 34import androidx.core.view.WindowInsetsCompat
34import androidx.core.view.WindowInsetsControllerCompat 35import androidx.core.view.WindowInsetsControllerCompat
35import androidx.navigation.fragment.NavHostFragment 36import androidx.navigation.fragment.NavHostFragment
36import kotlin.math.roundToInt
37import org.yuzu.yuzu_emu.NativeLibrary 37import org.yuzu.yuzu_emu.NativeLibrary
38import org.yuzu.yuzu_emu.R 38import org.yuzu.yuzu_emu.R
39import org.yuzu.yuzu_emu.databinding.ActivityEmulationBinding 39import org.yuzu.yuzu_emu.databinding.ActivityEmulationBinding
@@ -44,8 +44,10 @@ import org.yuzu.yuzu_emu.model.Game
44import org.yuzu.yuzu_emu.utils.ControllerMappingHelper 44import org.yuzu.yuzu_emu.utils.ControllerMappingHelper
45import org.yuzu.yuzu_emu.utils.ForegroundService 45import org.yuzu.yuzu_emu.utils.ForegroundService
46import org.yuzu.yuzu_emu.utils.InputHandler 46import org.yuzu.yuzu_emu.utils.InputHandler
47import org.yuzu.yuzu_emu.utils.MemoryUtil
47import org.yuzu.yuzu_emu.utils.NfcReader 48import org.yuzu.yuzu_emu.utils.NfcReader
48import org.yuzu.yuzu_emu.utils.ThemeHelper 49import org.yuzu.yuzu_emu.utils.ThemeHelper
50import kotlin.math.roundToInt
49 51
50class EmulationActivity : AppCompatActivity(), SensorEventListener { 52class 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
4package org.yuzu.yuzu_emu.fragments
5
6import android.app.Dialog
7import android.content.Intent
8import android.net.Uri
9import android.os.Bundle
10import androidx.fragment.app.DialogFragment
11import com.google.android.material.dialog.MaterialAlertDialogBuilder
12import org.yuzu.yuzu_emu.R
13
14class 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
29import org.yuzu.yuzu_emu.model.Game 29import org.yuzu.yuzu_emu.model.Game
30import org.yuzu.yuzu_emu.model.GamesViewModel 30import org.yuzu.yuzu_emu.model.GamesViewModel
31import org.yuzu.yuzu_emu.model.HomeViewModel 31import org.yuzu.yuzu_emu.model.HomeViewModel
32import org.yuzu.yuzu_emu.utils.FileUtil
33 32
34class SearchFragment : Fragment() { 33class 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 @@
4package org.yuzu.yuzu_emu.ui.main 4package org.yuzu.yuzu_emu.ui.main
5 5
6import android.content.Intent 6import android.content.Intent
7import android.net.Uri
7import android.os.Bundle 8import android.os.Bundle
8import android.view.View 9import android.view.View
9import android.view.ViewGroup.MarginLayoutParams 10import android.view.ViewGroup.MarginLayoutParams
@@ -42,6 +43,7 @@ import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel
42import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity 43import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity
43import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile 44import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
44import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment 45import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
46import org.yuzu.yuzu_emu.fragments.LongMessageDialogFragment
45import org.yuzu.yuzu_emu.fragments.MessageDialogFragment 47import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
46import org.yuzu.yuzu_emu.model.GamesViewModel 48import org.yuzu.yuzu_emu.model.GamesViewModel
47import org.yuzu.yuzu_emu.model.HomeViewModel 49import 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
7import android.database.Cursor 7import android.database.Cursor
8import android.net.Uri 8import android.net.Uri
9import android.provider.DocumentsContract 9import android.provider.DocumentsContract
10import android.provider.OpenableColumns
11import androidx.documentfile.provider.DocumentFile 10import androidx.documentfile.provider.DocumentFile
12import java.io.BufferedInputStream 11import java.io.BufferedInputStream
13import java.io.File 12import 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
6import android.content.SharedPreferences 6import android.content.SharedPreferences
7import android.net.Uri 7import android.net.Uri
8import androidx.preference.PreferenceManager 8import androidx.preference.PreferenceManager
9import java.util.*
10import kotlinx.serialization.encodeToString 9import kotlinx.serialization.encodeToString
11import kotlinx.serialization.json.Json 10import kotlinx.serialization.json.Json
12import org.yuzu.yuzu_emu.NativeLibrary 11import 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
4package org.yuzu.yuzu_emu.utils
5
6import android.app.ActivityManager
7import android.content.Context
8import org.yuzu.yuzu_emu.R
9import java.util.Locale
10
11class 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
20set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR}) 19set_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
62namespace { 66namespace {
63 67
64class EmulationSession final { 68class 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
527extern "C" { 533extern "C" {
528 534
529void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceChanged(JNIEnv* env, 535void 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
536void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceDestroyed(JNIEnv* env, 541void 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
543void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env, 547void 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
549int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, 552int 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
555void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeGpuDriver( 557void 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
563jboolean 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
577jboolean 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
587jboolean 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
569void Java_org_yuzu_yuzu_1emu_NativeLibrary_unPauseEmulation([[maybe_unused]] JNIEnv* env, 592void 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
574void Java_org_yuzu_yuzu_1emu_NativeLibrary_pauseEmulation([[maybe_unused]] JNIEnv* env, 596void 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
579void Java_org_yuzu_yuzu_1emu_NativeLibrary_stopEmulation([[maybe_unused]] JNIEnv* env, 600void 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
584void Java_org_yuzu_yuzu_1emu_NativeLibrary_resetRomMetadata([[maybe_unused]] JNIEnv* env, 604void 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
589jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isRunning([[maybe_unused]] JNIEnv* env, 608jboolean 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
594jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isPaused([[maybe_unused]] JNIEnv* env, 612jboolean 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
599jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHandheldOnly([[maybe_unused]] JNIEnv* env, 616void Java_org_yuzu_yuzu_1emu_NativeLibrary_muteAduio(JNIEnv* env, jclass clazz) {
600 [[maybe_unused]] jclass clazz) { 617 Settings::values.audio_muted = true;
618}
619
620void Java_org_yuzu_yuzu_1emu_NativeLibrary_unmuteAudio(JNIEnv* env, jclass clazz) {
621 Settings::values.audio_muted = false;
622}
623
624jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isMuted(JNIEnv* env, jclass clazz) {
625 return static_cast<jboolean>(Settings::values.audio_muted.GetValue());
626}
627
628jboolean 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
604jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_setDeviceType([[maybe_unused]] JNIEnv* env, 632jboolean 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
613jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadConnectEvent([[maybe_unused]] JNIEnv* env, 640jboolean 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
622jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadDisconnectEvent( 648jboolean 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}
629jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadButtonEvent([[maybe_unused]] JNIEnv* env, 655jboolean 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
642jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadJoystickEvent([[maybe_unused]] JNIEnv* env, 667jboolean 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
652jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadMotionEvent( 676jboolean 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
663jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onReadNfcTag([[maybe_unused]] JNIEnv* env, 686jboolean 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
676jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onRemoveNfcTag([[maybe_unused]] JNIEnv* env, 698jboolean 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
684void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchPressed([[maybe_unused]] JNIEnv* env, 705void 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
692void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchMoved([[maybe_unused]] JNIEnv* env, 712void 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
700void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased([[maybe_unused]] JNIEnv* env, 719void 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
707jbyteArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getIcon([[maybe_unused]] JNIEnv* env, 725jbyteArray 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
717jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getTitle([[maybe_unused]] JNIEnv* env, 734jstring 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
724jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getDescription([[maybe_unused]] JNIEnv* env, 740jstring 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
730jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGameId([[maybe_unused]] JNIEnv* env, 745jstring 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
736jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getRegions([[maybe_unused]] JNIEnv* env, 750jstring 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
742jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCompany([[maybe_unused]] JNIEnv* env, 755jstring 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
748jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHomebrew([[maybe_unused]] JNIEnv* env, 760jboolean 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
754void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmulation 765void 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
762jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore([[maybe_unused]] JNIEnv* env, 772jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore(JNIEnv* env, jclass clazz) {
763 [[maybe_unused]] jclass clazz) {
764 return {}; 773 return {};
765} 774}
766 775
767void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2Ljava_lang_String_2Z( 776void 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
771void Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadSettings([[maybe_unused]] JNIEnv* env, 779void Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadSettings(JNIEnv* env, jclass clazz) {
772 [[maybe_unused]] jclass clazz) {
773 Config{}; 780 Config{};
774} 781}
775 782
776jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getUserSetting([[maybe_unused]] JNIEnv* env, 783jstring 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
791void Java_org_yuzu_yuzu_1emu_NativeLibrary_setUserSetting([[maybe_unused]] JNIEnv* env, 797void 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
806void Java_org_yuzu_yuzu_1emu_NativeLibrary_initGameIni([[maybe_unused]] JNIEnv* env, 811void 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
814jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats([[maybe_unused]] JNIEnv* env, 818jdoubleArray 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
831void Java_org_yuzu_yuzu_1emu_utils_DirectoryInitialization_setSysDirectory( 834void 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
834void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2([[maybe_unused]] JNIEnv* env, 838void 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
846void Java_org_yuzu_yuzu_1emu_NativeLibrary_logDeviceInfo([[maybe_unused]] JNIEnv* env, 849void 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
10extern "C" {
11#endif
12
13JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_UnPauseEmulation(JNIEnv* env,
14 jclass clazz);
15
16JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_PauseEmulation(JNIEnv* env,
17 jclass clazz);
18
19JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_StopEmulation(JNIEnv* env,
20 jclass clazz);
21
22JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_ResetRomMetadata(JNIEnv* env,
23 jclass clazz);
24
25JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_IsRunning(JNIEnv* env,
26 jclass clazz);
27
28JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_isHandheldOnly(JNIEnv* env,
29 jclass clazz);
30
31JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_setDeviceType(JNIEnv* env,
32 jclass clazz,
33 jstring j_device,
34 jstring j_type);
35
36JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadConnectEvent(
37 JNIEnv* env, jclass clazz, jstring j_device);
38
39JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadDisconnectEvent(
40 JNIEnv* env, jclass clazz, jstring j_device);
41
42JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadEvent(
43 JNIEnv* env, jclass clazz, jstring j_device, jint j_button, jint action);
44
45JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadMoveEvent(
46 JNIEnv* env, jclass clazz, jstring j_device, jint axis, jfloat x, jfloat y);
47
48JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadAxisEvent(
49 JNIEnv* env, jclass clazz, jstring j_device, jint axis_id, jfloat axis_val);
50
51JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onReadNfcTag(JNIEnv* env,
52 jclass clazz,
53 jbyteArray j_data);
54
55JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onRemoveNfcTag(JNIEnv* env,
56 jclass clazz);
57
58JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchEvent(JNIEnv* env,
59 jclass clazz,
60 jfloat x, jfloat y,
61 jboolean pressed);
62
63JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchMoved(JNIEnv* env, jclass clazz,
64 jfloat x, jfloat y);
65
66JNIEXPORT jbyteArray JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetIcon(JNIEnv* env,
67 jclass clazz,
68 jstring j_file);
69
70JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetTitle(JNIEnv* env, jclass clazz,
71 jstring j_filename);
72
73JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetDescription(JNIEnv* env,
74 jclass clazz,
75 jstring j_filename);
76
77JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetGameId(JNIEnv* env, jclass clazz,
78 jstring j_filename);
79
80JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetRegions(JNIEnv* env,
81 jclass clazz,
82 jstring j_filename);
83
84JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetCompany(JNIEnv* env,
85 jclass clazz,
86 jstring j_filename);
87
88JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetGitRevision(JNIEnv* env,
89 jclass clazz);
90
91JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SetAppDirectory(JNIEnv* env,
92 jclass clazz,
93 jstring j_directory);
94
95JNIEXPORT void JNICALL
96Java_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
100JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_ReloadKeys(JNIEnv* env,
101 jclass clazz);
102
103JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_utils_DirectoryInitialization_SetSysDirectory(
104 JNIEnv* env, jclass clazz, jstring path_);
105
106JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SetSysDirectory(JNIEnv* env,
107 jclass clazz,
108 jstring path);
109
110JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_InitializeEmulation(JNIEnv* env,
111 jclass clazz);
112
113JNIEXPORT jint JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_DefaultCPUCore(JNIEnv* env,
114 jclass clazz);
115JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SetProfiling(JNIEnv* env, jclass clazz,
116 jboolean enable);
117
118JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_WriteProfileResults(JNIEnv* env,
119 jclass clazz);
120
121JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_NotifyOrientationChange(
122 JNIEnv* env, jclass clazz, jint layout_option, jint rotation);
123
124JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_Run__Ljava_lang_String_2(
125 JNIEnv* env, jclass clazz, jstring j_path);
126
127JNIEXPORT void JNICALL
128Java_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
131JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SurfaceChanged(JNIEnv* env,
132 jclass clazz,
133 jobject surf);
134
135JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SurfaceDestroyed(JNIEnv* env,
136 jclass clazz);
137
138JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_InitGameIni(JNIEnv* env, jclass clazz,
139 jstring j_game_id);
140
141JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_ReloadSettings(JNIEnv* env,
142 jclass clazz);
143
144JNIEXPORT 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
148JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetUserSetting(
149 JNIEnv* env, jclass clazz, jstring game_id, jstring section, jstring key);
150
151JNIEXPORT jdoubleArray JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetPerfStats(JNIEnv* env,
152 jclass clazz);
153
154JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_LogDeviceInfo(JNIEnv* env,
155 jclass clazz);
156
157JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SubmitInlineKeyboardText(
158 JNIEnv* env, jclass clazz, jstring j_text);
159
160JNIEXPORT 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
15kotlin.code.style=official 15kotlin.code.style=official
16kotlin.parallel.tasks.in.project=true 16kotlin.parallel.tasks.in.project=true
17android.defaults.buildfeatures.buildconfig=true 17android.defaults.buildfeatures.buildconfig=true
18
19# Android Gradle plugin 8.0.2
20android.suppressUnsupportedCompileSdk=34