@@ -9,21 +9,20 @@
import android.provider.Settings;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
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.lifecycle.ViewModelProvider;

import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.appbar.CollapsingToolbarLayout;
import com.google.android.material.appbar.MaterialToolbar;
import com.google.android.material.color.MaterialColors;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;

import org.dolphinemu.dolphinemu.NativeLibrary;
@@ -49,6 +48,8 @@ public final class SettingsActivity extends AppCompatActivity implements Setting

private CollapsingToolbarLayout mToolbarLayout;

private ActivitySettingsBinding mBinding;

public static void launch(Context context, MenuTag menuTag, String gameId, int revision,
boolean isWii)
{
@@ -82,8 +83,8 @@ protected void onCreate(Bundle savedInstanceState)
MainPresenter.skipRescanningLibrary();
}

ActivitySettingsBinding binding = ActivitySettingsBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
mBinding = ActivitySettingsBinding.inflate(getLayoutInflater());
setContentView(mBinding.getRoot());

WindowCompat.setDecorFitsSystemWindows(getWindow(), false);

@@ -98,17 +99,16 @@ protected void onCreate(Bundle savedInstanceState)
mPresenter = new SettingsActivityPresenter(this, getSettings());
mPresenter.onCreate(savedInstanceState, menuTag, gameID, revision, isWii, this);

mToolbarLayout = binding.toolbarSettingsLayout;
setSupportActionBar(binding.toolbarSettings);
mToolbarLayout = mBinding.toolbarSettingsLayout;
setSupportActionBar(mBinding.toolbarSettings);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);

// TODO: Remove this when CollapsingToolbarLayouts are fixed by Google
// https://github.com/material-components/material-components-android/issues/1310
ViewCompat.setOnApplyWindowInsetsListener(mToolbarLayout, null);

InsetsHelper.setUpSettingsLayout(this, binding.appbarSettings, binding.frameContentSettings,
binding.workaroundView);
ThemeHelper.enableScrollTint(this, binding.toolbarSettings, binding.appbarSettings);
setInsets();
ThemeHelper.enableScrollTint(this, mBinding.toolbarSettings, mBinding.appbarSettings);
}

@Override
@@ -344,4 +344,22 @@ public void setToolbarTitle(String title)
{
mToolbarLayout.setTitle(title);
}

private void setInsets()
{
ViewCompat.setOnApplyWindowInsetsListener(mBinding.appbarSettings, (v, windowInsets) ->
{
Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());

InsetsHelper.insetAppBar(insets, mBinding.appbarSettings);

mBinding.frameContentSettings.setPadding(insets.left, 0, insets.right, 0);

InsetsHelper.applyNavbarWorkaround(insets.bottom, mBinding.workaroundView);
ThemeHelper.setNavigationBarColor(this,
MaterialColors.getColor(mBinding.appbarSettings, R.attr.colorSurface));

return windowInsets;
});
}
}
@@ -10,6 +10,9 @@

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;
@@ -20,8 +23,6 @@
import org.dolphinemu.dolphinemu.databinding.FragmentSettingsBinding;
import org.dolphinemu.dolphinemu.features.settings.model.Settings;
import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem;
import org.dolphinemu.dolphinemu.utils.InsetsHelper;
import org.dolphinemu.dolphinemu.utils.Log;

import java.util.ArrayList;
import java.util.HashMap;
@@ -149,7 +150,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
divider.setLastItemDecorated(false);
recyclerView.addItemDecoration(divider);

InsetsHelper.setUpList(getContext(), recyclerView);
setInsets();

SettingsActivityView activity = (SettingsActivityView) getActivity();
mPresenter.onViewCreated(menuTag, activity.getSettings());
@@ -246,4 +247,15 @@ public void onExtensionSettingChanged(MenuTag menuTag, int value)
{
mActivity.onExtensionSettingChanged(menuTag, value);
}

private void setInsets()
{
ViewCompat.setOnApplyWindowInsetsListener(mBinding.listSettings, (v, windowInsets) ->
{
Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(0, 0, 0,
insets.bottom + getResources().getDimensionPixelSize(R.dimen.spacing_list));
return windowInsets;
});
}
}
@@ -9,14 +9,19 @@
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.splashscreen.SplashScreen;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;

import com.google.android.material.color.MaterialColors;
import com.google.android.material.tabs.TabLayout;

import org.dolphinemu.dolphinemu.R;
@@ -68,8 +73,7 @@ protected void onCreate(Bundle savedInstanceState)
setContentView(mBinding.getRoot());

WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
InsetsHelper.setUpMainLayout(this, mBinding.appbarMain, mBinding.buttonAddDirectory,
mBinding.pagerPlatforms, mBinding.workaroundView);
setInsets();
ThemeHelper.enableStatusBarScrollTint(this, mBinding.appbarMain);

setSupportActionBar(mBinding.toolbarMain);
@@ -398,4 +402,30 @@ private void checkTheme()
{
ThemeHelper.setCorrectTheme(this);
}

private void setInsets()
{
ViewCompat.setOnApplyWindowInsetsListener(mBinding.appbarMain, (v, windowInsets) ->
{
Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());

InsetsHelper.insetAppBar(insets, mBinding.appbarMain);

ViewGroup.MarginLayoutParams mlpFab =
(ViewGroup.MarginLayoutParams) mBinding.buttonAddDirectory.getLayoutParams();
int fabPadding = getResources().getDimensionPixelSize(R.dimen.spacing_large);
mlpFab.leftMargin = insets.left + fabPadding;
mlpFab.bottomMargin = insets.bottom + fabPadding;
mlpFab.rightMargin = insets.right + fabPadding;
mBinding.buttonAddDirectory.setLayoutParams(mlpFab);

mBinding.pagerPlatforms.setPadding(insets.left, 0, insets.right, 0);

InsetsHelper.applyNavbarWorkaround(insets.bottom, mBinding.workaroundView);
ThemeHelper.setNavigationBarColor(this,
MaterialColors.getColor(mBinding.appbarMain, R.attr.colorSurface));

return windowInsets;
});
}
}
@@ -10,6 +10,9 @@

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.GridLayoutManager;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
@@ -20,7 +23,6 @@
import org.dolphinemu.dolphinemu.adapters.GameAdapter;
import org.dolphinemu.dolphinemu.databinding.FragmentGridBinding;
import org.dolphinemu.dolphinemu.services.GameFileCacheManager;
import org.dolphinemu.dolphinemu.utils.InsetsHelper;

public final class PlatformGamesFragment extends Fragment implements PlatformGamesView
{
@@ -102,7 +104,7 @@ public void onGlobalLayout()

mSwipeRefresh.setOnRefreshListener(mOnRefreshListener);

InsetsHelper.setUpList(getContext(), mBinding.gridGames);
setInsets();

setRefreshing(GameFileCacheManager.isLoadingOrRescanning());

@@ -158,4 +160,16 @@ public void setRefreshing(boolean refreshing)
{
mBinding.swipeRefresh.setRefreshing(refreshing);
}

private void setInsets()
{
ViewCompat.setOnApplyWindowInsetsListener(mBinding.gridGames, (v, windowInsets) ->
{
Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(0, 0, 0,
insets.bottom + getResources().getDimensionPixelSize(R.dimen.spacing_list) +
getResources().getDimensionPixelSize(R.dimen.spacing_fab));
return windowInsets;
});
}
}
@@ -4,195 +4,20 @@
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;
import android.widget.FrameLayout;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsAnimationCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.core.widget.NestedScrollView;
import androidx.recyclerview.widget.RecyclerView;
import androidx.slidingpanelayout.widget.SlidingPaneLayout;
import androidx.viewpager.widget.ViewPager;

import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.color.MaterialColors;
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton;

import org.dolphinemu.dolphinemu.R;

import java.util.List;

public class InsetsHelper
{
public static final int FAB_INSET = 16;
public static final int EXTRA_NAV_INSET = 32;

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 int dpToPx(Context context, int dp)
{
return (int) (dp * ((float) context.getResources().getDisplayMetrics().densityDpi /
DisplayMetrics.DENSITY_DEFAULT) + 0.5f);
}

public static void setUpAppBarWithScrollView(AppCompatActivity activity,
AppBarLayout appBarLayout, NestedScrollView nestedScrollView, View workaroundView)
{
ViewCompat.setOnApplyWindowInsetsListener(appBarLayout, (v, windowInsets) ->
{
Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());

insetAppBar(insets, appBarLayout);

nestedScrollView.setPadding(insets.left, 0, insets.right, insets.bottom);

applyWorkaround(insets.bottom, workaroundView);

ThemeHelper.setNavigationBarColor(activity,
MaterialColors.getColor(appBarLayout, R.attr.colorSurface));

return windowInsets;
});
}

public static void setUpList(Context context, RecyclerView recyclerView)
{
ViewCompat.setOnApplyWindowInsetsListener(recyclerView, (v, windowInsets) ->
{
Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(0, 0, 0, insets.bottom + dpToPx(context, EXTRA_NAV_INSET));

return windowInsets;
});
}

