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.isDirectory | ||||
| import org.yuzu.yuzu_emu.utils.FileUtil.openContentUri | ||||
| import org.yuzu.yuzu_emu.utils.Log.error | ||||
| import org.yuzu.yuzu_emu.utils.Log.verbose | ||||
| import org.yuzu.yuzu_emu.utils.Log.warning | ||||
| import org.yuzu.yuzu_emu.utils.Log | ||||
| import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable | ||||
| 
 | ||||
| /** | ||||
|  | @ -465,7 +463,7 @@ object NativeLibrary { | |||
| 
 | ||||
|         val emulationActivity = sEmulationActivity.get() | ||||
|         if (emulationActivity == null) { | ||||
|             warning("[NativeLibrary] EmulationActivity is null, can't exit.") | ||||
|             Log.warning("[NativeLibrary] EmulationActivity is null, can't exit.") | ||||
|             return | ||||
|         } | ||||
| 
 | ||||
|  | @ -490,15 +488,27 @@ object NativeLibrary { | |||
|     } | ||||
| 
 | ||||
|     fun setEmulationActivity(emulationActivity: EmulationActivity?) { | ||||
|         verbose("[NativeLibrary] Registering EmulationActivity.") | ||||
|         Log.verbose("[NativeLibrary] Registering EmulationActivity.") | ||||
|         sEmulationActivity = WeakReference(emulationActivity) | ||||
|     } | ||||
| 
 | ||||
|     fun clearEmulationActivity() { | ||||
|         verbose("[NativeLibrary] Unregistering EmulationActivity.") | ||||
|         Log.verbose("[NativeLibrary] Unregistering EmulationActivity.") | ||||
|         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. | ||||
|      */ | ||||
|  |  | |||
|  | @ -28,6 +28,7 @@ import android.view.Surface | |||
| import android.view.View | ||||
| import android.view.inputmethod.InputMethodManager | ||||
| import android.widget.Toast | ||||
| import androidx.activity.viewModels | ||||
| import androidx.appcompat.app.AppCompatActivity | ||||
| import androidx.core.view.WindowCompat | ||||
| import androidx.core.view.WindowInsetsCompat | ||||
|  | @ -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.IntSetting | ||||
| 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.utils.ControllerMappingHelper | ||||
| import org.yuzu.yuzu_emu.utils.ForegroundService | ||||
|  | @ -70,8 +72,11 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { | |||
|     private val actionMute = "ACTION_EMULATOR_MUTE" | ||||
|     private val actionUnmute = "ACTION_EMULATOR_UNMUTE" | ||||
| 
 | ||||
|     private val emulationViewModel: EmulationViewModel by viewModels() | ||||
| 
 | ||||
|     override fun onDestroy() { | ||||
|         stopForegroundService(this) | ||||
|         emulationViewModel.clear() | ||||
|         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() { | ||||
|         val sensorManager = this.getSystemService(Context.SENSOR_SERVICE) as SensorManager | ||||
|         val gyroSensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE) | ||||
|  |  | |||
|  | @ -4,43 +4,43 @@ | |||
| package org.yuzu.yuzu_emu.disk_shader_cache | ||||
| 
 | ||||
| import androidx.annotation.Keep | ||||
| import androidx.lifecycle.ViewModelProvider | ||||
| import org.yuzu.yuzu_emu.NativeLibrary | ||||
| 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 | ||||
| object DiskShaderCacheProgress { | ||||
|     val finishLock = Object() | ||||
|     private lateinit var fragment: ShaderProgressDialogFragment | ||||
|     private lateinit var emulationViewModel: EmulationViewModel | ||||
| 
 | ||||
|     private fun prepareDialog() { | ||||
|         val emulationActivity = NativeLibrary.sEmulationActivity.get()!! | ||||
|         emulationActivity.runOnUiThread { | ||||
|             fragment = ShaderProgressDialogFragment.newInstance( | ||||
|                 emulationActivity.getString(R.string.loading), | ||||
|                 emulationActivity.getString(R.string.preparing_shaders) | ||||
|             ) | ||||
|             fragment.show( | ||||
|                 emulationActivity.supportFragmentManager, | ||||
|                 ShaderProgressDialogFragment.TAG | ||||
|             ) | ||||
|         } | ||||
|         synchronized(finishLock) { finishLock.wait() } | ||||
|     private fun prepareViewModel() { | ||||
|         emulationViewModel = | ||||
|             ViewModelProvider( | ||||
|                 NativeLibrary.sEmulationActivity.get() as EmulationActivity | ||||
|             )[EmulationViewModel::class.java] | ||||
|     } | ||||
| 
 | ||||
|     @JvmStatic | ||||
|     fun loadProgress(stage: Int, progress: Int, max: Int) { | ||||
|         val emulationActivity = NativeLibrary.sEmulationActivity.get() | ||||
|             ?: error("[DiskShaderCacheProgress] EmulationActivity not present") | ||||
|         if (emulationActivity == null) { | ||||
|             Log.error("[DiskShaderCacheProgress] EmulationActivity not present") | ||||
|             return | ||||
|         } | ||||
| 
 | ||||
|         emulationActivity.runOnUiThread { | ||||
|             when (LoadCallbackStage.values()[stage]) { | ||||
|             LoadCallbackStage.Prepare -> prepareDialog() | ||||
|             LoadCallbackStage.Build -> fragment.onUpdateProgress( | ||||
|                 LoadCallbackStage.Prepare -> prepareViewModel() | ||||
|                 LoadCallbackStage.Build -> emulationViewModel.updateProgress( | ||||
|                     emulationActivity.getString(R.string.building_shaders), | ||||
|                     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.view.ViewCompat | ||||
| import androidx.core.view.WindowInsetsCompat | ||||
| import androidx.core.view.isVisible | ||||
| import androidx.drawerlayout.widget.DrawerLayout | ||||
| import androidx.fragment.app.Fragment | ||||
| import androidx.fragment.app.activityViewModels | ||||
| import androidx.lifecycle.Lifecycle | ||||
| import androidx.lifecycle.lifecycleScope | ||||
| 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.utils.SettingsFile | ||||
| 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.utils.* | ||||
| 
 | ||||
|  | @ -66,6 +68,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 
 | ||||
|     private lateinit var game: Game | ||||
| 
 | ||||
|     private val emulationViewModel: EmulationViewModel by activityViewModels() | ||||
| 
 | ||||
|     private var isInFoldableLayout = false | ||||
| 
 | ||||
|     override fun onAttach(context: Context) { | ||||
|  | @ -130,9 +134,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
|         binding.showFpsText.setTextColor(Color.YELLOW) | ||||
|         binding.doneControlConfig.setOnClickListener { stopConfiguringControls() } | ||||
| 
 | ||||
|         // Setup overlay. | ||||
|         updateShowFpsOverlay() | ||||
| 
 | ||||
|         binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED) | ||||
|         binding.inGameMenu.getHeaderView(0).findViewById<TextView>(R.id.text_game_title).text = | ||||
|             game.title | ||||
|         binding.inGameMenu.setNavigationItemSelectedListener { | ||||
|  | @ -174,7 +176,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 
 | ||||
|                 R.id.menu_exit -> { | ||||
|                     emulationState.stop() | ||||
|                     requireActivity().finish() | ||||
|                     emulationViewModel.setIsEmulationStopping(true) | ||||
|                     binding.drawerLayout.close() | ||||
|                     binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED) | ||||
|                     true | ||||
|                 } | ||||
| 
 | ||||
|  | @ -188,6 +192,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
|             requireActivity(), | ||||
|             object : OnBackPressedCallback(true) { | ||||
|                 override fun handleOnBackPressed() { | ||||
|                     if (!NativeLibrary.isRunning()) { | ||||
|                         return | ||||
|                     } | ||||
| 
 | ||||
|                     if (binding.drawerLayout.isOpen) { | ||||
|                         binding.drawerLayout.close() | ||||
|                     } else { | ||||
|  | @ -204,6 +212,54 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
|                     .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) { | ||||
|  | @ -213,11 +269,21 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
|                 binding.drawerLayout.close() | ||||
|             } | ||||
|             if (EmulationMenuSettings.showOverlay) { | ||||
|                 binding.surfaceInputOverlay.post { binding.surfaceInputOverlay.isVisible = false } | ||||
|                 binding.surfaceInputOverlay.post { | ||||
|                     binding.surfaceInputOverlay.visibility = View.VISIBLE | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|             if (EmulationMenuSettings.showOverlay) { | ||||
|                 binding.surfaceInputOverlay.post { binding.surfaceInputOverlay.isVisible = true } | ||||
|             if (EmulationMenuSettings.showOverlay && | ||||
|                 emulationViewModel.emulationStarted.value == true | ||||
|             ) { | ||||
|                 binding.surfaceInputOverlay.post { | ||||
|                     binding.surfaceInputOverlay.visibility = View.VISIBLE | ||||
|                 } | ||||
|             } else { | ||||
|                 binding.surfaceInputOverlay.post { | ||||
|                     binding.surfaceInputOverlay.visibility = View.INVISIBLE | ||||
|                 } | ||||
|             } | ||||
|             if (!isInFoldableLayout) { | ||||
|                 if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { | ||||
|  | @ -226,9 +292,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
|                     binding.surfaceInputOverlay.layout = InputOverlay.LANDSCAPE | ||||
|                 } | ||||
|             } | ||||
|             if (!binding.surfaceInputOverlay.isInEditMode) { | ||||
|                 refreshInputOverlay() | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -260,10 +323,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
|         super.onDetach() | ||||
|     } | ||||
| 
 | ||||
|     private fun refreshInputOverlay() { | ||||
|         binding.surfaceInputOverlay.refreshControls() | ||||
|     } | ||||
| 
 | ||||
|     private fun resetInputOverlay() { | ||||
|         preferences.edit() | ||||
|             .remove(Settings.PREF_CONTROL_SCALE) | ||||
|  | @ -281,17 +340,15 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
|             val FRAMETIME = 2 | ||||
|             val SPEED = 3 | ||||
|             perfStatsUpdater = { | ||||
|                 if (emulationViewModel.emulationStarted.value == true) { | ||||
|                     val perfStats = NativeLibrary.getPerfStats() | ||||
|                     if (perfStats[FPS] > 0 && _binding != null) { | ||||
|                         binding.showFpsText.text = String.format("FPS: %.1f", perfStats[FPS]) | ||||
|                     } | ||||
| 
 | ||||
|                 if (!emulationState.isStopped) { | ||||
|                     perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 100) | ||||
|                 } | ||||
|             } | ||||
|             perfStatsUpdateHandler.post(perfStatsUpdater!!) | ||||
|             binding.showFpsText.text = resources.getString(R.string.emulation_game_loading) | ||||
|             binding.showFpsText.visibility = View.VISIBLE | ||||
|         } else { | ||||
|             if (perfStatsUpdater != null) { | ||||
|  | @ -349,7 +406,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 
 | ||||
|                         isInFoldableLayout = true | ||||
|                         binding.surfaceInputOverlay.layout = InputOverlay.FOLDABLE | ||||
|                         refreshInputOverlay() | ||||
|                     } | ||||
|                 } | ||||
|                 it.isSeparating | ||||
|  | @ -437,7 +493,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
|                                 .apply() | ||||
|                         } | ||||
|                         .setPositiveButton(android.R.string.ok) { _, _ -> | ||||
|                             refreshInputOverlay() | ||||
|                             binding.surfaceInputOverlay.refreshControls() | ||||
|                         } | ||||
|                         .setNegativeButton(android.R.string.cancel, null) | ||||
|                         .setNeutralButton(R.string.emulation_toggle_all) { _, _ -> } | ||||
|  | @ -461,7 +517,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
|                 R.id.menu_show_overlay -> { | ||||
|                     it.isChecked = !it.isChecked | ||||
|                     EmulationMenuSettings.showOverlay = it.isChecked | ||||
|                     refreshInputOverlay() | ||||
|                     binding.surfaceInputOverlay.refreshControls() | ||||
|                     true | ||||
|                 } | ||||
| 
 | ||||
|  | @ -567,14 +623,14 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
|         preferences.edit() | ||||
|             .putInt(Settings.PREF_CONTROL_SCALE, scale) | ||||
|             .apply() | ||||
|         refreshInputOverlay() | ||||
|         binding.surfaceInputOverlay.refreshControls() | ||||
|     } | ||||
| 
 | ||||
|     private fun setControlOpacity(opacity: Int) { | ||||
|         preferences.edit() | ||||
|             .putInt(Settings.PREF_CONTROL_OPACITY, opacity) | ||||
|             .apply() | ||||
|         refreshInputOverlay() | ||||
|         binding.surfaceInputOverlay.refreshControls() | ||||
|     } | ||||
| 
 | ||||
|     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 jmethodID s_exit_emulation_activity; | ||||
| 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; | ||||
| 
 | ||||
|  | @ -59,6 +61,14 @@ jmethodID GetDiskCacheLoadProgress() { | |||
|     return s_disk_cache_load_progress; | ||||
| } | ||||
| 
 | ||||
| jmethodID GetOnEmulationStarted() { | ||||
|     return s_on_emulation_started; | ||||
| } | ||||
| 
 | ||||
| jmethodID GetOnEmulationStopped() { | ||||
|     return s_on_emulation_stopped; | ||||
| } | ||||
| 
 | ||||
| } // namespace IDCache
 | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
|  | @ -85,6 +95,10 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) { | |||
|         env->GetStaticMethodID(s_native_library_class, "exitEmulationActivity", "(I)V"); | ||||
|     s_disk_cache_load_progress = | ||||
|         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
 | ||||
|     Common::FS::Android::RegisterCallbacks(env, s_native_library_class); | ||||
|  |  | |||
|  | @ -15,5 +15,7 @@ jclass GetDiskCacheProgressClass(); | |||
| jclass GetDiskCacheLoadCallbackStageClass(); | ||||
| jmethodID GetExitEmulationActivity(); | ||||
| jmethodID GetDiskCacheLoadProgress(); | ||||
| jmethodID GetOnEmulationStarted(); | ||||
| jmethodID GetOnEmulationStopped(); | ||||
| 
 | ||||
| } // namespace IDCache
 | ||||
|  |  | |||
|  | @ -335,6 +335,8 @@ public: | |||
| 
 | ||||
|         // Tear down the render window.
 | ||||
|         m_window.reset(); | ||||
| 
 | ||||
|         OnEmulationStopped(m_load_result); | ||||
|     } | ||||
| 
 | ||||
|     void PauseEmulation() { | ||||
|  | @ -376,6 +378,8 @@ public: | |||
|             m_system.InitializeDebugger(); | ||||
|         } | ||||
| 
 | ||||
|         OnEmulationStarted(); | ||||
| 
 | ||||
|         while (true) { | ||||
|             { | ||||
|                 [[maybe_unused]] std::unique_lock lock(m_mutex); | ||||
|  | @ -511,6 +515,18 @@ private: | |||
|                                   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: | ||||
|     static EmulationSession s_instance; | ||||
| 
 | ||||
|  |  | |||
|  | @ -26,6 +26,81 @@ | |||
|                 android:focusable="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 | ||||
|  | @ -41,11 +116,12 @@ | |||
|                 android:layout_height="match_parent" | ||||
|                 android:layout_gravity="center" | ||||
|                 android:focusable="true" | ||||
|                 android:focusableInTouchMode="true" /> | ||||
|                 android:focusableInTouchMode="true" | ||||
|                 android:visibility="invisible" /> | ||||
| 
 | ||||
|             <Button | ||||
|                 style="@style/Widget.Material3.Button.ElevatedButton" | ||||
|                 android:id="@+id/done_control_config" | ||||
|                 style="@style/Widget.Material3.Button.ElevatedButton" | ||||
|                 android:layout_width="wrap_content" | ||||
|                 android:layout_height="wrap_content" | ||||
|                 android:layout_gravity="center" | ||||
|  | @ -81,6 +157,7 @@ | |||
|         android:layout_height="match_parent" | ||||
|         android:layout_gravity="start|bottom" | ||||
|         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> | ||||
|  |  | |||
|  | @ -209,7 +209,6 @@ | |||
|     <string name="emulation_pause">Emulation pausieren</string> | ||||
|     <string name="emulation_unpause">Emulation fortsetzen</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> | ||||
| 
 | ||||
|  |  | |||
|  | @ -213,7 +213,6 @@ | |||
|     <string name="emulation_pause">Pausar Emulación</string> | ||||
|     <string name="emulation_unpause">Reanudar Emulación</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> | ||||
| 
 | ||||
|  |  | |||
|  | @ -213,7 +213,6 @@ | |||
|     <string name="emulation_pause">Mettre en pause l\'émulation</string> | ||||
|     <string name="emulation_unpause">Reprendre l\'émulation</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> | ||||
| 
 | ||||
|  |  | |||
|  | @ -213,7 +213,6 @@ | |||
|     <string name="emulation_pause">Metti in pausa l\'emulazione</string> | ||||
|     <string name="emulation_unpause">Riprendi Emulazione</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> | ||||
| 
 | ||||
|  |  | |||
|  | @ -211,7 +211,6 @@ | |||
|     <string name="emulation_pause">エミュレーションを一時停止</string> | ||||
|     <string name="emulation_unpause">エミュレーションを再開</string> | ||||
|     <string name="emulation_input_overlay">オーバーレイオプション</string> | ||||
|     <string name="emulation_game_loading">ロード中…</string> | ||||
| 
 | ||||
|     <string name="load_settings">設定をロード中…</string> | ||||
| 
 | ||||
|  |  | |||
|  | @ -213,7 +213,6 @@ | |||
|     <string name="emulation_pause">에뮬레이션 일시 중지</string> | ||||
|     <string name="emulation_unpause">에뮬레이션 일시 중지 해제</string> | ||||
|     <string name="emulation_input_overlay">오버레이 옵션</string> | ||||
|     <string name="emulation_game_loading">게임 불러오기 중...</string> | ||||
| 
 | ||||
|     <string name="load_settings">설정 불러오기 중...</string> | ||||
| 
 | ||||
|  |  | |||
|  | @ -213,7 +213,6 @@ | |||
|     <string name="emulation_pause">Pause Emulering</string> | ||||
|     <string name="emulation_unpause">Opphev pausing av emulering</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> | ||||
| 
 | ||||
|  |  | |||
|  | @ -213,7 +213,6 @@ | |||
|     <string name="emulation_pause">Wstrzymaj emulację</string> | ||||
|     <string name="emulation_unpause">Wznów emulację</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> | ||||
| 
 | ||||
|  |  | |||
|  | @ -213,7 +213,6 @@ | |||
|     <string name="emulation_pause">Pausa 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_game_loading">Jogo 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_unpause">Retomar emulaçã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> | ||||
| 
 | ||||
|  |  | |||
|  | @ -213,7 +213,6 @@ | |||
|     <string name="emulation_pause">Пауза эмуляции</string> | ||||
|     <string name="emulation_unpause">Возобновление эмуляции</string> | ||||
|     <string name="emulation_input_overlay">Настройки оверлея</string> | ||||
|     <string name="emulation_game_loading">Загрузка игры...</string> | ||||
| 
 | ||||
|     <string name="load_settings">Загрузка настроек...</string> | ||||
| 
 | ||||
|  |  | |||
|  | @ -213,7 +213,6 @@ | |||
|     <string name="emulation_pause">Пауза емуляції</string> | ||||
|     <string name="emulation_unpause">Відновлення емуляції</string> | ||||
|     <string name="emulation_input_overlay">Налаштування оверлея</string> | ||||
|     <string name="emulation_game_loading">Завантаження гри...</string> | ||||
| 
 | ||||
|     <string name="load_settings">Завантаження налаштувань...</string> | ||||
| 
 | ||||
|  |  | |||
|  | @ -213,7 +213,6 @@ | |||
|     <string name="emulation_pause">暂停模拟</string> | ||||
|     <string name="emulation_unpause">继续模拟</string> | ||||
|     <string name="emulation_input_overlay">虚拟按键选项</string> | ||||
|     <string name="emulation_game_loading">载入游戏中…</string> | ||||
| 
 | ||||
|     <string name="load_settings">正在载入设定…</string> | ||||
| 
 | ||||
|  |  | |||
|  | @ -213,7 +213,6 @@ | |||
|     <string name="emulation_pause">暫停模擬</string> | ||||
|     <string name="emulation_unpause">取消暫停模擬</string> | ||||
|     <string name="emulation_input_overlay">覆疊選項</string> | ||||
|     <string name="emulation_game_loading">遊戲正在載入…</string> | ||||
| 
 | ||||
|     <string name="load_settings">正在載入設定…</string> | ||||
| 
 | ||||
|  |  | |||
|  | @ -204,6 +204,7 @@ | |||
|     <string name="error_saving">Error saving %1$s.ini: %2$s</string> | ||||
|     <string name="unimplemented_menu">Unimplemented Menu</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_to_default">Reset to default</string> | ||||
|     <string name="reset_all_settings">Reset all settings?</string> | ||||
|  | @ -262,7 +263,6 @@ | |||
|     <string name="emulation_pause">Pause emulation</string> | ||||
|     <string name="emulation_unpause">Unpause emulation</string> | ||||
|     <string name="emulation_input_overlay">Overlay options</string> | ||||
|     <string name="emulation_game_loading">Game loading…</string> | ||||
| 
 | ||||
|     <string name="load_settings">Loading settings…</string> | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Charles Lombardo
						Charles Lombardo