Skip to content
Merged

E2e #176

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,14 @@
import com.google.android.material.navigation.NavigationView;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.appcompat.app.ActionBarDrawerToggle;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.drawerlayout.widget.DrawerLayout;
import com.sothree.slidinguppanel.SlidingUpPanelLayout;
import androidx.appcompat.app.AlertDialog;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import androidx.appcompat.app.AppCompatActivity;
Expand Down Expand Up @@ -530,6 +534,92 @@ public boolean onTouch(View v, MotionEvent event) {
if(secondaryContainer != null) {
primaryContainer = findViewById(R.id.fragment_container);
}

applyEdgeToEdgeInsets();
}

/**
* The window is drawn edge to edge, so the system bar insets have to be applied to the views which
* should not end up below the bars. The toolbars keep the status bar area painted with the action
* bar colour, while the now playing bar keeps the navigation bar area coloured.
*/
private void applyEdgeToEdgeInsets() {
if(fullScreen) {
// The bars are hidden in full screen mode, so there is nothing to keep clear of them.
return;
}

final View mainToolbar = findViewById(R.id.main_toolbar);
final View nowPlayingToolbar = findViewById(R.id.now_playing_toolbar);
final View swipeTarget = findViewById(R.id.slide_up_swipe_target);
final View nowPlayingContainer = findViewById(R.id.now_playing_fragment_container);
final View fragmentContainer = findViewById(R.id.fragment_container);
final View drawer = findViewById(R.id.left_drawer);
final View panel = findViewById(R.id.slide_up_panel);
final View header = drawerHeader;

final int panelHeight = panel instanceof SlidingUpPanelLayout ? ((SlidingUpPanelLayout) panel).getPanelHeight() : 0;
final int mainToolbarHeight = mainToolbar == null ? 0 : mainToolbar.getLayoutParams().height;
final int nowPlayingToolbarHeight = nowPlayingToolbar == null ? 0 : nowPlayingToolbar.getLayoutParams().height;
final int mainToolbarPaddingTop = mainToolbar == null ? 0 : mainToolbar.getPaddingTop();
final int nowPlayingToolbarPaddingTop = nowPlayingToolbar == null ? 0 : nowPlayingToolbar.getPaddingTop();
final int swipeTargetPaddingBottom = swipeTarget == null ? 0 : swipeTarget.getPaddingBottom();
final int nowPlayingContainerPaddingBottom = nowPlayingContainer == null ? 0 : nowPlayingContainer.getPaddingBottom();
final int fragmentContainerMarginTop = fragmentContainer == null ? 0 : ((ViewGroup.MarginLayoutParams) fragmentContainer.getLayoutParams()).topMargin;
final int drawerPaddingBottom = drawer == null ? 0 : drawer.getPaddingBottom();
final int headerHeight = header == null ? 0 : header.getLayoutParams().height;
final int headerPaddingTop = header == null ? 0 : header.getPaddingTop();
final int rootPaddingBottom = rootView == null ? 0 : rootView.getPaddingBottom();

ViewCompat.setOnApplyWindowInsetsListener(getWindow().getDecorView(), (view, windowInsets) -> {
Insets bars = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout());

// The toolbars grow by the status bar height so that their background paints it, while the
// padding keeps the title and the icons below it.
if(mainToolbar != null) {
ViewGroup.LayoutParams params = mainToolbar.getLayoutParams();
params.height = mainToolbarHeight + bars.top;
mainToolbar.setPadding(mainToolbar.getPaddingLeft(), mainToolbarPaddingTop + bars.top, mainToolbar.getPaddingRight(), mainToolbar.getPaddingBottom());
mainToolbar.setLayoutParams(params);
}
if(nowPlayingToolbar != null) {
ViewGroup.LayoutParams params = nowPlayingToolbar.getLayoutParams();
params.height = nowPlayingToolbarHeight + bars.top;
nowPlayingToolbar.setPadding(nowPlayingToolbar.getPaddingLeft(), nowPlayingToolbarPaddingTop + bars.top, nowPlayingToolbar.getPaddingRight(), nowPlayingToolbar.getPaddingBottom());
nowPlayingToolbar.setLayoutParams(params);
}
if(fragmentContainerMarginTop > 0) {
// The content is placed below the toolbar with a margin, which has to follow it.
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) fragmentContainer.getLayoutParams();
params.topMargin = fragmentContainerMarginTop + bars.top;
fragmentContainer.setLayoutParams(params);
}
if(swipeTarget != null) {
// The bar itself stays coloured below the buttons, so only its content is lifted.
swipeTarget.setPadding(swipeTarget.getPaddingLeft(), swipeTarget.getPaddingTop(), swipeTarget.getPaddingRight(), swipeTargetPaddingBottom + bars.bottom);
if(panel instanceof SlidingUpPanelLayout) {
((SlidingUpPanelLayout) panel).setPanelHeight(panelHeight + bars.bottom);
}
} else if(rootView != null) {
// Screens without a now playing bar keep all of their content clear of the navigation bar.
rootView.setPadding(rootView.getPaddingLeft(), rootView.getPaddingTop(), rootView.getPaddingRight(), rootPaddingBottom + bars.bottom);
}
if(nowPlayingContainer != null) {
nowPlayingContainer.setPadding(nowPlayingContainer.getPaddingLeft(), nowPlayingContainer.getPaddingTop(), nowPlayingContainer.getPaddingRight(), nowPlayingContainerPaddingBottom + bars.bottom);
}
if(header != null) {
// The drawer header is painted behind the status bar, so it grows and its content moves below it.
ViewGroup.LayoutParams params = header.getLayoutParams();
params.height = headerHeight + bars.top;
header.setPadding(header.getPaddingLeft(), headerPaddingTop + bars.top, header.getPaddingRight(), header.getPaddingBottom());
header.setLayoutParams(params);
}
if(drawer != null) {
drawer.setPadding(drawer.getPaddingLeft(), drawer.getPaddingTop(), drawer.getPaddingRight(), drawerPaddingBottom + bars.bottom);
}

return windowInsets;
});
}

