From 273e81bb94f800e08686ae2f02226d93d61825de Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Tue, 21 Mar 2023 01:58:25 -0400 Subject: [PATCH] android: Use modal navigation drawer as in game menu --- .../yuzu_emu/activities/EmulationActivity.kt | 127 ++--------------- .../org/yuzu/yuzu_emu/adapters/GameAdapter.kt | 5 +- .../yuzu_emu/fragments/EmulationFragment.kt | 121 +++++++++++++++- .../yuzu/yuzu_emu/fragments/MenuFragment.java | 129 ------------------ .../org/yuzu/yuzu_emu/utils/InsetsHelper.kt | 8 ++ .../yuzu/yuzu_emu/utils/SerializableHelper.kt | 37 +++++ .../src/main/res/drawable/ic_controller.xml | 9 ++ .../app/src/main/res/drawable/ic_exit.xml | 10 ++ .../app/src/main/res/drawable/ic_pause.xml | 9 ++ .../app/src/main/res/drawable/ic_play.xml | 9 ++ .../main/res/layout/activity_emulation.xml | 33 +---- .../main/res/layout/fragment_emulation.xml | 90 +++++++----- .../main/res/layout/fragment_ingame_menu.xml | 56 -------- .../src/main/res/layout/header_in_game.xml | 23 ++++ .../app/src/main/res/menu/menu_in_game.xml | 32 +++++ .../main/res/menu/menu_overlay_options.xml | 12 ++ .../app/src/main/res/values/strings.xml | 4 + 17 files changed, 342 insertions(+), 372 deletions(-) delete mode 100644 src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MenuFragment.java create mode 100644 src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/SerializableHelper.kt create mode 100644 src/android/app/src/main/res/drawable/ic_controller.xml create mode 100644 src/android/app/src/main/res/drawable/ic_exit.xml create mode 100644 src/android/app/src/main/res/drawable/ic_pause.xml create mode 100644 src/android/app/src/main/res/drawable/ic_play.xml delete mode 100644 src/android/app/src/main/res/layout/fragment_ingame_menu.xml create mode 100644 src/android/app/src/main/res/layout/header_in_game.xml create mode 100644 src/android/app/src/main/res/menu/menu_in_game.xml create mode 100644 src/android/app/src/main/res/menu/menu_overlay_options.xml 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 import android.content.Intent import android.graphics.Rect import android.os.Bundle -import android.view.MotionEvent import android.view.View import android.view.WindowManager -import androidx.activity.OnBackPressedCallback -import androidx.annotation.IntDef import androidx.appcompat.app.AppCompatActivity -import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity -import androidx.fragment.app.FragmentManager import androidx.preference.PreferenceManager import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.slider.Slider.OnChangeListener @@ -25,8 +20,9 @@ import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.databinding.DialogSliderBinding import org.yuzu.yuzu_emu.features.settings.model.Settings import org.yuzu.yuzu_emu.fragments.EmulationFragment -import org.yuzu.yuzu_emu.fragments.MenuFragment +import org.yuzu.yuzu_emu.model.Game import org.yuzu.yuzu_emu.utils.ControllerMappingHelper +import org.yuzu.yuzu_emu.utils.SerializableHelper.parcelable import org.yuzu.yuzu_emu.utils.ThemeHelper import kotlin.math.roundToInt @@ -37,11 +33,11 @@ open class EmulationActivity : AppCompatActivity() { //private Intent foregroundService; var isActivityRecreated = false - private var selectedTitle: String? = null - private var path: String? = null private var menuVisible = false private var emulationFragment: EmulationFragment? = null + private lateinit var game: Game + override fun onDestroy() { // TODO(bunnei): Disable notifications until we support app suspension. //stopService(foregroundService); @@ -54,9 +50,7 @@ open class EmulationActivity : AppCompatActivity() { super.onCreate(savedInstanceState) if (savedInstanceState == null) { // Get params we were passed - val gameToEmulate = intent - path = gameToEmulate.getStringExtra(EXTRA_SELECTED_GAME) - selectedTitle = gameToEmulate.getStringExtra(EXTRA_SELECTED_TITLE) + game = intent.parcelable(EXTRA_SELECTED_GAME)!! isActivityRecreated = false } else { isActivityRecreated = true @@ -73,34 +67,26 @@ open class EmulationActivity : AppCompatActivity() { emulationFragment = supportFragmentManager.findFragmentById(R.id.frame_emulation_fragment) as EmulationFragment? if (emulationFragment == null) { - emulationFragment = EmulationFragment.newInstance(path) + emulationFragment = EmulationFragment.newInstance(game) supportFragmentManager.beginTransaction() .add(R.id.frame_emulation_fragment, emulationFragment!!) .commit() } - title = selectedTitle + title = game.title // Start a foreground service to prevent the app from getting killed in the background // TODO(bunnei): Disable notifications until we support app suspension. //foregroundService = new Intent(EmulationActivity.this, ForegroundService.class); //startForegroundService(foregroundService); - - onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) { - override fun handleOnBackPressed() { - toggleMenu() - } - }) } override fun onSaveInstanceState(outState: Bundle) { - outState.putString(EXTRA_SELECTED_GAME, path) - outState.putString(EXTRA_SELECTED_TITLE, selectedTitle) + outState.putParcelable(EXTRA_SELECTED_GAME, game) super.onSaveInstanceState(outState) } private fun restoreState(savedInstanceState: Bundle) { - path = savedInstanceState.getString(EXTRA_SELECTED_GAME) - selectedTitle = savedInstanceState.getString(EXTRA_SELECTED_TITLE) + game = savedInstanceState.parcelable(EXTRA_SELECTED_GAME)!! // If an alert prompt was in progress when state was restored, retry displaying it NativeLibrary.retryDisplayAlertPrompt() @@ -110,6 +96,8 @@ open class EmulationActivity : AppCompatActivity() { window.attributes.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES + window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN or WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS) + // It would be nice to use IMMERSIVE_STICKY, but that doesn't show the toolbar. window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or @@ -119,15 +107,6 @@ open class EmulationActivity : AppCompatActivity() { View.SYSTEM_UI_FLAG_IMMERSIVE } - fun handleMenuAction(action: Int) { - when (action) { - MENU_ACTION_EXIT -> { - emulationFragment!!.stopEmulation() - finish() - } - } - } - private fun editControlsPlacement() { if (emulationFragment!!.isConfiguringControls) { emulationFragment!!.stopConfiguringControls() @@ -176,94 +155,14 @@ open class EmulationActivity : AppCompatActivity() { .show() } - override fun dispatchTouchEvent(event: MotionEvent): Boolean { - if (event.actionMasked == MotionEvent.ACTION_DOWN) { - var anyMenuClosed = false - var submenu = supportFragmentManager.findFragmentById(R.id.frame_submenu) - if (submenu != null && areCoordinatesOutside(submenu.view, event.x, event.y)) { - closeSubmenu() - submenu = null - anyMenuClosed = true - } - if (submenu == null) { - val menu = supportFragmentManager.findFragmentById(R.id.frame_menu) - if (menu != null && areCoordinatesOutside(menu.view, event.x, event.y)) { - closeMenu() - anyMenuClosed = true - } - } - if (anyMenuClosed) { - return true - } - } - return super.dispatchTouchEvent(event) - } - - @Retention(AnnotationRetention.SOURCE) - @IntDef( - MENU_ACTION_EDIT_CONTROLS_PLACEMENT, - MENU_ACTION_TOGGLE_CONTROLS, - MENU_ACTION_ADJUST_SCALE, - MENU_ACTION_EXIT, - MENU_ACTION_SHOW_FPS, - MENU_ACTION_RESET_OVERLAY, - MENU_ACTION_SHOW_OVERLAY, - MENU_ACTION_OPEN_SETTINGS - ) - annotation class MenuAction - - private fun closeSubmenu(): Boolean { - return supportFragmentManager.popBackStackImmediate( - BACKSTACK_NAME_SUBMENU, - FragmentManager.POP_BACK_STACK_INCLUSIVE - ) - } - - private fun closeMenu(): Boolean { - menuVisible = false - return supportFragmentManager.popBackStackImmediate( - BACKSTACK_NAME_MENU, - FragmentManager.POP_BACK_STACK_INCLUSIVE - ) - } - - private fun toggleMenu() { - if (!closeMenu()) { - val fragment: Fragment = MenuFragment.newInstance() - supportFragmentManager.beginTransaction() - .setCustomAnimations( - R.animator.menu_slide_in_from_start, - R.animator.menu_slide_out_to_start, - R.animator.menu_slide_in_from_start, - R.animator.menu_slide_out_to_start - ) - .add(R.id.frame_menu, fragment) - .addToBackStack(BACKSTACK_NAME_MENU) - .commit() - menuVisible = true - } - } - companion object { - private const val BACKSTACK_NAME_MENU = "menu" - private const val BACKSTACK_NAME_SUBMENU = "submenu" const val EXTRA_SELECTED_GAME = "SelectedGame" - const val EXTRA_SELECTED_TITLE = "SelectedTitle" - const val MENU_ACTION_EDIT_CONTROLS_PLACEMENT = 0 - const val MENU_ACTION_TOGGLE_CONTROLS = 1 - const val MENU_ACTION_ADJUST_SCALE = 2 - const val MENU_ACTION_EXIT = 3 - const val MENU_ACTION_SHOW_FPS = 4 - const val MENU_ACTION_RESET_OVERLAY = 6 - const val MENU_ACTION_SHOW_OVERLAY = 7 - const val MENU_ACTION_OPEN_SETTINGS = 8 private const val EMULATION_RUNNING_NOTIFICATION = 0x1000 @JvmStatic - fun launch(activity: FragmentActivity, path: String?, title: String?) { + fun launch(activity: FragmentActivity, game: Game) { val launcher = Intent(activity, EmulationActivity::class.java) - launcher.putExtra(EXTRA_SELECTED_GAME, path) - launcher.putExtra(EXTRA_SELECTED_TITLE, title) + launcher.putExtra(EXTRA_SELECTED_GAME, game) activity.startActivity(launcher) } 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 import android.view.ViewGroup import android.widget.ImageView import androidx.appcompat.app.AppCompatActivity -import androidx.fragment.app.FragmentActivity import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.RecyclerView import coil.load @@ -23,8 +22,8 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.R -import org.yuzu.yuzu_emu.activities.EmulationActivity.Companion.launch import org.yuzu.yuzu_emu.databinding.CardGameBinding +import org.yuzu.yuzu_emu.activities.EmulationActivity import org.yuzu.yuzu_emu.model.Game import org.yuzu.yuzu_emu.model.GameDatabase import org.yuzu.yuzu_emu.utils.Log @@ -181,7 +180,7 @@ class GameAdapter(private val activity: AppCompatActivity) : RecyclerView.Adapte */ override fun onClick(view: View) { val holder = view.tag as GameViewHolder - launch((view.context as FragmentActivity), holder.game.path, holder.game.title) + EmulationActivity.launch((view.context as AppCompatActivity), holder.game) } 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 import android.os.Bundle import android.os.Handler import android.view.* +import android.widget.TextView import android.widget.Toast +import androidx.activity.OnBackPressedCallback +import androidx.appcompat.widget.PopupMenu +import androidx.core.content.res.ResourcesCompat +import androidx.core.graphics.Insets +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat import androidx.fragment.app.Fragment import androidx.localbroadcastmanager.content.LocalBroadcastManager import androidx.preference.PreferenceManager @@ -20,10 +27,15 @@ import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.activities.EmulationActivity import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding import org.yuzu.yuzu_emu.features.settings.model.Settings +import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity +import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile +import org.yuzu.yuzu_emu.model.Game import org.yuzu.yuzu_emu.utils.DirectoryInitialization import org.yuzu.yuzu_emu.utils.DirectoryInitialization.DirectoryInitializationState import org.yuzu.yuzu_emu.utils.DirectoryStateReceiver +import org.yuzu.yuzu_emu.utils.InsetsHelper import org.yuzu.yuzu_emu.utils.Log +import org.yuzu.yuzu_emu.utils.SerializableHelper.parcelable class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.FrameCallback { private lateinit var preferences: SharedPreferences @@ -35,6 +47,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram private var _binding: FragmentEmulationBinding? = null private val binding get() = _binding!! + private lateinit var game: Game + override fun onAttach(context: Context) { super.onAttach(context) if (context is EmulationActivity) { @@ -54,8 +68,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram // So this fragment doesn't restart on configuration changes; i.e. rotation. retainInstance = true preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) - val gamePath = requireArguments().getString(KEY_GAMEPATH) - emulationState = EmulationState(gamePath) + game = requireArguments().parcelable(EmulationActivity.EXTRA_SELECTED_GAME)!! + emulationState = EmulationState(game.path) } /** @@ -78,6 +92,57 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram // Setup overlay. resetInputOverlay() updateShowFpsOverlay() + + binding.inGameMenu.getHeaderView(0).findViewById(R.id.text_game_title).text = + game.title + binding.inGameMenu.setNavigationItemSelectedListener { + when (it.itemId) { + R.id.menu_pause_emulation -> { + if (emulationState.isPaused) { + emulationState.run(false) + it.title = resources.getString(R.string.emulation_pause) + it.icon = ResourcesCompat.getDrawable( + resources, + R.drawable.ic_pause, + requireContext().theme + ) + } else { + emulationState.pause() + it.title = resources.getString(R.string.emulation_unpause) + it.icon = ResourcesCompat.getDrawable( + resources, + R.drawable.ic_play, + requireContext().theme + ) + } + true + } + R.id.menu_settings -> { + SettingsActivity.launch(requireContext(), SettingsFile.FILE_NAME_CONFIG, "") + true + } + R.id.menu_overlay_controls -> { + showOverlayOptions() + true + } + R.id.menu_exit -> { + requireActivity().finish() + emulationState.stop() + true + } + else -> true + } + } + + setInsets() + + requireActivity().onBackPressedDispatcher.addCallback( + requireActivity(), + object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + if (binding.drawerLayout.isOpen) binding.drawerLayout.close() else binding.drawerLayout.open() + } + }) } override fun onResume() { @@ -202,8 +267,30 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram NativeLibrary.DoFrame() } - fun stopEmulation() { - emulationState.stop() + private fun showOverlayOptions() { + val anchor = binding.inGameMenu.findViewById(R.id.menu_overlay_controls) + val popup = PopupMenu(requireContext(), anchor) + + popup.menuInflater.inflate(R.menu.menu_overlay_options, popup.menu) + + popup.setOnMenuItemClickListener { + when (it.itemId) { + R.id.menu_edit_overlay -> { + binding.drawerLayout.close() + binding.surfaceInputOverlay.requestFocus() + startConfiguringControls() + true + } + R.id.menu_reset_overlay -> { + binding.drawerLayout.close() + resetInputOverlay() + true + } + else -> true + } + } + + popup.show() } fun startConfiguringControls() { @@ -219,6 +306,27 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram val isConfiguringControls: Boolean get() = binding.surfaceInputOverlay.isInEditMode + private fun setInsets() { + ViewCompat.setOnApplyWindowInsetsListener(binding.inGameMenu) { v: View, windowInsets: WindowInsetsCompat -> + val cutInsets: Insets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout()) + var left = 0 + var right = 0 + if (ViewCompat.getLayoutDirection(v) == ViewCompat.LAYOUT_DIRECTION_LTR) { + left = cutInsets.left + } else { + right = cutInsets.right + } + + // Don't use padding if the navigation bar isn't in the way + if (InsetsHelper.getBottomPaddingRequired(requireActivity()) > 0) { + v.setPadding(left, cutInsets.top, right, 0) + } else { + v.setPadding(left, cutInsets.top, right, 0) + } + windowInsets + } + } + private class EmulationState(private val mGamePath: String?) { private var state: State private var surface: Surface? = null @@ -340,12 +448,11 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram } companion object { - private const val KEY_GAMEPATH = "gamepath" private val perfStatsUpdateHandler = Handler() - fun newInstance(gamePath: String?): EmulationFragment { + fun newInstance(game: Game): EmulationFragment { val args = Bundle() - args.putString(KEY_GAMEPATH, gamePath) + args.putParcelable(EmulationActivity.EXTRA_SELECTED_GAME, game) val fragment = EmulationFragment() fragment.arguments = args 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 @@ -package org.yuzu.yuzu_emu.fragments; - -import android.content.pm.PackageManager; -import android.graphics.Rect; -import android.os.Bundle; -import android.util.SparseIntArray; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.LinearLayout; - -import androidx.annotation.ColorInt; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.graphics.Insets; -import androidx.core.view.ViewCompat; -import androidx.core.view.WindowInsetsCompat; -import androidx.fragment.app.Fragment; - -import com.google.android.material.color.MaterialColors; -import com.google.android.material.elevation.ElevationOverlayProvider; - -import org.yuzu.yuzu_emu.R; -import org.yuzu.yuzu_emu.activities.EmulationActivity; - - -public final class MenuFragment extends Fragment implements View.OnClickListener -{ - private static final String KEY_TITLE = "title"; - private static final String KEY_WII = "wii"; - private static SparseIntArray buttonsActionsMap = new SparseIntArray(); - - private int mCutInset = 0; - - static - { - buttonsActionsMap.append(R.id.menu_exit, EmulationActivity.MENU_ACTION_EXIT); - } - - public static MenuFragment newInstance() - { - MenuFragment fragment = new MenuFragment(); - - Bundle arguments = new Bundle(); - fragment.setArguments(arguments); - - return fragment; - } - - // This is primarily intended to account for any navigation bar at the bottom of the screen - private int getBottomPaddingRequired() - { - Rect visibleFrame = new Rect(); - requireActivity().getWindow().getDecorView().getWindowVisibleDisplayFrame(visibleFrame); - return visibleFrame.bottom - visibleFrame.top - getResources().getDisplayMetrics().heightPixels; - } - - @NonNull - @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) - { - View rootView = inflater.inflate(R.layout.fragment_ingame_menu, container, false); - - LinearLayout options = rootView.findViewById(R.id.layout_options); - -// mPauseEmulation = options.findViewById(R.id.menu_pause_emulation); -// mUnpauseEmulation = options.findViewById(R.id.menu_unpause_emulation); -// -// updatePauseUnpauseVisibility(); -// -// if (!requireActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN)) -// { -// options.findViewById(R.id.menu_overlay_controls).setVisibility(View.GONE); -// } -// -// if (!getArguments().getBoolean(KEY_WII, true)) -// { -// options.findViewById(R.id.menu_refresh_wiimotes).setVisibility(View.GONE); -// } - - int bottomPaddingRequired = getBottomPaddingRequired(); - - // Provide a safe zone between the navigation bar and Exit Emulation to avoid accidental touches - float density = getResources().getDisplayMetrics().density; - if (bottomPaddingRequired >= 32 * density) - { - bottomPaddingRequired += 32 * density; - } - - if (bottomPaddingRequired > rootView.getPaddingBottom()) - { - rootView.setPadding(rootView.getPaddingLeft(), rootView.getPaddingTop(), - rootView.getPaddingRight(), bottomPaddingRequired); - } - - for (int childIndex = 0; childIndex < options.getChildCount(); childIndex++) - { - Button button = (Button) options.getChildAt(childIndex); - - button.setOnClickListener(this); - } - - rootView.findViewById(R.id.menu_exit).setOnClickListener(this); - -// mTitleText = rootView.findViewById(R.id.text_game_title); -// String title = getArguments().getString(KEY_TITLE, null); -// if (title != null) -// { -// mTitleText.setText(title); -// } - - if (getResources().getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_LTR) - { -// rootView.post(() -> NativeLibrary.SetObscuredPixelsLeft(rootView.getWidth())); - } - - return rootView; - } - - @Override - public void onClick(View button) - { - int action = buttonsActionsMap.get(button.getId()); - EmulationActivity activity = (EmulationActivity) requireActivity(); - activity.handleMenuAction(action); - } -} 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 @@ package org.yuzu.yuzu_emu.utils import android.annotation.SuppressLint +import android.app.Activity import android.content.Context +import android.graphics.Rect import android.view.ViewGroup.MarginLayoutParams import androidx.core.graphics.Insets import com.google.android.material.appbar.AppBarLayout @@ -27,4 +29,10 @@ object InsetsHelper { resources.getInteger(resourceId) } else 0 } + + fun getBottomPaddingRequired(activity: Activity): Int { + val visibleFrame = Rect() + activity.window.decorView.getWindowVisibleDisplayFrame(visibleFrame) + return visibleFrame.bottom - visibleFrame.top - activity.resources.displayMetrics.heightPixels + } } 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 @@ +package org.yuzu.yuzu_emu.utils + +import android.content.Intent +import android.os.Build +import android.os.Bundle +import android.os.Parcelable +import java.io.Serializable + +object SerializableHelper { + inline fun Bundle.serializable(key: String): T? { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) + getSerializable(key, T::class.java) + else + getSerializable(key) as? T + } + + inline fun Intent.serializable(key: String): T? { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) + getSerializableExtra(key, T::class.java) + else + getSerializableExtra(key) as? T + } + + inline fun Bundle.parcelable(key: String): T? { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) + getParcelable(key, T::class.java) + else + getParcelable(key) as? T + } + + inline fun Intent.parcelable(key: String): T? { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) + getParcelableExtra(key, T::class.java) + else + getParcelableExtra(key) as? T + } +} 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 @@ + + + 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 @@ + + + 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 @@ + + + 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 @@ + + + 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 @@ - + - - - - - - - - + android:layout_height="match_parent" /> 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 @@ - + tools:context="org.yuzu.yuzu_emu.fragments.EmulationFragment" + tools:openDrawer="start"> - -