citra_android: Implement edge-to-edge (#6349)

This commit is contained in:
Charles Lombardo 2023-03-23 14:36:54 -04:00 committed by GitHub
parent 8d563d37b4
commit 343717e683
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 280 additions and 87 deletions

View File

@ -11,7 +11,6 @@ import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;

View File

@ -7,6 +7,9 @@ import android.view.ViewGroup;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; 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.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager; 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; import org.citra.citra_emu.ui.DividerItemDecoration;
public class CheatListFragment extends Fragment { public class CheatListFragment extends Fragment {
private RecyclerView mRecyclerView;
private FloatingActionButton mFab;
@Nullable @Nullable
@Override @Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@ -28,19 +34,38 @@ public class CheatListFragment extends Fragment {
@Override @Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
RecyclerView recyclerView = view.findViewById(R.id.cheat_list); mRecyclerView = view.findViewById(R.id.cheat_list);
FloatingActionButton fab = view.findViewById(R.id.fab); mFab = view.findViewById(R.id.fab);
CheatsActivity activity = (CheatsActivity) requireActivity(); CheatsActivity activity = (CheatsActivity) requireActivity();
CheatsViewModel viewModel = new ViewModelProvider(activity).get(CheatsViewModel.class); CheatsViewModel viewModel = new ViewModelProvider(activity).get(CheatsViewModel.class);
recyclerView.setAdapter(new CheatsAdapter(activity, viewModel)); mRecyclerView.setAdapter(new CheatsAdapter(activity, viewModel));
recyclerView.setLayoutManager(new LinearLayoutManager(activity)); mRecyclerView.setLayoutManager(new LinearLayoutManager(activity));
recyclerView.addItemDecoration(new DividerItemDecoration(activity, null)); mRecyclerView.addItemDecoration(new DividerItemDecoration(activity, null));
fab.setOnClickListener(v -> { mFab.setOnClickListener(v -> {
viewModel.startAddingCheat(); viewModel.startAddingCheat();
viewModel.openDetailsView(); 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;
});
} }
} }

View File

@ -10,18 +10,26 @@ import android.view.ViewGroup;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat; 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.lifecycle.ViewModelProvider;
import androidx.slidingpanelayout.widget.SlidingPaneLayout; import androidx.slidingpanelayout.widget.SlidingPaneLayout;
import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.appbar.MaterialToolbar; import com.google.android.material.appbar.MaterialToolbar;
import org.citra.citra_emu.R; import org.citra.citra_emu.R;
import org.citra.citra_emu.features.cheats.model.Cheat; import org.citra.citra_emu.features.cheats.model.Cheat;
import org.citra.citra_emu.features.cheats.model.CheatsViewModel; import org.citra.citra_emu.features.cheats.model.CheatsViewModel;
import org.citra.citra_emu.ui.TwoPaneOnBackPressedCallback; import org.citra.citra_emu.ui.TwoPaneOnBackPressedCallback;
import org.citra.citra_emu.utils.InsetsHelper;
import org.citra.citra_emu.utils.ThemeUtil; import org.citra.citra_emu.utils.ThemeUtil;
import java.util.List;
public class CheatsActivity extends AppCompatActivity public class CheatsActivity extends AppCompatActivity
implements SlidingPaneLayout.PanelSlideListener { implements SlidingPaneLayout.PanelSlideListener {
private CheatsViewModel mViewModel; private CheatsViewModel mViewModel;
@ -44,14 +52,16 @@ public class CheatsActivity extends AppCompatActivity
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
mViewModel = new ViewModelProvider(this).get(CheatsViewModel.class); mViewModel = new ViewModelProvider(this).get(CheatsViewModel.class);
mViewModel.load(); mViewModel.load();
setContentView(R.layout.activity_cheats); setContentView(R.layout.activity_cheats);
mSlidingPaneLayout = findViewById(R.id.sliding_pane_layout); mSlidingPaneLayout = findViewById(R.id.sliding_pane_layout);
mCheatList = findViewById(R.id.cheat_list); mCheatList = findViewById(R.id.cheat_list_container);
mCheatDetails = findViewById(R.id.cheat_details); mCheatDetails = findViewById(R.id.cheat_details_container);
mCheatListLastFocus = mCheatList; mCheatListLastFocus = mCheatList;
mCheatDetailsLastFocus = mCheatDetails; mCheatDetailsLastFocus = mCheatDetails;
@ -71,6 +81,8 @@ public class CheatsActivity extends AppCompatActivity
MaterialToolbar toolbar = findViewById(R.id.toolbar_cheats); MaterialToolbar toolbar = findViewById(R.id.toolbar_cheats);
setSupportActionBar(toolbar); setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
setInsets();
} }
@Override @Override
@ -153,8 +165,7 @@ public class CheatsActivity extends AppCompatActivity
} }
} }
public static void setOnFocusChangeListenerRecursively(@NonNull View view, public static void setOnFocusChangeListenerRecursively(@NonNull View view, View.OnFocusChangeListener listener) {
View.OnFocusChangeListener listener) {
view.setOnFocusChangeListener(listener); view.setOnFocusChangeListener(listener);
if (view instanceof ViewGroup) { 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<WindowInsetsAnimationCompat> 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;
}
});
}
}
} }