@Override
Expand Down Expand Up @@ -1011,10 +1101,12 @@ private void applyTheme() {
actionbarCustomColor = prefs.getInt(Constants.PREFERENCES_KEY_ACTION_BAR_COLOR, -1);
actionbarNowPlayingCustomColor = prefs.getInt(Constants.PREFERENCES_KEY_ACTION_BAR_NOW_PLAYING_COLOR, -1);
}
// The drawer layout paints colorPrimaryDark behind the status bar; fall back to the surface when it is transparent.
// The action bar is drawn behind the status bar, so its colour decides the contrast of the status bar icons.
private void applyEdgeToEdge() {
int surface = MaterialColors.getColor(this, R.attr.colorSurface, Color.WHITE);
int statusColor = MaterialColors.getColor(this, R.attr.colorPrimaryDark, Color.TRANSPARENT);
int statusColor = actionbarColored && actionbarCustomColor != -1
? actionbarCustomColor
: MaterialColors.getColor(this, R.attr.actionbarBackgroundColor, Color.TRANSPARENT);
if(Color.alpha(statusColor) == 0) {
statusColor = surface;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,13 @@
import android.widget.ScrollView;
import android.widget.TextView;

import androidx.appcompat.app.AlertDialog;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.LinearSmoothScroller;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;

import com.google.android.material.dialog.MaterialAlertDialogBuilder;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
Expand Down Expand Up @@ -502,7 +503,7 @@ private void showVersionDialog() {
labels[i] = versionLabel(lyricsVersions.get(i));
}

new AlertDialog.Builder(context)
new MaterialAlertDialogBuilder(context)
.setTitle(R.string.lyrics_select_version)
.setSingleChoiceItems(labels, selectedVersion, new DialogInterface.OnClickListener() {
@Override
Expand Down
3 changes: 1 addition & 2 deletions app/src/main/res/layout/abstract_activity.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
android:layout_height="match_parent">

<!-- The main content view -->
<FrameLayout
Expand Down
14 changes: 14 additions & 0 deletions app/src/main/res/layout/preference_category_m3.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Category header for the platform preference framework, restyled to the M3
type scale and colour roles. Mirrors @android:layout/preference_category_material,
which hardcodes TextAppearance.Material.Body2 and ?android:attr/colorAccent. -->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dip"
android:textAppearance="@style/TextAppearance.Material3.TitleSmall"
android:textColor="?attr/colorPrimary"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:paddingTop="16dip" />
15 changes: 15 additions & 0 deletions app/src/main/res/values/styles.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,21 @@
<item name="tickVisible">false</item>
</style>

<!-- Narrow text button, so several fit in one row on small screens. -->
<style name="Widget.DSub2000.Button.Compact" parent="Widget.Material3.Button.TextButton">
<item name="android:minWidth">0dp</item>
<item name="android:insetLeft">0dp</item>
<item name="android:insetRight">0dp</item>
</style>

<!-- Material 3 header for the platform preference framework; applied through
android:preferenceCategoryStyle in themes.xml. The framework's
Preference.Material.Category style is private, so the layout item is repeated. -->
<style name="Preference.DSub2000.Category" parent="">
<item name="android:layout">@layout/preference_category_m3</item>
<item name="android:selectable">false</item>
</style>

<style name="TextAppearance.DSub2000.BarTitle" parent="TextAppearance.Material3.TitleLarge">
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
Expand Down
7 changes: 7 additions & 0 deletions app/src/main/res/values/themes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@
<item name="windowActionModeOverlay">true</item>
<item name="actionModeBackground">?attr/actionbarBackgroundColor</item>
<item name="drawerHeaderBackground">@drawable/drawer_header_light</item>
<!-- The platform preference framework builds framework AlertDialogs, which use
this attr. MaterialComponents points it at the Material 2 overlay; the M3
overlay keeps our colour roles, as it is applied on top of the activity theme. -->
<item name="android:alertDialogTheme">@style/ThemeOverlay.Material3.Dialog.Alert.Framework</item>
<item name="android:preferenceCategoryStyle">@style/Preference.DSub2000.Category</item>
</style>
<style name="Theme.DSub2000.Dark.Base" parent="Theme.Material3.Dark">
<item name="sliderStyle">@style/Widget.DSub2000.Slider</item>
Expand Down Expand Up @@ -194,6 +199,8 @@
<item name="windowActionModeOverlay">true</item>
<item name="actionModeBackground">?attr/actionbarBackgroundColor</item>
<item name="drawerHeaderBackground">@drawable/drawer_header_dark</item>
<item name="android:alertDialogTheme">@style/ThemeOverlay.Material3.Dialog.Alert.Framework</item>
<item name="android:preferenceCategoryStyle">@style/Preference.DSub2000.Category</item>
</style>
<style name="Theme.DSub2000.Light" parent="Theme.DSub2000.Light.Base">
</style>
Expand Down