summaryrefslogtreecommitdiffstats
path: root/src/android/app/src/main
diff options
context:
space:
mode:
authorcomex <comexk@gmail.com>2023-07-02 00:01:11 +0200
committercomex <comexk@gmail.com>2023-07-02 00:01:11 +0200
commit98685d48e3cb9f25f6919f004ec62cadf33afad2 (patch)
tree9df2ce7f57370641589bfae7196c77b090bcbe0f /src/android/app/src/main
parentPR feedback + constification (diff)
parentUpdate translations (2023-07-01) (#10972) (diff)
downloadyuzu-98685d48e3cb9f25f6919f004ec62cadf33afad2.tar
yuzu-98685d48e3cb9f25f6919f004ec62cadf33afad2.tar.gz
yuzu-98685d48e3cb9f25f6919f004ec62cadf33afad2.tar.bz2
yuzu-98685d48e3cb9f25f6919f004ec62cadf33afad2.tar.lz
yuzu-98685d48e3cb9f25f6919f004ec62cadf33afad2.tar.xz
yuzu-98685d48e3cb9f25f6919f004ec62cadf33afad2.tar.zst
yuzu-98685d48e3cb9f25f6919f004ec62cadf33afad2.zip
Diffstat (limited to 'src/android/app/src/main')
-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
19 files changed, 573 insertions, 454 deletions
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
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
@@ -69,7 +70,9 @@ SPDX-License-Identifier: GPL-3.0-or-later
android:resource="@xml/nfc_tech_filter" />
</activity>
- <service android:name="org.yuzu.yuzu_emu.utils.ForegroundService"/>
+ <service android:name="org.yuzu.yuzu_emu.utils.ForegroundService" android:foregroundServiceType="specialUse">
+ <property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE" android:value="Keep emulation running in background"/>
+ </service>
<provider
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 {
/**
* Unpauses emulation from a paused state.
*/
- external fun unPauseEmulation()
+ external fun unpauseEmulation()
/**
* Pauses emulation.
@@ -314,6 +314,21 @@ object NativeLibrary {
external fun isPaused(): Boolean
/**
+ * Mutes emulation sound
+ */
+ external fun muteAudio(): Boolean
+
+ /**
+ * Unmutes emulation sound
+ */
+ external fun unmuteAudio(): Boolean
+
+ /**
+ * Returns true if emulation audio is muted.
+ */
+ external fun isMuted(): Boolean
+
+ /**
* Returns the performance stats for the current game
*/
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
import android.view.Surface
import android.view.View
import android.view.inputmethod.InputMethodManager
+import android.widget.Toast
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.navigation.fragment.NavHostFragment
-import kotlin.math.roundToInt
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.ActivityEmulationBinding
@@ -44,8 +44,10 @@ import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.utils.ControllerMappingHelper
import org.yuzu.yuzu_emu.utils.ForegroundService
import org.yuzu.yuzu_emu.utils.InputHandler
+import org.yuzu.yuzu_emu.utils.MemoryUtil
import org.yuzu.yuzu_emu.utils.NfcReader
import org.yuzu.yuzu_emu.utils.ThemeHelper
+import kotlin.math.roundToInt
class EmulationActivity : AppCompatActivity(), SensorEventListener {
private lateinit var binding: ActivityEmulationBinding
@@ -63,6 +65,8 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
private val actionPause = "ACTION_EMULATOR_PAUSE"
private val actionPlay = "ACTION_EMULATOR_PLAY"
+ private val actionMute = "ACTION_EMULATOR_MUTE"
+ private val actionUnmute = "ACTION_EMULATOR_UNMUTE"
private val settingsViewModel: SettingsViewModel by viewModels()
@@ -102,6 +106,19 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
inputHandler = InputHandler()
inputHandler.initialize()
+ val memoryUtil = MemoryUtil(this)
+ if (memoryUtil.isLessThan(8, MemoryUtil.Gb)) {
+ Toast.makeText(
+ this,
+ getString(
+ R.string.device_memory_inadequate,
+ memoryUtil.getDeviceRAM(),
+ "8 ${getString(R.string.memory_gigabyte)}"
+ ),
+ Toast.LENGTH_LONG
+ ).show()
+ }
+
// Start a foreground service to prevent the app from getting killed in the background
val startIntent = Intent(this, ForegroundService::class.java)
startForegroundService(startIntent)
@@ -305,6 +322,41 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
pictureInPictureActions.add(pauseRemoteAction)
}
+ if (NativeLibrary.isMuted()) {
+ val unmuteIcon = Icon.createWithResource(
+ this@EmulationActivity,
+ R.drawable.ic_pip_unmute
+ )
+ val unmutePendingIntent = PendingIntent.getBroadcast(
+ this@EmulationActivity,
+ R.drawable.ic_pip_unmute,
+ Intent(actionUnmute),
+ pendingFlags
+ )
+ val unmuteRemoteAction = RemoteAction(
+ unmuteIcon,
+ getString(R.string.unmute),
+ getString(R.string.unmute),
+ unmutePendingIntent
+ )
+ pictureInPictureActions.add(unmuteRemoteAction)
+ } else {
+ val muteIcon = Icon.createWithResource(this@EmulationActivity, R.drawable.ic_pip_mute)
+ val mutePendingIntent = PendingIntent.getBroadcast(
+ this@EmulationActivity,
+ R.drawable.ic_pip_mute,
+ Intent(actionMute),
+ pendingFlags
+ )
+ val muteRemoteAction = RemoteAction(
+ muteIcon,
+ getString(R.string.mute),
+ getString(R.string.mute),
+ mutePendingIntent
+ )
+ pictureInPictureActions.add(muteRemoteAction)
+ }
+
return this.apply { setActions(pictureInPictureActions) }
}
@@ -322,10 +374,15 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
private var pictureInPictureReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent) {
if (intent.action == actionPlay) {
- if (NativeLibrary.isPaused()) NativeLibrary.unPauseEmulation()
+ if (NativeLibrary.isPaused()) NativeLibrary.unpauseEmulation()
} else if (intent.action == actionPause) {
if (!NativeLibrary.isPaused()) NativeLibrary.pauseEmulation()
}
+ if (intent.action == actionUnmute) {
+ if (NativeLibrary.isMuted()) NativeLibrary.unmuteAudio()
+ } else if (intent.action == actionMute) {
+ if (!NativeLibrary.isMuted()) NativeLibrary.muteAudio()
+ }
buildPictureInPictureParams()
}
}
@@ -339,6 +396,8 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
IntentFilter().apply {
addAction(actionPause)
addAction(actionPlay)
+ addAction(actionMute)
+ addAction(actionUnmute)
}.also {
registerReceiver(pictureInPictureReceiver, it)
}
@@ -347,6 +406,8 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
unregisterReceiver(pictureInPictureReceiver)
} catch (ignored: Exception) {
}
+ // Always resume audio, since there is no UI button
+ if (NativeLibrary.isMuted()) NativeLibrary.unmuteAudio()
}
}
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 {
State.PAUSED -> {
Log.debug("[EmulationFragment] Resuming emulation.")
NativeLibrary.surfaceChanged(surface)
- NativeLibrary.unPauseEmulation()
+ NativeLibrary.unpauseEmulation()
}
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() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
mainActivity = requireActivity() as MainActivity
- val optionsList: MutableList<HomeSetting> = mutableListOf(
- HomeSetting(
- R.string.advanced_settings,
- R.string.settings_description,
- R.drawable.ic_settings
- ) { SettingsActivity.launch(requireContext(), SettingsFile.FILE_NAME_CONFIG, "") },
- HomeSetting(
- R.string.open_user_folder,
- R.string.open_user_folder_description,
- R.drawable.ic_folder_open
- ) { openFileManager() },
- HomeSetting(
- R.string.preferences_theme,
- R.string.theme_and_color_description,
- R.drawable.ic_palette
- ) { SettingsActivity.launch(requireContext(), Settings.SECTION_THEME, "") },
- HomeSetting(
- R.string.install_gpu_driver,
- R.string.install_gpu_driver_description,
- R.drawable.ic_exit
- ) { driverInstaller() },
- HomeSetting(
- R.string.install_amiibo_keys,
- R.string.install_amiibo_keys_description,
- R.drawable.ic_nfc
- ) { mainActivity.getAmiiboKey.launch(arrayOf("*/*")) },
- HomeSetting(
- R.string.install_game_content,
- R.string.install_game_content_description,
- R.drawable.ic_system_update_alt
- ) { mainActivity.installGameUpdate.launch(arrayOf("*/*")) },
- HomeSetting(
- R.string.select_games_folder,
- R.string.select_games_folder_description,
- R.drawable.ic_add
- ) {
- mainActivity.getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data)
- },
- HomeSetting(
- R.string.manage_save_data,
- R.string.import_export_saves_description,
- R.drawable.ic_save
- ) {
- ImportExportSavesFragment().show(
- parentFragmentManager,
- ImportExportSavesFragment.TAG
+ val optionsList: MutableList<HomeSetting> = mutableListOf<HomeSetting>().apply {
+ add(
+ HomeSetting(
+ R.string.advanced_settings,
+ R.string.settings_description,
+ R.drawable.ic_settings
+ ) { SettingsActivity.launch(requireContext(), SettingsFile.FILE_NAME_CONFIG, "") }
+ )
+ add(
+ HomeSetting(
+ R.string.open_user_folder,
+ R.string.open_user_folder_description,
+ R.drawable.ic_folder_open
+ ) { openFileManager() }
+ )
+ add(
+ HomeSetting(
+ R.string.preferences_theme,
+ R.string.theme_and_color_description,
+ R.drawable.ic_palette
+ ) { SettingsActivity.launch(requireContext(), Settings.SECTION_THEME, "") }
+ )
+
+ if (GpuDriverHelper.supportsCustomDriverLoading()) {
+ add(
+ HomeSetting(
+ R.string.install_gpu_driver,
+ R.string.install_gpu_driver_description,
+ R.drawable.ic_exit
+ ) { driverInstaller() }
)
- },
- HomeSetting(
- R.string.install_prod_keys,
- R.string.install_prod_keys_description,
- R.drawable.ic_unlock
- ) { mainActivity.getProdKey.launch(arrayOf("*/*")) },
- HomeSetting(
- R.string.install_firmware,
- R.string.install_firmware_description,
- R.drawable.ic_firmware
- ) { mainActivity.getFirmware.launch(arrayOf("application/zip")) },
- HomeSetting(
- R.string.share_log,
- R.string.share_log_description,
- R.drawable.ic_log
- ) { shareLog() },
- HomeSetting(
- R.string.about,
- R.string.about_description,
- R.drawable.ic_info_outline
- ) {
- exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
- parentFragmentManager.primaryNavigationFragment?.findNavController()
- ?.navigate(R.id.action_homeSettingsFragment_to_aboutFragment)
}
- )
+
+ add(
+ HomeSetting(
+ R.string.install_amiibo_keys,
+ R.string.install_amiibo_keys_description,
+ R.drawable.ic_nfc
+ ) { mainActivity.getAmiiboKey.launch(arrayOf("*/*")) }
+ )
+ add(
+ HomeSetting(
+ R.string.install_game_content,
+ R.string.install_game_content_description,
+ R.drawable.ic_system_update_alt
+ ) { mainActivity.installGameUpdate.launch(arrayOf("*/*")) }
+ )
+ add(
+ HomeSetting(
+ R.string.select_games_folder,
+ R.string.select_games_folder_description,
+ R.drawable.ic_add
+ ) {
+ mainActivity.getGamesDirectory.launch(
+ Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data
+ )
+ }
+ )
+ add(
+ HomeSetting(
+ R.string.manage_save_data,
+ R.string.import_export_saves_description,
+ R.drawable.ic_save
+ ) {
+ ImportExportSavesFragment().show(
+ parentFragmentManager,
+ ImportExportSavesFragment.TAG
+ )
+ }
+ )
+ add(
+ HomeSetting(
+ R.string.install_prod_keys,
+ R.string.install_prod_keys_description,
+ R.drawable.ic_unlock
+ ) { mainActivity.getProdKey.launch(arrayOf("*/*")) }
+ )
+ add(
+ HomeSetting(
+ R.string.install_firmware,
+ R.string.install_firmware_description,
+ R.drawable.ic_firmware
+ ) { mainActivity.getFirmware.launch(arrayOf("application/zip")) }
+ )
+ add(
+ HomeSetting(
+ R.string.share_log,
+ R.string.share_log_description,
+ R.drawable.ic_log
+ ) { shareLog() }
+ )
+ add(
+ HomeSetting(
+ R.string.about,
+ R.string.about_description,
+ R.drawable.ic_info_outline
+ ) {
+ exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
+ parentFragmentManager.primaryNavigationFragment?.findNavController()
+ ?.navigate(R.id.action_homeSettingsFragment_to_aboutFragment)
+ }
+ )
+ }
if (!BuildConfig.PREMIUM) {
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 @@
+// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.fragments
+
+import android.app.Dialog
+import android.content.Intent
+import android.net.Uri
+import android.os.Bundle
+import androidx.fragment.app.DialogFragment
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import org.yuzu.yuzu_emu.R
+
+class LongMessageDialogFragment : DialogFragment() {
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ val titleId = requireArguments().getInt(TITLE)
+ val description = requireArguments().getString(DESCRIPTION)
+ val helpLinkId = requireArguments().getInt(HELP_LINK)
+
+ val dialog = MaterialAlertDialogBuilder(requireContext())
+ .setPositiveButton(R.string.close, null)
+ .setTitle(titleId)
+ .setMessage(description)
+
+ if (helpLinkId != 0) {
+ dialog.setNeutralButton(R.string.learn_more) { _, _ ->
+ openLink(getString(helpLinkId))
+ }
+ }
+
+ return dialog.show()
+ }
+
+ private fun openLink(link: String) {
+ val intent = Intent(Intent.ACTION_VIEW, Uri.parse(link))
+ startActivity(intent)
+ }
+
+ companion object {
+ const val TAG = "LongMessageDialogFragment"
+
+ private const val TITLE = "Title"
+ private const val DESCRIPTION = "Description"
+ private const val HELP_LINK = "Link"
+
+ fun newInstance(
+ titleId: Int,
+ description: String,
+ helpLinkId: Int = 0
+ ): LongMessageDialogFragment {
+ val dialog = LongMessageDialogFragment()
+ val bundle = Bundle()
+ bundle.apply {
+ putInt(TITLE, titleId)
+ putString(DESCRIPTION, description)
+ putInt(HELP_LINK, helpLinkId)
+ }
+ dialog.arguments = bundle
+ return dialog
+ }
+ }
+}
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
import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
-import org.yuzu.yuzu_emu.utils.FileUtil
class SearchFragment : Fragment() {
private var _binding: FragmentSearchBinding? = null
@@ -128,10 +127,7 @@ class SearchFragment : Fragment() {
R.id.chip_homebrew -> baseList.filter { it.isHomebrew }
- R.id.chip_retail -> baseList.filter {
- FileUtil.hasExtension(it.path, "xci") ||
- FileUtil.hasExtension(it.path, "nsp")
- }
+ R.id.chip_retail -> baseList.filter { !it.isHomebrew }
else -> baseList
}
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(
companion object {
val extensions: Set<String> = HashSet(
- listOf(".xci", ".nsp", ".nca", ".nro")
+ listOf("xci", "nsp", "nca", "nro")
)
}
}
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 @@
package org.yuzu.yuzu_emu.ui.main
import android.content.Intent
+import android.net.Uri
import android.os.Bundle
import android.view.View
import android.view.ViewGroup.MarginLayoutParams
@@ -42,6 +43,7 @@ import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel
import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
+import org.yuzu.yuzu_emu.fragments.LongMessageDialogFragment
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
@@ -294,7 +296,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
return@registerForActivityResult
}
- if (!FileUtil.hasExtension(result, "keys")) {
+ if (FileUtil.getExtension(result) != "keys") {
MessageDialogFragment.newInstance(
R.string.reading_keys_failure,
R.string.install_prod_keys_failure_extension_description
@@ -391,7 +393,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
return@registerForActivityResult
}
- if (!FileUtil.hasExtension(result, "bin")) {
+ if (FileUtil.getExtension(result) != "bin") {
MessageDialogFragment.newInstance(
R.string.reading_keys_failure,
R.string.install_amiibo_keys_failure_extension_description
@@ -481,62 +483,110 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
}
}
- val installGameUpdate =
- registerForActivityResult(ActivityResultContracts.OpenDocument()) {
- if (it == null) {
- return@registerForActivityResult
- }
-
+ val installGameUpdate = registerForActivityResult(
+ ActivityResultContracts.OpenMultipleDocuments()
+ ) { documents: List<Uri> ->
+ if (documents.isNotEmpty()) {
IndeterminateProgressDialogFragment.newInstance(
this@MainActivity,
R.string.install_game_content
) {
- val result = NativeLibrary.installFileToNand(it.toString())
+ var installSuccess = 0
+ var installOverwrite = 0
+ var errorBaseGame = 0
+ var errorExtension = 0
+ var errorOther = 0
+ var errorTotal = 0
lifecycleScope.launch {
- withContext(Dispatchers.Main) {
- when (result) {
+ documents.forEach {
+ when (NativeLibrary.installFileToNand(it.toString())) {
NativeLibrary.InstallFileToNandResult.Success -> {
- Toast.makeText(
- applicationContext,
- R.string.install_game_content_success,
- Toast.LENGTH_SHORT
- ).show()
+ installSuccess += 1
}
NativeLibrary.InstallFileToNandResult.SuccessFileOverwritten -> {
- Toast.makeText(
- applicationContext,
- R.string.install_game_content_success_overwrite,
- Toast.LENGTH_SHORT
- ).show()
+ installOverwrite += 1
}
NativeLibrary.InstallFileToNandResult.ErrorBaseGame -> {
- MessageDialogFragment.newInstance(
- R.string.install_game_content_failure,
- R.string.install_game_content_failure_base
- ).show(supportFragmentManager, MessageDialogFragment.TAG)
+ errorBaseGame += 1
}
NativeLibrary.InstallFileToNandResult.ErrorFilenameExtension -> {
- MessageDialogFragment.newInstance(
- R.string.install_game_content_failure,
- R.string.install_game_content_failure_file_extension,
- R.string.install_game_content_help_link
- ).show(supportFragmentManager, MessageDialogFragment.TAG)
+ errorExtension += 1
}
else -> {
- MessageDialogFragment.newInstance(
- R.string.install_game_content_failure,
- R.string.install_game_content_failure_description,
- R.string.install_game_content_help_link
- ).show(supportFragmentManager, MessageDialogFragment.TAG)
+ errorOther += 1
}
}
}
+ withContext(Dispatchers.Main) {
+ val separator = System.getProperty("line.separator") ?: "\n"
+ val installResult = StringBuilder()
+ if (installSuccess > 0) {
+ installResult.append(
+ getString(
+ R.string.install_game_content_success_install,
+ installSuccess
+ )
+ )
+ installResult.append(separator)
+ }
+ if (installOverwrite > 0) {
+ installResult.append(
+ getString(
+ R.string.install_game_content_success_overwrite,
+ installOverwrite
+ )
+ )
+ installResult.append(separator)
+ }
+ errorTotal = errorBaseGame + errorExtension + errorOther
+ if (errorTotal > 0) {
+ installResult.append(separator)
+ installResult.append(
+ getString(
+ R.string.install_game_content_failed_count,
+ errorTotal
+ )
+ )
+ installResult.append(separator)
+ if (errorBaseGame > 0) {
+ installResult.append(separator)
+ installResult.append(
+ getString(R.string.install_game_content_failure_base)
+ )
+ installResult.append(separator)
+ }
+ if (errorExtension > 0) {
+ installResult.append(separator)
+ installResult.append(
+ getString(R.string.install_game_content_failure_file_extension)
+ )
+ installResult.append(separator)
+ }
+ if (errorOther > 0) {
+ installResult.append(
+ getString(R.string.install_game_content_failure_description)
+ )
+ installResult.append(separator)
+ }
+ LongMessageDialogFragment.newInstance(
+ R.string.install_game_content_failure,
+ installResult.toString().trim(),
+ R.string.install_game_content_help_link
+ ).show(supportFragmentManager, LongMessageDialogFragment.TAG)
+ } else {
+ LongMessageDialogFragment.newInstance(
+ R.string.install_game_content_success,
+ installResult.toString().trim()
+ ).show(supportFragmentManager, LongMessageDialogFragment.TAG)
+ }
+ }
}
- return@newInstance result
+ return@newInstance installSuccess + installOverwrite + errorTotal
}.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
}
+ }
}
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
import android.database.Cursor
import android.net.Uri
import android.provider.DocumentsContract
-import android.provider.OpenableColumns
import androidx.documentfile.provider.DocumentFile
import java.io.BufferedInputStream
import java.io.File
@@ -185,19 +184,18 @@ object FileUtil {
/**
* Get file display name from given path
- * @param path content uri path
+ * @param uri content uri
* @return String display name
*/
- fun getFilename(context: Context, path: String): String {
- val resolver = context.contentResolver
+ fun getFilename(uri: Uri): String {
+ val resolver = YuzuApplication.appContext.contentResolver
val columns = arrayOf(
DocumentsContract.Document.COLUMN_DISPLAY_NAME
)
var filename = ""
var c: Cursor? = null
try {
- val mUri = Uri.parse(path)
- c = resolver.query(mUri, columns, null, null, null)
+ c = resolver.query(uri, columns, null, null, null)
c!!.moveToNext()
filename = c.getString(0)
} catch (e: Exception) {
@@ -326,25 +324,9 @@ object FileUtil {
}
}
- fun hasExtension(path: String, extension: String): Boolean =
- path.substring(path.lastIndexOf(".") + 1).contains(extension)
-
- fun hasExtension(uri: Uri, extension: String): Boolean {
- val fileName: String?
- val cursor = YuzuApplication.appContext.contentResolver.query(uri, null, null, null, null)
- val nameIndex = cursor?.getColumnIndex(OpenableColumns.DISPLAY_NAME)
- cursor?.moveToFirst()
-
- if (nameIndex == null) {
- return false
- }
-
- fileName = cursor.getString(nameIndex)
- cursor.close()
-
- if (fileName == null) {
- return false
- }
- return fileName.substring(fileName.lastIndexOf(".") + 1).contains(extension)
+ fun getExtension(uri: Uri): String {
+ val fileName = getFilename(uri)
+ return fileName.substring(fileName.lastIndexOf(".") + 1)
+ .lowercase()
}
}
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
import android.content.SharedPreferences
import android.net.Uri
import androidx.preference.PreferenceManager
-import java.util.*
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import org.yuzu.yuzu_emu.NativeLibrary
@@ -33,15 +32,9 @@ object GameHelper {
val children = FileUtil.listFiles(context, gamesUri)
for (file in children) {
if (!file.isDirectory) {
- val filename = file.uri.toString()
- val extensionStart = filename.lastIndexOf('.')
- if (extensionStart > 0) {
- val fileExtension = filename.substring(extensionStart)
-
- // Check that the file has an extension we care about before trying to read out of it.
- if (Game.extensions.contains(fileExtension.lowercase(Locale.getDefault()))) {
- games.add(getGame(filename))
- }
+ // Check that the file has an extension we care about before trying to read out of it.
+ if (Game.extensions.contains(FileUtil.getExtension(file.uri))) {
+ games.add(getGame(file.uri))
}
}
}
@@ -59,21 +52,19 @@ object GameHelper {
return games.toList()
}
- private fun getGame(filePath: String): Game {
+ private fun getGame(uri: Uri): Game {
+ val filePath = uri.toString()
var name = NativeLibrary.getTitle(filePath)
// If the game's title field is empty, use the filename.
if (name.isEmpty()) {
- name = filePath.substring(filePath.lastIndexOf("/") + 1)
+ name = FileUtil.getFilename(uri)
}
var gameId = NativeLibrary.getGameId(filePath)
// If the game's ID field is empty, use the filename without extension.
if (gameId.isEmpty()) {
- gameId = filePath.substring(
- filePath.lastIndexOf("/") + 1,
- filePath.lastIndexOf(".")
- )
+ gameId = name.substring(0, name.lastIndexOf("."))
}
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 {
initializeDriverParameters(context)
}
+ external fun supportsCustomDriverLoading(): Boolean
+
// Parse the custom driver metadata to retrieve the name.
val customDriverName: String?
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 @@
+// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.utils
+
+import android.app.ActivityManager
+import android.content.Context
+import org.yuzu.yuzu_emu.R
+import java.util.Locale
+
+class MemoryUtil(val context: Context) {
+
+ private val Long.floatForm: String
+ get() = String.format(Locale.ROOT, "%.2f", this.toDouble())
+
+ private fun bytesToSizeUnit(size: Long): String {
+ return when {
+ size < Kb -> "${size.floatForm} ${context.getString(R.string.memory_byte)}"
+ size < Mb -> "${(size / Kb).floatForm} ${context.getString(R.string.memory_kilobyte)}"
+ size < Gb -> "${(size / Mb).floatForm} ${context.getString(R.string.memory_megabyte)}"
+ size < Tb -> "${(size / Gb).floatForm} ${context.getString(R.string.memory_gigabyte)}"
+ size < Pb -> "${(size / Tb).floatForm} ${context.getString(R.string.memory_terabyte)}"
+ size < Eb -> "${(size / Pb).floatForm} ${context.getString(R.string.memory_petabyte)}"
+ else -> "${(size / Eb).floatForm} ${context.getString(R.string.memory_exabyte)}"
+ }
+ }
+
+ private val totalMemory =
+ with(context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager) {
+ val memInfo = ActivityManager.MemoryInfo()
+ getMemoryInfo(memInfo)
+ memInfo.totalMem
+ }
+
+ fun isLessThan(minimum: Int, size: Long): Boolean {
+ return when (size) {
+ Kb -> totalMemory < Mb && totalMemory < minimum
+ Mb -> totalMemory < Gb && (totalMemory / Mb) < minimum
+ Gb -> totalMemory < Tb && (totalMemory / Gb) < minimum
+ Tb -> totalMemory < Pb && (totalMemory / Tb) < minimum
+ Pb -> totalMemory < Eb && (totalMemory / Pb) < minimum
+ Eb -> totalMemory / Eb < minimum
+ else -> totalMemory < Kb && totalMemory < minimum
+ }
+ }
+
+ fun getDeviceRAM(): String {
+ return bytesToSizeUnit(totalMemory)
+ }
+
+ companion object {
+ const val Kb: Long = 1024
+ const val Mb = Kb * 1024
+ const val Gb = Mb * 1024
+ const val Tb = Gb * 1024
+ const val Pb = Tb * 1024
+ const val Eb = Pb * 1024
+ }
+}
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
id_cache.cpp
id_cache.h
native.cpp
- native.h
)
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 @@
#include <android/api-level.h>
#include <android/native_window_jni.h>
#include <core/loader/nro.h>
+#include <jni.h>
#include "common/detached_tasks.h"
#include "common/dynamic_library.h"
@@ -59,6 +60,9 @@
#include "video_core/rasterizer_interface.h"
#include "video_core/renderer_base.h"
+#define jconst [[maybe_unused]] const auto
+#define jauto [[maybe_unused]] auto
+
namespace {
class EmulationSession final {
@@ -98,8 +102,8 @@ public:
}
int InstallFileToNand(std::string filename) {
- const auto copy_func = [](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest,
- std::size_t block_size) {
+ jconst copy_func = [](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest,
+ std::size_t block_size) {
if (src == nullptr || dest == nullptr) {
return false;
}
@@ -108,10 +112,10 @@ public:
}
using namespace Common::Literals;
- std::vector<u8> buffer(1_MiB);
+ [[maybe_unused]] std::vector<u8> buffer(1_MiB);
for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) {
- const auto read = src->Read(buffer.data(), buffer.size(), i);
+ jconst read = src->Read(buffer.data(), buffer.size(), i);
dest->Write(buffer.data(), read, i);
}
return true;
@@ -128,14 +132,14 @@ public:
m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
m_system.GetFileSystemController().CreateFactories(*m_vfs);
- std::shared_ptr<FileSys::NSP> nsp;
+ [[maybe_unused]] std::shared_ptr<FileSys::NSP> nsp;
if (filename.ends_with("nsp")) {
nsp = std::make_shared<FileSys::NSP>(m_vfs->OpenFile(filename, FileSys::Mode::Read));
if (nsp->IsExtractedType()) {
return InstallError;
}
} else if (filename.ends_with("xci")) {
- const auto xci =
+ jconst xci =
std::make_shared<FileSys::XCI>(m_vfs->OpenFile(filename, FileSys::Mode::Read));
nsp = xci->GetSecurePartitionNSP();
} else {
@@ -150,7 +154,7 @@ public:
return InstallError;
}
- const auto res = m_system.GetFileSystemController().GetUserNANDContents()->InstallEntry(
+ jconst res = m_system.GetFileSystemController().GetUserNANDContents()->InstallEntry(
*nsp, true, copy_func);
switch (res) {
@@ -233,10 +237,11 @@ public:
m_system.SetFilesystem(m_vfs);
// Initialize system.
- auto android_keyboard = std::make_unique<SoftwareKeyboard::AndroidKeyboard>();
+ jauto android_keyboard = std::make_unique<SoftwareKeyboard::AndroidKeyboard>();
m_software_keyboard = android_keyboard.get();
m_system.SetShuttingDown(false);
m_system.ApplySettings();
+ Settings::LogSettings();
m_system.HIDCore().ReloadInputDevices();
m_system.SetAppletFrontendSet({
nullptr, // Amiibo Settings
@@ -330,7 +335,7 @@ public:
while (true) {
{
- std::unique_lock lock(m_mutex);
+ [[maybe_unused]] std::unique_lock lock(m_mutex);
if (m_cv.wait_for(lock, std::chrono::milliseconds(800),
[&]() { return !m_is_running; })) {
// Emulation halted.
@@ -362,7 +367,7 @@ public:
}
bool IsHandheldOnly() {
- const auto npad_style_set = m_system.HIDCore().GetSupportedStyleTag();
+ jconst npad_style_set = m_system.HIDCore().GetSupportedStyleTag();
if (npad_style_set.fullkey == 1) {
return false;
@@ -375,17 +380,17 @@ public:
return !Settings::values.use_docked_mode.GetValue();
}
- void SetDeviceType(int index, int type) {
- auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
+ void SetDeviceType([[maybe_unused]] int index, int type) {
+ jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
controller->SetNpadStyleIndex(static_cast<Core::HID::NpadStyleIndex>(type));
}
- void OnGamepadConnectEvent(int index) {
- auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
+ void OnGamepadConnectEvent([[maybe_unused]] int index) {
+ jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
// Ensure that player1 is configured correctly and handheld disconnected
if (controller->GetNpadIdType() == Core::HID::NpadIdType::Player1) {
- auto handheld =
+ jauto handheld =
m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) {
@@ -397,7 +402,8 @@ public:
// Ensure that handheld is configured correctly and player 1 disconnected
if (controller->GetNpadIdType() == Core::HID::NpadIdType::Handheld) {
- auto player1 = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
+ jauto player1 =
+ m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
if (controller->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::Handheld) {
player1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
@@ -411,8 +417,8 @@ public:
}
}
- void OnGamepadDisconnectEvent(int index) {
- auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
+ void OnGamepadDisconnectEvent([[maybe_unused]] int index) {
+ jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
controller->Disconnect();
}
@@ -428,7 +434,7 @@ private:
};
RomMetadata GetRomMetadata(const std::string& path) {
- if (auto search = m_rom_metadata_cache.find(path); search != m_rom_metadata_cache.end()) {
+ if (jauto search = m_rom_metadata_cache.find(path); search != m_rom_metadata_cache.end()) {
return search->second;
}
@@ -436,14 +442,14 @@ private:
}
RomMetadata CacheRomMetadata(const std::string& path) {
- const auto file = Core::GetGameFileFromPath(m_vfs, path);
- auto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0);
+ jconst file = Core::GetGameFileFromPath(m_vfs, path);
+ jauto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0);
RomMetadata entry;
loader->ReadTitle(entry.title);
loader->ReadIcon(entry.icon);
if (loader->GetFileType() == Loader::FileType::NRO) {
- auto loader_nro = dynamic_cast<Loader::AppLoader_NRO*>(loader.get());
+ jauto loader_nro = dynamic_cast<Loader::AppLoader_NRO*>(loader.get());
entry.isHomebrew = loader_nro->IsHomebrew();
} else {
entry.isHomebrew = false;
@@ -514,7 +520,7 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath) {
SCOPE_EXIT({ EmulationSession::GetInstance().ShutdownEmulation(); });
- const auto result = EmulationSession::GetInstance().InitializeEmulation(filepath);
+ jconst result = EmulationSession::GetInstance().InitializeEmulation(filepath);
if (result != Core::SystemResultStatus::Success) {
return result;
}
@@ -526,83 +532,104 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath) {
extern "C" {
-void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceChanged(JNIEnv* env,
- [[maybe_unused]] jclass clazz,
- jobject surf) {
+void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceChanged(JNIEnv* env, jobject instance,
+ [[maybe_unused]] jobject surf) {
EmulationSession::GetInstance().SetNativeWindow(ANativeWindow_fromSurface(env, surf));
EmulationSession::GetInstance().SurfaceChanged();
}
-void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceDestroyed(JNIEnv* env,
- [[maybe_unused]] jclass clazz) {
+void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceDestroyed(JNIEnv* env, jobject instance) {
ANativeWindow_release(EmulationSession::GetInstance().NativeWindow());
EmulationSession::GetInstance().SetNativeWindow(nullptr);
EmulationSession::GetInstance().SurfaceChanged();
}
-void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env,
- [[maybe_unused]] jclass clazz,
- jstring j_directory) {
+void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env, jobject instance,
+ [[maybe_unused]] jstring j_directory) {
Common::FS::SetAppDirectory(GetJString(env, j_directory));
}
-int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env,
- [[maybe_unused]] jclass clazz,
- jstring j_file) {
+int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject instance,
+ [[maybe_unused]] jstring j_file) {
return EmulationSession::GetInstance().InstallFileToNand(GetJString(env, j_file));
}
-void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeGpuDriver(
- JNIEnv* env, [[maybe_unused]] jclass clazz, jstring hook_lib_dir, jstring custom_driver_dir,
- jstring custom_driver_name, jstring file_redirect_dir) {
+void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeGpuDriver(JNIEnv* env, jclass clazz,
+ jstring hook_lib_dir,
+ jstring custom_driver_dir,
+ jstring custom_driver_name,
+ jstring file_redirect_dir) {
EmulationSession::GetInstance().InitializeGpuDriver(
GetJString(env, hook_lib_dir), GetJString(env, custom_driver_dir),
GetJString(env, custom_driver_name), GetJString(env, file_redirect_dir));
}
-jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadKeys(JNIEnv* env,
- [[maybe_unused]] jclass clazz) {
+[[maybe_unused]] static bool CheckKgslPresent() {
+ constexpr auto KgslPath{"/dev/kgsl-3d0"};
+
+ return access(KgslPath, F_OK) == 0;
+}
+
+[[maybe_unused]] bool SupportsCustomDriver() {
+ return android_get_device_api_level() >= 28 && CheckKgslPresent();
+}
+
+jboolean JNICALL Java_org_yuzu_yuzu_1emu_utils_GpuDriverHelper_supportsCustomDriverLoading(
+ JNIEnv* env, jobject instance) {
+#ifdef ARCHITECTURE_arm64
+ // If the KGSL device exists custom drivers can be loaded using adrenotools
+ return SupportsCustomDriver();
+#else
+ return false;
+#endif
+}
+
+jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadKeys(JNIEnv* env, jclass clazz) {
Core::Crypto::KeyManager::Instance().ReloadKeys();
return static_cast<jboolean>(Core::Crypto::KeyManager::Instance().AreKeysLoaded());
}
-void Java_org_yuzu_yuzu_1emu_NativeLibrary_unPauseEmulation([[maybe_unused]] JNIEnv* env,
- [[maybe_unused]] jclass clazz) {
+void Java_org_yuzu_yuzu_1emu_NativeLibrary_unpauseEmulation(JNIEnv* env, jclass clazz) {
EmulationSession::GetInstance().UnPauseEmulation();
}
-void Java_org_yuzu_yuzu_1emu_NativeLibrary_pauseEmulation([[maybe_unused]] JNIEnv* env,
- [[maybe_unused]] jclass clazz) {
+void Java_org_yuzu_yuzu_1emu_NativeLibrary_pauseEmulation(JNIEnv* env, jclass clazz) {
EmulationSession::GetInstance().PauseEmulation();
}
-void Java_org_yuzu_yuzu_1emu_NativeLibrary_stopEmulation([[maybe_unused]] JNIEnv* env,
- [[maybe_unused]] jclass clazz) {
+void Java_org_yuzu_yuzu_1emu_NativeLibrary_stopEmulation(JNIEnv* env, jclass clazz) {
EmulationSession::GetInstance().HaltEmulation();
}
-void Java_org_yuzu_yuzu_1emu_NativeLibrary_resetRomMetadata([[maybe_unused]] JNIEnv* env,
- [[maybe_unused]] jclass clazz) {
+void Java_org_yuzu_yuzu_1emu_NativeLibrary_resetRomMetadata(JNIEnv* env, jclass clazz) {
EmulationSession::GetInstance().ResetRomMetadata();
}
-jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isRunning([[maybe_unused]] JNIEnv* env,
- [[maybe_unused]] jclass clazz) {
+jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isRunning(JNIEnv* env, jclass clazz) {
return static_cast<jboolean>(EmulationSession::GetInstance().IsRunning());
}
-jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isPaused([[maybe_unused]] JNIEnv* env,
- [[maybe_unused]] jclass clazz) {
+jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isPaused(JNIEnv* env, jclass clazz) {
return static_cast<jboolean>(EmulationSession::GetInstance().IsPaused());
}
-jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHandheldOnly([[maybe_unused]] JNIEnv* env,
- [[maybe_unused]] jclass clazz) {
+void Java_org_yuzu_yuzu_1emu_NativeLibrary_muteAduio(JNIEnv* env, jclass clazz) {
+ Settings::values.audio_muted = true;
+}
+
+void Java_org_yuzu_yuzu_1emu_NativeLibrary_unmuteAudio(JNIEnv* env, jclass clazz) {
+ Settings::values.audio_muted = false;
+}
+
+jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isMuted(JNIEnv* env, jclass clazz) {
+ return static_cast<jboolean>(Settings::values.audio_muted.GetValue());
+}
+
+jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHandheldOnly(JNIEnv* env, jclass clazz) {
return EmulationSession::GetInstance().IsHandheldOnly();
}
-jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_setDeviceType([[maybe_unused]] JNIEnv* env,
- [[maybe_unused]] jclass clazz,
+jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_setDeviceType(JNIEnv* env, jclass clazz,
jint j_device, jint j_type) {
if (EmulationSession::GetInstance().IsRunning()) {
EmulationSession::GetInstance().SetDeviceType(j_device, j_type);
@@ -610,8 +637,7 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_setDeviceType([[maybe_unused]] JN
return static_cast<jboolean>(true);
}
-jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadConnectEvent([[maybe_unused]] JNIEnv* env,
- [[maybe_unused]] jclass clazz,
+jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadConnectEvent(JNIEnv* env, jclass clazz,
jint j_device) {
if (EmulationSession::GetInstance().IsRunning()) {
EmulationSession::GetInstance().OnGamepadConnectEvent(j_device);
@@ -619,17 +645,16 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadConnectEvent([[maybe_unu
return static_cast<jboolean>(true);
}
-jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadDisconnectEvent(
- [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint j_device) {
+jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadDisconnectEvent(JNIEnv* env, jclass clazz,
+ jint j_device) {
if (EmulationSession::GetInstance().IsRunning()) {
EmulationSession::GetInstance().OnGamepadDisconnectEvent(j_device);
}
return static_cast<jboolean>(true);
}
-jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadButtonEvent([[maybe_unused]] JNIEnv* env,
- [[maybe_unused]] jclass clazz,
- [[maybe_unused]] jint j_device,
- jint j_button, jint action) {
+jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadButtonEvent(JNIEnv* env, jclass clazz,
+ jint j_device, jint j_button,
+ jint action) {
if (EmulationSession::GetInstance().IsRunning()) {
// Ensure gamepad is connected
EmulationSession::GetInstance().OnGamepadConnectEvent(j_device);
@@ -639,8 +664,7 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadButtonEvent([[maybe_unus
return static_cast<jboolean>(true);
}
-jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadJoystickEvent([[maybe_unused]] JNIEnv* env,
- [[maybe_unused]] jclass clazz,
+jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadJoystickEvent(JNIEnv* env, jclass clazz,
jint j_device, jint stick_id,
jfloat x, jfloat y) {
if (EmulationSession::GetInstance().IsRunning()) {
@@ -650,9 +674,8 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadJoystickEvent([[maybe_un
}
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadMotionEvent(
- [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint j_device,
- jlong delta_timestamp, jfloat gyro_x, jfloat gyro_y, jfloat gyro_z, jfloat accel_x,
- jfloat accel_y, jfloat accel_z) {
+ JNIEnv* env, jclass clazz, jint j_device, jlong delta_timestamp, jfloat gyro_x, jfloat gyro_y,
+ jfloat gyro_z, jfloat accel_x, jfloat accel_y, jfloat accel_z) {
if (EmulationSession::GetInstance().IsRunning()) {
EmulationSession::GetInstance().Window().OnGamepadMotionEvent(
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(
return static_cast<jboolean>(true);
}
-jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onReadNfcTag([[maybe_unused]] JNIEnv* env,
- [[maybe_unused]] jclass clazz,
+jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onReadNfcTag(JNIEnv* env, jclass clazz,
jbyteArray j_data) {
jboolean isCopy{false};
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
return static_cast<jboolean>(true);
}
-jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onRemoveNfcTag([[maybe_unused]] JNIEnv* env,
- [[maybe_unused]] jclass clazz) {
+jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onRemoveNfcTag(JNIEnv* env, jclass clazz) {
if (EmulationSession::GetInstance().IsRunning()) {
EmulationSession::GetInstance().Window().OnRemoveNfcTag();
}
return static_cast<jboolean>(true);
}
-void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchPressed([[maybe_unused]] JNIEnv* env,
- [[maybe_unused]] jclass clazz, jint id,
+void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchPressed(JNIEnv* env, jclass clazz, jint id,
jfloat x, jfloat y) {
if (EmulationSession::GetInstance().IsRunning()) {
EmulationSession::GetInstance().Window().OnTouchPressed(id, x, y);
}
}
-void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchMoved([[maybe_unused]] JNIEnv* env,
- [[maybe_unused]] jclass clazz, jint id,
+void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchMoved(JNIEnv* env, jclass clazz, jint id,
jfloat x, jfloat y) {
if (EmulationSession::GetInstance().IsRunning()) {
EmulationSession::GetInstance().Window().OnTouchMoved(id, x, y);
}
}
-void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased([[maybe_unused]] JNIEnv* env,
- [[maybe_unused]] jclass clazz, jint id) {
+void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased(JNIEnv* env, jclass clazz, jint id) {
if (EmulationSession::GetInstance().IsRunning()) {
EmulationSession::GetInstance().Window().OnTouchReleased(id);
}
}
-jbyteArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getIcon([[maybe_unused]] JNIEnv* env,
- [[maybe_unused]] jclass clazz,
- [[maybe_unused]] jstring j_filename) {
- auto icon_data = EmulationSession::GetInstance().GetRomIcon(GetJString(env, j_filename));
+jbyteArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getIcon(JNIEnv* env, jclass clazz,
+ jstring j_filename) {
+ jauto icon_data = EmulationSession::GetInstance().GetRomIcon(GetJString(env, j_filename));
jbyteArray icon = env->NewByteArray(static_cast<jsize>(icon_data.size()));
env->SetByteArrayRegion(icon, 0, env->GetArrayLength(icon),
reinterpret_cast<jbyte*>(icon_data.data()));
return icon;
}
-jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getTitle([[maybe_unused]] JNIEnv* env,
- [[maybe_unused]] jclass clazz,
- [[maybe_unused]] jstring j_filename) {
- auto title = EmulationSession::GetInstance().GetRomTitle(GetJString(env, j_filename));
+jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getTitle(JNIEnv* env, jclass clazz,
+ jstring j_filename) {
+ jauto title = EmulationSession::GetInstance().GetRomTitle(GetJString(env, j_filename));
return env->NewStringUTF(title.c_str());
}
-jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getDescription([[maybe_unused]] JNIEnv* env,
- [[maybe_unused]] jclass clazz,
+jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getDescription(JNIEnv* env, jclass clazz,
jstring j_filename) {
return j_filename;
}
-jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGameId([[maybe_unused]] JNIEnv* env,
- [[maybe_unused]] jclass clazz,
+jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGameId(JNIEnv* env, jclass clazz,
jstring j_filename) {
return j_filename;
}
-jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getRegions([[maybe_unused]] JNIEnv* env,
- [[maybe_unused]] jclass clazz,
- [[maybe_unused]] jstring j_filename) {
+jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getRegions(JNIEnv* env, jclass clazz,
+ jstring j_filename) {
return env->NewStringUTF("");
}
-jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCompany([[maybe_unused]] JNIEnv* env,
- [[maybe_unused]] jclass clazz,
- [[maybe_unused]] jstring j_filename) {
+jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCompany(JNIEnv* env, jclass clazz,
+ jstring j_filename) {
return env->NewStringUTF("");
}
-jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHomebrew([[maybe_unused]] JNIEnv* env,
- [[maybe_unused]] jclass clazz,
- [[maybe_unused]] jstring j_filename) {
+jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHomebrew(JNIEnv* env, jclass clazz,
+ jstring j_filename) {
return EmulationSession::GetInstance().GetIsHomebrew(GetJString(env, j_filename));
}
-void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmulation
- [[maybe_unused]] (JNIEnv* env, [[maybe_unused]] jclass clazz) {
+void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmulation(JNIEnv* env, jclass clazz) {
// Create the default config.ini.
Config{};
// Initialize the emulated system.
EmulationSession::GetInstance().System().Initialize();
}
-jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore([[maybe_unused]] JNIEnv* env,
- [[maybe_unused]] jclass clazz) {
+jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore(JNIEnv* env, jclass clazz) {
return {};
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2Ljava_lang_String_2Z(
- [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, [[maybe_unused]] jstring j_file,
- [[maybe_unused]] jstring j_savestate, [[maybe_unused]] jboolean j_delete_savestate) {}
+ JNIEnv* env, jclass clazz, jstring j_file, jstring j_savestate, jboolean j_delete_savestate) {}
-void Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadSettings([[maybe_unused]] JNIEnv* env,
- [[maybe_unused]] jclass clazz) {
+void Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadSettings(JNIEnv* env, jclass clazz) {
Config{};
}
-jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getUserSetting([[maybe_unused]] JNIEnv* env,
- [[maybe_unused]] jclass clazz,
+jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getUserSetting(JNIEnv* env, jclass clazz,
jstring j_game_id, jstring j_section,
jstring j_key) {
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
return env->NewStringUTF("");
}
-void Java_org_yuzu_yuzu_1emu_NativeLibrary_setUserSetting([[maybe_unused]] JNIEnv* env,
- [[maybe_unused]] jclass clazz,
+void Java_org_yuzu_yuzu_1emu_NativeLibrary_setUserSetting(JNIEnv* env, jclass clazz,
jstring j_game_id, jstring j_section,
jstring j_key, jstring j_value) {
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
env->ReleaseStringUTFChars(j_value, value.data());
}
-void Java_org_yuzu_yuzu_1emu_NativeLibrary_initGameIni([[maybe_unused]] JNIEnv* env,
- [[maybe_unused]] jclass clazz,
+void Java_org_yuzu_yuzu_1emu_NativeLibrary_initGameIni(JNIEnv* env, jclass clazz,
jstring j_game_id) {
std::string_view game_id = env->GetStringUTFChars(j_game_id, 0);
env->ReleaseStringUTFChars(j_game_id, game_id.data());
}
-jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats([[maybe_unused]] JNIEnv* env,
- [[maybe_unused]] jclass clazz) {
+jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats(JNIEnv* env, jclass clazz) {
jdoubleArray j_stats = env->NewDoubleArray(4);
if (EmulationSession::GetInstance().IsRunning()) {
- const auto results = EmulationSession::GetInstance().PerfStats();
+ jconst results = EmulationSession::GetInstance().PerfStats();
// Converting the structure into an array makes it easier to pass it to the frontend
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]]
return j_stats;
}
-void Java_org_yuzu_yuzu_1emu_utils_DirectoryInitialization_setSysDirectory(
- [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jstring j_path) {}
+void Java_org_yuzu_yuzu_1emu_utils_DirectoryInitialization_setSysDirectory(JNIEnv* env,
+ jclass clazz,
+ jstring j_path) {}
-void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2([[maybe_unused]] JNIEnv* env,
- [[maybe_unused]] jclass clazz,
+void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2(JNIEnv* env, jclass clazz,
jstring j_path) {
const std::string path = GetJString(env, j_path);
@@ -843,8 +846,7 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2([[maybe_unus
}
}
-void Java_org_yuzu_yuzu_1emu_NativeLibrary_logDeviceInfo([[maybe_unused]] JNIEnv* env,
- [[maybe_unused]] jclass clazz) {
+void Java_org_yuzu_yuzu_1emu_NativeLibrary_logDeviceInfo(JNIEnv* env, jclass clazz) {
LOG_INFO(Frontend, "yuzu Version: {}-{}", Common::g_scm_branch, Common::g_scm_desc);
LOG_INFO(Frontend, "Host OS: Android API level {}", android_get_device_api_level());
}
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 @@
-// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include <jni.h>
-
-// Function calls from the Java side
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_UnPauseEmulation(JNIEnv* env,
- jclass clazz);
-
-JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_PauseEmulation(JNIEnv* env,
- jclass clazz);
-
-JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_StopEmulation(JNIEnv* env,
- jclass clazz);
-
-JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_ResetRomMetadata(JNIEnv* env,
- jclass clazz);
-
-JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_IsRunning(JNIEnv* env,
- jclass clazz);
-
-JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_isHandheldOnly(JNIEnv* env,
- jclass clazz);
-
-JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_setDeviceType(JNIEnv* env,
- jclass clazz,
- jstring j_device,
- jstring j_type);
-
-JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadConnectEvent(
- JNIEnv* env, jclass clazz, jstring j_device);
-
-JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadDisconnectEvent(
- JNIEnv* env, jclass clazz, jstring j_device);
-
-JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadEvent(
- JNIEnv* env, jclass clazz, jstring j_device, jint j_button, jint action);
-
-JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadMoveEvent(
- JNIEnv* env, jclass clazz, jstring j_device, jint axis, jfloat x, jfloat y);
-
-JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadAxisEvent(
- JNIEnv* env, jclass clazz, jstring j_device, jint axis_id, jfloat axis_val);
-
-JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onReadNfcTag(JNIEnv* env,
- jclass clazz,
- jbyteArray j_data);
-
-JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onRemoveNfcTag(JNIEnv* env,
- jclass clazz);
-
-JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchEvent(JNIEnv* env,
- jclass clazz,
- jfloat x, jfloat y,
- jboolean pressed);
-
-JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchMoved(JNIEnv* env, jclass clazz,
- jfloat x, jfloat y);
-
-JNIEXPORT jbyteArray JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetIcon(JNIEnv* env,
- jclass clazz,
- jstring j_file);
-
-JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetTitle(JNIEnv* env, jclass clazz,
- jstring j_filename);
-
-JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetDescription(JNIEnv* env,
- jclass clazz,
- jstring j_filename);
-
-JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetGameId(JNIEnv* env, jclass clazz,
- jstring j_filename);
-
-JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetRegions(JNIEnv* env,
- jclass clazz,
- jstring j_filename);
-
-JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetCompany(JNIEnv* env,
- jclass clazz,
- jstring j_filename);
-
-JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetGitRevision(JNIEnv* env,
- jclass clazz);
-
-JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SetAppDirectory(JNIEnv* env,
- jclass clazz,
- jstring j_directory);
-
-JNIEXPORT void JNICALL
-Java_org_yuzu_yuzu_1emu_NativeLibrary_Java_org_yuzu_yuzu_1emu_NativeLibrary_InitializeGpuDriver(
- JNIEnv* env, jclass clazz, jstring hook_lib_dir, jstring custom_driver_dir,
- jstring custom_driver_name, jstring file_redirect_dir);
-
-JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_ReloadKeys(JNIEnv* env,
- jclass clazz);
-
-JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_utils_DirectoryInitialization_SetSysDirectory(
- JNIEnv* env, jclass clazz, jstring path_);
-
-JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SetSysDirectory(JNIEnv* env,
- jclass clazz,
- jstring path);
-
-JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_InitializeEmulation(JNIEnv* env,
- jclass clazz);
-
-JNIEXPORT jint JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_DefaultCPUCore(JNIEnv* env,
- jclass clazz);
-JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SetProfiling(JNIEnv* env, jclass clazz,
- jboolean enable);
-
-JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_WriteProfileResults(JNIEnv* env,
- jclass clazz);
-
-JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_NotifyOrientationChange(
- JNIEnv* env, jclass clazz, jint layout_option, jint rotation);
-
-JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_Run__Ljava_lang_String_2(
- JNIEnv* env, jclass clazz, jstring j_path);
-
-JNIEXPORT void JNICALL
-Java_org_yuzu_yuzu_1emu_NativeLibrary_Run__Ljava_lang_String_2Ljava_lang_String_2Z(
- JNIEnv* env, jclass clazz, jstring j_file, jstring j_savestate, jboolean j_delete_savestate);
-
-JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SurfaceChanged(JNIEnv* env,
- jclass clazz,
- jobject surf);
-
-JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SurfaceDestroyed(JNIEnv* env,
- jclass clazz);
-
-JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_InitGameIni(JNIEnv* env, jclass clazz,
- jstring j_game_id);
-
-JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_ReloadSettings(JNIEnv* env,
- jclass clazz);
-
-JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SetUserSetting(
- JNIEnv* env, jclass clazz, jstring j_game_id, jstring j_section, jstring j_key,
- jstring j_value);
-
-JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetUserSetting(
- JNIEnv* env, jclass clazz, jstring game_id, jstring section, jstring key);
-
-JNIEXPORT jdoubleArray JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetPerfStats(JNIEnv* env,
- jclass clazz);
-
-JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_LogDeviceInfo(JNIEnv* env,
- jclass clazz);
-
-JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SubmitInlineKeyboardText(
- JNIEnv* env, jclass clazz, jstring j_text);
-
-JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SubmitInlineKeyboardInput(
- JNIEnv* env, jclass clazz, jint j_key_code);
-
-#ifdef __cplusplus
-}
-#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 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M7,9v6h4l5,5V4l-5,5H7z" />
+</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 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <path
+ android:fillColor="@android:color/white"
+ 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" />
+</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 @@
<string name="share_log_missing">No log file found</string>
<string name="install_game_content">Install game content</string>
<string name="install_game_content_description">Install game updates or DLC</string>
- <string name="install_game_content_failure">Error installing file to NAND</string>
- <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>
- <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>
- <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>
- <string name="install_game_content_success">Game content installed successfully</string>
- <string name="install_game_content_success_overwrite">Game content was overwritten successfully</string>
+ <string name="install_game_content_failure">Error installing file(s) to NAND</string>
+ <string name="install_game_content_failure_description">Please ensure content(s) are valid and that the prod.keys file is installed.</string>
+ <string name="install_game_content_failure_base">Installation of base games isn\'t permitted in order to avoid possible conflicts.</string>
+ <string name="install_game_content_failure_file_extension">Only NSP and XCI content is supported. Please verify the game content(s) are valid.</string>
+ <string name="install_game_content_failed_count">%1$d installation error(s)</string>
+ <string name="install_game_content_success">Game content(s) installed successfully</string>
+ <string name="install_game_content_success_install">%1$d installed successfully</string>
+ <string name="install_game_content_success_overwrite">%1$d overwritten successfully</string>
<string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string>
<!-- About screen strings -->
@@ -270,6 +272,7 @@
<string name="fatal_error">Fatal Error</string>
<string name="fatal_error_message">A fatal error occurred. Check the log for details.\nContinuing emulation may result in crashes and bugs.</string>
<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>
+ <string name="device_memory_inadequate">Device RAM: %1$s\nRecommended: %2$s</string>
<!-- Region Names -->
<string name="region_japan">Japan</string>
@@ -300,6 +303,15 @@
<string name="language_traditional_chinese">Traditional Chinese (正體中文)</string>
<string name="language_brazilian_portuguese">Brazilian Portuguese (Português do Brasil)</string>
+ <!-- Memory Sizes -->
+ <string name="memory_byte">Byte</string>
+ <string name="memory_kilobyte">KB</string>
+ <string name="memory_megabyte">MB</string>
+ <string name="memory_gigabyte">GB</string>
+ <string name="memory_terabyte">TB</string>
+ <string name="memory_petabyte">PB</string>
+ <string name="memory_exabyte">EB</string>
+
<!-- Renderer APIs -->
<string name="renderer_vulkan">Vulkan</string>
<string name="renderer_none">None</string>
@@ -387,6 +399,8 @@
<string name="picture_in_picture_description">Minimize window when placed in the background</string>
<string name="pause">Pause</string>
<string name="play">Play</string>
+ <string name="mute">Mute</string>
+ <string name="unmute">Unmute</string>
<!-- Licenses screen strings -->
<string name="licenses">Licenses</string>