From 343717e683fef1a164f5ae3e7c0b5982fbaab1ae Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Thu, 23 Mar 2023 14:36:54 -0400 Subject: [PATCH] citra_android: Implement edge-to-edge (#6349) --- .../cheats/ui/CheatDetailsFragment.java | 1 - .../features/cheats/ui/CheatListFragment.java | 37 ++++++++-- .../features/cheats/ui/CheatsActivity.java | 71 +++++++++++++++++-- .../settings/ui/SettingsActivity.java | 22 +++++- .../settings/ui/SettingsFragment.java | 23 ++++-- .../settings/ui/SettingsFrameLayout.java | 48 ------------- .../citra/citra_emu/ui/main/MainActivity.java | 24 +++++++ .../ui/platform/PlatformGamesFragment.java | 14 +++- .../citra/citra_emu/utils/InsetsHelper.java | 33 +++++++++ .../org/citra/citra_emu/utils/ThemeUtil.java | 41 +++++++++++ .../src/main/res/layout/activity_cheats.xml | 21 +++--- .../app/src/main/res/layout/activity_main.xml | 5 +- .../src/main/res/layout/activity_settings.xml | 6 +- .../main/res/layout/fragment_cheat_list.xml | 3 +- .../app/src/main/res/layout/fragment_grid.xml | 1 + .../src/main/res/layout/fragment_settings.xml | 8 ++- .../src/main/res/layout/list_item_cheat.xml | 5 +- .../app/src/main/res/values/dimens.xml | 1 + .../app/src/main/res/values/themes.xml | 3 +- 19 files changed, 280 insertions(+), 87 deletions(-) delete mode 100644 src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFrameLayout.java create mode 100644 src/android/app/src/main/java/org/citra/citra_emu/utils/InsetsHelper.java diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/cheats/ui/CheatDetailsFragment.java b/src/android/app/src/main/java/org/citra/citra_emu/features/cheats/ui/CheatDetailsFragment.java index f4833c0c5..83b3430cd 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/cheats/ui/CheatDetailsFragment.java +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/cheats/ui/CheatDetailsFragment.java @@ -11,7 +11,6 @@ import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/cheats/ui/CheatListFragment.java b/src/android/app/src/main/java/org/citra/citra_emu/features/cheats/ui/CheatListFragment.java index 6c67a31d4..552cf796e 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/cheats/ui/CheatListFragment.java +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/cheats/ui/CheatListFragment.java @@ -7,6 +7,9 @@ import android.view.ViewGroup; 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 androidx.lifecycle.ViewModelProvider; import androidx.recyclerview.widget.LinearLayoutManager; @@ -19,6 +22,9 @@ import org.citra.citra_emu.features.cheats.model.CheatsViewModel; import org.citra.citra_emu.ui.DividerItemDecoration; public class CheatListFragment extends Fragment { + private RecyclerView mRecyclerView; + private FloatingActionButton mFab; + @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @@ -28,19 +34,38 @@ public class CheatListFragment extends Fragment { @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - RecyclerView recyclerView = view.findViewById(R.id.cheat_list); - FloatingActionButton fab = view.findViewById(R.id.fab); + mRecyclerView = view.findViewById(R.id.cheat_list); + mFab = view.findViewById(R.id.fab); CheatsActivity activity = (CheatsActivity) requireActivity(); CheatsViewModel viewModel = new ViewModelProvider(activity).get(CheatsViewModel.class); - recyclerView.setAdapter(new CheatsAdapter(activity, viewModel)); - recyclerView.setLayoutManager(new LinearLayoutManager(activity)); - recyclerView.addItemDecoration(new DividerItemDecoration(activity, null)); + mRecyclerView.setAdapter(new CheatsAdapter(activity, viewModel)); + mRecyclerView.setLayoutManager(new LinearLayoutManager(activity)); + mRecyclerView.addItemDecoration(new DividerItemDecoration(activity, null)); - fab.setOnClickListener(v -> { + mFab.setOnClickListener(v -> { viewModel.startAddingCheat(); viewModel.openDetailsView(); }); + + setInsets(); + } + + private void setInsets() { + ViewCompat.setOnApplyWindowInsetsListener(mRecyclerView, (v, windowInsets) -> { + Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()); + v.setPadding(0, 0, 0, insets.bottom + getResources().getDimensionPixelSize(R.dimen.spacing_fab_list)); + + ViewGroup.MarginLayoutParams mlpFab = + (ViewGroup.MarginLayoutParams) mFab.getLayoutParams(); + int fabPadding = getResources().getDimensionPixelSize(R.dimen.spacing_large); + mlpFab.leftMargin = insets.left + fabPadding; + mlpFab.bottomMargin = insets.bottom + fabPadding; + mlpFab.rightMargin = insets.right + fabPadding; + mFab.setLayoutParams(mlpFab); + + return windowInsets; + }); } } diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/cheats/ui/CheatsActivity.java b/src/android/app/src/main/java/org/citra/citra_emu/features/cheats/ui/CheatsActivity.java index a6ab89429..5df4bc83d 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/cheats/ui/CheatsActivity.java +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/cheats/ui/CheatsActivity.java @@ -10,18 +10,26 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; +import androidx.core.graphics.Insets; import androidx.core.view.ViewCompat; +import androidx.core.view.WindowCompat; +import androidx.core.view.WindowInsetsAnimationCompat; +import androidx.core.view.WindowInsetsCompat; import androidx.lifecycle.ViewModelProvider; import androidx.slidingpanelayout.widget.SlidingPaneLayout; +import com.google.android.material.appbar.AppBarLayout; import com.google.android.material.appbar.MaterialToolbar; import org.citra.citra_emu.R; import org.citra.citra_emu.features.cheats.model.Cheat; import org.citra.citra_emu.features.cheats.model.CheatsViewModel; import org.citra.citra_emu.ui.TwoPaneOnBackPressedCallback; +import org.citra.citra_emu.utils.InsetsHelper; import org.citra.citra_emu.utils.ThemeUtil; +import java.util.List; + public class CheatsActivity extends AppCompatActivity implements SlidingPaneLayout.PanelSlideListener { private CheatsViewModel mViewModel; @@ -44,14 +52,16 @@ public class CheatsActivity extends AppCompatActivity super.onCreate(savedInstanceState); + WindowCompat.setDecorFitsSystemWindows(getWindow(), false); + mViewModel = new ViewModelProvider(this).get(CheatsViewModel.class); mViewModel.load(); setContentView(R.layout.activity_cheats); mSlidingPaneLayout = findViewById(R.id.sliding_pane_layout); - mCheatList = findViewById(R.id.cheat_list); - mCheatDetails = findViewById(R.id.cheat_details); + mCheatList = findViewById(R.id.cheat_list_container); + mCheatDetails = findViewById(R.id.cheat_details_container); mCheatListLastFocus = mCheatList; mCheatDetailsLastFocus = mCheatDetails; @@ -71,6 +81,8 @@ public class CheatsActivity extends AppCompatActivity MaterialToolbar toolbar = findViewById(R.id.toolbar_cheats); setSupportActionBar(toolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(true); + + setInsets(); } @Override @@ -153,8 +165,7 @@ public class CheatsActivity extends AppCompatActivity } } - public static void setOnFocusChangeListenerRecursively(@NonNull View view, - View.OnFocusChangeListener listener) { + public static void setOnFocusChangeListenerRecursively(@NonNull View view, View.OnFocusChangeListener listener) { view.setOnFocusChangeListener(listener); if (view instanceof ViewGroup) { @@ -165,4 +176,56 @@ public class CheatsActivity extends AppCompatActivity } } } + + private void setInsets() { + AppBarLayout appBarLayout = findViewById(R.id.appbar_cheats); + ViewCompat.setOnApplyWindowInsetsListener(mSlidingPaneLayout, (v, windowInsets) -> { + Insets barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()); + Insets keyboardInsets = windowInsets.getInsets(WindowInsetsCompat.Type.ime()); + + InsetsHelper.insetAppBar(barInsets, appBarLayout); + mSlidingPaneLayout.setPadding(barInsets.left, 0, barInsets.right, 0); + + // Set keyboard insets if the system supports smooth keyboard animations + ViewGroup.MarginLayoutParams mlpDetails = + (ViewGroup.MarginLayoutParams) mCheatDetails.getLayoutParams(); + if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.R) { + if (keyboardInsets.bottom > 0) { + mlpDetails.bottomMargin = keyboardInsets.bottom; + } else { + mlpDetails.bottomMargin = barInsets.bottom; + } + } else { + if (mlpDetails.bottomMargin == 0) { + mlpDetails.bottomMargin = barInsets.bottom; + } + } + mCheatDetails.setLayoutParams(mlpDetails); + + return windowInsets; + }); + + // Update the layout for every frame that the keyboard animates in + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) { + ViewCompat.setWindowInsetsAnimationCallback(mCheatDetails, + new WindowInsetsAnimationCompat.Callback( + WindowInsetsAnimationCompat.Callback.DISPATCH_MODE_STOP) { + int keyboardInsets = 0; + int barInsets = 0; + + @NonNull + @Override + public WindowInsetsCompat onProgress(@NonNull WindowInsetsCompat insets, + @NonNull List runningAnimations) { + ViewGroup.MarginLayoutParams mlpDetails = + (ViewGroup.MarginLayoutParams) mCheatDetails.getLayoutParams(); + keyboardInsets = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom; + barInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars()).bottom; + mlpDetails.bottomMargin = Math.max(keyboardInsets, barInsets); + mCheatDetails.setLayoutParams(mlpDetails); + return insets; + } + }); + } + } } diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsActivity.java b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsActivity.java index cfbcf5099..19aacb7f5 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsActivity.java +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsActivity.java @@ -8,13 +8,19 @@ import android.os.Bundle; import android.provider.Settings; import android.view.Menu; import android.view.MenuInflater; +import android.widget.FrameLayout; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; +import androidx.core.graphics.Insets; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowCompat; +import androidx.core.view.WindowInsetsCompat; import androidx.fragment.app.FragmentTransaction; import androidx.localbroadcastmanager.content.LocalBroadcastManager; +import com.google.android.material.appbar.AppBarLayout; import com.google.android.material.appbar.MaterialToolbar; import org.citra.citra_emu.NativeLibrary; @@ -22,6 +28,7 @@ import org.citra.citra_emu.R; import org.citra.citra_emu.utils.DirectoryInitialization; import org.citra.citra_emu.utils.DirectoryStateReceiver; import org.citra.citra_emu.utils.EmulationMenuSettings; +import org.citra.citra_emu.utils.InsetsHelper; import org.citra.citra_emu.utils.ThemeUtil; public final class SettingsActivity extends AppCompatActivity implements SettingsActivityView { @@ -44,9 +51,10 @@ public final class SettingsActivity extends AppCompatActivity implements Setting ThemeUtil.applyTheme(this); super.onCreate(savedInstanceState); - setContentView(R.layout.activity_settings); + WindowCompat.setDecorFitsSystemWindows(getWindow(), false); + Intent launcher = getIntent(); String gameID = launcher.getStringExtra(ARG_GAME_ID); String menuTag = launcher.getStringExtra(ARG_MENU_TAG); @@ -57,6 +65,8 @@ public final class SettingsActivity extends AppCompatActivity implements Setting MaterialToolbar toolbar = findViewById(R.id.toolbar_settings); setSupportActionBar(toolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(true); + + setInsets(); } @Override @@ -219,4 +229,14 @@ public final class SettingsActivity extends AppCompatActivity implements Setting private SettingsFragment getFragment() { return (SettingsFragment) getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG); } + + private void setInsets() { + AppBarLayout appBar = findViewById(R.id.appbar_settings); + FrameLayout frame = findViewById(R.id.frame_content); + ViewCompat.setOnApplyWindowInsetsListener(frame, (v, windowInsets) -> { + Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()); + InsetsHelper.insetAppBar(insets, appBar); + return windowInsets; + }); + } } diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragment.java b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragment.java index 5799dcb8d..76d4223f5 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragment.java +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragment.java @@ -8,6 +8,9 @@ import android.view.ViewGroup; 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 androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -29,6 +32,8 @@ public final class SettingsFragment extends Fragment implements SettingsFragment private SettingsAdapter mAdapter; + private RecyclerView mRecyclerView; + public static Fragment newInstance(String menuTag, String gameId) { SettingsFragment fragment = new SettingsFragment(); @@ -71,15 +76,17 @@ public final class SettingsFragment extends Fragment implements SettingsFragment public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { LinearLayoutManager manager = new LinearLayoutManager(getActivity()); - RecyclerView recyclerView = view.findViewById(R.id.list_settings); + mRecyclerView = view.findViewById(R.id.list_settings); - recyclerView.setAdapter(mAdapter); - recyclerView.setLayoutManager(manager); - recyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), null)); + mRecyclerView.setAdapter(mAdapter); + mRecyclerView.setLayoutManager(manager); + mRecyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), null)); SettingsActivityView activity = (SettingsActivityView) getActivity(); mPresenter.onViewCreated(activity.getSettings()); + + setInsets(); } @Override @@ -133,4 +140,12 @@ public final class SettingsFragment extends Fragment implements SettingsFragment public void onSettingChanged() { mActivity.onSettingChanged(); } + + private void setInsets() { + ViewCompat.setOnApplyWindowInsetsListener(mRecyclerView, (v, windowInsets) -> { + Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()); + v.setPadding(insets.left, 0, insets.right, insets.bottom); + return windowInsets; + }); + } } diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFrameLayout.java b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFrameLayout.java deleted file mode 100644 index 67bde5709..000000000 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFrameLayout.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.citra.citra_emu.features.settings.ui; - -import android.content.Context; -import android.util.AttributeSet; -import android.widget.FrameLayout; - -/** - * FrameLayout subclass with few Properties added to simplify animations. - * Don't remove the methods appearing as unused, in order not to break the menu animations - */ -public final class SettingsFrameLayout extends FrameLayout { - private float mVisibleness = 1.0f; - - public SettingsFrameLayout(Context context) { - super(context); - } - - public SettingsFrameLayout(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public SettingsFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - public SettingsFrameLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - } - - public float getYFraction() { - return getY() / getHeight(); - } - - public void setYFraction(float yFraction) { - final int height = getHeight(); - setY((height > 0) ? (yFraction * height) : -9999); - } - - public float getVisibleness() { - return mVisibleness; - } - - public void setVisibleness(float visibleness) { - setScaleX(visibleness); - setScaleY(visibleness); - setAlpha(visibleness); - } -} diff --git a/src/android/app/src/main/java/org/citra/citra_emu/ui/main/MainActivity.java b/src/android/app/src/main/java/org/citra/citra_emu/ui/main/MainActivity.java index 99bf494b2..b2833d1ac 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/ui/main/MainActivity.java +++ b/src/android/app/src/main/java/org/citra/citra_emu/ui/main/MainActivity.java @@ -6,6 +6,7 @@ import android.os.Bundle; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; +import android.widget.FrameLayout; import android.widget.Toast; import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.contract.ActivityResultContracts; @@ -15,6 +16,13 @@ import androidx.appcompat.widget.Toolbar; import androidx.core.splashscreen.SplashScreen; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import java.util.Collections; +import androidx.core.graphics.Insets; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowCompat; +import androidx.core.view.WindowInsetsCompat; + +import com.google.android.material.appbar.AppBarLayout; + import org.citra.citra_emu.NativeLibrary; import org.citra.citra_emu.R; import org.citra.citra_emu.activities.EmulationActivity; @@ -27,6 +35,7 @@ import org.citra.citra_emu.utils.BillingManager; import org.citra.citra_emu.utils.CitraDirectoryHelper; import org.citra.citra_emu.utils.DirectoryInitialization; import org.citra.citra_emu.utils.FileBrowserHelper; +import org.citra.citra_emu.utils.InsetsHelper; import org.citra.citra_emu.utils.PermissionsHandler; import org.citra.citra_emu.utils.PicassoUtils; import org.citra.citra_emu.utils.StartupHandler; @@ -112,6 +121,8 @@ public final class MainActivity extends AppCompatActivity implements MainView { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); + WindowCompat.setDecorFitsSystemWindows(getWindow(), false); + findViews(); setSupportActionBar(mToolbar); @@ -136,6 +147,8 @@ public final class MainActivity extends AppCompatActivity implements MainView { // Dismiss previous notifications (should not happen unless a crash occurred) EmulationActivity.tryDismissRunningNotification(this); + + setInsets(); } @Override @@ -276,4 +289,15 @@ public final class MainActivity extends AppCompatActivity implements MainView { public static void invokePremiumBilling(Runnable callback) { mBillingManager.invokePremiumBilling(callback); } + + private void setInsets() { + AppBarLayout appBar = findViewById(R.id.appbar); + FrameLayout frame = findViewById(R.id.games_platform_frame); + ViewCompat.setOnApplyWindowInsetsListener(frame, (v, windowInsets) -> { + Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()); + InsetsHelper.insetAppBar(insets, appBar); + frame.setPadding(insets.left, 0, insets.right, 0); + return windowInsets; + }); + } } diff --git a/src/android/app/src/main/java/org/citra/citra_emu/ui/platform/PlatformGamesFragment.java b/src/android/app/src/main/java/org/citra/citra_emu/ui/platform/PlatformGamesFragment.java index 4d863d9f9..2fd91e7ca 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/ui/platform/PlatformGamesFragment.java +++ b/src/android/app/src/main/java/org/citra/citra_emu/ui/platform/PlatformGamesFragment.java @@ -7,7 +7,9 @@ import android.view.View; import android.view.ViewGroup; import android.widget.TextView; -import androidx.core.content.ContextCompat; +import androidx.core.graphics.Insets; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager; @@ -68,6 +70,8 @@ public final class PlatformGamesFragment extends Fragment implements PlatformGam pullToRefresh.setProgressBackgroundColorSchemeColor(MaterialColors.getColor(pullToRefresh, R.attr.colorPrimary)); pullToRefresh.setColorSchemeColors(MaterialColors.getColor(pullToRefresh, R.attr.colorOnPrimary)); + + setInsets(); } @Override @@ -92,4 +96,12 @@ public final class PlatformGamesFragment extends Fragment implements PlatformGam mRecyclerView = root.findViewById(R.id.grid_games); mTextView = root.findViewById(R.id.gamelist_empty_text); } + + private void setInsets() { + ViewCompat.setOnApplyWindowInsetsListener(mRecyclerView, (v, windowInsets) -> { + Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()); + v.setPadding(0, 0, 0, insets.bottom); + return windowInsets; + }); + } } diff --git a/src/android/app/src/main/java/org/citra/citra_emu/utils/InsetsHelper.java b/src/android/app/src/main/java/org/citra/citra_emu/utils/InsetsHelper.java new file mode 100644 index 000000000..55f8a463e --- /dev/null +++ b/src/android/app/src/main/java/org/citra/citra_emu/utils/InsetsHelper.java @@ -0,0 +1,33 @@ +package org.citra.citra_emu.utils; + +import android.content.Context; +import android.content.res.Resources; +import android.view.ViewGroup; + +import androidx.core.graphics.Insets; + +import com.google.android.material.appbar.AppBarLayout; + +public class InsetsHelper { + public static final int THREE_BUTTON_NAVIGATION = 0; + public static final int TWO_BUTTON_NAVIGATION = 1; + public static final int GESTURE_NAVIGATION = 2; + + public static void insetAppBar(Insets insets, AppBarLayout appBarLayout) + { + ViewGroup.MarginLayoutParams mlpAppBar = + (ViewGroup.MarginLayoutParams) appBarLayout.getLayoutParams(); + mlpAppBar.leftMargin = insets.left; + mlpAppBar.rightMargin = insets.right; + appBarLayout.setLayoutParams(mlpAppBar); + } + + public static int getSystemGestureType(Context context) { + Resources resources = context.getResources(); + int resourceId = resources.getIdentifier("config_navBarInteractionMode", "integer", "android"); + if (resourceId != 0) { + return resources.getInteger(resourceId); + } + return 0; + } +} diff --git a/src/android/app/src/main/java/org/citra/citra_emu/utils/ThemeUtil.java b/src/android/app/src/main/java/org/citra/citra_emu/utils/ThemeUtil.java index 98556514a..fbccc9df1 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/utils/ThemeUtil.java +++ b/src/android/app/src/main/java/org/citra/citra_emu/utils/ThemeUtil.java @@ -1,21 +1,31 @@ package org.citra.citra_emu.utils; +import android.app.Activity; import android.content.SharedPreferences; import android.content.res.Configuration; +import android.graphics.Color; import android.os.Build; import android.preference.PreferenceManager; +import androidx.annotation.ColorInt; +import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatDelegate; +import androidx.core.content.ContextCompat; import androidx.core.view.WindowCompat; import androidx.core.view.WindowInsetsControllerCompat; +import com.google.android.material.color.MaterialColors; + import org.citra.citra_emu.CitraApplication; +import org.citra.citra_emu.R; import org.citra.citra_emu.features.settings.utils.SettingsFile; public class ThemeUtil { private static SharedPreferences mPreferences = PreferenceManager.getDefaultSharedPreferences(CitraApplication.getAppContext()); + public static final float NAV_BAR_ALPHA = 0.9f; + private static void applyTheme(int designValue, AppCompatActivity activity) { switch (designValue) { case 0: @@ -34,9 +44,40 @@ public class ThemeUtil { int systemReportedThemeMode = activity.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; WindowInsetsControllerCompat windowController = WindowCompat.getInsetsController(activity.getWindow(), activity.getWindow().getDecorView()); windowController.setAppearanceLightStatusBars(systemReportedThemeMode == Configuration.UI_MODE_NIGHT_NO); + windowController.setAppearanceLightNavigationBars(systemReportedThemeMode == Configuration.UI_MODE_NIGHT_NO); + + setNavigationBarColor(activity, MaterialColors.getColor(activity.getWindow().getDecorView(), R.attr.colorSurface)); } public static void applyTheme(AppCompatActivity activity) { applyTheme(mPreferences.getInt(SettingsFile.KEY_DESIGN, 0), activity); } + + public static void setNavigationBarColor(@NonNull Activity activity, @ColorInt int color) { + int gestureType = InsetsHelper.getSystemGestureType(activity.getApplicationContext()); + int orientation = activity.getResources().getConfiguration().orientation; + + // Use a solid color when the navigation bar is on the left/right edge of the screen + if ((gestureType == InsetsHelper.THREE_BUTTON_NAVIGATION || + gestureType == InsetsHelper.TWO_BUTTON_NAVIGATION) && + orientation == Configuration.ORIENTATION_LANDSCAPE) { + activity.getWindow().setNavigationBarColor(color); + } else if (gestureType == InsetsHelper.THREE_BUTTON_NAVIGATION || + gestureType == InsetsHelper.TWO_BUTTON_NAVIGATION) { + // Use semi-transparent color when in portrait mode with three/two button navigation to + // partially see list items behind the navigation bar + activity.getWindow().setNavigationBarColor(ThemeUtil.getColorWithOpacity(color, NAV_BAR_ALPHA)); + } else { + // Use transparent color when using gesture navigation + activity.getWindow().setNavigationBarColor( + ContextCompat.getColor(activity.getApplicationContext(), + android.R.color.transparent)); + } + } + + @ColorInt + public static int getColorWithOpacity(@ColorInt int color, float alphaFactor) { + return Color.argb(Math.round(alphaFactor * Color.alpha(color)), Color.red(color), + Color.green(color), Color.blue(color)); + } } diff --git a/src/android/app/src/main/res/layout/activity_cheats.xml b/src/android/app/src/main/res/layout/activity_cheats.xml index 0b6204b6d..7b63b11c3 100644 --- a/src/android/app/src/main/res/layout/activity_cheats.xml +++ b/src/android/app/src/main/res/layout/activity_cheats.xml @@ -2,6 +2,7 @@ @@ -17,14 +18,12 @@ android:id="@+id/appbar_cheats" android:layout_width="match_parent" android:layout_height="wrap_content" - app:elevation="0dp" - app:liftOnScroll="false"> + android:fitsSystemWindows="true"> + android:layout_height="?attr/actionBarSize" /> @@ -40,18 +39,20 @@ app:layout_constraintTop_toBottomOf="@id/coordinator_cheats"> + android:id="@+id/cheat_list_container" + android:name="org.citra.citra_emu.features.cheats.ui.CheatListFragment" + tools:layout="@layout/fragment_cheat_list" /> + android:id="@+id/cheat_details_container" + android:name="org.citra.citra_emu.features.cheats.ui.CheatDetailsFragment" + tools:layout="@layout/fragment_cheat_details" /> diff --git a/src/android/app/src/main/res/layout/activity_main.xml b/src/android/app/src/main/res/layout/activity_main.xml index 1f737c321..c567abe69 100644 --- a/src/android/app/src/main/res/layout/activity_main.xml +++ b/src/android/app/src/main/res/layout/activity_main.xml @@ -9,13 +9,14 @@ + android:layout_height="wrap_content" + android:fitsSystemWindows="true" + app:liftOnScrollTargetViewId="@id/grid_games"> diff --git a/src/android/app/src/main/res/layout/activity_settings.xml b/src/android/app/src/main/res/layout/activity_settings.xml index 9da84faea..c57abfd0a 100644 --- a/src/android/app/src/main/res/layout/activity_settings.xml +++ b/src/android/app/src/main/res/layout/activity_settings.xml @@ -8,13 +8,13 @@ + android:layout_height="wrap_content" + android:fitsSystemWindows="true"> + android:layout_height="?attr/actionBarSize" /> diff --git a/src/android/app/src/main/res/layout/fragment_cheat_list.xml b/src/android/app/src/main/res/layout/fragment_cheat_list.xml index 679a49c28..04bf76ffe 100644 --- a/src/android/app/src/main/res/layout/fragment_cheat_list.xml +++ b/src/android/app/src/main/res/layout/fragment_cheat_list.xml @@ -9,6 +9,7 @@ android:id="@+id/cheat_list" android:layout_width="match_parent" android:layout_height="0dp" + android:clipToPadding="false" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" @@ -21,7 +22,7 @@ android:src="@drawable/ic_add" android:contentDescription="@string/cheats_add" android:layout_margin="16dp" - app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent" app:layout_constraintBottom_toBottomOf="parent" /> diff --git a/src/android/app/src/main/res/layout/fragment_grid.xml b/src/android/app/src/main/res/layout/fragment_grid.xml index 5978bf998..3be511789 100644 --- a/src/android/app/src/main/res/layout/fragment_grid.xml +++ b/src/android/app/src/main/res/layout/fragment_grid.xml @@ -26,6 +26,7 @@ android:id="@+id/grid_games" android:layout_width="match_parent" android:layout_height="match_parent" + android:clipToPadding="false" tools:listitem="@layout/card_game" /> diff --git a/src/android/app/src/main/res/layout/fragment_settings.xml b/src/android/app/src/main/res/layout/fragment_settings.xml index f8724552c..46ca12e93 100644 --- a/src/android/app/src/main/res/layout/fragment_settings.xml +++ b/src/android/app/src/main/res/layout/fragment_settings.xml @@ -1,6 +1,7 @@ - @@ -8,6 +9,7 @@ + android:layout_height="match_parent" + android:clipToPadding="false" /> - + diff --git a/src/android/app/src/main/res/layout/list_item_cheat.xml b/src/android/app/src/main/res/layout/list_item_cheat.xml index d31ae63f9..5afa11794 100644 --- a/src/android/app/src/main/res/layout/list_item_cheat.xml +++ b/src/android/app/src/main/res/layout/list_item_cheat.xml @@ -24,11 +24,12 @@ 4dp 12dp 16dp + 80dp 20dp diff --git a/src/android/app/src/main/res/values/themes.xml b/src/android/app/src/main/res/values/themes.xml index 4a412948a..d1f1522d9 100644 --- a/src/android/app/src/main/res/values/themes.xml +++ b/src/android/app/src/main/res/values/themes.xml @@ -38,7 +38,8 @@ @color/citra_inversePrimary @drawable/ic_back - @color/citra_surface + @android:color/transparent + @android:color/transparent @color/citra_primary @style/CitraMaterialDialog @style/CitraShapedPopup