View File

@ -8,13 +8,19 @@ import android.os.Bundle;
import android.provider.Settings; import android.provider.Settings;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
import android.widget.FrameLayout;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity; 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.fragment.app.FragmentTransaction;
import androidx.localbroadcastmanager.content.LocalBroadcastManager; import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.appbar.MaterialToolbar; import com.google.android.material.appbar.MaterialToolbar;
import org.citra.citra_emu.NativeLibrary; 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.DirectoryInitialization;
import org.citra.citra_emu.utils.DirectoryStateReceiver; import org.citra.citra_emu.utils.DirectoryStateReceiver;
import org.citra.citra_emu.utils.EmulationMenuSettings; import org.citra.citra_emu.utils.EmulationMenuSettings;
import org.citra.citra_emu.utils.InsetsHelper;
import org.citra.citra_emu.utils.ThemeUtil; import org.citra.citra_emu.utils.ThemeUtil;
public final class SettingsActivity extends AppCompatActivity implements SettingsActivityView { public final class SettingsActivity extends AppCompatActivity implements SettingsActivityView {
@ -44,9 +51,10 @@ public final class SettingsActivity extends AppCompatActivity implements Setting
ThemeUtil.applyTheme(this); ThemeUtil.applyTheme(this);
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_settings); setContentView(R.layout.activity_settings);
WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
Intent launcher = getIntent(); Intent launcher = getIntent();
String gameID = launcher.getStringExtra(ARG_GAME_ID); String gameID = launcher.getStringExtra(ARG_GAME_ID);
String menuTag = launcher.getStringExtra(ARG_MENU_TAG); 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); MaterialToolbar toolbar = findViewById(R.id.toolbar_settings);
setSupportActionBar(toolbar); setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
setInsets();
} }
@Override @Override
@ -219,4 +229,14 @@ public final class SettingsActivity extends AppCompatActivity implements Setting
private SettingsFragment getFragment() { private SettingsFragment getFragment() {
return (SettingsFragment) getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG); 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;
});
}
} }

View File

