Android: Pad menu fragment when expanding to cutout area

This commit is contained in:
Charles Lombardo 2022-12-01 15:45:37 -05:00
parent 385dfb60a0
commit 2a0939ab98
7 changed files with 119 additions and 66 deletions

View File

@ -16,6 +16,7 @@ import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.TextView;
import android.widget.Toast;
@ -25,6 +26,9 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.PopupMenu;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
@ -342,6 +346,8 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP
ActivityEmulationBinding binding = ActivityEmulationBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
setInsets(binding.frameMenu);
// Find or create the EmulationFragment
mEmulationFragment = (EmulationFragment) getSupportFragmentManager()
.findFragmentById(R.id.frame_emulation_fragment);
@ -538,6 +544,28 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP
}
}
private void setInsets(View view)
{
ViewCompat.setOnApplyWindowInsetsListener(view, (v, windowInsets) ->
{
Insets cutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout());
ViewGroup.MarginLayoutParams mlpMenu =
(ViewGroup.MarginLayoutParams) v.getLayoutParams();
int menuWidth = getResources().getDimensionPixelSize(R.dimen.menu_width);
if (ViewCompat.getLayoutDirection(v) == ViewCompat.LAYOUT_DIRECTION_LTR)
{
mlpMenu.width = cutInsets.left + menuWidth;
}
else
{
mlpMenu.width = cutInsets.right + menuWidth;
}
NativeLibrary.SetObscuredPixelsTop(cutInsets.top);
NativeLibrary.SetObscuredPixelsLeft(cutInsets.left);
return windowInsets;
});
}
public void showOverlayControlsMenu(@NonNull View anchor)
{
PopupMenu popup = new PopupMenu(this, anchor);

View File

@ -3,7 +3,6 @@
package org.dolphinemu.dolphinemu.fragments;
import android.content.pm.PackageManager;
import android.graphics.Rect;
import android.os.Bundle;
import android.util.SparseIntArray;
import android.view.LayoutInflater;
@ -14,6 +13,9 @@ import android.widget.LinearLayout;
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 org.dolphinemu.dolphinemu.NativeLibrary;
@ -21,6 +23,7 @@ import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.activities.EmulationActivity;
import org.dolphinemu.dolphinemu.databinding.FragmentIngameMenuBinding;
import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting;
import org.dolphinemu.dolphinemu.utils.InsetsHelper;
public final class MenuFragment extends Fragment implements View.OnClickListener
{
@ -28,6 +31,8 @@ public final class MenuFragment extends Fragment implements View.OnClickListener
private static final String KEY_WII = "wii";
private static SparseIntArray buttonsActionsMap = new SparseIntArray();
private int mCutInset = 0;
static
{
buttonsActionsMap
@ -68,14 +73,6 @@ public final class MenuFragment extends Fragment implements View.OnClickListener
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,
@ -88,6 +85,7 @@ public final class MenuFragment extends Fragment implements View.OnClickListener
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState)
{
setInsets();
updatePauseUnpauseVisibility();
if (!requireActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN))
@ -100,21 +98,6 @@ public final class MenuFragment extends Fragment implements View.OnClickListener
mBinding.menuRefreshWiimotes.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 > view.getPaddingBottom())
{
view.setPadding(view.getPaddingLeft(), view.getPaddingTop(),
view.getPaddingRight(), bottomPaddingRequired);
}
LinearLayout options = mBinding.layoutOptions;
for (int childIndex = 0; childIndex < options.getChildCount(); childIndex++)
{
@ -130,11 +113,41 @@ public final class MenuFragment extends Fragment implements View.OnClickListener
{
mBinding.textGameTitle.setText(title);
}
}
if (getResources().getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_LTR)
private void setInsets()
{
ViewCompat.setOnApplyWindowInsetsListener(mBinding.getRoot(), (v, windowInsets) ->
{
view.post(() -> NativeLibrary.SetObscuredPixelsLeft(view.getWidth()));
}
Insets cutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout());
mCutInset = cutInsets.left;
int left = 0;
int right = 0;
if (ViewCompat.getLayoutDirection(v) == ViewCompat.LAYOUT_DIRECTION_LTR)
{
left = cutInsets.left;
}
else
{
right = cutInsets.right;
}
v.post(() -> NativeLibrary.SetObscuredPixelsLeft(v.getWidth()));
// Don't use padding if the navigation bar isn't in the way
if (InsetsHelper.getBottomPaddingRequired(requireActivity()) > 0)
{
v.setPadding(left, cutInsets.top, right,
cutInsets.bottom + InsetsHelper.getNavigationBarHeight(requireContext()));
}
else
{
v.setPadding(left, cutInsets.top, right,
cutInsets.bottom + getResources().getDimensionPixelSize(R.dimen.spacing_large));
}
return windowInsets;
});
}
@Override
@ -155,7 +168,7 @@ public final class MenuFragment extends Fragment implements View.OnClickListener
{
super.onDestroyView();
NativeLibrary.SetObscuredPixelsLeft(0);
NativeLibrary.SetObscuredPixelsLeft(mCutInset);
mBinding = null;
}

View File