public static void setUpMainLayout(AppCompatActivity activity, AppBarLayout appBarLayout,
ExtendedFloatingActionButton fab, ViewPager viewPager, View workaroundView)
{
ViewCompat.setOnApplyWindowInsetsListener(appBarLayout, (v, windowInsets) ->
{
Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());

insetAppBar(insets, appBarLayout);

ViewGroup.MarginLayoutParams mlpFab = (ViewGroup.MarginLayoutParams) fab.getLayoutParams();
int fabPadding =
InsetsHelper.dpToPx(activity.getApplicationContext(), FAB_INSET);
mlpFab.leftMargin = insets.left + fabPadding;
mlpFab.bottomMargin = insets.bottom + fabPadding;
mlpFab.rightMargin = insets.right + fabPadding;
fab.setLayoutParams(mlpFab);

viewPager.setPadding(insets.left, 0, insets.right, 0);

applyWorkaround(insets.bottom, workaroundView);

ThemeHelper.setNavigationBarColor(activity,
MaterialColors.getColor(appBarLayout, R.attr.colorSurface));

return windowInsets;
});
}

public static void setUpSettingsLayout(AppCompatActivity activity,
AppBarLayout appBarLayout, FrameLayout frameLayout, View workaroundView)
{
ViewCompat.setOnApplyWindowInsetsListener(appBarLayout, (v, windowInsets) ->
{
Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());

insetAppBar(insets, appBarLayout);

frameLayout.setPadding(insets.left, 0, insets.right, 0);

applyWorkaround(insets.bottom, workaroundView);

ThemeHelper.setNavigationBarColor(activity,
MaterialColors.getColor(appBarLayout, R.attr.colorSurface));

return windowInsets;
});
}

public static void setUpCheatsLayout(AppCompatActivity activity, AppBarLayout appBarLayout,
SlidingPaneLayout slidingPaneLayout, View cheatDetails, View workaroundView)
{
ViewCompat.setOnApplyWindowInsetsListener(appBarLayout, (v, windowInsets) ->
{
Insets barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
Insets keyboardInsets = windowInsets.getInsets(WindowInsetsCompat.Type.ime());

insetAppBar(barInsets, appBarLayout);

slidingPaneLayout.setPadding(barInsets.left, 0, barInsets.right, 0);

// Set keyboard insets if the system supports smooth keyboard animations
ViewGroup.MarginLayoutParams mlpDetails =
(ViewGroup.MarginLayoutParams) cheatDetails.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;
}
}
cheatDetails.setLayoutParams(mlpDetails);

applyWorkaround(barInsets.bottom, workaroundView);

ThemeHelper.setNavigationBarColor(activity,
MaterialColors.getColor(appBarLayout, R.attr.colorSurface));

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(cheatDetails,
new WindowInsetsAnimationCompat.Callback(
WindowInsetsAnimationCompat.Callback.DISPATCH_MODE_STOP)
{
int keyboardInsets = 0;
int barInsets = 0;

@NonNull
@Override
public WindowInsetsCompat onProgress(@NonNull WindowInsetsCompat insets,
@NonNull List<WindowInsetsAnimationCompat> runningAnimations)
{
ViewGroup.MarginLayoutParams mlpDetails =
(ViewGroup.MarginLayoutParams) cheatDetails.getLayoutParams();
keyboardInsets = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom;
barInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars()).bottom;
mlpDetails.bottomMargin = Math.max(keyboardInsets, barInsets);
cheatDetails.setLayoutParams(mlpDetails);
return insets;
}
});
}
}

private static void insetAppBar(Insets insets, AppBarLayout appBarLayout)
public static void insetAppBar(Insets insets, AppBarLayout appBarLayout)
{
ViewGroup.MarginLayoutParams mlpAppBar =
(ViewGroup.MarginLayoutParams) appBarLayout.getLayoutParams();
@@ -204,7 +29,7 @@ private static void insetAppBar(Insets insets, AppBarLayout appBarLayout)

// Workaround for a bug on Android 13 that allows users to interact with UI behind the
// navigation bar https://issuetracker.google.com/issues/248761842
private static void applyWorkaround(int bottomInset, View workaroundView)
public static void applyNavbarWorkaround(int bottomInset, View workaroundView)
{
if (bottomInset > 0)
{
@@ -2,6 +2,9 @@
<dimen name="spacing_small">4dp</dimen>
<dimen name="spacing_medlarge">12dp</dimen>
<dimen name="spacing_large">16dp</dimen>
<dimen name="spacing_xtralarge">32dp</dimen>
<dimen name="spacing_list">64dp</dimen>
<dimen name="spacing_fab">72dp</dimen>
<dimen name="menu_width">256dp</dimen>
<dimen name="card_width">135dp</dimen>
</resources>