@ -8,6 +8,9 @@ import android.view.ViewGroup;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; 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.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
@ -29,6 +32,8 @@ public final class SettingsFragment extends Fragment implements SettingsFragment
private SettingsAdapter mAdapter; private SettingsAdapter mAdapter;
private RecyclerView mRecyclerView;
public static Fragment newInstance(String menuTag, String gameId) { public static Fragment newInstance(String menuTag, String gameId) {
SettingsFragment fragment = new SettingsFragment(); SettingsFragment fragment = new SettingsFragment();
@ -71,15 +76,17 @@ public final class SettingsFragment extends Fragment implements SettingsFragment
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
LinearLayoutManager manager = new LinearLayoutManager(getActivity()); LinearLayoutManager manager = new LinearLayoutManager(getActivity());
RecyclerView recyclerView = view.findViewById(R.id.list_settings); mRecyclerView = view.findViewById(R.id.list_settings);
recyclerView.setAdapter(mAdapter); mRecyclerView.setAdapter(mAdapter);
recyclerView.setLayoutManager(manager); mRecyclerView.setLayoutManager(manager);
recyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), null)); mRecyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), null));
SettingsActivityView activity = (SettingsActivityView) getActivity(); SettingsActivityView activity = (SettingsActivityView) getActivity();
mPresenter.onViewCreated(activity.getSettings()); mPresenter.onViewCreated(activity.getSettings());
setInsets();
} }
@Override @Override
@ -133,4 +140,12 @@ public final class SettingsFragment extends Fragment implements SettingsFragment
public void onSettingChanged() { public void onSettingChanged() {
mActivity.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;
});
}
} }

View File

@ -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);
}
}

View File

@ -6,6 +6,7 @@ import android.os.Bundle;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
import android.view.MenuItem; import android.view.MenuItem;
import android.widget.FrameLayout;
import android.widget.Toast; import android.widget.Toast;
import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts; import androidx.activity.result.contract.ActivityResultContracts;
@ -15,6 +16,13 @@ import androidx.appcompat.widget.Toolbar;
import androidx.core.splashscreen.SplashScreen; import androidx.core.splashscreen.SplashScreen;
import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import java.util.Collections; 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.NativeLibrary;
import org.citra.citra_emu.R; import org.citra.citra_emu.R;
import org.citra.citra_emu.activities.EmulationActivity; 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.CitraDirectoryHelper;
import org.citra.citra_emu.utils.DirectoryInitialization; import org.citra.citra_emu.utils.DirectoryInitialization;
import org.citra.citra_emu.utils.FileBrowserHelper; 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.PermissionsHandler;
import org.citra.citra_emu.utils.PicassoUtils; import org.citra.citra_emu.utils.PicassoUtils;
import org.citra.citra_emu.utils.StartupHandler; import org.citra.citra_emu.utils.StartupHandler;
@ -112,6 +121,8 @@ public final class MainActivity extends AppCompatActivity implements MainView {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); setContentView(R.layout.activity_main);
WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
findViews(); findViews();
setSupportActionBar(mToolbar); setSupportActionBar(mToolbar);
@ -136,6 +147,8 @@ public final class MainActivity extends AppCompatActivity implements MainView {
// Dismiss previous notifications (should not happen unless a crash occurred) // Dismiss previous notifications (should not happen unless a crash occurred)
EmulationActivity.tryDismissRunningNotification(this); EmulationActivity.tryDismissRunningNotification(this);
setInsets();
} }
@Override @Override
@ -276,4 +289,15 @@ public final class MainActivity extends AppCompatActivity implements MainView {
public static void invokePremiumBilling(Runnable callback) { public static void invokePremiumBilling(Runnable callback) {
mBillingManager.invokePremiumBilling(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;
});
}
} }

View File

@ -7,7 +7,9 @@ import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.TextView; 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.fragment.app.Fragment;
import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager; 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.setProgressBackgroundColorSchemeColor(MaterialColors.getColor(pullToRefresh, R.attr.colorPrimary));
pullToRefresh.setColorSchemeColors(MaterialColors.getColor(pullToRefresh, R.attr.colorOnPrimary)); pullToRefresh.setColorSchemeColors(MaterialColors.getColor(pullToRefresh, R.attr.colorOnPrimary));
setInsets();
} }
@Override @Override
@ -92,4 +96,12 @@ public final class PlatformGamesFragment extends Fragment implements PlatformGam
mRecyclerView = root.findViewById(R.id.grid_games); mRecyclerView = root.findViewById(R.id.grid_games);
mTextView = root.findViewById(R.id.gamelist_empty_text); 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;
});
}
} }

