forked from eden-emu/eden
		
	android: Game loading/shutting down indicators
This commit is contained in:
		
							parent
							
								
									2b09d48329
								
							
						
					
					
						commit
						5b69f2967e
					
				
					 26 changed files with 311 additions and 210 deletions
				
			
		|  | @ -22,9 +22,7 @@ import org.yuzu.yuzu_emu.utils.FileUtil.exists | ||||||
| import org.yuzu.yuzu_emu.utils.FileUtil.getFileSize | import org.yuzu.yuzu_emu.utils.FileUtil.getFileSize | ||||||
| import org.yuzu.yuzu_emu.utils.FileUtil.isDirectory | import org.yuzu.yuzu_emu.utils.FileUtil.isDirectory | ||||||
| import org.yuzu.yuzu_emu.utils.FileUtil.openContentUri | import org.yuzu.yuzu_emu.utils.FileUtil.openContentUri | ||||||
| import org.yuzu.yuzu_emu.utils.Log.error | import org.yuzu.yuzu_emu.utils.Log | ||||||
| import org.yuzu.yuzu_emu.utils.Log.verbose |  | ||||||
| import org.yuzu.yuzu_emu.utils.Log.warning |  | ||||||
| import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable | import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  | @ -465,7 +463,7 @@ object NativeLibrary { | ||||||
| 
 | 
 | ||||||
|         val emulationActivity = sEmulationActivity.get() |         val emulationActivity = sEmulationActivity.get() | ||||||
|         if (emulationActivity == null) { |         if (emulationActivity == null) { | ||||||
|             warning("[NativeLibrary] EmulationActivity is null, can't exit.") |             Log.warning("[NativeLibrary] EmulationActivity is null, can't exit.") | ||||||
|             return |             return | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | @ -490,15 +488,27 @@ object NativeLibrary { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fun setEmulationActivity(emulationActivity: EmulationActivity?) { |     fun setEmulationActivity(emulationActivity: EmulationActivity?) { | ||||||
|         verbose("[NativeLibrary] Registering EmulationActivity.") |         Log.verbose("[NativeLibrary] Registering EmulationActivity.") | ||||||
|         sEmulationActivity = WeakReference(emulationActivity) |         sEmulationActivity = WeakReference(emulationActivity) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fun clearEmulationActivity() { |     fun clearEmulationActivity() { | ||||||
|         verbose("[NativeLibrary] Unregistering EmulationActivity.") |         Log.verbose("[NativeLibrary] Unregistering EmulationActivity.") | ||||||
|         sEmulationActivity.clear() |         sEmulationActivity.clear() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     @Keep | ||||||
|  |     @JvmStatic | ||||||
|  |     fun onEmulationStarted() { | ||||||
|  |         sEmulationActivity.get()!!.onEmulationStarted() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Keep | ||||||
|  |     @JvmStatic | ||||||
|  |     fun onEmulationStopped(status: Int) { | ||||||
|  |         sEmulationActivity.get()!!.onEmulationStopped(status) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Logs the Yuzu version, Android version and, CPU. |      * Logs the Yuzu version, Android version and, CPU. | ||||||
|      */ |      */ | ||||||
|  |  | ||||||
|  | @ -28,6 +28,7 @@ import android.view.Surface | ||||||
| import android.view.View | import android.view.View | ||||||
| import android.view.inputmethod.InputMethodManager | import android.view.inputmethod.InputMethodManager | ||||||
| import android.widget.Toast | import android.widget.Toast | ||||||
|  | import androidx.activity.viewModels | ||||||
| import androidx.appcompat.app.AppCompatActivity | import androidx.appcompat.app.AppCompatActivity | ||||||
| import androidx.core.view.WindowCompat | import androidx.core.view.WindowCompat | ||||||
| import androidx.core.view.WindowInsetsCompat | import androidx.core.view.WindowInsetsCompat | ||||||
|  | @ -41,6 +42,7 @@ import org.yuzu.yuzu_emu.databinding.ActivityEmulationBinding | ||||||
| import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting | import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting | ||||||
| import org.yuzu.yuzu_emu.features.settings.model.IntSetting | import org.yuzu.yuzu_emu.features.settings.model.IntSetting | ||||||
| import org.yuzu.yuzu_emu.features.settings.model.Settings | import org.yuzu.yuzu_emu.features.settings.model.Settings | ||||||
|  | import org.yuzu.yuzu_emu.model.EmulationViewModel | ||||||
| import org.yuzu.yuzu_emu.model.Game | import org.yuzu.yuzu_emu.model.Game | ||||||
| import org.yuzu.yuzu_emu.utils.ControllerMappingHelper | import org.yuzu.yuzu_emu.utils.ControllerMappingHelper | ||||||
| import org.yuzu.yuzu_emu.utils.ForegroundService | import org.yuzu.yuzu_emu.utils.ForegroundService | ||||||
|  | @ -70,8 +72,11 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { | ||||||
|     private val actionMute = "ACTION_EMULATOR_MUTE" |     private val actionMute = "ACTION_EMULATOR_MUTE" | ||||||
|     private val actionUnmute = "ACTION_EMULATOR_UNMUTE" |     private val actionUnmute = "ACTION_EMULATOR_UNMUTE" | ||||||
| 
 | 
 | ||||||
|  |     private val emulationViewModel: EmulationViewModel by viewModels() | ||||||
|  | 
 | ||||||
|     override fun onDestroy() { |     override fun onDestroy() { | ||||||
|         stopForegroundService(this) |         stopForegroundService(this) | ||||||
|  |         emulationViewModel.clear() | ||||||
|         super.onDestroy() |         super.onDestroy() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -416,6 +421,16 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     fun onEmulationStarted() { | ||||||
|  |         emulationViewModel.setEmulationStarted(true) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fun onEmulationStopped(status: Int) { | ||||||
|  |         if (status == 0) { | ||||||
|  |             finish() | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     private fun startMotionSensorListener() { |     private fun startMotionSensorListener() { | ||||||
|         val sensorManager = this.getSystemService(Context.SENSOR_SERVICE) as SensorManager |         val sensorManager = this.getSystemService(Context.SENSOR_SERVICE) as SensorManager | ||||||
|         val gyroSensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE) |         val gyroSensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE) | ||||||
|  |  | ||||||
|  | @ -4,43 +4,43 @@ | ||||||
| package org.yuzu.yuzu_emu.disk_shader_cache | package org.yuzu.yuzu_emu.disk_shader_cache | ||||||
| 
 | 
 | ||||||
| import androidx.annotation.Keep | import androidx.annotation.Keep | ||||||
|  | import androidx.lifecycle.ViewModelProvider | ||||||
| import org.yuzu.yuzu_emu.NativeLibrary | import org.yuzu.yuzu_emu.NativeLibrary | ||||||
| import org.yuzu.yuzu_emu.R | import org.yuzu.yuzu_emu.R | ||||||
| import org.yuzu.yuzu_emu.disk_shader_cache.ui.ShaderProgressDialogFragment | import org.yuzu.yuzu_emu.activities.EmulationActivity | ||||||
|  | import org.yuzu.yuzu_emu.model.EmulationViewModel | ||||||
|  | import org.yuzu.yuzu_emu.utils.Log | ||||||
| 
 | 
 | ||||||
| @Keep | @Keep | ||||||
| object DiskShaderCacheProgress { | object DiskShaderCacheProgress { | ||||||
|     val finishLock = Object() |     private lateinit var emulationViewModel: EmulationViewModel | ||||||
|     private lateinit var fragment: ShaderProgressDialogFragment |  | ||||||
| 
 | 
 | ||||||
|     private fun prepareDialog() { |     private fun prepareViewModel() { | ||||||
|         val emulationActivity = NativeLibrary.sEmulationActivity.get()!! |         emulationViewModel = | ||||||
|         emulationActivity.runOnUiThread { |             ViewModelProvider( | ||||||
|             fragment = ShaderProgressDialogFragment.newInstance( |                 NativeLibrary.sEmulationActivity.get() as EmulationActivity | ||||||
|                 emulationActivity.getString(R.string.loading), |             )[EmulationViewModel::class.java] | ||||||
|                 emulationActivity.getString(R.string.preparing_shaders) |  | ||||||
|             ) |  | ||||||
|             fragment.show( |  | ||||||
|                 emulationActivity.supportFragmentManager, |  | ||||||
|                 ShaderProgressDialogFragment.TAG |  | ||||||
|             ) |  | ||||||
|         } |  | ||||||
|         synchronized(finishLock) { finishLock.wait() } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @JvmStatic |     @JvmStatic | ||||||
|     fun loadProgress(stage: Int, progress: Int, max: Int) { |     fun loadProgress(stage: Int, progress: Int, max: Int) { | ||||||
|         val emulationActivity = NativeLibrary.sEmulationActivity.get() |         val emulationActivity = NativeLibrary.sEmulationActivity.get() | ||||||
|             ?: error("[DiskShaderCacheProgress] EmulationActivity not present") |         if (emulationActivity == null) { | ||||||
|  |             Log.error("[DiskShaderCacheProgress] EmulationActivity not present") | ||||||
|  |             return | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         when (LoadCallbackStage.values()[stage]) { |         emulationActivity.runOnUiThread { | ||||||
|             LoadCallbackStage.Prepare -> prepareDialog() |             when (LoadCallbackStage.values()[stage]) { | ||||||
|             LoadCallbackStage.Build -> fragment.onUpdateProgress( |                 LoadCallbackStage.Prepare -> prepareViewModel() | ||||||
|                 emulationActivity.getString(R.string.building_shaders), |                 LoadCallbackStage.Build -> emulationViewModel.updateProgress( | ||||||
|                 progress, |                     emulationActivity.getString(R.string.building_shaders), | ||||||
|                 max |                     progress, | ||||||
|             ) |                     max | ||||||
|             LoadCallbackStage.Complete -> fragment.dismiss() |                 ) | ||||||
|  | 
 | ||||||
|  |                 LoadCallbackStage.Complete -> {} | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,31 +0,0 @@ | ||||||
| // SPDX-FileCopyrightText: 2023 yuzu Emulator Project |  | ||||||
| // SPDX-License-Identifier: GPL-2.0-or-later |  | ||||||
| 
 |  | ||||||
| package org.yuzu.yuzu_emu.disk_shader_cache |  | ||||||
| 
 |  | ||||||
| import androidx.lifecycle.LiveData |  | ||||||
| import androidx.lifecycle.MutableLiveData |  | ||||||
| import androidx.lifecycle.ViewModel |  | ||||||
| 
 |  | ||||||
| class ShaderProgressViewModel : ViewModel() { |  | ||||||
|     private val _progress = MutableLiveData(0) |  | ||||||
|     val progress: LiveData<Int> get() = _progress |  | ||||||
| 
 |  | ||||||
|     private val _max = MutableLiveData(0) |  | ||||||
|     val max: LiveData<Int> get() = _max |  | ||||||
| 
 |  | ||||||
|     private val _message = MutableLiveData("") |  | ||||||
|     val message: LiveData<String> get() = _message |  | ||||||
| 
 |  | ||||||
|     fun setProgress(progress: Int) { |  | ||||||
|         _progress.postValue(progress) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fun setMax(max: Int) { |  | ||||||
|         _max.postValue(max) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fun setMessage(msg: String) { |  | ||||||
|         _message.postValue(msg) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -1,103 +0,0 @@ | ||||||
| // SPDX-FileCopyrightText: 2023 yuzu Emulator Project |  | ||||||
| // SPDX-License-Identifier: GPL-2.0-or-later |  | ||||||
| 
 |  | ||||||
| package org.yuzu.yuzu_emu.disk_shader_cache.ui |  | ||||||
| 
 |  | ||||||
| import android.app.Dialog |  | ||||||
| import android.os.Bundle |  | ||||||
| import android.view.LayoutInflater |  | ||||||
| import android.view.View |  | ||||||
| import android.view.ViewGroup |  | ||||||
| import androidx.appcompat.app.AlertDialog |  | ||||||
| import androidx.fragment.app.DialogFragment |  | ||||||
| import androidx.lifecycle.ViewModelProvider |  | ||||||
| import com.google.android.material.dialog.MaterialAlertDialogBuilder |  | ||||||
| import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding |  | ||||||
| import org.yuzu.yuzu_emu.disk_shader_cache.DiskShaderCacheProgress |  | ||||||
| import org.yuzu.yuzu_emu.disk_shader_cache.ShaderProgressViewModel |  | ||||||
| 
 |  | ||||||
| class ShaderProgressDialogFragment : DialogFragment() { |  | ||||||
|     private var _binding: DialogProgressBarBinding? = null |  | ||||||
|     private val binding get() = _binding!! |  | ||||||
| 
 |  | ||||||
|     private lateinit var alertDialog: AlertDialog |  | ||||||
| 
 |  | ||||||
|     private lateinit var shaderProgressViewModel: ShaderProgressViewModel |  | ||||||
| 
 |  | ||||||
|     override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { |  | ||||||
|         _binding = DialogProgressBarBinding.inflate(layoutInflater) |  | ||||||
|         shaderProgressViewModel = |  | ||||||
|             ViewModelProvider(requireActivity())[ShaderProgressViewModel::class.java] |  | ||||||
| 
 |  | ||||||
|         val title = requireArguments().getString(TITLE) |  | ||||||
|         val message = requireArguments().getString(MESSAGE) |  | ||||||
| 
 |  | ||||||
|         isCancelable = false |  | ||||||
|         alertDialog = MaterialAlertDialogBuilder(requireActivity()) |  | ||||||
|             .setView(binding.root) |  | ||||||
|             .setTitle(title) |  | ||||||
|             .setMessage(message) |  | ||||||
|             .create() |  | ||||||
|         return alertDialog |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     override fun onCreateView( |  | ||||||
|         inflater: LayoutInflater, |  | ||||||
|         container: ViewGroup?, |  | ||||||
|         savedInstanceState: Bundle? |  | ||||||
|     ): View { |  | ||||||
|         return binding.root |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     override fun onViewCreated(view: View, savedInstanceState: Bundle?) { |  | ||||||
|         super.onViewCreated(view, savedInstanceState) |  | ||||||
|         shaderProgressViewModel.progress.observe(viewLifecycleOwner) { progress -> |  | ||||||
|             binding.progressBar.progress = progress |  | ||||||
|             setUpdateText() |  | ||||||
|         } |  | ||||||
|         shaderProgressViewModel.max.observe(viewLifecycleOwner) { max -> |  | ||||||
|             binding.progressBar.max = max |  | ||||||
|             setUpdateText() |  | ||||||
|         } |  | ||||||
|         shaderProgressViewModel.message.observe(viewLifecycleOwner) { msg -> |  | ||||||
|             alertDialog.setMessage(msg) |  | ||||||
|         } |  | ||||||
|         synchronized(DiskShaderCacheProgress.finishLock) { |  | ||||||
|             DiskShaderCacheProgress.finishLock.notifyAll() |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     override fun onDestroyView() { |  | ||||||
|         super.onDestroyView() |  | ||||||
|         _binding = null |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fun onUpdateProgress(msg: String, progress: Int, max: Int) { |  | ||||||
|         shaderProgressViewModel.setProgress(progress) |  | ||||||
|         shaderProgressViewModel.setMax(max) |  | ||||||
|         shaderProgressViewModel.setMessage(msg) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private fun setUpdateText() { |  | ||||||
|         binding.progressText.text = String.format( |  | ||||||
|             "%d/%d", |  | ||||||
|             shaderProgressViewModel.progress.value, |  | ||||||
|             shaderProgressViewModel.max.value |  | ||||||
|         ) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     companion object { |  | ||||||
|         const val TAG = "ProgressDialogFragment" |  | ||||||
|         const val TITLE = "title" |  | ||||||
|         const val MESSAGE = "message" |  | ||||||
| 
 |  | ||||||
|         fun newInstance(title: String, message: String): ShaderProgressDialogFragment { |  | ||||||
|             val frag = ShaderProgressDialogFragment() |  | ||||||
|             val args = Bundle() |  | ||||||
|             args.putString(TITLE, title) |  | ||||||
|             args.putString(MESSAGE, message) |  | ||||||
|             frag.arguments = args |  | ||||||
|             return frag |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -24,8 +24,9 @@ import androidx.core.content.res.ResourcesCompat | ||||||
| import androidx.core.graphics.Insets | import androidx.core.graphics.Insets | ||||||
| import androidx.core.view.ViewCompat | import androidx.core.view.ViewCompat | ||||||
| import androidx.core.view.WindowInsetsCompat | import androidx.core.view.WindowInsetsCompat | ||||||
| import androidx.core.view.isVisible | import androidx.drawerlayout.widget.DrawerLayout | ||||||
| import androidx.fragment.app.Fragment | import androidx.fragment.app.Fragment | ||||||
|  | import androidx.fragment.app.activityViewModels | ||||||
| import androidx.lifecycle.Lifecycle | import androidx.lifecycle.Lifecycle | ||||||
| import androidx.lifecycle.lifecycleScope | import androidx.lifecycle.lifecycleScope | ||||||
| import androidx.lifecycle.repeatOnLifecycle | import androidx.lifecycle.repeatOnLifecycle | ||||||
|  | @ -50,6 +51,7 @@ import org.yuzu.yuzu_emu.features.settings.model.IntSetting | ||||||
| import org.yuzu.yuzu_emu.features.settings.model.Settings | import org.yuzu.yuzu_emu.features.settings.model.Settings | ||||||
| import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile | import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile | ||||||
| import org.yuzu.yuzu_emu.model.Game | import org.yuzu.yuzu_emu.model.Game | ||||||
|  | import org.yuzu.yuzu_emu.model.EmulationViewModel | ||||||
| import org.yuzu.yuzu_emu.overlay.InputOverlay | import org.yuzu.yuzu_emu.overlay.InputOverlay | ||||||
| import org.yuzu.yuzu_emu.utils.* | import org.yuzu.yuzu_emu.utils.* | ||||||
| 
 | 
 | ||||||
|  | @ -66,6 +68,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | ||||||
| 
 | 
 | ||||||
|     private lateinit var game: Game |     private lateinit var game: Game | ||||||
| 
 | 
 | ||||||
|  |     private val emulationViewModel: EmulationViewModel by activityViewModels() | ||||||
|  | 
 | ||||||
|     private var isInFoldableLayout = false |     private var isInFoldableLayout = false | ||||||
| 
 | 
 | ||||||
|     override fun onAttach(context: Context) { |     override fun onAttach(context: Context) { | ||||||
|  | @ -130,9 +134,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | ||||||
|         binding.showFpsText.setTextColor(Color.YELLOW) |         binding.showFpsText.setTextColor(Color.YELLOW) | ||||||
|         binding.doneControlConfig.setOnClickListener { stopConfiguringControls() } |         binding.doneControlConfig.setOnClickListener { stopConfiguringControls() } | ||||||
| 
 | 
 | ||||||
|         // Setup overlay. |         binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED) | ||||||
|         updateShowFpsOverlay() |  | ||||||
| 
 |  | ||||||
|         binding.inGameMenu.getHeaderView(0).findViewById<TextView>(R.id.text_game_title).text = |         binding.inGameMenu.getHeaderView(0).findViewById<TextView>(R.id.text_game_title).text = | ||||||
|             game.title |             game.title | ||||||
|         binding.inGameMenu.setNavigationItemSelectedListener { |         binding.inGameMenu.setNavigationItemSelectedListener { | ||||||
|  | @ -174,7 +176,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | ||||||
| 
 | 
 | ||||||
|                 R.id.menu_exit -> { |                 R.id.menu_exit -> { | ||||||
|                     emulationState.stop() |                     emulationState.stop() | ||||||
|                     requireActivity().finish() |                     emulationViewModel.setIsEmulationStopping(true) | ||||||
|  |                     binding.drawerLayout.close() | ||||||
|  |                     binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED) | ||||||
|                     true |                     true | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|  | @ -188,6 +192,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | ||||||
|             requireActivity(), |             requireActivity(), | ||||||
|             object : OnBackPressedCallback(true) { |             object : OnBackPressedCallback(true) { | ||||||
|                 override fun handleOnBackPressed() { |                 override fun handleOnBackPressed() { | ||||||
|  |                     if (!NativeLibrary.isRunning()) { | ||||||
|  |                         return | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|                     if (binding.drawerLayout.isOpen) { |                     if (binding.drawerLayout.isOpen) { | ||||||
|                         binding.drawerLayout.close() |                         binding.drawerLayout.close() | ||||||
|                     } else { |                     } else { | ||||||
|  | @ -204,6 +212,54 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | ||||||
|                     .collect { updateFoldableLayout(requireActivity() as EmulationActivity, it) } |                     .collect { updateFoldableLayout(requireActivity() as EmulationActivity, it) } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  |         GameIconUtils.loadGameIcon(game, binding.loadingImage) | ||||||
|  |         binding.loadingTitle.text = game.title | ||||||
|  |         binding.loadingTitle.isSelected = true | ||||||
|  |         binding.loadingText.isSelected = true | ||||||
|  | 
 | ||||||
|  |         emulationViewModel.shaderProgress.observe(viewLifecycleOwner) { | ||||||
|  |             if (it > 0 && it != emulationViewModel.totalShaders.value!!) { | ||||||
|  |                 binding.loadingProgressIndicator.isIndeterminate = false | ||||||
|  | 
 | ||||||
|  |                 if (it < binding.loadingProgressIndicator.max) { | ||||||
|  |                     binding.loadingProgressIndicator.progress = it | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (it == emulationViewModel.totalShaders.value!!) { | ||||||
|  |                 binding.loadingText.setText(R.string.loading) | ||||||
|  |                 binding.loadingProgressIndicator.isIndeterminate = true | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         emulationViewModel.totalShaders.observe(viewLifecycleOwner) { | ||||||
|  |             binding.loadingProgressIndicator.max = it | ||||||
|  |         } | ||||||
|  |         emulationViewModel.shaderMessage.observe(viewLifecycleOwner) { | ||||||
|  |             if (it.isNotEmpty()) { | ||||||
|  |                 binding.loadingText.text = it | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         emulationViewModel.emulationStarted.observe(viewLifecycleOwner) { started -> | ||||||
|  |             if (started) { | ||||||
|  |                 binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED) | ||||||
|  |                 ViewUtils.showView(binding.surfaceInputOverlay) | ||||||
|  |                 ViewUtils.hideView(binding.loadingIndicator) | ||||||
|  | 
 | ||||||
|  |                 // Setup overlay | ||||||
|  |                 updateShowFpsOverlay() | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         emulationViewModel.isEmulationStopping.observe(viewLifecycleOwner) { | ||||||
|  |             if (it) { | ||||||
|  |                 binding.loadingText.setText(R.string.shutting_down) | ||||||
|  |                 ViewUtils.showView(binding.loadingIndicator) | ||||||
|  |                 ViewUtils.hideView(binding.inputContainer) | ||||||
|  |                 ViewUtils.hideView(binding.showFpsText) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     override fun onConfigurationChanged(newConfig: Configuration) { |     override fun onConfigurationChanged(newConfig: Configuration) { | ||||||
|  | @ -213,11 +269,21 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | ||||||
|                 binding.drawerLayout.close() |                 binding.drawerLayout.close() | ||||||
|             } |             } | ||||||
|             if (EmulationMenuSettings.showOverlay) { |             if (EmulationMenuSettings.showOverlay) { | ||||||
|                 binding.surfaceInputOverlay.post { binding.surfaceInputOverlay.isVisible = false } |                 binding.surfaceInputOverlay.post { | ||||||
|  |                     binding.surfaceInputOverlay.visibility = View.VISIBLE | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
|         } else { |         } else { | ||||||
|             if (EmulationMenuSettings.showOverlay) { |             if (EmulationMenuSettings.showOverlay && | ||||||
|                 binding.surfaceInputOverlay.post { binding.surfaceInputOverlay.isVisible = true } |                 emulationViewModel.emulationStarted.value == true | ||||||
|  |             ) { | ||||||
|  |                 binding.surfaceInputOverlay.post { | ||||||
|  |                     binding.surfaceInputOverlay.visibility = View.VISIBLE | ||||||
|  |                 } | ||||||
|  |             } else { | ||||||
|  |                 binding.surfaceInputOverlay.post { | ||||||
|  |                     binding.surfaceInputOverlay.visibility = View.INVISIBLE | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
|             if (!isInFoldableLayout) { |             if (!isInFoldableLayout) { | ||||||
|                 if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { |                 if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { | ||||||
|  | @ -226,9 +292,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | ||||||
|                     binding.surfaceInputOverlay.layout = InputOverlay.LANDSCAPE |                     binding.surfaceInputOverlay.layout = InputOverlay.LANDSCAPE | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             if (!binding.surfaceInputOverlay.isInEditMode) { |  | ||||||
|                 refreshInputOverlay() |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -260,10 +323,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | ||||||
|         super.onDetach() |         super.onDetach() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private fun refreshInputOverlay() { |  | ||||||
|         binding.surfaceInputOverlay.refreshControls() |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private fun resetInputOverlay() { |     private fun resetInputOverlay() { | ||||||
|         preferences.edit() |         preferences.edit() | ||||||
|             .remove(Settings.PREF_CONTROL_SCALE) |             .remove(Settings.PREF_CONTROL_SCALE) | ||||||
|  | @ -281,17 +340,15 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | ||||||
|             val FRAMETIME = 2 |             val FRAMETIME = 2 | ||||||
|             val SPEED = 3 |             val SPEED = 3 | ||||||
|             perfStatsUpdater = { |             perfStatsUpdater = { | ||||||
|                 val perfStats = NativeLibrary.getPerfStats() |                 if (emulationViewModel.emulationStarted.value == true) { | ||||||
|                 if (perfStats[FPS] > 0 && _binding != null) { |                     val perfStats = NativeLibrary.getPerfStats() | ||||||
|                     binding.showFpsText.text = String.format("FPS: %.1f", perfStats[FPS]) |                     if (perfStats[FPS] > 0 && _binding != null) { | ||||||
|                 } |                         binding.showFpsText.text = String.format("FPS: %.1f", perfStats[FPS]) | ||||||
| 
 |                     } | ||||||
|                 if (!emulationState.isStopped) { |  | ||||||
|                     perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 100) |                     perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 100) | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             perfStatsUpdateHandler.post(perfStatsUpdater!!) |             perfStatsUpdateHandler.post(perfStatsUpdater!!) | ||||||
|             binding.showFpsText.text = resources.getString(R.string.emulation_game_loading) |  | ||||||
|             binding.showFpsText.visibility = View.VISIBLE |             binding.showFpsText.visibility = View.VISIBLE | ||||||
|         } else { |         } else { | ||||||
|             if (perfStatsUpdater != null) { |             if (perfStatsUpdater != null) { | ||||||
|  | @ -349,7 +406,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | ||||||
| 
 | 
 | ||||||
|                         isInFoldableLayout = true |                         isInFoldableLayout = true | ||||||
|                         binding.surfaceInputOverlay.layout = InputOverlay.FOLDABLE |                         binding.surfaceInputOverlay.layout = InputOverlay.FOLDABLE | ||||||
|                         refreshInputOverlay() |  | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|                 it.isSeparating |                 it.isSeparating | ||||||
|  | @ -437,7 +493,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | ||||||
|                                 .apply() |                                 .apply() | ||||||
|                         } |                         } | ||||||
|                         .setPositiveButton(android.R.string.ok) { _, _ -> |                         .setPositiveButton(android.R.string.ok) { _, _ -> | ||||||
|                             refreshInputOverlay() |                             binding.surfaceInputOverlay.refreshControls() | ||||||
|                         } |                         } | ||||||
|                         .setNegativeButton(android.R.string.cancel, null) |                         .setNegativeButton(android.R.string.cancel, null) | ||||||
|                         .setNeutralButton(R.string.emulation_toggle_all) { _, _ -> } |                         .setNeutralButton(R.string.emulation_toggle_all) { _, _ -> } | ||||||
|  | @ -461,7 +517,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | ||||||
|                 R.id.menu_show_overlay -> { |                 R.id.menu_show_overlay -> { | ||||||
|                     it.isChecked = !it.isChecked |                     it.isChecked = !it.isChecked | ||||||
|                     EmulationMenuSettings.showOverlay = it.isChecked |                     EmulationMenuSettings.showOverlay = it.isChecked | ||||||
|                     refreshInputOverlay() |                     binding.surfaceInputOverlay.refreshControls() | ||||||
|                     true |                     true | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|  | @ -567,14 +623,14 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | ||||||
|         preferences.edit() |         preferences.edit() | ||||||
|             .putInt(Settings.PREF_CONTROL_SCALE, scale) |             .putInt(Settings.PREF_CONTROL_SCALE, scale) | ||||||
|             .apply() |             .apply() | ||||||
|         refreshInputOverlay() |         binding.surfaceInputOverlay.refreshControls() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private fun setControlOpacity(opacity: Int) { |     private fun setControlOpacity(opacity: Int) { | ||||||
|         preferences.edit() |         preferences.edit() | ||||||
|             .putInt(Settings.PREF_CONTROL_OPACITY, opacity) |             .putInt(Settings.PREF_CONTROL_OPACITY, opacity) | ||||||
|             .apply() |             .apply() | ||||||
|         refreshInputOverlay() |         binding.surfaceInputOverlay.refreshControls() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private fun setInsets() { |     private fun setInsets() { | ||||||
|  |  | ||||||
|  | @ -0,0 +1,59 @@ | ||||||
|  | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||||||
|  | // SPDX-License-Identifier: GPL-3.0-or-later | ||||||
|  | 
 | ||||||
|  | package org.yuzu.yuzu_emu.model | ||||||
|  | 
 | ||||||
|  | import androidx.lifecycle.LiveData | ||||||
|  | import androidx.lifecycle.MutableLiveData | ||||||
|  | import androidx.lifecycle.ViewModel | ||||||
|  | 
 | ||||||
|  | class EmulationViewModel : ViewModel() { | ||||||
|  |     private val _emulationStarted = MutableLiveData(false) | ||||||
|  |     val emulationStarted: LiveData<Boolean> get() = _emulationStarted | ||||||
|  | 
 | ||||||
|  |     private val _isEmulationStopping = MutableLiveData(false) | ||||||
|  |     val isEmulationStopping: LiveData<Boolean> get() = _isEmulationStopping | ||||||
|  | 
 | ||||||
|  |     private val _shaderProgress = MutableLiveData(0) | ||||||
|  |     val shaderProgress: LiveData<Int> get() = _shaderProgress | ||||||
|  | 
 | ||||||
|  |     private val _totalShaders = MutableLiveData(0) | ||||||
|  |     val totalShaders: LiveData<Int> get() = _totalShaders | ||||||
|  | 
 | ||||||
|  |     private val _shaderMessage = MutableLiveData("") | ||||||
|  |     val shaderMessage: LiveData<String> get() = _shaderMessage | ||||||
|  | 
 | ||||||
|  |     fun setEmulationStarted(started: Boolean) { | ||||||
|  |         _emulationStarted.postValue(started) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fun setIsEmulationStopping(value: Boolean) { | ||||||
|  |         _isEmulationStopping.value = value | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fun setShaderProgress(progress: Int) { | ||||||
|  |         _shaderProgress.value = progress | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fun setTotalShaders(max: Int) { | ||||||
|  |         _totalShaders.value = max | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fun setShaderMessage(msg: String) { | ||||||
|  |         _shaderMessage.value = msg | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fun updateProgress(msg: String, progress: Int, max: Int) { | ||||||
|  |         setShaderMessage(msg) | ||||||
|  |         setShaderProgress(progress) | ||||||
|  |         setTotalShaders(max) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fun clear() { | ||||||
|  |         _emulationStarted.value = false | ||||||
|  |         _isEmulationStopping.value = false | ||||||
|  |         _shaderProgress.value = 0 | ||||||
|  |         _totalShaders.value = 0 | ||||||
|  |         _shaderMessage.value = "" | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -15,6 +15,8 @@ static jclass s_disk_cache_progress_class; | ||||||
| static jclass s_load_callback_stage_class; | static jclass s_load_callback_stage_class; | ||||||
| static jmethodID s_exit_emulation_activity; | static jmethodID s_exit_emulation_activity; | ||||||
| static jmethodID s_disk_cache_load_progress; | static jmethodID s_disk_cache_load_progress; | ||||||
|  | static jmethodID s_on_emulation_started; | ||||||
|  | static jmethodID s_on_emulation_stopped; | ||||||
| 
 | 
 | ||||||
| static constexpr jint JNI_VERSION = JNI_VERSION_1_6; | static constexpr jint JNI_VERSION = JNI_VERSION_1_6; | ||||||
| 
 | 
 | ||||||
|  | @ -59,6 +61,14 @@ jmethodID GetDiskCacheLoadProgress() { | ||||||
|     return s_disk_cache_load_progress; |     return s_disk_cache_load_progress; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | jmethodID GetOnEmulationStarted() { | ||||||
|  |     return s_on_emulation_started; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | jmethodID GetOnEmulationStopped() { | ||||||
|  |     return s_on_emulation_stopped; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } // namespace IDCache
 | } // namespace IDCache
 | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
|  | @ -85,6 +95,10 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) { | ||||||
|         env->GetStaticMethodID(s_native_library_class, "exitEmulationActivity", "(I)V"); |         env->GetStaticMethodID(s_native_library_class, "exitEmulationActivity", "(I)V"); | ||||||
|     s_disk_cache_load_progress = |     s_disk_cache_load_progress = | ||||||
|         env->GetStaticMethodID(s_disk_cache_progress_class, "loadProgress", "(III)V"); |         env->GetStaticMethodID(s_disk_cache_progress_class, "loadProgress", "(III)V"); | ||||||
|  |     s_on_emulation_started = | ||||||
|  |         env->GetStaticMethodID(s_native_library_class, "onEmulationStarted", "()V"); | ||||||
|  |     s_on_emulation_stopped = | ||||||
|  |         env->GetStaticMethodID(s_native_library_class, "onEmulationStopped", "(I)V"); | ||||||
| 
 | 
 | ||||||
|     // Initialize Android Storage
 |     // Initialize Android Storage
 | ||||||
|     Common::FS::Android::RegisterCallbacks(env, s_native_library_class); |     Common::FS::Android::RegisterCallbacks(env, s_native_library_class); | ||||||
|  |  | ||||||
|  | @ -15,5 +15,7 @@ jclass GetDiskCacheProgressClass(); | ||||||
| jclass GetDiskCacheLoadCallbackStageClass(); | jclass GetDiskCacheLoadCallbackStageClass(); | ||||||
| jmethodID GetExitEmulationActivity(); | jmethodID GetExitEmulationActivity(); | ||||||
| jmethodID GetDiskCacheLoadProgress(); | jmethodID GetDiskCacheLoadProgress(); | ||||||
|  | jmethodID GetOnEmulationStarted(); | ||||||
|  | jmethodID GetOnEmulationStopped(); | ||||||
| 
 | 
 | ||||||
| } // namespace IDCache
 | } // namespace IDCache
 | ||||||
|  |  | ||||||
|  | @ -335,6 +335,8 @@ public: | ||||||
| 
 | 
 | ||||||
|         // Tear down the render window.
 |         // Tear down the render window.
 | ||||||
|         m_window.reset(); |         m_window.reset(); | ||||||
|  | 
 | ||||||
|  |         OnEmulationStopped(m_load_result); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void PauseEmulation() { |     void PauseEmulation() { | ||||||
|  | @ -376,6 +378,8 @@ public: | ||||||
|             m_system.InitializeDebugger(); |             m_system.InitializeDebugger(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         OnEmulationStarted(); | ||||||
|  | 
 | ||||||
|         while (true) { |         while (true) { | ||||||
|             { |             { | ||||||
|                 [[maybe_unused]] std::unique_lock lock(m_mutex); |                 [[maybe_unused]] std::unique_lock lock(m_mutex); | ||||||
|  | @ -511,6 +515,18 @@ private: | ||||||
|                                   static_cast<jint>(progress), static_cast<jint>(max)); |                                   static_cast<jint>(progress), static_cast<jint>(max)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     static void OnEmulationStarted() { | ||||||
|  |         JNIEnv* env = IDCache::GetEnvForThread(); | ||||||
|  |         env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), | ||||||
|  |                                   IDCache::GetOnEmulationStarted()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     static void OnEmulationStopped(Core::SystemResultStatus result) { | ||||||
|  |         JNIEnv* env = IDCache::GetEnvForThread(); | ||||||
|  |         env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), | ||||||
|  |                                   IDCache::GetOnEmulationStopped(), static_cast<jint>(result)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
| private: | private: | ||||||
|     static EmulationSession s_instance; |     static EmulationSession s_instance; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -26,6 +26,81 @@ | ||||||
|                 android:focusable="false" |                 android:focusable="false" | ||||||
|                 android:focusableInTouchMode="false" /> |                 android:focusableInTouchMode="false" /> | ||||||
| 
 | 
 | ||||||
|  |             <com.google.android.material.card.MaterialCardView | ||||||
|  |                 android:id="@+id/loading_indicator" | ||||||
|  |                 style="?attr/materialCardViewOutlinedStyle" | ||||||
|  |                 android:layout_width="wrap_content" | ||||||
|  |                 android:layout_height="wrap_content" | ||||||
|  |                 android:layout_gravity="center" | ||||||
|  |                 android:focusable="false"> | ||||||
|  | 
 | ||||||
|  |                 <androidx.constraintlayout.widget.ConstraintLayout | ||||||
|  |                     android:id="@+id/loading_layout" | ||||||
|  |                     android:layout_width="wrap_content" | ||||||
|  |                     android:layout_height="wrap_content" | ||||||
|  |                     android:gravity="center_horizontal"> | ||||||
|  | 
 | ||||||
|  |                     <ImageView | ||||||
|  |                         android:id="@+id/loading_image" | ||||||
|  |                         android:layout_width="wrap_content" | ||||||
|  |                         android:layout_height="0dp" | ||||||
|  |                         android:adjustViewBounds="true" | ||||||
|  |                         app:layout_constraintBottom_toBottomOf="@+id/linearLayout" | ||||||
|  |                         app:layout_constraintStart_toStartOf="parent" | ||||||
|  |                         app:layout_constraintTop_toTopOf="@+id/linearLayout" | ||||||
|  |                         tools:src="@drawable/default_icon" /> | ||||||
|  | 
 | ||||||
|  |                     <LinearLayout | ||||||
|  |                         android:id="@+id/linearLayout" | ||||||
|  |                         android:layout_width="wrap_content" | ||||||
|  |                         android:layout_height="wrap_content" | ||||||
|  |                         android:orientation="vertical" | ||||||
|  |                         android:paddingHorizontal="24dp" | ||||||
|  |                         android:paddingVertical="36dp" | ||||||
|  |                         app:layout_constraintBottom_toBottomOf="parent" | ||||||
|  |                         app:layout_constraintEnd_toEndOf="parent" | ||||||
|  |                         app:layout_constraintStart_toEndOf="@id/loading_image" | ||||||
|  |                         app:layout_constraintTop_toTopOf="parent"> | ||||||
|  | 
 | ||||||
|  |                         <com.google.android.material.textview.MaterialTextView | ||||||
|  |                             android:id="@+id/loading_title" | ||||||
|  |                             style="@style/TextAppearance.Material3.TitleMedium" | ||||||
|  |                             android:layout_width="match_parent" | ||||||
|  |                             android:layout_height="wrap_content" | ||||||
|  |                             android:ellipsize="marquee" | ||||||
|  |                             android:marqueeRepeatLimit="marquee_forever" | ||||||
|  |                             android:requiresFadingEdge="horizontal" | ||||||
|  |                             android:singleLine="true" | ||||||
|  |                             android:textAlignment="viewStart" | ||||||
|  |                             tools:text="@string/games" /> | ||||||
|  | 
 | ||||||
|  |                         <com.google.android.material.textview.MaterialTextView | ||||||
|  |                             android:id="@+id/loading_text" | ||||||
|  |                             style="@style/TextAppearance.Material3.TitleSmall" | ||||||
|  |                             android:layout_width="match_parent" | ||||||
|  |                             android:layout_height="wrap_content" | ||||||
|  |                             android:layout_marginTop="4dp" | ||||||
|  |                             android:ellipsize="marquee" | ||||||
|  |                             android:marqueeRepeatLimit="marquee_forever" | ||||||
|  |                             android:requiresFadingEdge="horizontal" | ||||||
|  |                             android:singleLine="true" | ||||||
|  |                             android:text="@string/loading" | ||||||
|  |                             android:textAlignment="viewStart" /> | ||||||
|  | 
 | ||||||
|  |                         <com.google.android.material.progressindicator.LinearProgressIndicator | ||||||
|  |                             android:id="@+id/loading_progress_indicator" | ||||||
|  |                             android:layout_width="192dp" | ||||||
|  |                             android:layout_height="wrap_content" | ||||||
|  |                             android:layout_marginTop="12dp" | ||||||
|  |                             android:indeterminate="true" | ||||||
|  |                             app:trackCornerRadius="8dp" /> | ||||||
|  | 
 | ||||||
|  |                     </LinearLayout> | ||||||
|  | 
 | ||||||
|  |                 </androidx.constraintlayout.widget.ConstraintLayout> | ||||||
|  | 
 | ||||||
|  |             </com.google.android.material.card.MaterialCardView> | ||||||
|  | 
 | ||||||
|         </FrameLayout> |         </FrameLayout> | ||||||
| 
 | 
 | ||||||
|         <FrameLayout |         <FrameLayout | ||||||
|  | @ -41,11 +116,12 @@ | ||||||
|                 android:layout_height="match_parent" |                 android:layout_height="match_parent" | ||||||
|                 android:layout_gravity="center" |                 android:layout_gravity="center" | ||||||
|                 android:focusable="true" |                 android:focusable="true" | ||||||
|                 android:focusableInTouchMode="true" /> |                 android:focusableInTouchMode="true" | ||||||
|  |                 android:visibility="invisible" /> | ||||||
| 
 | 
 | ||||||
|             <Button |             <Button | ||||||
|                 style="@style/Widget.Material3.Button.ElevatedButton" |  | ||||||
|                 android:id="@+id/done_control_config" |                 android:id="@+id/done_control_config" | ||||||
|  |                 style="@style/Widget.Material3.Button.ElevatedButton" | ||||||
|                 android:layout_width="wrap_content" |                 android:layout_width="wrap_content" | ||||||
|                 android:layout_height="wrap_content" |                 android:layout_height="wrap_content" | ||||||
|                 android:layout_gravity="center" |                 android:layout_gravity="center" | ||||||
|  | @ -81,6 +157,7 @@ | ||||||
|         android:layout_height="match_parent" |         android:layout_height="match_parent" | ||||||
|         android:layout_gravity="start|bottom" |         android:layout_gravity="start|bottom" | ||||||
|         app:headerLayout="@layout/header_in_game" |         app:headerLayout="@layout/header_in_game" | ||||||
|         app:menu="@menu/menu_in_game" /> |         app:menu="@menu/menu_in_game" | ||||||
|  |         tools:visibility="gone" /> | ||||||
| 
 | 
 | ||||||
| </androidx.drawerlayout.widget.DrawerLayout> | </androidx.drawerlayout.widget.DrawerLayout> | ||||||
|  |  | ||||||
|  | @ -209,7 +209,6 @@ | ||||||
|     <string name="emulation_pause">Emulation pausieren</string> |     <string name="emulation_pause">Emulation pausieren</string> | ||||||
|     <string name="emulation_unpause">Emulation fortsetzen</string> |     <string name="emulation_unpause">Emulation fortsetzen</string> | ||||||
|     <string name="emulation_input_overlay">Overlay-Optionen</string> |     <string name="emulation_input_overlay">Overlay-Optionen</string> | ||||||
|     <string name="emulation_game_loading">Spiel lädt…</string> |  | ||||||
| 
 | 
 | ||||||
|     <string name="load_settings">Lädt Einstellungen...</string> |     <string name="load_settings">Lädt Einstellungen...</string> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -213,7 +213,6 @@ | ||||||
|     <string name="emulation_pause">Pausar Emulación</string> |     <string name="emulation_pause">Pausar Emulación</string> | ||||||
|     <string name="emulation_unpause">Reanudar Emulación</string> |     <string name="emulation_unpause">Reanudar Emulación</string> | ||||||
|     <string name="emulation_input_overlay">Opciones de pantalla </string> |     <string name="emulation_input_overlay">Opciones de pantalla </string> | ||||||
|     <string name="emulation_game_loading">Cargando juego...</string> |  | ||||||
| 
 | 
 | ||||||
|     <string name="load_settings">Cargando configuración...</string> |     <string name="load_settings">Cargando configuración...</string> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -213,7 +213,6 @@ | ||||||
|     <string name="emulation_pause">Mettre en pause l\'émulation</string> |     <string name="emulation_pause">Mettre en pause l\'émulation</string> | ||||||
|     <string name="emulation_unpause">Reprendre l\'émulation</string> |     <string name="emulation_unpause">Reprendre l\'émulation</string> | ||||||
|     <string name="emulation_input_overlay">Options de l\'overlay</string> |     <string name="emulation_input_overlay">Options de l\'overlay</string> | ||||||
|     <string name="emulation_game_loading">Chargement du jeu...</string> |  | ||||||
| 
 | 
 | ||||||
|     <string name="load_settings">Chargement des paramètres…</string> |     <string name="load_settings">Chargement des paramètres…</string> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -213,7 +213,6 @@ | ||||||
|     <string name="emulation_pause">Metti in pausa l\'emulazione</string> |     <string name="emulation_pause">Metti in pausa l\'emulazione</string> | ||||||
|     <string name="emulation_unpause">Riprendi Emulazione</string> |     <string name="emulation_unpause">Riprendi Emulazione</string> | ||||||
|     <string name="emulation_input_overlay">Impostazioni Overlay</string> |     <string name="emulation_input_overlay">Impostazioni Overlay</string> | ||||||
|     <string name="emulation_game_loading">Caricamento del gioco...</string> |  | ||||||
| 
 | 
 | ||||||
|     <string name="load_settings">Caricamento delle impostazioni...</string> |     <string name="load_settings">Caricamento delle impostazioni...</string> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -211,7 +211,6 @@ | ||||||
|     <string name="emulation_pause">エミュレーションを一時停止</string> |     <string name="emulation_pause">エミュレーションを一時停止</string> | ||||||
|     <string name="emulation_unpause">エミュレーションを再開</string> |     <string name="emulation_unpause">エミュレーションを再開</string> | ||||||
|     <string name="emulation_input_overlay">オーバーレイオプション</string> |     <string name="emulation_input_overlay">オーバーレイオプション</string> | ||||||
|     <string name="emulation_game_loading">ロード中…</string> |  | ||||||
| 
 | 
 | ||||||
|     <string name="load_settings">設定をロード中…</string> |     <string name="load_settings">設定をロード中…</string> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -213,7 +213,6 @@ | ||||||
|     <string name="emulation_pause">에뮬레이션 일시 중지</string> |     <string name="emulation_pause">에뮬레이션 일시 중지</string> | ||||||
|     <string name="emulation_unpause">에뮬레이션 일시 중지 해제</string> |     <string name="emulation_unpause">에뮬레이션 일시 중지 해제</string> | ||||||
|     <string name="emulation_input_overlay">오버레이 옵션</string> |     <string name="emulation_input_overlay">오버레이 옵션</string> | ||||||
|     <string name="emulation_game_loading">게임 불러오기 중...</string> |  | ||||||
| 
 | 
 | ||||||
|     <string name="load_settings">설정 불러오기 중...</string> |     <string name="load_settings">설정 불러오기 중...</string> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -213,7 +213,6 @@ | ||||||
|     <string name="emulation_pause">Pause Emulering</string> |     <string name="emulation_pause">Pause Emulering</string> | ||||||
|     <string name="emulation_unpause">Opphev pausing av emulering</string> |     <string name="emulation_unpause">Opphev pausing av emulering</string> | ||||||
|     <string name="emulation_input_overlay">Alternativer for overlegg</string> |     <string name="emulation_input_overlay">Alternativer for overlegg</string> | ||||||
|     <string name="emulation_game_loading">Spillet lastes inn...</string> |  | ||||||
| 
 | 
 | ||||||
|     <string name="load_settings">Laster inn innstillinger...</string> |     <string name="load_settings">Laster inn innstillinger...</string> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -213,7 +213,6 @@ | ||||||
|     <string name="emulation_pause">Wstrzymaj emulację</string> |     <string name="emulation_pause">Wstrzymaj emulację</string> | ||||||
|     <string name="emulation_unpause">Wznów emulację</string> |     <string name="emulation_unpause">Wznów emulację</string> | ||||||
|     <string name="emulation_input_overlay">Opcje nakładki</string> |     <string name="emulation_input_overlay">Opcje nakładki</string> | ||||||
|     <string name="emulation_game_loading">Wczytywanie gry...</string> |  | ||||||
| 
 | 
 | ||||||
|     <string name="load_settings">Wczytywanie ustawień...</string> |     <string name="load_settings">Wczytywanie ustawień...</string> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -213,7 +213,6 @@ | ||||||
|     <string name="emulation_pause">Pausa emulação</string> |     <string name="emulation_pause">Pausa emulação</string> | ||||||
|     <string name="emulation_unpause">Retomar emulação</string> |     <string name="emulation_unpause">Retomar emulação</string> | ||||||
|     <string name="emulation_input_overlay">Opções de sobreposição </string> |     <string name="emulation_input_overlay">Opções de sobreposição </string> | ||||||
|     <string name="emulation_game_loading">Jogo a carregar...</string> |  | ||||||
| 
 | 
 | ||||||
|     <string name="load_settings">Configurações a carregar...</string> |     <string name="load_settings">Configurações a carregar...</string> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -213,7 +213,6 @@ | ||||||
|     <string name="emulation_pause">Pausa emulação</string> |     <string name="emulation_pause">Pausa emulação</string> | ||||||
|     <string name="emulation_unpause">Retomar emulação</string> |     <string name="emulation_unpause">Retomar emulação</string> | ||||||
|     <string name="emulation_input_overlay">Opções de sobreposição </string> |     <string name="emulation_input_overlay">Opções de sobreposição </string> | ||||||
|     <string name="emulation_game_loading">Jogo a carregar...</string> |  | ||||||
| 
 | 
 | ||||||
|     <string name="load_settings">Configurações a carregar...</string> |     <string name="load_settings">Configurações a carregar...</string> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -213,7 +213,6 @@ | ||||||
|     <string name="emulation_pause">Пауза эмуляции</string> |     <string name="emulation_pause">Пауза эмуляции</string> | ||||||
|     <string name="emulation_unpause">Возобновление эмуляции</string> |     <string name="emulation_unpause">Возобновление эмуляции</string> | ||||||
|     <string name="emulation_input_overlay">Настройки оверлея</string> |     <string name="emulation_input_overlay">Настройки оверлея</string> | ||||||
|     <string name="emulation_game_loading">Загрузка игры...</string> |  | ||||||
| 
 | 
 | ||||||
|     <string name="load_settings">Загрузка настроек...</string> |     <string name="load_settings">Загрузка настроек...</string> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -213,7 +213,6 @@ | ||||||
|     <string name="emulation_pause">Пауза емуляції</string> |     <string name="emulation_pause">Пауза емуляції</string> | ||||||
|     <string name="emulation_unpause">Відновлення емуляції</string> |     <string name="emulation_unpause">Відновлення емуляції</string> | ||||||
|     <string name="emulation_input_overlay">Налаштування оверлея</string> |     <string name="emulation_input_overlay">Налаштування оверлея</string> | ||||||
|     <string name="emulation_game_loading">Завантаження гри...</string> |  | ||||||
| 
 | 
 | ||||||
|     <string name="load_settings">Завантаження налаштувань...</string> |     <string name="load_settings">Завантаження налаштувань...</string> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -213,7 +213,6 @@ | ||||||
|     <string name="emulation_pause">暂停模拟</string> |     <string name="emulation_pause">暂停模拟</string> | ||||||
|     <string name="emulation_unpause">继续模拟</string> |     <string name="emulation_unpause">继续模拟</string> | ||||||
|     <string name="emulation_input_overlay">虚拟按键选项</string> |     <string name="emulation_input_overlay">虚拟按键选项</string> | ||||||
|     <string name="emulation_game_loading">载入游戏中…</string> |  | ||||||
| 
 | 
 | ||||||
|     <string name="load_settings">正在载入设定…</string> |     <string name="load_settings">正在载入设定…</string> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -213,7 +213,6 @@ | ||||||
|     <string name="emulation_pause">暫停模擬</string> |     <string name="emulation_pause">暫停模擬</string> | ||||||
|     <string name="emulation_unpause">取消暫停模擬</string> |     <string name="emulation_unpause">取消暫停模擬</string> | ||||||
|     <string name="emulation_input_overlay">覆疊選項</string> |     <string name="emulation_input_overlay">覆疊選項</string> | ||||||
|     <string name="emulation_game_loading">遊戲正在載入…</string> |  | ||||||
| 
 | 
 | ||||||
|     <string name="load_settings">正在載入設定…</string> |     <string name="load_settings">正在載入設定…</string> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -204,6 +204,7 @@ | ||||||
|     <string name="error_saving">Error saving %1$s.ini: %2$s</string> |     <string name="error_saving">Error saving %1$s.ini: %2$s</string> | ||||||
|     <string name="unimplemented_menu">Unimplemented Menu</string> |     <string name="unimplemented_menu">Unimplemented Menu</string> | ||||||
|     <string name="loading">Loading…</string> |     <string name="loading">Loading…</string> | ||||||
|  |     <string name="shutting_down">Shutting down…</string> | ||||||
|     <string name="reset_setting_confirmation">Do you want to reset this setting back to its default value?</string> |     <string name="reset_setting_confirmation">Do you want to reset this setting back to its default value?</string> | ||||||
|     <string name="reset_to_default">Reset to default</string> |     <string name="reset_to_default">Reset to default</string> | ||||||
|     <string name="reset_all_settings">Reset all settings?</string> |     <string name="reset_all_settings">Reset all settings?</string> | ||||||
|  | @ -262,7 +263,6 @@ | ||||||
|     <string name="emulation_pause">Pause emulation</string> |     <string name="emulation_pause">Pause emulation</string> | ||||||
|     <string name="emulation_unpause">Unpause emulation</string> |     <string name="emulation_unpause">Unpause emulation</string> | ||||||
|     <string name="emulation_input_overlay">Overlay options</string> |     <string name="emulation_input_overlay">Overlay options</string> | ||||||
|     <string name="emulation_game_loading">Game loading…</string> |  | ||||||
| 
 | 
 | ||||||
|     <string name="load_settings">Loading settings…</string> |     <string name="load_settings">Loading settings…</string> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Charles Lombardo
						Charles Lombardo