summaryrefslogtreecommitdiff
path: root/src/android
diff options
context:
space:
mode:
authorGravatar Charles Lombardo2023-03-21 01:58:25 -0400
committerGravatar bunnei2023-06-03 00:05:46 -0700
commit273e81bb94f800e08686ae2f02226d93d61825de (patch)
tree8df1b38df9c920fec7542c9fe78c86de635922cd /src/android
parentandroid: Make Game class parcelable (diff)
downloadyuzu-273e81bb94f800e08686ae2f02226d93d61825de.tar.gz
yuzu-273e81bb94f800e08686ae2f02226d93d61825de.tar.xz
yuzu-273e81bb94f800e08686ae2f02226d93d61825de.zip
android: Use modal navigation drawer as in game menu
Diffstat (limited to 'src/android')
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt127
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt5
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt121
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MenuFragment.java129
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InsetsHelper.kt8
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/SerializableHelper.kt37
-rw-r--r--src/android/app/src/main/res/drawable/ic_controller.xml9
-rw-r--r--src/android/app/src/main/res/drawable/ic_exit.xml10
-rw-r--r--src/android/app/src/main/res/drawable/ic_pause.xml9
-rw-r--r--src/android/app/src/main/res/drawable/ic_play.xml9
-rw-r--r--src/android/app/src/main/res/layout/activity_emulation.xml33
-rw-r--r--src/android/app/src/main/res/layout/fragment_emulation.xml92
-rw-r--r--src/android/app/src/main/res/layout/fragment_ingame_menu.xml56
-rw-r--r--src/android/app/src/main/res/layout/header_in_game.xml23
-rw-r--r--src/android/app/src/main/res/menu/menu_in_game.xml32
-rw-r--r--src/android/app/src/main/res/menu/menu_overlay_options.xml12
-rw-r--r--src/android/app/src/main/res/values/strings.xml4
17 files changed, 343 insertions, 373 deletions
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
index b222344c3..0da7562a6 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
@@ -8,15 +8,10 @@ import android.content.DialogInterface
8import android.content.Intent 8import android.content.Intent
9import android.graphics.Rect 9import android.graphics.Rect
10import android.os.Bundle 10import android.os.Bundle
11import android.view.MotionEvent
12import android.view.View 11import android.view.View
13import android.view.WindowManager 12import android.view.WindowManager
14import androidx.activity.OnBackPressedCallback
15import androidx.annotation.IntDef
16import androidx.appcompat.app.AppCompatActivity 13import androidx.appcompat.app.AppCompatActivity
17import androidx.fragment.app.Fragment
18import androidx.fragment.app.FragmentActivity 14import androidx.fragment.app.FragmentActivity
19import androidx.fragment.app.FragmentManager
20import androidx.preference.PreferenceManager 15import androidx.preference.PreferenceManager
21import com.google.android.material.dialog.MaterialAlertDialogBuilder 16import com.google.android.material.dialog.MaterialAlertDialogBuilder
22import com.google.android.material.slider.Slider.OnChangeListener 17import com.google.android.material.slider.Slider.OnChangeListener
@@ -25,8 +20,9 @@ import org.yuzu.yuzu_emu.R
25import org.yuzu.yuzu_emu.databinding.DialogSliderBinding 20import org.yuzu.yuzu_emu.databinding.DialogSliderBinding
26import org.yuzu.yuzu_emu.features.settings.model.Settings 21import org.yuzu.yuzu_emu.features.settings.model.Settings
27import org.yuzu.yuzu_emu.fragments.EmulationFragment 22import org.yuzu.yuzu_emu.fragments.EmulationFragment
28import org.yuzu.yuzu_emu.fragments.MenuFragment 23import org.yuzu.yuzu_emu.model.Game
29import org.yuzu.yuzu_emu.utils.ControllerMappingHelper 24import org.yuzu.yuzu_emu.utils.ControllerMappingHelper
25import org.yuzu.yuzu_emu.utils.SerializableHelper.parcelable
30import org.yuzu.yuzu_emu.utils.ThemeHelper 26import org.yuzu.yuzu_emu.utils.ThemeHelper
31import kotlin.math.roundToInt 27import kotlin.math.roundToInt
32 28
@@ -37,11 +33,11 @@ open class EmulationActivity : AppCompatActivity() {
37 //private Intent foregroundService; 33 //private Intent foregroundService;
38 34
39 var isActivityRecreated = false 35 var isActivityRecreated = false
40 private var selectedTitle: String? = null
41 private var path: String? = null
42 private var menuVisible = false 36 private var menuVisible = false
43 private var emulationFragment: EmulationFragment? = null 37 private var emulationFragment: EmulationFragment? = null
44 38
39 private lateinit var game: Game
40
45 override fun onDestroy() { 41 override fun onDestroy() {
46 // TODO(bunnei): Disable notifications until we support app suspension. 42 // TODO(bunnei): Disable notifications until we support app suspension.
47 //stopService(foregroundService); 43 //stopService(foregroundService);
@@ -54,9 +50,7 @@ open class EmulationActivity : AppCompatActivity() {
54 super.onCreate(savedInstanceState) 50 super.onCreate(savedInstanceState)
55 if (savedInstanceState == null) { 51 if (savedInstanceState == null) {
56 // Get params we were passed 52 // Get params we were passed
57 val gameToEmulate = intent 53 game = intent.parcelable(EXTRA_SELECTED_GAME)!!
58 path = gameToEmulate.getStringExtra(EXTRA_SELECTED_GAME)
59 selectedTitle = gameToEmulate.getStringExtra(EXTRA_SELECTED_TITLE)
60 isActivityRecreated = false 54 isActivityRecreated = false
61 } else { 55 } else {
62 isActivityRecreated = true 56 isActivityRecreated = true
@@ -73,34 +67,26 @@ open class EmulationActivity : AppCompatActivity() {
73 emulationFragment = 67 emulationFragment =
74 supportFragmentManager.findFragmentById(R.id.frame_emulation_fragment) as EmulationFragment? 68 supportFragmentManager.findFragmentById(R.id.frame_emulation_fragment) as EmulationFragment?
75 if (emulationFragment == null) { 69 if (emulationFragment == null) {
76 emulationFragment = EmulationFragment.newInstance(path) 70 emulationFragment = EmulationFragment.newInstance(game)
77 supportFragmentManager.beginTransaction() 71 supportFragmentManager.beginTransaction()
78 .add(R.id.frame_emulation_fragment, emulationFragment!!) 72 .add(R.id.frame_emulation_fragment, emulationFragment!!)
79 .commit() 73 .commit()
80 } 74 }
81 title = selectedTitle 75 title = game.title
82 76
83 // Start a foreground service to prevent the app from getting killed in the background 77 // Start a foreground service to prevent the app from getting killed in the background
84 // TODO(bunnei): Disable notifications until we support app suspension. 78 // TODO(bunnei): Disable notifications until we support app suspension.
85 //foregroundService = new Intent(EmulationActivity.this, ForegroundService.class); 79 //foregroundService = new Intent(EmulationActivity.this, ForegroundService.class);
86 //startForegroundService(foregroundService); 80 //startForegroundService(foregroundService);
87
88 onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
89 override fun handleOnBackPressed() {
90 toggleMenu()
91 }
92 })
93 } 81 }
94 82
95 override fun onSaveInstanceState(outState: Bundle) { 83 override fun onSaveInstanceState(outState: Bundle) {
96 outState.putString(EXTRA_SELECTED_GAME, path) 84 outState.putParcelable(EXTRA_SELECTED_GAME, game)
97 outState.putString(EXTRA_SELECTED_TITLE, selectedTitle)
98 super.onSaveInstanceState(outState) 85 super.onSaveInstanceState(outState)
99 } 86 }
100 87
101 private fun restoreState(savedInstanceState: Bundle) { 88 private fun restoreState(savedInstanceState: Bundle) {
102 path = savedInstanceState.getString(EXTRA_SELECTED_GAME) 89 game = savedInstanceState.parcelable(EXTRA_SELECTED_GAME)!!
103 selectedTitle = savedInstanceState.getString(EXTRA_SELECTED_TITLE)
104 90
105 // If an alert prompt was in progress when state was restored, retry displaying it 91 // If an alert prompt was in progress when state was restored, retry displaying it
106 NativeLibrary.retryDisplayAlertPrompt() 92 NativeLibrary.retryDisplayAlertPrompt()
@@ -110,6 +96,8 @@ open class EmulationActivity : AppCompatActivity() {
110 window.attributes.layoutInDisplayCutoutMode = 96 window.attributes.layoutInDisplayCutoutMode =
111 WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES 97 WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
112 98
99 window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN or WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS)
100
113 // It would be nice to use IMMERSIVE_STICKY, but that doesn't show the toolbar. 101 // It would be nice to use IMMERSIVE_STICKY, but that doesn't show the toolbar.
114 window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or 102 window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
115 View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or 103 View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
@@ -119,15 +107,6 @@ open class EmulationActivity : AppCompatActivity() {
119 View.SYSTEM_UI_FLAG_IMMERSIVE 107 View.SYSTEM_UI_FLAG_IMMERSIVE
120 } 108 }
121 109
122 fun handleMenuAction(action: Int) {
123 when (action) {
124 MENU_ACTION_EXIT -> {
125 emulationFragment!!.stopEmulation()
126 finish()
127 }
128 }
129 }
130
131 private fun editControlsPlacement() { 110 private fun editControlsPlacement() {
132 if (emulationFragment!!.isConfiguringControls) { 111 if (emulationFragment!!.isConfiguringControls) {
133 emulationFragment!!.stopConfiguringControls() 112 emulationFragment!!.stopConfiguringControls()
@@ -176,94 +155,14 @@ open class EmulationActivity : AppCompatActivity() {
176 .show() 155 .show()
177 } 156 }
178 157
179 override fun dispatchTouchEvent(event: MotionEvent): Boolean {
180 if (event.actionMasked == MotionEvent.ACTION_DOWN) {
181 var anyMenuClosed = false
182 var submenu = supportFragmentManager.findFragmentById(R.id.frame_submenu)
183 if (submenu != null && areCoordinatesOutside(submenu.view, event.x, event.y)) {
184 closeSubmenu()
185 submenu = null
186 anyMenuClosed = true
187 }
188 if (submenu == null) {
189 val menu = supportFragmentManager.findFragmentById(R.id.frame_menu)
190 if (menu != null && areCoordinatesOutside(menu.view, event.x, event.y)) {
191 closeMenu()
192 anyMenuClosed = true
193 }
194 }
195 if (anyMenuClosed) {
196 return true
197 }
198 }
199 return super.dispatchTouchEvent(event)
200 }
201
202 @Retention(AnnotationRetention.SOURCE)
203 @IntDef(
204 MENU_ACTION_EDIT_CONTROLS_PLACEMENT,
205 MENU_ACTION_TOGGLE_CONTROLS,
206 MENU_ACTION_ADJUST_SCALE,
207 MENU_ACTION_EXIT,
208 MENU_ACTION_SHOW_FPS,
209 MENU_ACTION_RESET_OVERLAY,
210 MENU_ACTION_SHOW_OVERLAY,
211 MENU_ACTION_OPEN_SETTINGS
212 )
213 annotation class MenuAction
214
215 private fun closeSubmenu(): Boolean {
216 return supportFragmentManager.popBackStackImmediate(
217 BACKSTACK_NAME_SUBMENU,
218 FragmentManager.POP_BACK_STACK_INCLUSIVE
219 )
220 }
221
222 private fun closeMenu(): Boolean {
223 menuVisible = false
224 return supportFragmentManager.popBackStackImmediate(
225 BACKSTACK_NAME_MENU,
226 FragmentManager.POP_BACK_STACK_INCLUSIVE
227 )
228 }
229
230 private fun toggleMenu() {
231 if (!closeMenu()) {
232 val fragment: Fragment = MenuFragment.newInstance()
233 supportFragmentManager.beginTransaction()
234 .setCustomAnimations(
235 R.animator.menu_slide_in_from_start,
236 R.animator.menu_slide_out_to_start,
237 R.animator.menu_slide_in_from_start,
238 R.animator.menu_slide_out_to_start
239 )
240 .add(R.id.frame_menu, fragment)
241 .addToBackStack(BACKSTACK_NAME_MENU)
242 .commit()
243 menuVisible = true
244 }
245 }
246
247 companion object { 158 companion object {
248 private const val BACKSTACK_NAME_MENU = "menu"
249 private const val BACKSTACK_NAME_SUBMENU = "submenu"
250 const val EXTRA_SELECTED_GAME = "SelectedGame" 159 const val EXTRA_SELECTED_GAME = "SelectedGame"
251 const val EXTRA_SELECTED_TITLE = "SelectedTitle"
252 const val MENU_ACTION_EDIT_CONTROLS_PLACEMENT = 0
253 const val MENU_ACTION_TOGGLE_CONTROLS = 1
254 const val MENU_ACTION_ADJUST_SCALE = 2
255 const val MENU_ACTION_EXIT = 3
256 const val MENU_ACTION_SHOW_FPS = 4
257 const val MENU_ACTION_RESET_OVERLAY = 6
258 const val MENU_ACTION_SHOW_OVERLAY = 7
259 const val MENU_ACTION_OPEN_SETTINGS = 8
260 private const val EMULATION_RUNNING_NOTIFICATION = 0x1000 160 private const val EMULATION_RUNNING_NOTIFICATION = 0x1000
261 161
262 @JvmStatic 162 @JvmStatic
263 fun launch(activity: FragmentActivity, path: String?, title: String?) { 163 fun launch(activity: FragmentActivity, game: Game) {
264 val launcher = Intent(activity, EmulationActivity::class.java) 164 val launcher = Intent(activity, EmulationActivity::class.java)
265 launcher.putExtra(EXTRA_SELECTED_GAME, path) 165 launcher.putExtra(EXTRA_SELECTED_GAME, game)
266 launcher.putExtra(EXTRA_SELECTED_TITLE, title)
267 activity.startActivity(launcher) 166 activity.startActivity(launcher)
268 } 167 }
269 168
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
index e9f926d84..0295801ad 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
@@ -13,7 +13,6 @@ import android.view.View
13import android.view.ViewGroup 13import android.view.ViewGroup
14import android.widget.ImageView 14import android.widget.ImageView
15import androidx.appcompat.app.AppCompatActivity 15import androidx.appcompat.app.AppCompatActivity
16import androidx.fragment.app.FragmentActivity
17import androidx.lifecycle.lifecycleScope 16import androidx.lifecycle.lifecycleScope
18import androidx.recyclerview.widget.RecyclerView 17import androidx.recyclerview.widget.RecyclerView
19import coil.load 18import coil.load
@@ -23,8 +22,8 @@ import kotlinx.coroutines.launch
23import kotlinx.coroutines.withContext 22import kotlinx.coroutines.withContext
24import org.yuzu.yuzu_emu.NativeLibrary 23import org.yuzu.yuzu_emu.NativeLibrary
25import org.yuzu.yuzu_emu.R 24import org.yuzu.yuzu_emu.R
26import org.yuzu.yuzu_emu.activities.EmulationActivity.Companion.launch
27import org.yuzu.yuzu_emu.databinding.CardGameBinding 25import org.yuzu.yuzu_emu.databinding.CardGameBinding
26import org.yuzu.yuzu_emu.activities.EmulationActivity
28import org.yuzu.yuzu_emu.model.Game 27import org.yuzu.yuzu_emu.model.Game
29import org.yuzu.yuzu_emu.model.GameDatabase 28import org.yuzu.yuzu_emu.model.GameDatabase
30import org.yuzu.yuzu_emu.utils.Log 29import org.yuzu.yuzu_emu.utils.Log
@@ -181,7 +180,7 @@ class GameAdapter(private val activity: AppCompatActivity) : RecyclerView.Adapte
181 */ 180 */
182 override fun onClick(view: View) { 181 override fun onClick(view: View) {
183 val holder = view.tag as GameViewHolder 182 val holder = view.tag as GameViewHolder
184 launch((view.context as FragmentActivity), holder.game.path, holder.game.title) 183 EmulationActivity.launch((view.context as AppCompatActivity), holder.game)
185 } 184 }
186 185
187 private fun isValidGame(path: String): Boolean { 186 private fun isValidGame(path: String): Boolean {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
index 0889b6f7f..4ba283ddd 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
@@ -10,7 +10,14 @@ import android.graphics.Color
10import android.os.Bundle 10import android.os.Bundle
11import android.os.Handler 11import android.os.Handler
12import android.view.* 12import android.view.*
13import android.widget.TextView
13import android.widget.Toast 14import android.widget.Toast
15import androidx.activity.OnBackPressedCallback
16import androidx.appcompat.widget.PopupMenu
17import androidx.core.content.res.ResourcesCompat
18import androidx.core.graphics.Insets
19import androidx.core.view.ViewCompat
20import androidx.core.view.WindowInsetsCompat
14import androidx.fragment.app.Fragment 21import androidx.fragment.app.Fragment
15import androidx.localbroadcastmanager.content.LocalBroadcastManager 22import androidx.localbroadcastmanager.content.LocalBroadcastManager
16import androidx.preference.PreferenceManager 23import androidx.preference.PreferenceManager
@@ -20,10 +27,15 @@ import org.yuzu.yuzu_emu.YuzuApplication
20import org.yuzu.yuzu_emu.activities.EmulationActivity 27import org.yuzu.yuzu_emu.activities.EmulationActivity
21import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding 28import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding
22import org.yuzu.yuzu_emu.features.settings.model.Settings 29import org.yuzu.yuzu_emu.features.settings.model.Settings
30import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity
31import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
32import org.yuzu.yuzu_emu.model.Game
23import org.yuzu.yuzu_emu.utils.DirectoryInitialization 33import org.yuzu.yuzu_emu.utils.DirectoryInitialization
24import org.yuzu.yuzu_emu.utils.DirectoryInitialization.DirectoryInitializationState 34import org.yuzu.yuzu_emu.utils.DirectoryInitialization.DirectoryInitializationState
25import org.yuzu.yuzu_emu.utils.DirectoryStateReceiver 35import org.yuzu.yuzu_emu.utils.DirectoryStateReceiver
36import org.yuzu.yuzu_emu.utils.InsetsHelper
26import org.yuzu.yuzu_emu.utils.Log 37import org.yuzu.yuzu_emu.utils.Log
38import org.yuzu.yuzu_emu.utils.SerializableHelper.parcelable
27 39
28class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.FrameCallback { 40class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.FrameCallback {
29 private lateinit var preferences: SharedPreferences 41 private lateinit var preferences: SharedPreferences
@@ -35,6 +47,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
35 private var _binding: FragmentEmulationBinding? = null 47 private var _binding: FragmentEmulationBinding? = null
36 private val binding get() = _binding!! 48 private val binding get() = _binding!!
37 49
50 private lateinit var game: Game
51
38 override fun onAttach(context: Context) { 52 override fun onAttach(context: Context) {
39 super.onAttach(context) 53 super.onAttach(context)
40 if (context is EmulationActivity) { 54 if (context is EmulationActivity) {
@@ -54,8 +68,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
54 // So this fragment doesn't restart on configuration changes; i.e. rotation. 68 // So this fragment doesn't restart on configuration changes; i.e. rotation.
55 retainInstance = true 69 retainInstance = true
56 preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) 70 preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
57 val gamePath = requireArguments().getString(KEY_GAMEPATH) 71 game = requireArguments().parcelable(EmulationActivity.EXTRA_SELECTED_GAME)!!
58 emulationState = EmulationState(gamePath) 72 emulationState = EmulationState(game.path)
59 } 73 }
60 74
61 /** 75 /**
@@ -78,6 +92,57 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
78 // Setup overlay. 92 // Setup overlay.
79 resetInputOverlay() 93 resetInputOverlay()
80 updateShowFpsOverlay() 94 updateShowFpsOverlay()
95
96 binding.inGameMenu.getHeaderView(0).findViewById<TextView>(R.id.text_game_title).text =
97 game.title
98 binding.inGameMenu.setNavigationItemSelectedListener {
99 when (it.itemId) {
100 R.id.menu_pause_emulation -> {
101 if (emulationState.isPaused) {
102 emulationState.run(false)
103 it.title = resources.getString(R.string.emulation_pause)
104 it.icon = ResourcesCompat.getDrawable(
105 resources,
106 R.drawable.ic_pause,
107 requireContext().theme
108 )
109 } else {
110 emulationState.pause()
111 it.title = resources.getString(R.string.emulation_unpause)
112 it.icon = ResourcesCompat.getDrawable(
113 resources,
114 R.drawable.ic_play,
115 requireContext().theme
116 )
117 }
118 true
119 }
120 R.id.menu_settings -> {
121 SettingsActivity.launch(requireContext(), SettingsFile.FILE_NAME_CONFIG, "")
122 true
123 }
124 R.id.menu_overlay_controls -> {
125 showOverlayOptions()
126 true
127 }
128 R.id.menu_exit -> {
129 requireActivity().finish()
130 emulationState.stop()
131 true
132 }
133 else -> true
134 }
135 }
136
137 setInsets()
138
139 requireActivity().onBackPressedDispatcher.addCallback(
140 requireActivity(),
141 object : OnBackPressedCallback(true) {
142 override fun handleOnBackPressed() {
143 if (binding.drawerLayout.isOpen) binding.drawerLayout.close() else binding.drawerLayout.open()
144 }
145 })
81 } 146 }
82 147
83 override fun onResume() { 148 override fun onResume() {
@@ -202,8 +267,30 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
202 NativeLibrary.DoFrame() 267 NativeLibrary.DoFrame()
203 } 268 }
204 269
205 fun stopEmulation() { 270 private fun showOverlayOptions() {
206 emulationState.stop() 271 val anchor = binding.inGameMenu.findViewById<View>(R.id.menu_overlay_controls)
272 val popup = PopupMenu(requireContext(), anchor)
273
274 popup.menuInflater.inflate(R.menu.menu_overlay_options, popup.menu)
275
276 popup.setOnMenuItemClickListener {
277 when (it.itemId) {
278 R.id.menu_edit_overlay -> {
279 binding.drawerLayout.close()
280 binding.surfaceInputOverlay.requestFocus()
281 startConfiguringControls()
282 true
283 }
284 R.id.menu_reset_overlay -> {
285 binding.drawerLayout.close()
286 resetInputOverlay()
287 true
288 }
289 else -> true
290 }
291 }
292
293 popup.show()
207 } 294 }
208 295
209 fun startConfiguringControls() { 296 fun startConfiguringControls() {
@@ -219,6 +306,27 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
219 val isConfiguringControls: Boolean 306 val isConfiguringControls: Boolean
220 get() = binding.surfaceInputOverlay.isInEditMode 307 get() = binding.surfaceInputOverlay.isInEditMode
221 308
309 private fun setInsets() {
310 ViewCompat.setOnApplyWindowInsetsListener(binding.inGameMenu) { v: View, windowInsets: WindowInsetsCompat ->
311 val cutInsets: Insets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
312 var left = 0
313 var right = 0
314 if (ViewCompat.getLayoutDirection(v) == ViewCompat.LAYOUT_DIRECTION_LTR) {
315 left = cutInsets.left
316 } else {
317 right = cutInsets.right
318 }
319
320 // Don't use padding if the navigation bar isn't in the way
321 if (InsetsHelper.getBottomPaddingRequired(requireActivity()) > 0) {
322 v.setPadding(left, cutInsets.top, right, 0)
323 } else {
324 v.setPadding(left, cutInsets.top, right, 0)
325 }
326 windowInsets
327 }
328 }
329
222 private class EmulationState(private val mGamePath: String?) { 330 private class EmulationState(private val mGamePath: String?) {
223 private var state: State 331 private var state: State
224 private var surface: Surface? = null 332 private var surface: Surface? = null
@@ -340,12 +448,11 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
340 } 448 }
341 449
342 companion object { 450 companion object {
343 private const val KEY_GAMEPATH = "gamepath"
344 private val perfStatsUpdateHandler = Handler() 451 private val perfStatsUpdateHandler = Handler()
345 452
346 fun newInstance(gamePath: String?): EmulationFragment { 453 fun newInstance(game: Game): EmulationFragment {
347 val args = Bundle() 454 val args = Bundle()
348 args.putString(KEY_GAMEPATH, gamePath) 455 args.putParcelable(EmulationActivity.EXTRA_SELECTED_GAME, game)
349 val fragment = EmulationFragment() 456 val fragment = EmulationFragment()
350 fragment.arguments = args 457 fragment.arguments = args
351 return fragment 458 return fragment
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MenuFragment.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MenuFragment.java
deleted file mode 100644
index 5dc3f5545..000000000
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MenuFragment.java
+++ /dev/null
@@ -1,129 +0,0 @@
1package org.yuzu.yuzu_emu.fragments;
2
3import android.content.pm.PackageManager;
4import android.graphics.Rect;
5import android.os.Bundle;
6import android.util.SparseIntArray;
7import android.view.LayoutInflater;
8import android.view.View;
9import android.view.ViewGroup;
10import android.widget.Button;
11import android.widget.LinearLayout;
12
13import androidx.annotation.ColorInt;
14import androidx.annotation.NonNull;
15import androidx.annotation.Nullable;
16import androidx.core.graphics.Insets;
17import androidx.core.view.ViewCompat;
18import androidx.core.view.WindowInsetsCompat;
19import androidx.fragment.app.Fragment;
20
21import com.google.android.material.color.MaterialColors;
22import com.google.android.material.elevation.ElevationOverlayProvider;
23
24import org.yuzu.yuzu_emu.R;
25import org.yuzu.yuzu_emu.activities.EmulationActivity;
26
27
28public final class MenuFragment extends Fragment implements View.OnClickListener
29{
30 private static final String KEY_TITLE = "title";
31 private static final String KEY_WII = "wii";
32 private static SparseIntArray buttonsActionsMap = new SparseIntArray();
33
34 private int mCutInset = 0;
35
36 static
37 {
38 buttonsActionsMap.append(R.id.menu_exit, EmulationActivity.MENU_ACTION_EXIT);
39 }
40
41 public static MenuFragment newInstance()
42 {
43 MenuFragment fragment = new MenuFragment();
44
45 Bundle arguments = new Bundle();
46 fragment.setArguments(arguments);
47
48 return fragment;
49 }
50
51 // This is primarily intended to account for any navigation bar at the bottom of the screen
52 private int getBottomPaddingRequired()
53 {
54 Rect visibleFrame = new Rect();
55 requireActivity().getWindow().getDecorView().getWindowVisibleDisplayFrame(visibleFrame);
56 return visibleFrame.bottom - visibleFrame.top - getResources().getDisplayMetrics().heightPixels;
57 }
58
59 @NonNull
60 @Override
61 public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
62 Bundle savedInstanceState)
63 {
64 View rootView = inflater.inflate(R.layout.fragment_ingame_menu, container, false);
65
66 LinearLayout options = rootView.findViewById(R.id.layout_options);
67
68// mPauseEmulation = options.findViewById(R.id.menu_pause_emulation);
69// mUnpauseEmulation = options.findViewById(R.id.menu_unpause_emulation);
70//
71// updatePauseUnpauseVisibility();
72//
73// if (!requireActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN))
74// {
75// options.findViewById(R.id.menu_overlay_controls).setVisibility(View.GONE);
76// }
77//
78// if (!getArguments().getBoolean(KEY_WII, true))
79// {
80// options.findViewById(R.id.menu_refresh_wiimotes).setVisibility(View.GONE);
81// }
82
83 int bottomPaddingRequired = getBottomPaddingRequired();
84
85 // Provide a safe zone between the navigation bar and Exit Emulation to avoid accidental touches
86 float density = getResources().getDisplayMetrics().density;
87 if (bottomPaddingRequired >= 32 * density)
88 {
89 bottomPaddingRequired += 32 * density;
90 }
91
92 if (bottomPaddingRequired > rootView.getPaddingBottom())
93 {
94 rootView.setPadding(rootView.getPaddingLeft(), rootView.getPaddingTop(),
95 rootView.getPaddingRight(), bottomPaddingRequired);
96 }
97
98 for (int childIndex = 0; childIndex < options.getChildCount(); childIndex++)
99 {
100 Button button = (Button) options.getChildAt(childIndex);
101
102 button.setOnClickListener(this);
103 }
104
105 rootView.findViewById(R.id.menu_exit).setOnClickListener(this);
106
107// mTitleText = rootView.findViewById(R.id.text_game_title);
108// String title = getArguments().getString(KEY_TITLE, null);
109// if (title != null)
110// {
111// mTitleText.setText(title);
112// }
113
114 if (getResources().getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_LTR)
115 {
116// rootView.post(() -> NativeLibrary.SetObscuredPixelsLeft(rootView.getWidth()));
117 }
118
119 return rootView;
120 }
121
122 @Override
123 public void onClick(View button)
124 {
125 int action = buttonsActionsMap.get(button.getId());
126 EmulationActivity activity = (EmulationActivity) requireActivity();
127 activity.handleMenuAction(action);
128 }
129}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InsetsHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InsetsHelper.kt
index 37f08ac26..e7a04d917 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InsetsHelper.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InsetsHelper.kt
@@ -1,7 +1,9 @@
1package org.yuzu.yuzu_emu.utils 1package org.yuzu.yuzu_emu.utils
2 2
3import android.annotation.SuppressLint 3import android.annotation.SuppressLint
4import android.app.Activity
4import android.content.Context 5import android.content.Context
6import android.graphics.Rect
5import android.view.ViewGroup.MarginLayoutParams 7import android.view.ViewGroup.MarginLayoutParams
6import androidx.core.graphics.Insets 8import androidx.core.graphics.Insets
7import com.google.android.material.appbar.AppBarLayout 9import com.google.android.material.appbar.AppBarLayout
@@ -27,4 +29,10 @@ object InsetsHelper {
27 resources.getInteger(resourceId) 29 resources.getInteger(resourceId)
28 } else 0 30 } else 0
29 } 31 }
32
33 fun getBottomPaddingRequired(activity: Activity): Int {
34 val visibleFrame = Rect()
35 activity.window.decorView.getWindowVisibleDisplayFrame(visibleFrame)
36 return visibleFrame.bottom - visibleFrame.top - activity.resources.displayMetrics.heightPixels
37 }
30} 38}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/SerializableHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/SerializableHelper.kt
new file mode 100644
index 000000000..23ffbaf68
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/SerializableHelper.kt
@@ -0,0 +1,37 @@
1package org.yuzu.yuzu_emu.utils
2
3import android.content.Intent
4import android.os.Build
5import android.os.Bundle
6import android.os.Parcelable
7import java.io.Serializable
8
9object SerializableHelper {
10 inline fun <reified T : Serializable> Bundle.serializable(key: String): T? {
11 return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
12 getSerializable(key, T::class.java)
13 else
14 getSerializable(key) as? T
15 }
16
17 inline fun <reified T : Serializable> Intent.serializable(key: String): T? {
18 return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
19 getSerializableExtra(key, T::class.java)
20 else
21 getSerializableExtra(key) as? T
22 }
23
24 inline fun <reified T : Parcelable> Bundle.parcelable(key: String): T? {
25 return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
26 getParcelable(key, T::class.java)
27 else
28 getParcelable(key) as? T
29 }
30
31 inline fun <reified T : Parcelable> Intent.parcelable(key: String): T? {
32 return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
33 getParcelableExtra(key, T::class.java)
34 else
35 getParcelableExtra(key) as? T
36 }
37}
diff --git a/src/android/app/src/main/res/drawable/ic_controller.xml b/src/android/app/src/main/res/drawable/ic_controller.xml
new file mode 100644
index 000000000..2359c35be
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/ic_controller.xml
@@ -0,0 +1,9 @@
1<vector xmlns:android="http://schemas.android.com/apk/res/android"
2 android:width="24dp"
3 android:height="24dp"
4 android:viewportHeight="24"
5 android:viewportWidth="24">
6 <path
7 android:fillColor="?attr/colorControlNormal"
8 android:pathData="M21,6L3,6c-1.1,0 -2,0.9 -2,2v8c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2L23,8c0,-1.1 -0.9,-2 -2,-2zM11,13L8,13v3L6,16v-3L3,13v-2h3L6,8h2v3h3v2zM15.5,15c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5zM19.5,12c-0.83,0 -1.5,-0.67 -1.5,-1.5S18.67,9 19.5,9s1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5z" />
9</vector>
diff --git a/src/android/app/src/main/res/drawable/ic_exit.xml b/src/android/app/src/main/res/drawable/ic_exit.xml
new file mode 100644
index 000000000..a55a1d387
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/ic_exit.xml
@@ -0,0 +1,10 @@
1<vector xmlns:android="http://schemas.android.com/apk/res/android"
2 android:width="24dp"
3 android:height="24dp"
4 android:autoMirrored="true"
5 android:viewportHeight="24"
6 android:viewportWidth="24">
7 <path
8 android:fillColor="?attr/colorControlNormal"
9 android:pathData="M10.09,15.59L11.5,17l5,-5 -5,-5 -1.41,1.41L12.67,11H3v2h9.67l-2.58,2.59zM19,3H5c-1.11,0 -2,0.9 -2,2v4h2V5h14v14H5v-4H3v4c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2z" />
10</vector>
diff --git a/src/android/app/src/main/res/drawable/ic_pause.xml b/src/android/app/src/main/res/drawable/ic_pause.xml
new file mode 100644
index 000000000..adb3ababc
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/ic_pause.xml
@@ -0,0 +1,9 @@
1<vector xmlns:android="http://schemas.android.com/apk/res/android"
2 android:width="24dp"
3 android:height="24dp"
4 android:viewportHeight="24"
5 android:viewportWidth="24">
6 <path
7 android:fillColor="?attr/colorControlNormal"
8 android:pathData="M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z" />
9</vector>
diff --git a/src/android/app/src/main/res/drawable/ic_play.xml b/src/android/app/src/main/res/drawable/ic_play.xml
new file mode 100644
index 000000000..7f01dc599
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/ic_play.xml
@@ -0,0 +1,9 @@
1<vector xmlns:android="http://schemas.android.com/apk/res/android"
2 android:width="24dp"
3 android:height="24dp"
4 android:viewportHeight="24"
5 android:viewportWidth="24">
6 <path
7 android:fillColor="?attr/colorControlNormal"
8 android:pathData="M8,5v14l11,-7z" />
9</vector>
diff --git a/src/android/app/src/main/res/layout/activity_emulation.xml b/src/android/app/src/main/res/layout/activity_emulation.xml
index debc26e6c..f6360a65b 100644
--- a/src/android/app/src/main/res/layout/activity_emulation.xml
+++ b/src/android/app/src/main/res/layout/activity_emulation.xml
@@ -1,32 +1,13 @@
1<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" 1<FrameLayout
2 android:layout_width="match_parent" 2 xmlns:android="http://schemas.android.com/apk/res/android"
3 android:layout_height="match_parent" 3 android:id="@+id/frame_content"
4 android:keepScreenOn="true" 4 android:layout_width="match_parent"
5 xmlns:tools="http://schemas.android.com/tools" 5 android:layout_height="match_parent"
6 android:id="@+id/frame_content"> 6 android:keepScreenOn="true">
7 7
8 <FrameLayout 8 <FrameLayout
9 android:id="@+id/frame_emulation_fragment" 9 android:id="@+id/frame_emulation_fragment"
10 android:layout_width="match_parent" 10 android:layout_width="match_parent"
11 android:layout_height="match_parent"/> 11 android:layout_height="match_parent" />
12
13 <LinearLayout
14 android:layout_width="match_parent"
15 android:layout_height="match_parent"
16 android:orientation="horizontal"
17 android:baselineAligned="false">
18
19 <FrameLayout
20 android:id="@+id/frame_menu"
21 android:layout_width="@dimen/menu_width"
22 android:layout_height="match_parent"
23 tools:layout="@layout/fragment_ingame_menu"/>
24
25 <FrameLayout
26 android:id="@+id/frame_submenu"
27 android:layout_width="match_parent"
28 android:layout_height="match_parent"/>
29
30 </LinearLayout>
31 12
32</FrameLayout> 13</FrameLayout>
diff --git a/src/android/app/src/main/res/layout/fragment_emulation.xml b/src/android/app/src/main/res/layout/fragment_emulation.xml
index a3e5707ef..be11f028f 100644
--- a/src/android/app/src/main/res/layout/fragment_emulation.xml
+++ b/src/android/app/src/main/res/layout/fragment_emulation.xml
@@ -1,47 +1,63 @@
1<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" 1<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
2 xmlns:app="http://schemas.android.com/apk/res-auto"
2 xmlns:tools="http://schemas.android.com/tools" 3 xmlns:tools="http://schemas.android.com/tools"
4 android:id="@+id/drawer_layout"
3 android:layout_width="match_parent" 5 android:layout_width="match_parent"
4 android:layout_height="match_parent" 6 android:layout_height="match_parent"
5 android:keepScreenOn="true" 7 android:keepScreenOn="true"
6 tools:context="org.yuzu.yuzu_emu.fragments.EmulationFragment"> 8 tools:context="org.yuzu.yuzu_emu.fragments.EmulationFragment"
9 tools:openDrawer="start">
7 10
8 <!-- This is what everything is rendered to during emulation --> 11 <androidx.coordinatorlayout.widget.CoordinatorLayout
9 <Button
10 android:id="@+id/done_control_config"
11 style="@style/Widget.Material3.Button.Icon"
12 android:layout_width="wrap_content"
13 android:layout_height="wrap_content"
14 android:layout_gravity="center"
15 android:padding="@dimen/spacing_small"
16 android:text="@string/emulation_done"
17 android:visibility="gone" />
18
19 <!-- This is the onscreen input overlay -->
20 <SurfaceView
21 android:id="@+id/surface_emulation"
22 android:layout_width="match_parent" 12 android:layout_width="match_parent"
23 android:layout_height="match_parent" 13 android:layout_height="match_parent">
24 android:focusable="false"
25 android:focusableInTouchMode="false" />
26 14
27 <org.yuzu.yuzu_emu.overlay.InputOverlay 15 <!-- This is the onscreen input overlay -->
28 android:id="@+id/surface_input_overlay" 16 <SurfaceView
29 android:layout_width="match_parent" 17 android:id="@+id/surface_emulation"
30 android:layout_height="match_parent" 18 android:layout_width="match_parent"
31 android:focusable="true" 19 android:layout_height="match_parent"
32 android:focusableInTouchMode="true" /> 20 android:focusable="false"
21 android:focusableInTouchMode="false" />
22
23 <TextView
24 android:id="@+id/show_fps_text"
25 android:layout_width="wrap_content"
26 android:layout_height="wrap_content"
27 android:layout_marginStart="18dp"
28 android:layout_marginTop="2dp"
29 android:clickable="false"
30 android:linksClickable="false"
31 android:longClickable="false"
32 android:shadowColor="@android:color/black"
33 android:textColor="@android:color/white"
34 android:textSize="12sp" />
33 35
34 <TextView 36 <org.yuzu.yuzu_emu.overlay.InputOverlay
35 android:id="@+id/show_fps_text" 37 android:id="@+id/surface_input_overlay"
38 android:layout_width="match_parent"
39 android:layout_height="match_parent"
40 android:focusable="true"
41 android:focusableInTouchMode="true" />
42
43 <!-- This is what everything is rendered to during emulation -->
44 <Button
45 style="@style/Widget.Material3.Button.ElevatedButton"
46 android:id="@+id/done_control_config"
47 android:layout_width="wrap_content"
48 android:layout_height="wrap_content"
49 android:layout_gravity="center"
50 android:text="@string/emulation_done"
51 android:visibility="gone" />
52
53 </androidx.coordinatorlayout.widget.CoordinatorLayout>
54
55 <com.google.android.material.navigation.NavigationView
56 android:id="@+id/in_game_menu"
36 android:layout_width="wrap_content" 57 android:layout_width="wrap_content"
37 android:layout_height="wrap_content" 58 android:layout_height="match_parent"
38 android:layout_marginStart="18dp" 59 android:layout_gravity="start"
39 android:layout_marginTop="2dp" 60 app:headerLayout="@layout/header_in_game"
40 android:clickable="false" 61 app:menu="@menu/menu_in_game" />
41 android:linksClickable="false" 62
42 android:longClickable="false" 63</androidx.drawerlayout.widget.DrawerLayout>
43 android:shadowColor="@android:color/black"
44 android:textColor="@android:color/white"
45 android:textSize="12sp" />
46
47</FrameLayout>
diff --git a/src/android/app/src/main/res/layout/fragment_ingame_menu.xml b/src/android/app/src/main/res/layout/fragment_ingame_menu.xml
deleted file mode 100644
index ce618ef7b..000000000
--- a/src/android/app/src/main/res/layout/fragment_ingame_menu.xml
+++ /dev/null
@@ -1,56 +0,0 @@
1<?xml version="1.0" encoding="utf-8"?>
2<LinearLayout
3 xmlns:android="http://schemas.android.com/apk/res/android"
4 xmlns:tools="http://schemas.android.com/tools"
5 android:orientation="vertical"
6 android:layout_width="match_parent"
7 android:layout_height="match_parent"
8 android:background="?attr/colorSurface"
9 android:elevation="3dp"
10 tools:layout_width="250dp">
11
12 <TextView
13 android:id="@+id/text_game_title"
14 android:layout_width="match_parent"
15 android:layout_height="wrap_content"
16 android:layout_marginHorizontal="32dp"
17 android:layout_marginVertical="24dp"
18 android:ellipsize="end"
19 android:letterSpacing="0"
20 android:maxLines="@integer/game_title_lines"
21 android:textSize="20sp"
22 android:textColor="?attr/colorOnSurface"
23 tools:text="The Legend of Zelda: Breath of the Wild" />
24
25 <com.google.android.material.divider.MaterialDivider
26 android:id="@+id/divider"
27 android:layout_width="match_parent"
28 android:layout_height="wrap_content" />
29
30 <ScrollView
31 android:layout_width="match_parent"
32 android:layout_height="0dp"
33 android:layout_weight="1"
34 android:scrollbarSize="4dp"
35 android:fadeScrollbars="false">
36
37 <LinearLayout
38 android:id="@+id/layout_options"
39 android:layout_width="match_parent"
40 android:layout_height="wrap_content"
41 android:orientation="vertical" />
42
43 </ScrollView>
44
45 <com.google.android.material.divider.MaterialDivider
46 android:id="@+id/divider_2"
47 android:layout_width="match_parent"
48 android:layout_height="wrap_content" />
49
50 <Button
51 android:id="@+id/menu_exit"
52 style="@style/InGameMenuOption"
53 android:layout_marginTop="@dimen/spacing_large"
54 android:text="@string/emulation_exit" />
55
56</LinearLayout> \ No newline at end of file
diff --git a/src/android/app/src/main/res/layout/header_in_game.xml b/src/android/app/src/main/res/layout/header_in_game.xml
new file mode 100644
index 000000000..135d429c5
--- /dev/null
+++ b/src/android/app/src/main/res/layout/header_in_game.xml
@@ -0,0 +1,23 @@
1<?xml version="1.0" encoding="utf-8"?>
2<androidx.constraintlayout.widget.ConstraintLayout
3 xmlns:android="http://schemas.android.com/apk/res/android"
4 xmlns:app="http://schemas.android.com/apk/res-auto"
5 xmlns:tools="http://schemas.android.com/tools"
6 android:layout_width="match_parent"
7 android:layout_height="match_parent"
8 android:fitsSystemWindows="true">
9
10 <com.google.android.material.textview.MaterialTextView
11 android:id="@+id/text_game_title"
12 android:layout_width="0dp"
13 android:layout_height="wrap_content"
14 android:layout_margin="24dp"
15 android:textAppearance="?attr/textAppearanceHeadlineMedium"
16 android:textColor="?attr/colorOnSurface"
17 android:textAlignment="viewStart"
18 app:layout_constraintEnd_toEndOf="parent"
19 app:layout_constraintStart_toStartOf="parent"
20 app:layout_constraintTop_toTopOf="parent"
21 tools:text="Super Mario Odyssey" />
22
23</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/src/android/app/src/main/res/menu/menu_in_game.xml b/src/android/app/src/main/res/menu/menu_in_game.xml
new file mode 100644
index 000000000..f68459640
--- /dev/null
+++ b/src/android/app/src/main/res/menu/menu_in_game.xml
@@ -0,0 +1,32 @@
1<?xml version="1.0" encoding="utf-8"?>
2<menu xmlns:android="http://schemas.android.com/apk/res/android">
3
4 <item android:title="">
5
6 <menu>
7
8 <item
9 android:id="@+id/menu_pause_emulation"
10 android:icon="@drawable/ic_pause"
11 android:title="@string/emulation_pause" />
12
13 <item
14 android:id="@+id/menu_settings"
15 android:icon="@drawable/ic_settings"
16 android:title="@string/preferences_settings" />
17
18 <item
19 android:id="@+id/menu_overlay_controls"
20 android:icon="@drawable/ic_controller"
21 android:title="@string/emulation_input_overlay" />
22
23 </menu>
24
25 </item>
26
27 <item
28 android:id="@+id/menu_exit"
29 android:icon="@drawable/ic_exit"
30 android:title="@string/emulation_exit" />
31
32</menu>
diff --git a/src/android/app/src/main/res/menu/menu_overlay_options.xml b/src/android/app/src/main/res/menu/menu_overlay_options.xml
new file mode 100644
index 000000000..75c84cdf3
--- /dev/null
+++ b/src/android/app/src/main/res/menu/menu_overlay_options.xml
@@ -0,0 +1,12 @@
1<?xml version="1.0" encoding="utf-8"?>
2<menu xmlns:android="http://schemas.android.com/apk/res/android">
3
4 <item
5 android:id="@+id/menu_edit_overlay"
6 android:title="@string/emulation_touch_overlay_edit" />
7
8 <item
9 android:id="@+id/menu_reset_overlay"
10 android:title="@string/emulation_touch_overlay_reset" />
11
12</menu>
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index 7da113728..c471425f2 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -87,6 +87,10 @@
87 <string name="emulation_toggle_controls">Toggle Controls</string> 87 <string name="emulation_toggle_controls">Toggle Controls</string>
88 <string name="emulation_control_scale">Adjust Scale</string> 88 <string name="emulation_control_scale">Adjust Scale</string>
89 <string name="emulation_touch_overlay_reset">Reset Overlay</string> 89 <string name="emulation_touch_overlay_reset">Reset Overlay</string>
90 <string name="emulation_touch_overlay_edit">Edit Overlay</string>
91 <string name="emulation_pause">Pause Emulation</string>
92 <string name="emulation_unpause">Unpause Emulation</string>
93 <string name="emulation_input_overlay">Input Overlay</string>
90 94
91 <string name="load_settings">Loading Settings…</string> 95 <string name="load_settings">Loading Settings…</string>
92 96