aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xREADME.md2
-rwxr-xr-xsrc/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt22
-rwxr-xr-xsrc/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt15
-rwxr-xr-xsrc/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt24
-rwxr-xr-xsrc/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress.kt52
-rwxr-xr-xsrc/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt110
-rwxr-xr-xsrc/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt59
-rwxr-xr-xsrc/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt77
-rwxr-xr-xsrc/android/app/src/main/jni/id_cache.cpp14
-rwxr-xr-xsrc/android/app/src/main/jni/id_cache.h2
-rwxr-xr-xsrc/android/app/src/main/jni/native.cpp22
-rwxr-xr-xsrc/android/app/src/main/res/layout/fragment_emulation.xml83
-rwxr-xr-xsrc/android/app/src/main/res/values-de/strings.xml1
-rwxr-xr-xsrc/android/app/src/main/res/values-es/strings.xml1
-rwxr-xr-xsrc/android/app/src/main/res/values-fr/strings.xml1
-rwxr-xr-xsrc/android/app/src/main/res/values-it/strings.xml1
-rwxr-xr-xsrc/android/app/src/main/res/values-ja/strings.xml1
-rwxr-xr-xsrc/android/app/src/main/res/values-ko/strings.xml1
-rwxr-xr-xsrc/android/app/src/main/res/values-nb/strings.xml1
-rwxr-xr-xsrc/android/app/src/main/res/values-pl/strings.xml1
-rwxr-xr-xsrc/android/app/src/main/res/values-pt-rBR/strings.xml1
-rwxr-xr-xsrc/android/app/src/main/res/values-pt-rPT/strings.xml1
-rwxr-xr-xsrc/android/app/src/main/res/values-ru/strings.xml1
-rwxr-xr-xsrc/android/app/src/main/res/values-uk/strings.xml1
-rwxr-xr-xsrc/android/app/src/main/res/values-zh-rCN/strings.xml1
-rwxr-xr-xsrc/android/app/src/main/res/values-zh-rTW/strings.xml1
-rwxr-xr-xsrc/android/app/src/main/res/values/strings.xml2
27 files changed, 394 insertions, 104 deletions
diff --git a/README.md b/README.md
index 9c5f62966..2ee786023 100755
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
1yuzu emulator early access 1yuzu emulator early access
2============= 2=============
3 3
4This is the source code for early-access 3848. 4This is the source code for early-access 3849.
5 5
6## Legal Notice 6## Legal Notice
7 7
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 5a7cf4ed7..c8706d7a6 100755
--- 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
@@ -22,9 +22,7 @@ import org.yuzu.yuzu_emu.utils.FileUtil.exists
22import org.yuzu.yuzu_emu.utils.FileUtil.getFileSize 22import org.yuzu.yuzu_emu.utils.FileUtil.getFileSize
23import org.yuzu.yuzu_emu.utils.FileUtil.isDirectory 23import org.yuzu.yuzu_emu.utils.FileUtil.isDirectory
24import org.yuzu.yuzu_emu.utils.FileUtil.openContentUri 24import org.yuzu.yuzu_emu.utils.FileUtil.openContentUri
25import org.yuzu.yuzu_emu.utils.Log.error 25import org.yuzu.yuzu_emu.utils.Log
26import org.yuzu.yuzu_emu.utils.Log.verbose
27import org.yuzu.yuzu_emu.utils.Log.warning
28import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable 26import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable
29 27
30/** 28/**
@@ -465,7 +463,7 @@ object NativeLibrary {
465 463
466 val emulationActivity = sEmulationActivity.get() 464 val emulationActivity = sEmulationActivity.get()
467 if (emulationActivity == null) { 465 if (emulationActivity == null) {
468 warning("[NativeLibrary] EmulationActivity is null, can't exit.") 466 Log.warning("[NativeLibrary] EmulationActivity is null, can't exit.")
469 return 467 return
470 } 468 }
471 469
@@ -490,15 +488,27 @@ object NativeLibrary {
490 } 488 }
491 489
492 fun setEmulationActivity(emulationActivity: EmulationActivity?) { 490 fun setEmulationActivity(emulationActivity: EmulationActivity?) {
493 verbose("[NativeLibrary] Registering EmulationActivity.") 491 Log.verbose("[NativeLibrary] Registering EmulationActivity.")
494 sEmulationActivity = WeakReference(emulationActivity) 492 sEmulationActivity = WeakReference(emulationActivity)
495 } 493 }
496 494
497 fun clearEmulationActivity() { 495 fun clearEmulationActivity() {
498 verbose("[NativeLibrary] Unregistering EmulationActivity.") 496 Log.verbose("[NativeLibrary] Unregistering EmulationActivity.")
499 sEmulationActivity.clear() 497 sEmulationActivity.clear()
500 } 498 }
501 499
500 @Keep
501 @JvmStatic
502 fun onEmulationStarted() {
503 sEmulationActivity.get()!!.onEmulationStarted()
504 }
505
506 @Keep
507 @JvmStatic
508 fun onEmulationStopped(status: Int) {
509 sEmulationActivity.get()!!.onEmulationStopped(status)
510 }
511
502 /** 512 /**
503 * Logs the Yuzu version, Android version and, CPU. 513 * Logs the Yuzu version, Android version and, CPU.
504 */ 514 */
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 dbd602a1d..bbd328c71 100755
--- 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
@@ -28,6 +28,7 @@ import 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 android.widget.Toast
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
@@ -41,6 +42,7 @@ import org.yuzu.yuzu_emu.databinding.ActivityEmulationBinding
41import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting 42import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
42import org.yuzu.yuzu_emu.features.settings.model.IntSetting 43import org.yuzu.yuzu_emu.features.settings.model.IntSetting
43import org.yuzu.yuzu_emu.features.settings.model.Settings 44import org.yuzu.yuzu_emu.features.settings.model.Settings
45import org.yuzu.yuzu_emu.model.EmulationViewModel
44import org.yuzu.yuzu_emu.model.Game 46import org.yuzu.yuzu_emu.model.Game
45import org.yuzu.yuzu_emu.utils.ControllerMappingHelper 47import org.yuzu.yuzu_emu.utils.ControllerMappingHelper
46import org.yuzu.yuzu_emu.utils.ForegroundService 48import org.yuzu.yuzu_emu.utils.ForegroundService
@@ -70,8 +72,11 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
70 private val actionMute = "ACTION_EMULATOR_MUTE" 72 private val actionMute = "ACTION_EMULATOR_MUTE"
71 private val actionUnmute = "ACTION_EMULATOR_UNMUTE" 73 private val actionUnmute = "ACTION_EMULATOR_UNMUTE"
72 74
75 private val emulationViewModel: EmulationViewModel by viewModels()
76
73 override fun onDestroy() { 77 override fun onDestroy() {
74 stopForegroundService(this) 78 stopForegroundService(this)
79 emulationViewModel.clear()
75 super.onDestroy() 80 super.onDestroy()
76 } 81 }
77 82
@@ -416,6 +421,16 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
416 } 421 }
417 } 422 }
418 423
424 fun onEmulationStarted() {
425 emulationViewModel.setEmulationStarted(true)
426 }
427
428 fun onEmulationStopped(status: Int) {
429 if (status == 0) {
430 finish()
431 }
432 }
433
419 private fun startMotionSensorListener() { 434 private fun startMotionSensorListener() {
420 val sensorManager = this.getSystemService(Context.SENSOR_SERVICE) as SensorManager 435 val sensorManager = this.getSystemService(Context.SENSOR_SERVICE) as SensorManager
421 val gyroSensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE) 436 val gyroSensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
index e91277d35..13359ef36 100755
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
@@ -3,8 +3,6 @@
3 3
4package org.yuzu.yuzu_emu.adapters 4package org.yuzu.yuzu_emu.adapters
5 5
6import android.graphics.Bitmap
7import android.graphics.BitmapFactory
8import android.net.Uri 6import android.net.Uri
9import android.text.TextUtils 7import android.text.TextUtils
10import android.view.LayoutInflater 8import android.view.LayoutInflater
@@ -15,23 +13,20 @@ import android.widget.Toast
15import androidx.appcompat.app.AppCompatActivity 13import androidx.appcompat.app.AppCompatActivity
16import androidx.documentfile.provider.DocumentFile 14import androidx.documentfile.provider.DocumentFile
17import androidx.lifecycle.ViewModelProvider 15import androidx.lifecycle.ViewModelProvider
18import androidx.lifecycle.lifecycleScope
19import androidx.navigation.findNavController 16import androidx.navigation.findNavController
20import androidx.preference.PreferenceManager 17import androidx.preference.PreferenceManager
21import androidx.recyclerview.widget.AsyncDifferConfig 18import androidx.recyclerview.widget.AsyncDifferConfig
22import androidx.recyclerview.widget.DiffUtil 19import androidx.recyclerview.widget.DiffUtil
23import androidx.recyclerview.widget.ListAdapter 20import androidx.recyclerview.widget.ListAdapter
24import androidx.recyclerview.widget.RecyclerView 21import androidx.recyclerview.widget.RecyclerView
25import coil.load
26import kotlinx.coroutines.launch
27import org.yuzu.yuzu_emu.HomeNavigationDirections 22import org.yuzu.yuzu_emu.HomeNavigationDirections
28import org.yuzu.yuzu_emu.NativeLibrary
29import org.yuzu.yuzu_emu.R 23import org.yuzu.yuzu_emu.R
30import org.yuzu.yuzu_emu.YuzuApplication 24import org.yuzu.yuzu_emu.YuzuApplication
31import org.yuzu.yuzu_emu.adapters.GameAdapter.GameViewHolder 25import org.yuzu.yuzu_emu.adapters.GameAdapter.GameViewHolder
32import org.yuzu.yuzu_emu.databinding.CardGameBinding 26import org.yuzu.yuzu_emu.databinding.CardGameBinding
33import org.yuzu.yuzu_emu.model.Game 27import org.yuzu.yuzu_emu.model.Game
34import org.yuzu.yuzu_emu.model.GamesViewModel 28import org.yuzu.yuzu_emu.model.GamesViewModel
29import org.yuzu.yuzu_emu.utils.GameIconUtils
35 30
36class GameAdapter(private val activity: AppCompatActivity) : 31class GameAdapter(private val activity: AppCompatActivity) :
37 ListAdapter<Game, GameViewHolder>(AsyncDifferConfig.Builder(DiffCallback()).build()), 32 ListAdapter<Game, GameViewHolder>(AsyncDifferConfig.Builder(DiffCallback()).build()),
@@ -98,12 +93,7 @@ class GameAdapter(private val activity: AppCompatActivity) :
98 this.game = game 93 this.game = game
99 94
100 binding.imageGameScreen.scaleType = ImageView.ScaleType.CENTER_CROP 95 binding.imageGameScreen.scaleType = ImageView.ScaleType.CENTER_CROP
101 activity.lifecycleScope.launch { 96 GameIconUtils.loadGameIcon(game, binding.imageGameScreen)
102 val bitmap = decodeGameIcon(game.path)
103 binding.imageGameScreen.load(bitmap) {
104 error(R.drawable.default_icon)
105 }
106 }
107 97
108 binding.textGameTitle.text = game.title.replace("[\\t\\n\\r]+".toRegex(), " ") 98 binding.textGameTitle.text = game.title.replace("[\\t\\n\\r]+".toRegex(), " ")
109 99
@@ -126,14 +116,4 @@ class GameAdapter(private val activity: AppCompatActivity) :
126 return oldItem == newItem 116 return oldItem == newItem
127 } 117 }
128 } 118 }
129
130 private fun decodeGameIcon(uri: String): Bitmap? {
131 val data = NativeLibrary.getIcon(uri)
132 return BitmapFactory.decodeByteArray(
133 data,
134 0,
135 data.size,
136 BitmapFactory.Options()
137 )
138 }
139} 119}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress.kt
index a18efef19..6f4b5b13f 100755
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress.kt
@@ -4,43 +4,43 @@
4package org.yuzu.yuzu_emu.disk_shader_cache 4package org.yuzu.yuzu_emu.disk_shader_cache
5 5
6import androidx.annotation.Keep 6import androidx.annotation.Keep
7import androidx.lifecycle.ViewModelProvider
7import org.yuzu.yuzu_emu.NativeLibrary 8import org.yuzu.yuzu_emu.NativeLibrary
8import org.yuzu.yuzu_emu.R 9import org.yuzu.yuzu_emu.R
9import org.yuzu.yuzu_emu.disk_shader_cache.ui.ShaderProgressDialogFragment 10import org.yuzu.yuzu_emu.activities.EmulationActivity
11import org.yuzu.yuzu_emu.model.EmulationViewModel
12import org.yuzu.yuzu_emu.utils.Log
10 13
11@Keep 14@Keep
12object DiskShaderCacheProgress { 15object DiskShaderCacheProgress {
13 val finishLock = Object() 16 private lateinit var emulationViewModel: EmulationViewModel
14 private lateinit var fragment: ShaderProgressDialogFragment
15 17
16 private fun prepareDialog() { 18 private fun prepareViewModel() {
17 val emulationActivity = NativeLibrary.sEmulationActivity.get()!! 19 emulationViewModel =
18 emulationActivity.runOnUiThread { 20 ViewModelProvider(
19 fragment = ShaderProgressDialogFragment.newInstance( 21 NativeLibrary.sEmulationActivity.get() as EmulationActivity
20 emulationActivity.getString(R.string.loading), 22 )[EmulationViewModel::class.java]
21 emulationActivity.getString(R.string.preparing_shaders)
22 )
23 fragment.show(
24 emulationActivity.supportFragmentManager,
25 ShaderProgressDialogFragment.TAG
26 )
27 }
28 synchronized(finishLock) { finishLock.wait() }
29 } 23 }
30 24
31 @JvmStatic 25 @JvmStatic
32 fun loadProgress(stage: Int, progress: Int, max: Int) { 26 fun loadProgress(stage: Int, progress: Int, max: Int) {
33 val emulationActivity = NativeLibrary.sEmulationActivity.get() 27 val emulationActivity = NativeLibrary.sEmulationActivity.get()
34 ?: error("[DiskShaderCacheProgress] EmulationActivity not present") 28 if (emulationActivity == null) {
35 29 Log.error("[DiskShaderCacheProgress] EmulationActivity not present")
36 when (LoadCallbackStage.values()[stage]) { 30 return
37 LoadCallbackStage.Prepare -> prepareDialog() 31 }
38 LoadCallbackStage.Build -> fragment.onUpdateProgress( 32
39 emulationActivity.getString(R.string.building_shaders), 33 emulationActivity.runOnUiThread {
40 progress, 34 when (LoadCallbackStage.values()[stage]) {
41 max 35 LoadCallbackStage.Prepare -> prepareViewModel()
42 ) 36 LoadCallbackStage.Build -> emulationViewModel.updateProgress(
43 LoadCallbackStage.Complete -> fragment.dismiss() 37 emulationActivity.getString(R.string.building_shaders),
38 progress,
39 max
40 )
41
42 LoadCallbackStage.Complete -> {}
43 }
44 } 44 }
45 } 45 }
46 46
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 53f19c4f8..944ae652e 100755
--- 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
@@ -24,8 +24,9 @@ import androidx.core.content.res.ResourcesCompat
24import androidx.core.graphics.Insets 24import androidx.core.graphics.Insets
25import androidx.core.view.ViewCompat 25import androidx.core.view.ViewCompat
26import androidx.core.view.WindowInsetsCompat 26import androidx.core.view.WindowInsetsCompat
27import androidx.core.view.isVisible 27import androidx.drawerlayout.widget.DrawerLayout
28import androidx.fragment.app.Fragment 28import androidx.fragment.app.Fragment
29import androidx.fragment.app.activityViewModels
29import androidx.lifecycle.Lifecycle 30import androidx.lifecycle.Lifecycle
30import androidx.lifecycle.lifecycleScope 31import androidx.lifecycle.lifecycleScope
31import androidx.lifecycle.repeatOnLifecycle 32import androidx.lifecycle.repeatOnLifecycle
@@ -50,6 +51,7 @@ import org.yuzu.yuzu_emu.features.settings.model.IntSetting
50import org.yuzu.yuzu_emu.features.settings.model.Settings 51import org.yuzu.yuzu_emu.features.settings.model.Settings
51import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile 52import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
52import org.yuzu.yuzu_emu.model.Game 53import org.yuzu.yuzu_emu.model.Game
54import org.yuzu.yuzu_emu.model.EmulationViewModel
53import org.yuzu.yuzu_emu.overlay.InputOverlay 55import org.yuzu.yuzu_emu.overlay.InputOverlay
54import org.yuzu.yuzu_emu.utils.* 56import org.yuzu.yuzu_emu.utils.*
55 57
@@ -66,6 +68,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
66 68
67 private lateinit var game: Game 69 private lateinit var game: Game
68 70
71 private val emulationViewModel: EmulationViewModel by activityViewModels()
72
69 private var isInFoldableLayout = false 73 private var isInFoldableLayout = false
70 74
71 override fun onAttach(context: Context) { 75 override fun onAttach(context: Context) {
@@ -130,9 +134,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
130 binding.showFpsText.setTextColor(Color.YELLOW) 134 binding.showFpsText.setTextColor(Color.YELLOW)
131 binding.doneControlConfig.setOnClickListener { stopConfiguringControls() } 135 binding.doneControlConfig.setOnClickListener { stopConfiguringControls() }
132 136
133 // Setup overlay. 137 binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
134 updateShowFpsOverlay()
135
136 binding.inGameMenu.getHeaderView(0).findViewById<TextView>(R.id.text_game_title).text = 138 binding.inGameMenu.getHeaderView(0).findViewById<TextView>(R.id.text_game_title).text =
137 game.title 139 game.title
138 binding.inGameMenu.setNavigationItemSelectedListener { 140 binding.inGameMenu.setNavigationItemSelectedListener {
@@ -174,7 +176,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
174 176
175 R.id.menu_exit -> { 177 R.id.menu_exit -> {
176 emulationState.stop() 178 emulationState.stop()
177 requireActivity().finish() 179 emulationViewModel.setIsEmulationStopping(true)
180 binding.drawerLayout.close()
181 binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
178 true 182 true
179 } 183 }
180 184
@@ -188,6 +192,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
188 requireActivity(), 192 requireActivity(),
189 object : OnBackPressedCallback(true) { 193 object : OnBackPressedCallback(true) {
190 override fun handleOnBackPressed() { 194 override fun handleOnBackPressed() {
195 if (!NativeLibrary.isRunning()) {
196 return
197 }
198
191 if (binding.drawerLayout.isOpen) { 199 if (binding.drawerLayout.isOpen) {
192 binding.drawerLayout.close() 200 binding.drawerLayout.close()
193 } else { 201 } else {
@@ -204,6 +212,54 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
204 .collect { updateFoldableLayout(requireActivity() as EmulationActivity, it) } 212 .collect { updateFoldableLayout(requireActivity() as EmulationActivity, it) }
205 } 213 }
206 } 214 }
215
216 GameIconUtils.loadGameIcon(game, binding.loadingImage)
217 binding.loadingTitle.text = game.title
218 binding.loadingTitle.isSelected = true
219 binding.loadingText.isSelected = true
220
221 emulationViewModel.shaderProgress.observe(viewLifecycleOwner) {
222 if (it > 0 && it != emulationViewModel.totalShaders.value!!) {
223 binding.loadingProgressIndicator.isIndeterminate = false
224
225 if (it < binding.loadingProgressIndicator.max) {
226 binding.loadingProgressIndicator.progress = it
227 }
228 }
229
230 if (it == emulationViewModel.totalShaders.value!!) {
231 binding.loadingText.setText(R.string.loading)
232 binding.loadingProgressIndicator.isIndeterminate = true
233 }
234 }
235 emulationViewModel.totalShaders.observe(viewLifecycleOwner) {
236 binding.loadingProgressIndicator.max = it
237 }
238 emulationViewModel.shaderMessage.observe(viewLifecycleOwner) {
239 if (it.isNotEmpty()) {
240 binding.loadingText.text = it
241 }
242 }
243
244 emulationViewModel.emulationStarted.observe(viewLifecycleOwner) { started ->
245 if (started) {
246 binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
247 ViewUtils.showView(binding.surfaceInputOverlay)
248 ViewUtils.hideView(binding.loadingIndicator)
249
250 // Setup overlay
251 updateShowFpsOverlay()
252 }
253 }
254
255 emulationViewModel.isEmulationStopping.observe(viewLifecycleOwner) {
256 if (it) {
257 binding.loadingText.setText(R.string.shutting_down)
258 ViewUtils.showView(binding.loadingIndicator)
259 ViewUtils.hideView(binding.inputContainer)
260 ViewUtils.hideView(binding.showFpsText)
261 }
262 }
207 } 263 }
208 264
209 override fun onConfigurationChanged(newConfig: Configuration) { 265 override fun onConfigurationChanged(newConfig: Configuration) {
@@ -213,11 +269,21 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
213 binding.drawerLayout.close() 269 binding.drawerLayout.close()
214 } 270 }
215 if (EmulationMenuSettings.showOverlay) { 271 if (EmulationMenuSettings.showOverlay) {
216 binding.surfaceInputOverlay.post { binding.surfaceInputOverlay.isVisible = false } 272 binding.surfaceInputOverlay.post {
273 binding.surfaceInputOverlay.visibility = View.VISIBLE
274 }
217 } 275 }
218 } else { 276 } else {
219 if (EmulationMenuSettings.showOverlay) { 277 if (EmulationMenuSettings.showOverlay &&
220 binding.surfaceInputOverlay.post { binding.surfaceInputOverlay.isVisible = true } 278 emulationViewModel.emulationStarted.value == true
279 ) {
280 binding.surfaceInputOverlay.post {
281 binding.surfaceInputOverlay.visibility = View.VISIBLE
282 }
283 } else {
284 binding.surfaceInputOverlay.post {
285 binding.surfaceInputOverlay.visibility = View.INVISIBLE
286 }
221 } 287 }
222 if (!isInFoldableLayout) { 288 if (!isInFoldableLayout) {
223 if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { 289 if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
@@ -226,9 +292,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
226 binding.surfaceInputOverlay.layout = InputOverlay.LANDSCAPE 292 binding.surfaceInputOverlay.layout = InputOverlay.LANDSCAPE
227 } 293 }
228 } 294 }
229 if (!binding.surfaceInputOverlay.isInEditMode) {
230 refreshInputOverlay()
231 }
232 } 295 }
233 } 296 }
234 297
@@ -260,10 +323,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
260 super.onDetach() 323 super.onDetach()
261 } 324 }
262 325
263 private fun refreshInputOverlay() {
264 binding.surfaceInputOverlay.refreshControls()
265 }
266
267 private fun resetInputOverlay() { 326 private fun resetInputOverlay() {
268 preferences.edit() 327 preferences.edit()
269 .remove(Settings.PREF_CONTROL_SCALE) 328 .remove(Settings.PREF_CONTROL_SCALE)
@@ -281,17 +340,15 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
281 val FRAMETIME = 2 340 val FRAMETIME = 2
282 val SPEED = 3 341 val SPEED = 3
283 perfStatsUpdater = { 342 perfStatsUpdater = {
284 val perfStats = NativeLibrary.getPerfStats() 343 if (emulationViewModel.emulationStarted.value == true) {
285 if (perfStats[FPS] > 0 && _binding != null) { 344 val perfStats = NativeLibrary.getPerfStats()
286 binding.showFpsText.text = String.format("FPS: %.1f", perfStats[FPS]) 345 if (perfStats[FPS] > 0 && _binding != null) {
287 } 346 binding.showFpsText.text = String.format("FPS: %.1f", perfStats[FPS])
288 347 }
289 if (!emulationState.isStopped) {
290 perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 100) 348 perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 100)
291 } 349 }
292 } 350 }
293 perfStatsUpdateHandler.post(perfStatsUpdater!!) 351 perfStatsUpdateHandler.post(perfStatsUpdater!!)
294 binding.showFpsText.text = resources.getString(R.string.emulation_game_loading)
295 binding.showFpsText.visibility = View.VISIBLE 352 binding.showFpsText.visibility = View.VISIBLE
296 } else { 353 } else {
297 if (perfStatsUpdater != null) { 354 if (perfStatsUpdater != null) {
@@ -349,7 +406,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
349 406
350 isInFoldableLayout = true 407 isInFoldableLayout = true
351 binding.surfaceInputOverlay.layout = InputOverlay.FOLDABLE 408 binding.surfaceInputOverlay.layout = InputOverlay.FOLDABLE
352 refreshInputOverlay()
353 } 409 }
354 } 410 }
355 it.isSeparating 411 it.isSeparating
@@ -437,7 +493,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
437 .apply() 493 .apply()
438 } 494 }
439 .setPositiveButton(android.R.string.ok) { _, _ -> 495 .setPositiveButton(android.R.string.ok) { _, _ ->
440 refreshInputOverlay() 496 binding.surfaceInputOverlay.refreshControls()
441 } 497 }
442 .setNegativeButton(android.R.string.cancel, null) 498 .setNegativeButton(android.R.string.cancel, null)
443 .setNeutralButton(R.string.emulation_toggle_all) { _, _ -> } 499 .setNeutralButton(R.string.emulation_toggle_all) { _, _ -> }
@@ -461,7 +517,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
461 R.id.menu_show_overlay -> { 517 R.id.menu_show_overlay -> {
462 it.isChecked = !it.isChecked 518 it.isChecked = !it.isChecked
463 EmulationMenuSettings.showOverlay = it.isChecked 519 EmulationMenuSettings.showOverlay = it.isChecked
464 refreshInputOverlay() 520 binding.surfaceInputOverlay.refreshControls()
465 true 521 true
466 } 522 }
467 523
@@ -567,14 +623,14 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
567 preferences.edit() 623 preferences.edit()
568 .putInt(Settings.PREF_CONTROL_SCALE, scale) 624 .putInt(Settings.PREF_CONTROL_SCALE, scale)
569 .apply() 625 .apply()
570 refreshInputOverlay() 626 binding.surfaceInputOverlay.refreshControls()
571 } 627 }
572 628
573 private fun setControlOpacity(opacity: Int) { 629 private fun setControlOpacity(opacity: Int) {
574 preferences.edit() 630 preferences.edit()
575 .putInt(Settings.PREF_CONTROL_OPACITY, opacity) 631 .putInt(Settings.PREF_CONTROL_OPACITY, opacity)
576 .apply() 632 .apply()
577 refreshInputOverlay() 633 binding.surfaceInputOverlay.refreshControls()
578 } 634 }
579 635
580 private fun setInsets() { 636 private fun setInsets() {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt
new file mode 100755
index 000000000..e35f51bc3
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt
@@ -0,0 +1,59 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4package org.yuzu.yuzu_emu.model
5
6import androidx.lifecycle.LiveData
7import androidx.lifecycle.MutableLiveData
8import androidx.lifecycle.ViewModel
9
10class EmulationViewModel : ViewModel() {
11 private val _emulationStarted = MutableLiveData(false)
12 val emulationStarted: LiveData<Boolean> get() = _emulationStarted
13
14 private val _isEmulationStopping = MutableLiveData(false)
15 val isEmulationStopping: LiveData<Boolean> get() = _isEmulationStopping
16
17 private val _shaderProgress = MutableLiveData(0)
18 val shaderProgress: LiveData<Int> get() = _shaderProgress
19
20 private val _totalShaders = MutableLiveData(0)
21 val totalShaders: LiveData<Int> get() = _totalShaders
22
23 private val _shaderMessage = MutableLiveData("")
24 val shaderMessage: LiveData<String> get() = _shaderMessage
25
26 fun setEmulationStarted(started: Boolean) {
27 _emulationStarted.postValue(started)
28 }
29
30 fun setIsEmulationStopping(value: Boolean) {
31 _isEmulationStopping.value = value
32 }
33
34 fun setShaderProgress(progress: Int) {
35 _shaderProgress.value = progress
36 }
37
38 fun setTotalShaders(max: Int) {
39 _totalShaders.value = max
40 }
41
42 fun setShaderMessage(msg: String) {
43 _shaderMessage.value = msg
44 }
45
46 fun updateProgress(msg: String, progress: Int, max: Int) {
47 setShaderMessage(msg)
48 setShaderProgress(progress)
49 setTotalShaders(max)
50 }
51
52 fun clear() {
53 _emulationStarted.value = false
54 _isEmulationStopping.value = false
55 _shaderProgress.value = 0
56 _totalShaders.value = 0
57 _shaderMessage.value = ""
58 }
59}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt
new file mode 100755
index 000000000..c0fe596d7
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt
@@ -0,0 +1,77 @@
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.graphics.Bitmap
7import android.graphics.BitmapFactory
8import android.widget.ImageView
9import androidx.core.graphics.drawable.toDrawable
10import coil.ImageLoader
11import coil.decode.DataSource
12import coil.fetch.DrawableResult
13import coil.fetch.FetchResult
14import coil.fetch.Fetcher
15import coil.key.Keyer
16import coil.memory.MemoryCache
17import coil.request.ImageRequest
18import coil.request.Options
19import org.yuzu.yuzu_emu.NativeLibrary
20import org.yuzu.yuzu_emu.R
21import org.yuzu.yuzu_emu.YuzuApplication
22import org.yuzu.yuzu_emu.model.Game
23
24class GameIconFetcher(
25 private val game: Game,
26 private val options: Options
27) : Fetcher {
28 override suspend fun fetch(): FetchResult {
29 return DrawableResult(
30 drawable = decodeGameIcon(game.path)!!.toDrawable(options.context.resources),
31 isSampled = false,
32 dataSource = DataSource.DISK
33 )
34 }
35
36 private fun decodeGameIcon(uri: String): Bitmap? {
37 val data = NativeLibrary.getIcon(uri)
38 return BitmapFactory.decodeByteArray(
39 data,
40 0,
41 data.size,
42 BitmapFactory.Options()
43 )
44 }
45
46 class Factory : Fetcher.Factory<Game> {
47 override fun create(data: Game, options: Options, imageLoader: ImageLoader): Fetcher =
48 GameIconFetcher(data, options)
49 }
50}
51
52class GameIconKeyer : Keyer<Game> {
53 override fun key(data: Game, options: Options): String = data.path
54}
55
56object GameIconUtils {
57 private val imageLoader = ImageLoader.Builder(YuzuApplication.appContext)
58 .components {
59 add(GameIconKeyer())
60 add(GameIconFetcher.Factory())
61 }
62 .memoryCache {
63 MemoryCache.Builder(YuzuApplication.appContext)
64 .maxSizePercent(0.25)
65 .build()
66 }
67 .build()
68
69 fun loadGameIcon(game: Game, imageView: ImageView) {
70 val request = ImageRequest.Builder(YuzuApplication.appContext)
71 .data(game)
72 .target(imageView)
73 .error(R.drawable.default_icon)
74 .build()
75 imageLoader.enqueue(request)
76 }
77}
diff --git a/src/android/app/src/main/jni/id_cache.cpp b/src/android/app/src/main/jni/id_cache.cpp
index 9cbbf23a3..960abf95a 100755
--- a/src/android/app/src/main/jni/id_cache.cpp
+++ b/src/android/app/src/main/jni/id_cache.cpp
@@ -15,6 +15,8 @@ static jclass s_disk_cache_progress_class;
15static jclass s_load_callback_stage_class; 15static jclass s_load_callback_stage_class;
16static jmethodID s_exit_emulation_activity; 16static jmethodID s_exit_emulation_activity;
17static jmethodID s_disk_cache_load_progress; 17static jmethodID s_disk_cache_load_progress;
18static jmethodID s_on_emulation_started;
19static jmethodID s_on_emulation_stopped;
18 20
19static constexpr jint JNI_VERSION = JNI_VERSION_1_6; 21static constexpr jint JNI_VERSION = JNI_VERSION_1_6;
20 22
@@ -59,6 +61,14 @@ jmethodID GetDiskCacheLoadProgress() {
59 return s_disk_cache_load_progress; 61 return s_disk_cache_load_progress;
60} 62}
61 63
64jmethodID GetOnEmulationStarted() {
65 return s_on_emulation_started;
66}
67
68jmethodID GetOnEmulationStopped() {
69 return s_on_emulation_stopped;
70}
71
62} // namespace IDCache 72} // namespace IDCache
63 73
64#ifdef __cplusplus 74#ifdef __cplusplus
@@ -85,6 +95,10 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
85 env->GetStaticMethodID(s_native_library_class, "exitEmulationActivity", "(I)V"); 95 env->GetStaticMethodID(s_native_library_class, "exitEmulationActivity", "(I)V");
86 s_disk_cache_load_progress = 96 s_disk_cache_load_progress =
87 env->GetStaticMethodID(s_disk_cache_progress_class, "loadProgress", "(III)V"); 97 env->GetStaticMethodID(s_disk_cache_progress_class, "loadProgress", "(III)V");
98 s_on_emulation_started =
99 env->GetStaticMethodID(s_native_library_class, "onEmulationStarted", "()V");
100 s_on_emulation_stopped =
101 env->GetStaticMethodID(s_native_library_class, "onEmulationStopped", "(I)V");
88 102
89 // Initialize Android Storage 103 // Initialize Android Storage
90 Common::FS::Android::RegisterCallbacks(env, s_native_library_class); 104 Common::FS::Android::RegisterCallbacks(env, s_native_library_class);
diff --git a/src/android/app/src/main/jni/id_cache.h b/src/android/app/src/main/jni/id_cache.h
index be535fe1e..b76158928 100755
--- a/src/android/app/src/main/jni/id_cache.h
+++ b/src/android/app/src/main/jni/id_cache.h
@@ -15,5 +15,7 @@ jclass GetDiskCacheProgressClass();
15jclass GetDiskCacheLoadCallbackStageClass(); 15jclass GetDiskCacheLoadCallbackStageClass();
16jmethodID GetExitEmulationActivity(); 16jmethodID GetExitEmulationActivity();
17jmethodID GetDiskCacheLoadProgress(); 17jmethodID GetDiskCacheLoadProgress();
18jmethodID GetOnEmulationStarted();
19jmethodID GetOnEmulationStopped();
18 20
19} // namespace IDCache 21} // namespace IDCache
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp
index b2adfdeda..0f2a6d9e4 100755
--- a/src/android/app/src/main/jni/native.cpp
+++ b/src/android/app/src/main/jni/native.cpp
@@ -203,12 +203,10 @@ public:
203 } 203 }
204 204
205 bool IsRunning() const { 205 bool IsRunning() const {
206 std::scoped_lock lock(m_mutex);
207 return m_is_running; 206 return m_is_running;
208 } 207 }
209 208
210 bool IsPaused() const { 209 bool IsPaused() const {
211 std::scoped_lock lock(m_mutex);
212 return m_is_running && m_is_paused; 210 return m_is_running && m_is_paused;
213 } 211 }
214 212
@@ -335,6 +333,8 @@ public:
335 333
336 // Tear down the render window. 334 // Tear down the render window.
337 m_window.reset(); 335 m_window.reset();
336
337 OnEmulationStopped(m_load_result);
338 } 338 }
339 339
340 void PauseEmulation() { 340 void PauseEmulation() {
@@ -376,6 +376,8 @@ public:
376 m_system.InitializeDebugger(); 376 m_system.InitializeDebugger();
377 } 377 }
378 378
379 OnEmulationStarted();
380
379 while (true) { 381 while (true) {
380 { 382 {
381 [[maybe_unused]] std::unique_lock lock(m_mutex); 383 [[maybe_unused]] std::unique_lock lock(m_mutex);
@@ -511,6 +513,18 @@ private:
511 static_cast<jint>(progress), static_cast<jint>(max)); 513 static_cast<jint>(progress), static_cast<jint>(max));
512 } 514 }
513 515
516 static void OnEmulationStarted() {
517 JNIEnv* env = IDCache::GetEnvForThread();
518 env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(),
519 IDCache::GetOnEmulationStarted());
520 }
521
522 static void OnEmulationStopped(Core::SystemResultStatus result) {
523 JNIEnv* env = IDCache::GetEnvForThread();
524 env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(),
525 IDCache::GetOnEmulationStopped(), static_cast<jint>(result));
526 }
527
514private: 528private:
515 static EmulationSession s_instance; 529 static EmulationSession s_instance;
516 530
@@ -528,8 +542,8 @@ private:
528 Core::PerfStatsResults m_perf_stats{}; 542 Core::PerfStatsResults m_perf_stats{};
529 std::shared_ptr<FileSys::VfsFilesystem> m_vfs; 543 std::shared_ptr<FileSys::VfsFilesystem> m_vfs;
530 Core::SystemResultStatus m_load_result{Core::SystemResultStatus::ErrorNotInitialized}; 544 Core::SystemResultStatus m_load_result{Core::SystemResultStatus::ErrorNotInitialized};
531 bool m_is_running{}; 545 std::atomic<bool> m_is_running = false;
532 bool m_is_paused{}; 546 std::atomic<bool> m_is_paused = false;
533 SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{}; 547 SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{};
534 std::unique_ptr<Service::Account::ProfileManager> m_profile_manager; 548 std::unique_ptr<Service::Account::ProfileManager> m_profile_manager;
535 std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider; 549 std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider;
diff --git a/src/android/app/src/main/res/layout/fragment_emulation.xml b/src/android/app/src/main/res/layout/fragment_emulation.xml
index e54a10e8f..da97d85c1 100755
--- a/src/android/app/src/main/res/layout/fragment_emulation.xml
+++ b/src/android/app/src/main/res/layout/fragment_emulation.xml
@@ -26,6 +26,81 @@
26 android:focusable="false" 26 android:focusable="false"
27 android:focusableInTouchMode="false" /> 27 android:focusableInTouchMode="false" />
28 28
29 <com.google.android.material.card.MaterialCardView
30 android:id="@+id/loading_indicator"
31 style="?attr/materialCardViewOutlinedStyle"
32 android:layout_width="wrap_content"
33 android:layout_height="wrap_content"
34 android:layout_gravity="center"
35 android:focusable="false">
36
37 <androidx.constraintlayout.widget.ConstraintLayout
38 android:id="@+id/loading_layout"
39 android:layout_width="wrap_content"
40 android:layout_height="wrap_content"
41 android:gravity="center_horizontal">
42
43 <ImageView
44 android:id="@+id/loading_image"
45 android:layout_width="wrap_content"
46 android:layout_height="0dp"
47 android:adjustViewBounds="true"
48 app:layout_constraintBottom_toBottomOf="@+id/linearLayout"
49 app:layout_constraintStart_toStartOf="parent"
50 app:layout_constraintTop_toTopOf="@+id/linearLayout"
51 tools:src="@drawable/default_icon" />
52
53 <LinearLayout
54 android:id="@+id/linearLayout"
55 android:layout_width="wrap_content"
56 android:layout_height="wrap_content"
57 android:orientation="vertical"
58 android:paddingHorizontal="24dp"
59 android:paddingVertical="36dp"
60 app:layout_constraintBottom_toBottomOf="parent"
61 app:layout_constraintEnd_toEndOf="parent"
62 app:layout_constraintStart_toEndOf="@id/loading_image"
63 app:layout_constraintTop_toTopOf="parent">
64
65 <com.google.android.material.textview.MaterialTextView
66 android:id="@+id/loading_title"
67 style="@style/TextAppearance.Material3.TitleMedium"
68 android:layout_width="match_parent"
69 android:layout_height="wrap_content"
70 android:ellipsize="marquee"
71 android:marqueeRepeatLimit="marquee_forever"
72 android:requiresFadingEdge="horizontal"
73 android:singleLine="true"
74 android:textAlignment="viewStart"
75 tools:text="@string/games" />
76
77 <com.google.android.material.textview.MaterialTextView
78 android:id="@+id/loading_text"
79 style="@style/TextAppearance.Material3.TitleSmall"
80 android:layout_width="match_parent"
81 android:layout_height="wrap_content"
82 android:layout_marginTop="4dp"
83 android:ellipsize="marquee"
84 android:marqueeRepeatLimit="marquee_forever"
85 android:requiresFadingEdge="horizontal"
86 android:singleLine="true"
87 android:text="@string/loading"
88 android:textAlignment="viewStart" />
89
90 <com.google.android.material.progressindicator.LinearProgressIndicator
91 android:id="@+id/loading_progress_indicator"
92 android:layout_width="192dp"
93 android:layout_height="wrap_content"
94 android:layout_marginTop="12dp"
95 android:indeterminate="true"
96 app:trackCornerRadius="8dp" />
97
98 </LinearLayout>
99
100 </androidx.constraintlayout.widget.ConstraintLayout>
101
102 </com.google.android.material.card.MaterialCardView>
103
29 </FrameLayout> 104 </FrameLayout>
30 105
31 <FrameLayout 106 <FrameLayout
@@ -41,11 +116,12 @@
41 android:layout_height="match_parent" 116 android:layout_height="match_parent"
42 android:layout_gravity="center" 117 android:layout_gravity="center"
43 android:focusable="true" 118 android:focusable="true"
44 android:focusableInTouchMode="true" /> 119 android:focusableInTouchMode="true"
120 android:visibility="invisible" />
45 121
46 <Button 122 <Button
47 style="@style/Widget.Material3.Button.ElevatedButton"
48 android:id="@+id/done_control_config" 123 android:id="@+id/done_control_config"
124 style="@style/Widget.Material3.Button.ElevatedButton"
49 android:layout_width="wrap_content" 125 android:layout_width="wrap_content"
50 android:layout_height="wrap_content" 126 android:layout_height="wrap_content"
51 android:layout_gravity="center" 127 android:layout_gravity="center"
@@ -81,6 +157,7 @@
81 android:layout_height="match_parent" 157 android:layout_height="match_parent"
82 android:layout_gravity="start|bottom" 158 android:layout_gravity="start|bottom"
83 app:headerLayout="@layout/header_in_game" 159 app:headerLayout="@layout/header_in_game"
84 app:menu="@menu/menu_in_game" /> 160 app:menu="@menu/menu_in_game"
161 tools:visibility="gone" />
85 162
86</androidx.drawerlayout.widget.DrawerLayout> 163</androidx.drawerlayout.widget.DrawerLayout>
diff --git a/src/android/app/src/main/res/values-de/strings.xml b/src/android/app/src/main/res/values-de/strings.xml
index 0c1d91264..daaa7ffde 100755
--- a/src/android/app/src/main/res/values-de/strings.xml
+++ b/src/android/app/src/main/res/values-de/strings.xml
@@ -209,7 +209,6 @@
209 <string name="emulation_pause">Emulation pausieren</string> 209 <string name="emulation_pause">Emulation pausieren</string>
210 <string name="emulation_unpause">Emulation fortsetzen</string> 210 <string name="emulation_unpause">Emulation fortsetzen</string>
211 <string name="emulation_input_overlay">Overlay-Optionen</string> 211 <string name="emulation_input_overlay">Overlay-Optionen</string>
212 <string name="emulation_game_loading">Spiel lädt…</string>
213 212
214 <string name="load_settings">Lädt Einstellungen...</string> 213 <string name="load_settings">Lädt Einstellungen...</string>
215 214
diff --git a/src/android/app/src/main/res/values-es/strings.xml b/src/android/app/src/main/res/values-es/strings.xml
index 357f956d1..e9129cb00 100755
--- a/src/android/app/src/main/res/values-es/strings.xml
+++ b/src/android/app/src/main/res/values-es/strings.xml
@@ -213,7 +213,6 @@
213 <string name="emulation_pause">Pausar Emulación</string> 213 <string name="emulation_pause">Pausar Emulación</string>
214 <string name="emulation_unpause">Reanudar Emulación</string> 214 <string name="emulation_unpause">Reanudar Emulación</string>
215 <string name="emulation_input_overlay">Opciones de pantalla </string> 215 <string name="emulation_input_overlay">Opciones de pantalla </string>
216 <string name="emulation_game_loading">Cargando juego...</string>
217 216
218 <string name="load_settings">Cargando configuración...</string> 217 <string name="load_settings">Cargando configuración...</string>
219 218
diff --git a/src/android/app/src/main/res/values-fr/strings.xml b/src/android/app/src/main/res/values-fr/strings.xml
index dfca1c830..2d99d618e 100755
--- a/src/android/app/src/main/res/values-fr/strings.xml
+++ b/src/android/app/src/main/res/values-fr/strings.xml
@@ -213,7 +213,6 @@
213 <string name="emulation_pause">Mettre en pause l\'émulation</string> 213 <string name="emulation_pause">Mettre en pause l\'émulation</string>
214 <string name="emulation_unpause">Reprendre l\'émulation</string> 214 <string name="emulation_unpause">Reprendre l\'émulation</string>
215 <string name="emulation_input_overlay">Options de l\'overlay</string> 215 <string name="emulation_input_overlay">Options de l\'overlay</string>
216 <string name="emulation_game_loading">Chargement du jeu...</string>
217 216
218 <string name="load_settings">Chargement des paramètres…</string> 217 <string name="load_settings">Chargement des paramètres…</string>
219 218
diff --git a/src/android/app/src/main/res/values-it/strings.xml b/src/android/app/src/main/res/values-it/strings.xml
index 089d93ed6..d9c3de385 100755
--- a/src/android/app/src/main/res/values-it/strings.xml
+++ b/src/android/app/src/main/res/values-it/strings.xml
@@ -213,7 +213,6 @@
213 <string name="emulation_pause">Metti in pausa l\'emulazione</string> 213 <string name="emulation_pause">Metti in pausa l\'emulazione</string>
214 <string name="emulation_unpause">Riprendi Emulazione</string> 214 <string name="emulation_unpause">Riprendi Emulazione</string>
215 <string name="emulation_input_overlay">Impostazioni Overlay</string> 215 <string name="emulation_input_overlay">Impostazioni Overlay</string>
216 <string name="emulation_game_loading">Caricamento del gioco...</string>
217 216
218 <string name="load_settings">Caricamento delle impostazioni...</string> 217 <string name="load_settings">Caricamento delle impostazioni...</string>
219 218
diff --git a/src/android/app/src/main/res/values-ja/strings.xml b/src/android/app/src/main/res/values-ja/strings.xml
index 39b590bee..7a226cd5c 100755
--- a/src/android/app/src/main/res/values-ja/strings.xml
+++ b/src/android/app/src/main/res/values-ja/strings.xml
@@ -211,7 +211,6 @@
211 <string name="emulation_pause">エミュレーションを一時停止</string> 211 <string name="emulation_pause">エミュレーションを一時停止</string>
212 <string name="emulation_unpause">エミュレーションを再開</string> 212 <string name="emulation_unpause">エミュレーションを再開</string>
213 <string name="emulation_input_overlay">オーバーレイオプション</string> 213 <string name="emulation_input_overlay">オーバーレイオプション</string>
214 <string name="emulation_game_loading">ロード中…</string>
215 214
216 <string name="load_settings">設定をロード中…</string> 215 <string name="load_settings">設定をロード中…</string>
217 216
diff --git a/src/android/app/src/main/res/values-ko/strings.xml b/src/android/app/src/main/res/values-ko/strings.xml
index cbcb2873f..427b6e5a0 100755
--- a/src/android/app/src/main/res/values-ko/strings.xml
+++ b/src/android/app/src/main/res/values-ko/strings.xml
@@ -213,7 +213,6 @@
213 <string name="emulation_pause">에뮬레이션 일시 중지</string> 213 <string name="emulation_pause">에뮬레이션 일시 중지</string>
214 <string name="emulation_unpause">에뮬레이션 일시 중지 해제</string> 214 <string name="emulation_unpause">에뮬레이션 일시 중지 해제</string>
215 <string name="emulation_input_overlay">오버레이 옵션</string> 215 <string name="emulation_input_overlay">오버레이 옵션</string>
216 <string name="emulation_game_loading">게임 불러오기 중...</string>
217 216
218 <string name="load_settings">설정 불러오기 중...</string> 217 <string name="load_settings">설정 불러오기 중...</string>
219 218
diff --git a/src/android/app/src/main/res/values-nb/strings.xml b/src/android/app/src/main/res/values-nb/strings.xml
index e48a4be38..ce8d7a9e4 100755
--- a/src/android/app/src/main/res/values-nb/strings.xml
+++ b/src/android/app/src/main/res/values-nb/strings.xml
@@ -213,7 +213,6 @@
213 <string name="emulation_pause">Pause Emulering</string> 213 <string name="emulation_pause">Pause Emulering</string>
214 <string name="emulation_unpause">Opphev pausing av emulering</string> 214 <string name="emulation_unpause">Opphev pausing av emulering</string>
215 <string name="emulation_input_overlay">Alternativer for overlegg</string> 215 <string name="emulation_input_overlay">Alternativer for overlegg</string>
216 <string name="emulation_game_loading">Spillet lastes inn...</string>
217 216
218 <string name="load_settings">Laster inn innstillinger...</string> 217 <string name="load_settings">Laster inn innstillinger...</string>
219 218
diff --git a/src/android/app/src/main/res/values-pl/strings.xml b/src/android/app/src/main/res/values-pl/strings.xml
index bc9c0f7f4..c2c24b48f 100755
--- a/src/android/app/src/main/res/values-pl/strings.xml
+++ b/src/android/app/src/main/res/values-pl/strings.xml
@@ -213,7 +213,6 @@
213 <string name="emulation_pause">Wstrzymaj emulację</string> 213 <string name="emulation_pause">Wstrzymaj emulację</string>
214 <string name="emulation_unpause">Wznów emulację</string> 214 <string name="emulation_unpause">Wznów emulację</string>
215 <string name="emulation_input_overlay">Opcje nakładki</string> 215 <string name="emulation_input_overlay">Opcje nakładki</string>
216 <string name="emulation_game_loading">Wczytywanie gry...</string>
217 216
218 <string name="load_settings">Wczytywanie ustawień...</string> 217 <string name="load_settings">Wczytywanie ustawień...</string>
219 218
diff --git a/src/android/app/src/main/res/values-pt-rBR/strings.xml b/src/android/app/src/main/res/values-pt-rBR/strings.xml
index 75fe0edbf..04f276108 100755
--- a/src/android/app/src/main/res/values-pt-rBR/strings.xml
+++ b/src/android/app/src/main/res/values-pt-rBR/strings.xml
@@ -213,7 +213,6 @@
213 <string name="emulation_pause">Pausa emulação</string> 213 <string name="emulation_pause">Pausa emulação</string>
214 <string name="emulation_unpause">Retomar emulação</string> 214 <string name="emulation_unpause">Retomar emulação</string>
215 <string name="emulation_input_overlay">Opções de sobreposição </string> 215 <string name="emulation_input_overlay">Opções de sobreposição </string>
216 <string name="emulation_game_loading">Jogo a carregar...</string>
217 216
218 <string name="load_settings">Configurações a carregar...</string> 217 <string name="load_settings">Configurações a carregar...</string>
219 218
diff --git a/src/android/app/src/main/res/values-pt-rPT/strings.xml b/src/android/app/src/main/res/values-pt-rPT/strings.xml
index 96b040c66..66a3a1a2e 100755
--- a/src/android/app/src/main/res/values-pt-rPT/strings.xml
+++ b/src/android/app/src/main/res/values-pt-rPT/strings.xml
@@ -213,7 +213,6 @@
213 <string name="emulation_pause">Pausa emulação</string> 213 <string name="emulation_pause">Pausa emulação</string>
214 <string name="emulation_unpause">Retomar emulação</string> 214 <string name="emulation_unpause">Retomar emulação</string>
215 <string name="emulation_input_overlay">Opções de sobreposição </string> 215 <string name="emulation_input_overlay">Opções de sobreposição </string>
216 <string name="emulation_game_loading">Jogo a carregar...</string>
217 216
218 <string name="load_settings">Configurações a carregar...</string> 217 <string name="load_settings">Configurações a carregar...</string>
219 218
diff --git a/src/android/app/src/main/res/values-ru/strings.xml b/src/android/app/src/main/res/values-ru/strings.xml
index 8d954f59e..f770e954f 100755
--- a/src/android/app/src/main/res/values-ru/strings.xml
+++ b/src/android/app/src/main/res/values-ru/strings.xml
@@ -213,7 +213,6 @@
213 <string name="emulation_pause">Пауза эмуляции</string> 213 <string name="emulation_pause">Пауза эмуляции</string>
214 <string name="emulation_unpause">Возобновление эмуляции</string> 214 <string name="emulation_unpause">Возобновление эмуляции</string>
215 <string name="emulation_input_overlay">Настройки оверлея</string> 215 <string name="emulation_input_overlay">Настройки оверлея</string>
216 <string name="emulation_game_loading">Загрузка игры...</string>
217 216
218 <string name="load_settings">Загрузка настроек...</string> 217 <string name="load_settings">Загрузка настроек...</string>
219 218
diff --git a/src/android/app/src/main/res/values-uk/strings.xml b/src/android/app/src/main/res/values-uk/strings.xml
index 6c028535b..ea3ab1b15 100755
--- a/src/android/app/src/main/res/values-uk/strings.xml
+++ b/src/android/app/src/main/res/values-uk/strings.xml
@@ -213,7 +213,6 @@
213 <string name="emulation_pause">Пауза емуляції</string> 213 <string name="emulation_pause">Пауза емуляції</string>
214 <string name="emulation_unpause">Відновлення емуляції</string> 214 <string name="emulation_unpause">Відновлення емуляції</string>
215 <string name="emulation_input_overlay">Налаштування оверлея</string> 215 <string name="emulation_input_overlay">Налаштування оверлея</string>
216 <string name="emulation_game_loading">Завантаження гри...</string>
217 216
218 <string name="load_settings">Завантаження налаштувань...</string> 217 <string name="load_settings">Завантаження налаштувань...</string>
219 218
diff --git a/src/android/app/src/main/res/values-zh-rCN/strings.xml b/src/android/app/src/main/res/values-zh-rCN/strings.xml
index e4ad2ed07..b45a5a528 100755
--- a/src/android/app/src/main/res/values-zh-rCN/strings.xml
+++ b/src/android/app/src/main/res/values-zh-rCN/strings.xml
@@ -213,7 +213,6 @@
213 <string name="emulation_pause">暂停模拟</string> 213 <string name="emulation_pause">暂停模拟</string>
214 <string name="emulation_unpause">继续模拟</string> 214 <string name="emulation_unpause">继续模拟</string>
215 <string name="emulation_input_overlay">虚拟按键选项</string> 215 <string name="emulation_input_overlay">虚拟按键选项</string>
216 <string name="emulation_game_loading">载入游戏中…</string>
217 216
218 <string name="load_settings">正在载入设定…</string> 217 <string name="load_settings">正在载入设定…</string>
219 218
diff --git a/src/android/app/src/main/res/values-zh-rTW/strings.xml b/src/android/app/src/main/res/values-zh-rTW/strings.xml
index 0d32f23df..3aab889e4 100755
--- a/src/android/app/src/main/res/values-zh-rTW/strings.xml
+++ b/src/android/app/src/main/res/values-zh-rTW/strings.xml
@@ -213,7 +213,6 @@
213 <string name="emulation_pause">暫停模擬</string> 213 <string name="emulation_pause">暫停模擬</string>
214 <string name="emulation_unpause">取消暫停模擬</string> 214 <string name="emulation_unpause">取消暫停模擬</string>
215 <string name="emulation_input_overlay">覆疊選項</string> 215 <string name="emulation_input_overlay">覆疊選項</string>
216 <string name="emulation_game_loading">遊戲正在載入…</string>
217 216
218 <string name="load_settings">正在載入設定…</string> 217 <string name="load_settings">正在載入設定…</string>
219 218
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index d43891cec..b163e6fc1 100755
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -204,6 +204,7 @@
204 <string name="error_saving">Error saving %1$s.ini: %2$s</string> 204 <string name="error_saving">Error saving %1$s.ini: %2$s</string>
205 <string name="unimplemented_menu">Unimplemented Menu</string> 205 <string name="unimplemented_menu">Unimplemented Menu</string>
206 <string name="loading">Loading…</string> 206 <string name="loading">Loading…</string>
207 <string name="shutting_down">Shutting down…</string>
207 <string name="reset_setting_confirmation">Do you want to reset this setting back to its default value?</string> 208 <string name="reset_setting_confirmation">Do you want to reset this setting back to its default value?</string>
208 <string name="reset_to_default">Reset to default</string> 209 <string name="reset_to_default">Reset to default</string>
209 <string name="reset_all_settings">Reset all settings?</string> 210 <string name="reset_all_settings">Reset all settings?</string>
@@ -262,7 +263,6 @@
262 <string name="emulation_pause">Pause emulation</string> 263 <string name="emulation_pause">Pause emulation</string>
263 <string name="emulation_unpause">Unpause emulation</string> 264 <string name="emulation_unpause">Unpause emulation</string>
264 <string name="emulation_input_overlay">Overlay options</string> 265 <string name="emulation_input_overlay">Overlay options</string>
265 <string name="emulation_game_loading">Game loading…</string>
266 266
267 <string name="load_settings">Loading settings…</string> 267 <string name="load_settings">Loading settings…</string>
268 268