View File

@ -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;
}
}

View File

@ -1,21 +1,31 @@
package org.citra.citra_emu.utils; package org.citra.citra_emu.utils;
import android.app.Activity;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.graphics.Color;
import android.os.Build; import android.os.Build;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate; import androidx.appcompat.app.AppCompatDelegate;
import androidx.core.content.ContextCompat;
import androidx.core.view.WindowCompat; import androidx.core.view.WindowCompat;
import androidx.core.view.WindowInsetsControllerCompat; import androidx.core.view.WindowInsetsControllerCompat;
import com.google.android.material.color.MaterialColors;
import org.citra.citra_emu.CitraApplication; import org.citra.citra_emu.CitraApplication;
import org.citra.citra_emu.R;
import org.citra.citra_emu.features.settings.utils.SettingsFile; import org.citra.citra_emu.features.settings.utils.SettingsFile;
public class ThemeUtil { public class ThemeUtil {
private static SharedPreferences mPreferences = PreferenceManager.getDefaultSharedPreferences(CitraApplication.getAppContext()); 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) { private static void applyTheme(int designValue, AppCompatActivity activity) {
switch (designValue) { switch (designValue) {
case 0: case 0:
@ -34,9 +44,40 @@ public class ThemeUtil {
int systemReportedThemeMode = activity.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; int systemReportedThemeMode = activity.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
WindowInsetsControllerCompat windowController = WindowCompat.getInsetsController(activity.getWindow(), activity.getWindow().getDecorView()); WindowInsetsControllerCompat windowController = WindowCompat.getInsetsController(activity.getWindow(), activity.getWindow().getDecorView());
windowController.setAppearanceLightStatusBars(systemReportedThemeMode == Configuration.UI_MODE_NIGHT_NO); 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) { public static void applyTheme(AppCompatActivity activity) {
applyTheme(mPreferences.getInt(SettingsFile.KEY_DESIGN, 0), 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));
}
} }

View File

@ -2,6 +2,7 @@
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
@ -17,14 +18,12 @@
android:id="@+id/appbar_cheats" android:id="@+id/appbar_cheats"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:elevation="0dp" android:fitsSystemWindows="true">
app:liftOnScroll="false">
<com.google.android.material.appbar.MaterialToolbar <com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar_cheats" android:id="@+id/toolbar_cheats"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="?attr/actionBarSize" />
android:background="?attr/colorSurface" />
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>
@ -40,18 +39,20 @@
app:layout_constraintTop_toBottomOf="@id/coordinator_cheats"> app:layout_constraintTop_toBottomOf="@id/coordinator_cheats">
<androidx.fragment.app.FragmentContainerView <androidx.fragment.app.FragmentContainerView
android:layout_width="320dp" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_weight="1" android:layout_weight="1"
android:id="@+id/cheat_list" android:id="@+id/cheat_list_container"
android:name="org.citra.citra_emu.features.cheats.ui.CheatListFragment" /> android:name="org.citra.citra_emu.features.cheats.ui.CheatListFragment"
tools:layout="@layout/fragment_cheat_list" />
<androidx.fragment.app.FragmentContainerView <androidx.fragment.app.FragmentContainerView
android:layout_width="320dp" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_weight="1" android:layout_weight="1"
android:id="@+id/cheat_details" android:id="@+id/cheat_details_container"
android:name="org.citra.citra_emu.features.cheats.ui.CheatDetailsFragment" /> android:name="org.citra.citra_emu.features.cheats.ui.CheatDetailsFragment"
tools:layout="@layout/fragment_cheat_details" />
</androidx.slidingpanelayout.widget.SlidingPaneLayout> </androidx.slidingpanelayout.widget.SlidingPaneLayout>

View File

@ -9,13 +9,14 @@
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar" android:id="@+id/appbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
android:fitsSystemWindows="true"
app:liftOnScrollTargetViewId="@id/grid_games">
<com.google.android.material.appbar.MaterialToolbar <com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar_main" android:id="@+id/toolbar_main"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" android:layout_height="?attr/actionBarSize"
android:background="?attr/colorSurface"
app:subtitleTextColor="?attr/colorOnSurface" app:subtitleTextColor="?attr/colorOnSurface"
app:titleTextColor="?attr/colorOnSurface" /> app:titleTextColor="?attr/colorOnSurface" />

View File

@ -8,13 +8,13 @@
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_settings" android:id="@+id/appbar_settings"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
android:fitsSystemWindows="true">
<com.google.android.material.appbar.MaterialToolbar <com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar_settings" android:id="@+id/toolbar_settings"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" android:layout_height="?attr/actionBarSize" />
android:background="?attr/colorSurface" />
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>

View File

@ -9,6 +9,7 @@
android:id="@+id/cheat_list" android:id="@+id/cheat_list"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:clipToPadding="false"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
@ -21,7 +22,7 @@
android:src="@drawable/ic_add" android:src="@drawable/ic_add"
android:contentDescription="@string/cheats_add" android:contentDescription="@string/cheats_add"
android:layout_margin="16dp" android:layout_margin="16dp"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent" /> app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -26,6 +26,7 @@
android:id="@+id/grid_games" android:id="@+id/grid_games"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:clipToPadding="false"
tools:listitem="@layout/card_game" /> tools:listitem="@layout/card_game" />
</RelativeLayout> </RelativeLayout>

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<org.citra.citra_emu.features.settings.ui.SettingsFrameLayout <FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/list_settings_root"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?attr/colorSurface"> android:background="?attr/colorSurface">
@ -8,6 +9,7 @@
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/list_settings" android:id="@+id/list_settings"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" /> android:layout_height="match_parent"
android:clipToPadding="false" />
</org.citra.citra_emu.features.settings.ui.SettingsFrameLayout> </FrameLayout>

View File

@ -24,11 +24,12 @@
<CheckBox <CheckBox
android:id="@+id/checkbox" android:id="@+id/checkbox"
android:layout_width="48dp" android:layout_width="wrap_content"
android:layout_height="64dp" android:layout_height="wrap_content"
android:focusable="true" android:focusable="true"
android:gravity="center" android:gravity="center"
android:nextFocusLeft="@id/root" android:nextFocusLeft="@id/root"
android:layout_marginEnd="8dp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/text_name" app:layout_constraintStart_toEndOf="@id/text_name"

View File

@ -6,6 +6,7 @@
<dimen name="spacing_small">4dp</dimen> <dimen name="spacing_small">4dp</dimen>
<dimen name="spacing_medlarge">12dp</dimen> <dimen name="spacing_medlarge">12dp</dimen>
<dimen name="spacing_large">16dp</dimen> <dimen name="spacing_large">16dp</dimen>
<dimen name="spacing_fab_list">80dp</dimen>
<dimen name="dialog_margin">20dp</dimen> <dimen name="dialog_margin">20dp</dimen>
</resources> </resources>

View File

@ -38,7 +38,8 @@
<item name="colorPrimaryInverse">@color/citra_inversePrimary</item> <item name="colorPrimaryInverse">@color/citra_inversePrimary</item>
<item name="homeAsUpIndicator">@drawable/ic_back</item> <item name="homeAsUpIndicator">@drawable/ic_back</item>
<item name="android:statusBarColor">@color/citra_surface</item> <item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
<item name="android:textColorLink">@color/citra_primary</item> <item name="android:textColorLink">@color/citra_primary</item>
<item name="materialAlertDialogTheme">@style/CitraMaterialDialog</item> <item name="materialAlertDialogTheme">@style/CitraMaterialDialog</item>
<item name="popupMenuStyle">@style/CitraShapedPopup</item> <item name="popupMenuStyle">@style/CitraShapedPopup</item>