@ -1,7 +1,9 @@
package org.dolphinemu.dolphinemu.utils;
import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Rect;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.ViewGroup;
@ -222,4 +224,24 @@ public class InsetsHelper
}
return 0;
}
public static int getNavigationBarHeight(Context context)
{
Resources resources = context.getResources();
int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
if (resourceId > 0)
{
return resources.getDimensionPixelSize(resourceId);
}
return 0;
}
// This is primarily intended to account for any navigation bar at the bottom of the screen
public static int getBottomPaddingRequired(Activity activity)
{
Rect visibleFrame = new Rect();
activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(visibleFrame);
return visibleFrame.bottom - visibleFrame.top -
activity.getResources().getDisplayMetrics().heightPixels;
}
}

View File

@ -22,7 +22,7 @@
<FrameLayout
android:id="@+id/frame_menu"
android:layout_width="260dp"
android:layout_width="@dimen/menu_width"
android:layout_height="match_parent"
tools:layout="@layout/fragment_ingame_menu"/>

View File

@ -1,20 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="16dp"
android:background="?attr/colorSurface"
tools:layout_width="250dp">
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorSurface"
tools:layout_width="250dp">
<TextView
android:id="@+id/text_game_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginHorizontal="32dp"
android:layout_marginTop="32dp"
android:layout_marginVertical="24dp"
android:ellipsize="end"
android:letterSpacing="0"
android:maxLines="@integer/game_title_lines"
@ -22,11 +21,16 @@
android:textColor="?attr/colorOnSurface"
tools:text="The Legend of Zelda: The Wind Waker" />
<com.google.android.material.divider.MaterialDivider
android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:scrollbarSize="8dp"
android:scrollbarSize="4dp"
android:fadeScrollbars="false">
<LinearLayout
@ -38,73 +42,62 @@
<Button
android:id="@+id/menu_pause_emulation"
style="@style/InGameMenuOption"
android:letterSpacing="0"
android:text="@string/pause_emulation" />
<Button
android:id="@+id/menu_unpause_emulation"
style="@style/InGameMenuOption"
android:letterSpacing="0"
android:text="@string/unpause_emulation"
android:visibility="gone"/>
android:visibility="gone" />
<Button
android:id="@+id/menu_take_screenshot"
style="@style/InGameMenuOption"
android:letterSpacing="0"
android:text="@string/emulation_screenshot" />
<Button
android:id="@+id/menu_quicksave"
style="@style/InGameMenuOption"
android:letterSpacing="0"
android:text="@string/emulation_quicksave"
android:visibility="gone"/>
android:visibility="gone" />
<Button
android:id="@+id/menu_quickload"
style="@style/InGameMenuOption"
android:letterSpacing="0"
android:text="@string/emulation_quickload"
android:visibility="gone"/>
android:visibility="gone" />
<Button
android:id="@+id/menu_emulation_save_root"
style="@style/InGameMenuOption"
android:letterSpacing="0"
android:text="@string/emulation_savestate"
android:visibility="gone"/>
android:visibility="gone" />
<Button
android:id="@+id/menu_emulation_load_root"
style="@style/InGameMenuOption"
android:letterSpacing="0"
android:text="@string/emulation_loadstate"
android:visibility="gone"/>
android:visibility="gone" />
<Button
android:id="@+id/menu_settings"
style="@style/InGameMenuOption"
android:letterSpacing="0"
android:text="@string/grid_menu_settings" />
<Button
android:id="@+id/menu_overlay_controls"
style="@style/InGameMenuOption"
android:letterSpacing="0"
android:text="@string/emulation_overlay_controls" />
<Button
android:id="@+id/menu_refresh_wiimotes"
android:text="@string/emulation_refresh_wiimotes"
android:letterSpacing="0"
style="@style/InGameMenuOption"/>
style="@style/InGameMenuOption" />
<Button
android:id="@+id/menu_change_disc"
android:text="@string/emulation_change_disc"
android:letterSpacing="0"
style="@style/InGameMenuOption"/>
style="@style/InGameMenuOption" />
</LinearLayout>
@ -113,14 +106,12 @@
<com.google.android.material.divider.MaterialDivider
android:id="@+id/divider_2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginBottom="16dp" />
android:layout_height="wrap_content" />
<Button
android:id="@+id/menu_exit"
style="@style/InGameMenuOption"
android:letterSpacing="0"
android:layout_marginTop="@dimen/spacing_large"
android:text="@string/emulation_exit" />
</LinearLayout>

View File

@ -2,5 +2,6 @@
<dimen name="spacing_small">4dp</dimen>
<dimen name="spacing_medlarge">12dp</dimen>
<dimen name="spacing_large">16dp</dimen>
<dimen name="menu_width">256dp</dimen>
<dimen name="card_width">135dp</dimen>
</resources>

View File

@ -2,16 +2,14 @@
<resources>
<!-- Custom button styles -->
<style name="InGameMenuOption" parent="Widget.Material3.Button.TextButton">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">48dp</item>
<item name="android:textColor">?attr/colorOnSurface</item>
<item name="android:textSize">16sp</item>
<item name="android:fontFamily">sans-serif-condensed</item>
<item name="android:textAllCaps">false</item>
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">48dp</item>
<item name="android:gravity">center_vertical|left</item>
<item name="android:paddingLeft">32dp</item>
<item name="android:paddingRight">32dp</item>
<item name="android:layout_margin">0dp</item>
</style>
<style name="OverlayInGameMenuOption" parent="InGameMenuOption">