Bottom navigation (#7176)

This commit is contained in:
ByteHamster 2024-09-10 20:59:18 +02:00 committed by GitHub
parent 60f6e175a8
commit 6cbc58ba78
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 526 additions and 177 deletions

View File

@ -52,6 +52,7 @@ public class NavigationDrawerTest {
EspressoTestUtils.clearPreferences(); EspressoTestUtils.clearPreferences();
EspressoTestUtils.clearDatabase(); EspressoTestUtils.clearDatabase();
UserPreferences.setBottomNavigationEnabled(false);
} }
@After @After

View File

@ -10,14 +10,19 @@ import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
import android.util.Log; import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.EditText; import android.widget.EditText;
import android.widget.RelativeLayout;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBarDrawerToggle; import androidx.appcompat.app.ActionBarDrawerToggle;
import androidx.appcompat.view.menu.MenuBuilder;
import androidx.appcompat.widget.ListPopupWindow;
import androidx.core.graphics.Insets; import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat; import androidx.core.view.ViewCompat;
import androidx.core.view.WindowCompat; import androidx.core.view.WindowCompat;
@ -32,48 +37,62 @@ import androidx.work.WorkInfo;
import androidx.work.WorkManager; import androidx.work.WorkManager;
import com.bumptech.glide.Glide; import com.bumptech.glide.Glide;
import com.google.android.material.appbar.MaterialToolbar; import com.google.android.material.appbar.MaterialToolbar;
import com.google.android.material.bottomnavigation.BottomNavigationView;
import com.google.android.material.bottomsheet.BottomSheetBehavior; import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.android.material.navigation.NavigationBarView;
import com.google.android.material.snackbar.Snackbar; import com.google.android.material.snackbar.Snackbar;
import de.danoeh.antennapod.R; import de.danoeh.antennapod.R;
import de.danoeh.antennapod.net.download.service.feed.FeedUpdateManagerImpl;
import de.danoeh.antennapod.net.download.serviceinterface.FeedUpdateManager;
import de.danoeh.antennapod.net.sync.serviceinterface.SynchronizationQueueSink;
import de.danoeh.antennapod.ui.appstartintent.MediaButtonStarter;
import de.danoeh.antennapod.ui.common.ThemeSwitcher;
import de.danoeh.antennapod.ui.screen.rating.RatingDialogManager;
import de.danoeh.antennapod.event.EpisodeDownloadEvent; import de.danoeh.antennapod.event.EpisodeDownloadEvent;
import de.danoeh.antennapod.event.FeedUpdateRunningEvent; import de.danoeh.antennapod.event.FeedUpdateRunningEvent;
import de.danoeh.antennapod.event.MessageEvent; import de.danoeh.antennapod.event.MessageEvent;
import de.danoeh.antennapod.ui.screen.AddFeedFragment;
import de.danoeh.antennapod.ui.screen.AllEpisodesFragment;
import de.danoeh.antennapod.ui.screen.playback.audio.AudioPlayerFragment;
import de.danoeh.antennapod.ui.screen.download.CompletedDownloadsFragment;
import de.danoeh.antennapod.ui.screen.download.DownloadLogFragment;
import de.danoeh.antennapod.ui.screen.feed.FeedItemlistFragment;
import de.danoeh.antennapod.ui.screen.InboxFragment;
import de.danoeh.antennapod.ui.screen.drawer.NavDrawerFragment;
import de.danoeh.antennapod.ui.screen.PlaybackHistoryFragment;
import de.danoeh.antennapod.ui.screen.queue.QueueFragment;
import de.danoeh.antennapod.ui.screen.SearchFragment;
import de.danoeh.antennapod.ui.screen.subscriptions.SubscriptionFragment;
import de.danoeh.antennapod.ui.TransitionEffect;
import de.danoeh.antennapod.model.download.DownloadStatus; import de.danoeh.antennapod.model.download.DownloadStatus;
import de.danoeh.antennapod.model.feed.FeedItemFilter;
import de.danoeh.antennapod.net.download.service.feed.FeedUpdateManagerImpl;
import de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterface; import de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterface;
import de.danoeh.antennapod.net.download.serviceinterface.FeedUpdateManager;
import de.danoeh.antennapod.net.sync.serviceinterface.SynchronizationQueueSink;
import de.danoeh.antennapod.playback.cast.CastEnabledActivity; import de.danoeh.antennapod.playback.cast.CastEnabledActivity;
import de.danoeh.antennapod.storage.database.DBReader;
import de.danoeh.antennapod.storage.importexport.AutomaticDatabaseExportWorker; import de.danoeh.antennapod.storage.importexport.AutomaticDatabaseExportWorker;
import de.danoeh.antennapod.storage.preferences.UserPreferences; import de.danoeh.antennapod.storage.preferences.UserPreferences;
import de.danoeh.antennapod.ui.TransitionEffect;
import de.danoeh.antennapod.ui.appstartintent.MainActivityStarter; import de.danoeh.antennapod.ui.appstartintent.MainActivityStarter;
import de.danoeh.antennapod.ui.appstartintent.MediaButtonStarter;
import de.danoeh.antennapod.ui.common.ThemeSwitcher;
import de.danoeh.antennapod.ui.common.ThemeUtils; import de.danoeh.antennapod.ui.common.ThemeUtils;
import de.danoeh.antennapod.ui.discovery.DiscoveryFragment; import de.danoeh.antennapod.ui.discovery.DiscoveryFragment;
import de.danoeh.antennapod.ui.screen.AddFeedFragment;
import de.danoeh.antennapod.ui.screen.AllEpisodesFragment;
import de.danoeh.antennapod.ui.screen.InboxFragment;
import de.danoeh.antennapod.ui.screen.PlaybackHistoryFragment;
import de.danoeh.antennapod.ui.screen.SearchFragment;
import de.danoeh.antennapod.ui.screen.download.CompletedDownloadsFragment;
import de.danoeh.antennapod.ui.screen.download.DownloadLogFragment;
import de.danoeh.antennapod.ui.screen.drawer.BottomNavigationMoreAdapter;
import de.danoeh.antennapod.ui.screen.drawer.DrawerPreferencesDialog;
import de.danoeh.antennapod.ui.screen.drawer.NavDrawerFragment;
import de.danoeh.antennapod.ui.screen.drawer.NavListAdapter;
import de.danoeh.antennapod.ui.screen.drawer.NavigationNames;
import de.danoeh.antennapod.ui.screen.feed.FeedItemlistFragment;
import de.danoeh.antennapod.ui.screen.home.HomeFragment; import de.danoeh.antennapod.ui.screen.home.HomeFragment;
import de.danoeh.antennapod.ui.screen.playback.audio.AudioPlayerFragment;
import de.danoeh.antennapod.ui.screen.preferences.PreferenceActivity;
import de.danoeh.antennapod.ui.screen.queue.QueueFragment;
import de.danoeh.antennapod.ui.screen.rating.RatingDialogManager;
import de.danoeh.antennapod.ui.screen.subscriptions.SubscriptionFragment;
import de.danoeh.antennapod.ui.view.LockableBottomSheetBehavior; import de.danoeh.antennapod.ui.view.LockableBottomSheetBehavior;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;
import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode; import org.greenrobot.eventbus.ThreadMode;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
@ -94,11 +113,12 @@ public class MainActivity extends CastEnabledActivity {
private @Nullable DrawerLayout drawerLayout; private @Nullable DrawerLayout drawerLayout;
private @Nullable ActionBarDrawerToggle drawerToggle; private @Nullable ActionBarDrawerToggle drawerToggle;
private BottomNavigationView bottomNavigationView;
private View navDrawer; private View navDrawer;
private LockableBottomSheetBehavior sheetBehavior; private LockableBottomSheetBehavior sheetBehavior;
private RecyclerView.RecycledViewPool recycledViewPool = new RecyclerView.RecycledViewPool(); private RecyclerView.RecycledViewPool recycledViewPool = new RecyclerView.RecycledViewPool();
private int lastTheme = 0; private int lastTheme = 0;
private Insets navigationBarInsets = Insets.NONE; private Insets systemBarInsets = Insets.NONE;
@NonNull @NonNull
public static Intent getIntentToOpenFeed(@NonNull Context context, long feedId) { public static Intent getIntentToOpenFeed(@NonNull Context context, long feedId) {
@ -122,11 +142,24 @@ public class MainActivity extends CastEnabledActivity {
drawerLayout = findViewById(R.id.drawer_layout); drawerLayout = findViewById(R.id.drawer_layout);
navDrawer = findViewById(R.id.navDrawerFragment); navDrawer = findViewById(R.id.navDrawerFragment);
setNavDrawerSize(); bottomNavigationView = findViewById(R.id.bottomNavigationView);
if (UserPreferences.isBottomNavigationEnabled()) {
buildBottomNavigationMenu();
if (drawerLayout == null) { // Tablet mode
navDrawer.setVisibility(View.GONE);
} else {
drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
}
drawerLayout = null;
} else {
bottomNavigationView.setVisibility(View.GONE);
bottomNavigationView = null;
setNavDrawerSize();
}
// Consume navigation bar insets - we apply them in setPlayerVisible() // Consume navigation bar insets - we apply them in setPlayerVisible()
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main_view), (v, insets) -> { ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main_view), (v, insets) -> {
navigationBarInsets = insets.getInsets(WindowInsetsCompat.Type.navigationBars()); systemBarInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars());
updateInsets(); updateInsets();
return new WindowInsetsCompat.Builder(insets) return new WindowInsetsCompat.Builder(insets)
.setInsets(WindowInsetsCompat.Type.navigationBars(), Insets.NONE) .setInsets(WindowInsetsCompat.Type.navigationBars(), Insets.NONE)
@ -320,28 +353,35 @@ public class MainActivity extends CastEnabledActivity {
private void updateInsets() { private void updateInsets() {
setPlayerVisible(findViewById(R.id.audioplayerFragment).getVisibility() == View.VISIBLE); setPlayerVisible(findViewById(R.id.audioplayerFragment).getVisibility() == View.VISIBLE);
int playerHeight = (int) getResources().getDimension(R.dimen.external_player_height);
sheetBehavior.setPeekHeight(playerHeight + navigationBarInsets.bottom);
} }
public void setPlayerVisible(boolean visible) { public void setPlayerVisible(boolean visible) {
getBottomSheet().setLocked(!visible); getBottomSheet().setLocked(!visible);
findViewById(R.id.audioplayerFragment).setVisibility(visible ? View.VISIBLE : View.GONE);
if (visible) { if (visible) {
bottomSheetCallback.onStateChanged(null, getBottomSheet().getState()); // Update toolbar visibility bottomSheetCallback.onStateChanged(null, getBottomSheet().getState()); // Update toolbar visibility
} else { } else {
getBottomSheet().setState(BottomSheetBehavior.STATE_COLLAPSED); getBottomSheet().setState(BottomSheetBehavior.STATE_COLLAPSED);
} }
FragmentContainerView mainView = findViewById(R.id.main_view); View bottomPaddingView = findViewById(R.id.bottom_padding);
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mainView.getLayoutParams(); ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) bottomPaddingView.getLayoutParams();
params.height = systemBarInsets.bottom;
bottomPaddingView.setLayoutParams(params);
int externalPlayerHeight = (int) getResources().getDimension(R.dimen.external_player_height); int externalPlayerHeight = (int) getResources().getDimension(R.dimen.external_player_height);
params.setMargins(navigationBarInsets.left, 0, navigationBarInsets.right, FragmentContainerView mainView = findViewById(R.id.main_content_view);
navigationBarInsets.bottom + (visible ? externalPlayerHeight : 0)); params = (ViewGroup.MarginLayoutParams) mainView.getLayoutParams();
params.setMargins(systemBarInsets.left, 0, systemBarInsets.right, (visible ? externalPlayerHeight : 0));
mainView.setLayoutParams(params); mainView.setLayoutParams(params);
sheetBehavior.setPeekHeight(externalPlayerHeight);
sheetBehavior.setGestureInsetBottomIgnored(true);
FragmentContainerView playerView = findViewById(R.id.playerFragment); FragmentContainerView playerView = findViewById(R.id.playerFragment);
ViewGroup.MarginLayoutParams playerParams = (ViewGroup.MarginLayoutParams) playerView.getLayoutParams(); ViewGroup.MarginLayoutParams playerParams = (ViewGroup.MarginLayoutParams) playerView.getLayoutParams();
playerParams.setMargins(navigationBarInsets.left, 0, navigationBarInsets.right, 0); playerParams.setMargins(systemBarInsets.left, 0, systemBarInsets.right, 0);
playerView.setLayoutParams(playerParams); playerView.setLayoutParams(playerParams);
findViewById(R.id.audioplayerFragment).setVisibility(visible ? View.VISIBLE : View.GONE); RelativeLayout playerContent = findViewById(R.id.playerContent);
playerContent.setPadding(systemBarInsets.left, systemBarInsets.top, systemBarInsets.right, 0);
} }
public RecyclerView.RecycledViewPool getRecycledViewPool() { public RecyclerView.RecycledViewPool getRecycledViewPool() {
@ -393,6 +433,15 @@ public class MainActivity extends CastEnabledActivity {
public void loadFragment(String tag, Bundle args) { public void loadFragment(String tag, Bundle args) {
NavDrawerFragment.saveLastNavFragment(this, tag); NavDrawerFragment.saveLastNavFragment(this, tag);
if (bottomNavigationView != null) {
int bottomSelectedItem = NavigationNames.getBottomNavigationItemId(tag);
if (bottomNavigationView.getMenu().findItem(bottomSelectedItem) == null) {
bottomSelectedItem = R.id.bottom_navigation_more;
}
bottomNavigationView.setOnItemSelectedListener(null);
bottomNavigationView.setSelectedItemId(bottomSelectedItem);
bottomNavigationView.setOnItemSelectedListener(bottomItemSelectedListener);
}
loadFragment(createFragmentInstance(tag, args)); loadFragment(createFragmentInstance(tag, args));
} }
@ -412,7 +461,7 @@ public class MainActivity extends CastEnabledActivity {
fragmentManager.popBackStack(); fragmentManager.popBackStack();
} }
FragmentTransaction t = fragmentManager.beginTransaction(); FragmentTransaction t = fragmentManager.beginTransaction();
t.replace(R.id.main_view, fragment, MAIN_FRAGMENT_TAG); t.replace(R.id.main_content_view, fragment, MAIN_FRAGMENT_TAG);
fragmentManager.popBackStack(); fragmentManager.popBackStack();
// TODO: we have to allow state loss here // TODO: we have to allow state loss here
// since this function can get called from an AsyncTask which // since this function can get called from an AsyncTask which
@ -443,7 +492,7 @@ public class MainActivity extends CastEnabledActivity {
transaction transaction
.hide(getSupportFragmentManager().findFragmentByTag(MAIN_FRAGMENT_TAG)) .hide(getSupportFragmentManager().findFragmentByTag(MAIN_FRAGMENT_TAG))
.add(R.id.main_view, fragment, MAIN_FRAGMENT_TAG) .add(R.id.main_content_view, fragment, MAIN_FRAGMENT_TAG)
.addToBackStack(null) .addToBackStack(null)
.commit(); .commit();
} }
@ -452,6 +501,85 @@ public class MainActivity extends CastEnabledActivity {
loadChildFragment(fragment, TransitionEffect.NONE); loadChildFragment(fragment, TransitionEffect.NONE);
} }
private void buildBottomNavigationMenu() {
List<String> drawerItems = UserPreferences.getVisibleDrawerItemOrder();
drawerItems.remove(NavListAdapter.SUBSCRIPTION_LIST_TAG);
Menu menu = bottomNavigationView.getMenu();
menu.clear();
for (int i = 0; i < drawerItems.size() && i < bottomNavigationView.getMaxItemCount() - 1; i++) {
String tag = drawerItems.get(i);
MenuItem item = menu.add(0, NavigationNames.getBottomNavigationItemId(tag),
0, getString(NavigationNames.getLabel(tag)));
item.setIcon(NavigationNames.getDrawable(tag));
}
MenuItem moreItem = menu.add(0, R.id.bottom_navigation_more, 0, getString(R.string.searchpreference_more));
moreItem.setIcon(R.drawable.dots_vertical);
bottomNavigationView.setOnItemSelectedListener(bottomItemSelectedListener);
if (bottomNavigationView.getMenu().findItem(R.id.bottom_navigation_inbox) != null) {
Observable.fromCallable(() -> DBReader.getTotalEpisodeCount(new FeedItemFilter(FeedItemFilter.NEW)))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result ->
bottomNavigationView.getOrCreateBadge(R.id.bottom_navigation_inbox).setNumber(result),
error -> Log.e(TAG, Log.getStackTraceString(error)));
}
}
private final NavigationBarView.OnItemSelectedListener bottomItemSelectedListener = item -> {
sheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
if (item.getItemId() == R.id.bottom_navigation_more) {
showBottomNavigationMorePopup();
return false;
} else {
loadFragment(NavigationNames.getBottomNavigationFragmentTag(item.getItemId()), null);
return true;
}
};
private void showBottomNavigationMorePopup() {
List<String> drawerItems = UserPreferences.getVisibleDrawerItemOrder();
drawerItems.remove(NavListAdapter.SUBSCRIPTION_LIST_TAG);
final List<MenuItem> popupMenuItems = new ArrayList<>();
for (int i = bottomNavigationView.getMaxItemCount() - 1; i < drawerItems.size(); i++) {
String tag = drawerItems.get(i);
MenuItem item = new MenuBuilder(this).add(0, NavigationNames.getBottomNavigationItemId(tag),
0, getString(NavigationNames.getLabel(tag)));
item.setIcon(NavigationNames.getDrawable(tag));
popupMenuItems.add(item);
}
MenuItem customizeItem = new MenuBuilder(this).add(0, R.id.bottom_navigation_settings,
0, getString(R.string.pref_nav_drawer_items_title));
customizeItem.setIcon(R.drawable.ic_pencil);
popupMenuItems.add(customizeItem);
MenuItem settingsItem = new MenuBuilder(this).add(0, R.id.bottom_navigation_settings,
0, getString(R.string.settings_label));
settingsItem.setIcon(R.drawable.ic_settings);
popupMenuItems.add(settingsItem);
final ListPopupWindow listPopupWindow = new ListPopupWindow(this);
listPopupWindow.setWidth((int) (250 * getResources().getDisplayMetrics().density));
listPopupWindow.setAnchorView(bottomNavigationView);
listPopupWindow.setAdapter(new BottomNavigationMoreAdapter(this, popupMenuItems));
listPopupWindow.setOnItemClickListener((parent, view, position, id) -> {
if (position == popupMenuItems.size() - 1) {
startActivity(new Intent(this, PreferenceActivity.class));
} else if (position == popupMenuItems.size() - 2) {
new DrawerPreferencesDialog(this, this::buildBottomNavigationMenu).show();
} else {
loadFragment(NavigationNames.getBottomNavigationFragmentTag(
popupMenuItems.get(position).getItemId()), null);
}
listPopupWindow.dismiss();
});
listPopupWindow.setDropDownGravity(Gravity.END | Gravity.BOTTOM);
listPopupWindow.setModal(true);
listPopupWindow.show();
}
@Override @Override
protected void onPostCreate(Bundle savedInstanceState) { protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState); super.onPostCreate(savedInstanceState);
@ -507,7 +635,9 @@ public class MainActivity extends CastEnabledActivity {
super.onResume(); super.onResume();
handleNavIntent(); handleNavIntent();
if (lastTheme != ThemeSwitcher.getNoTitleTheme(this)) { boolean hasBottomNavigation = bottomNavigationView != null;
if (lastTheme != ThemeSwitcher.getNoTitleTheme(this)
|| hasBottomNavigation != UserPreferences.isBottomNavigationEnabled()) {
finish(); finish();
startActivity(new Intent(this, MainActivity.class)); startActivity(new Intent(this, MainActivity.class));
} }
@ -566,7 +696,7 @@ public class MainActivity extends CastEnabledActivity {
String toPage = UserPreferences.getDefaultPage(); String toPage = UserPreferences.getDefaultPage();
if (NavDrawerFragment.getLastNavFragment(this).equals(toPage) if (NavDrawerFragment.getLastNavFragment(this).equals(toPage)
|| UserPreferences.DEFAULT_PAGE_REMEMBER.equals(toPage)) { || UserPreferences.DEFAULT_PAGE_REMEMBER.equals(toPage)) {
if (UserPreferences.backButtonOpensDrawer() && drawerLayout != null) { if (UserPreferences.backButtonOpensDrawer() && drawerLayout != null && bottomNavigationView == null) {
drawerLayout.openDrawer(navDrawer); drawerLayout.openDrawer(navDrawer);
} else { } else {
super.onBackPressed(); super.onBackPressed();
@ -644,7 +774,7 @@ public class MainActivity extends CastEnabledActivity {
public Snackbar showSnackbarAbovePlayer(CharSequence text, int duration) { public Snackbar showSnackbarAbovePlayer(CharSequence text, int duration) {
Snackbar s; Snackbar s;
if (getBottomSheet().getState() == BottomSheetBehavior.STATE_COLLAPSED) { if (getBottomSheet().getState() == BottomSheetBehavior.STATE_COLLAPSED) {
s = Snackbar.make(findViewById(R.id.main_view), text, duration); s = Snackbar.make(findViewById(R.id.main_content_view), text, duration);
if (findViewById(R.id.audioplayerFragment).getVisibility() == View.VISIBLE) { if (findViewById(R.id.audioplayerFragment).getVisibility() == View.VISIBLE) {
s.setAnchorView(findViewById(R.id.audioplayerFragment)); s.setAnchorView(findViewById(R.id.audioplayerFragment));
} }

View File

@ -8,7 +8,6 @@ import android.widget.ProgressBar;
import android.widget.TextView; import android.widget.TextView;
import androidx.cardview.widget.CardView; import androidx.cardview.widget.CardView;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.elevation.SurfaceColors;
import de.danoeh.antennapod.R; import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.ui.CoverLoader; import de.danoeh.antennapod.ui.CoverLoader;
@ -56,8 +55,7 @@ public class HorizontalItemViewHolder extends RecyclerView.ViewHolder {
this.item = item; this.item = item;
card.setAlpha(1.0f); card.setAlpha(1.0f);
float density = activity.getResources().getDisplayMetrics().density; card.setCardBackgroundColor(ThemeUtils.getColorFromAttr(activity, R.attr.colorSurfaceContainer));
card.setCardBackgroundColor(SurfaceColors.getColorForElevation(activity, 1 * density));
new CoverLoader() new CoverLoader()
.withUri(ImageResourceUtils.getEpisodeListImageLocation(item)) .withUri(ImageResourceUtils.getEpisodeListImageLocation(item))
.withFallbackUri(item.getFeed().getImageUrl()) .withFallbackUri(item.getFeed().getImageUrl())

View File

@ -0,0 +1,35 @@
package de.danoeh.antennapod.ui.screen.drawer;
import android.content.Context;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import de.danoeh.antennapod.R;
import java.util.List;
public class BottomNavigationMoreAdapter extends ArrayAdapter<MenuItem> {
private final Context context;
private final List<MenuItem> listItems;
public BottomNavigationMoreAdapter(Context context, List<MenuItem> listItems) {
super(context, R.layout.bottom_navigation_more_listitem, listItems);
this.context = context;
this.listItems = listItems;
}
@Override
public View getView(int position, View view, ViewGroup parent) {
if (view == null) {
view = View.inflate(context, R.layout.bottom_navigation_more_listitem, null);
}
MenuItem item = listItems.get(position);
((ImageView) view.findViewById(R.id.coverImage)).setImageDrawable(item.getIcon());
((TextView) view.findViewById(R.id.titleLabel)).setText(item.getTitle());
return view;
}
}

View File

@ -35,7 +35,7 @@ public class DrawerPreferencesDialog extends ReorderDialog {
final List<String> drawerItemOrder = UserPreferences.getVisibleDrawerItemOrder(); final List<String> drawerItemOrder = UserPreferences.getVisibleDrawerItemOrder();
for (String tag : drawerItemOrder) { for (String tag : drawerItemOrder) {
settingsDialogItems.add(new ReorderDialogItem(ReorderDialogItem.ViewType.Section, settingsDialogItems.add(new ReorderDialogItem(ReorderDialogItem.ViewType.Section,
tag, context.getString(NavListAdapter.getLabel(tag)))); tag, context.getString(NavigationNames.getLabel(tag))));
} }
settingsDialogItems.add(new ReorderDialogItem(ReorderDialogItem.ViewType.Header, settingsDialogItems.add(new ReorderDialogItem(ReorderDialogItem.ViewType.Header,
@ -44,7 +44,7 @@ public class DrawerPreferencesDialog extends ReorderDialog {
final List<String> hiddenDrawerItems = UserPreferences.getHiddenDrawerItems(); final List<String> hiddenDrawerItems = UserPreferences.getHiddenDrawerItems();
for (String sectionTag : hiddenDrawerItems) { for (String sectionTag : hiddenDrawerItems) {
settingsDialogItems.add(new ReorderDialogItem(ReorderDialogItem.ViewType.Section, settingsDialogItems.add(new ReorderDialogItem(ReorderDialogItem.ViewType.Section,
sectionTag, context.getString(NavListAdapter.getLabel(sectionTag)))); sectionTag, context.getString(NavigationNames.getLabel(sectionTag))));
} }
return settingsDialogItems; return settingsDialogItems;
} }

View File

@ -14,7 +14,6 @@ import android.widget.ImageView;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.StringRes; import androidx.annotation.StringRes;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
@ -29,12 +28,8 @@ import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.storage.database.NavDrawerData; import de.danoeh.antennapod.storage.database.NavDrawerData;
import de.danoeh.antennapod.storage.preferences.UserPreferences; import de.danoeh.antennapod.storage.preferences.UserPreferences;
import de.danoeh.antennapod.ui.common.ImagePlaceholder; import de.danoeh.antennapod.ui.common.ImagePlaceholder;
import de.danoeh.antennapod.ui.screen.AddFeedFragment;
import de.danoeh.antennapod.ui.screen.AllEpisodesFragment;
import de.danoeh.antennapod.ui.screen.InboxFragment; import de.danoeh.antennapod.ui.screen.InboxFragment;
import de.danoeh.antennapod.ui.screen.PlaybackHistoryFragment;
import de.danoeh.antennapod.ui.screen.download.CompletedDownloadsFragment; import de.danoeh.antennapod.ui.screen.download.CompletedDownloadsFragment;
import de.danoeh.antennapod.ui.screen.home.HomeFragment;
import de.danoeh.antennapod.ui.screen.preferences.PreferenceActivity; import de.danoeh.antennapod.ui.screen.preferences.PreferenceActivity;
import de.danoeh.antennapod.ui.screen.queue.QueueFragment; import de.danoeh.antennapod.ui.screen.queue.QueueFragment;
import de.danoeh.antennapod.ui.screen.subscriptions.SubscriptionFragment; import de.danoeh.antennapod.ui.screen.subscriptions.SubscriptionFragment;
@ -100,54 +95,6 @@ public class NavListAdapter extends RecyclerView.Adapter<NavListAdapter.Holder>
notifyDataSetChanged(); notifyDataSetChanged();
} }
public static @StringRes int getLabel(String tag) {
switch (tag) {
case HomeFragment.TAG:
return R.string.home_label;
case QueueFragment.TAG:
return R.string.queue_label;
case InboxFragment.TAG:
return R.string.inbox_label;
case AllEpisodesFragment.TAG:
return R.string.episodes_label;
case SubscriptionFragment.TAG:
return R.string.subscriptions_label;
case CompletedDownloadsFragment.TAG:
return R.string.downloads_label;
case PlaybackHistoryFragment.TAG:
return R.string.playback_history_label;
case AddFeedFragment.TAG:
return R.string.add_feed_label;
case NavListAdapter.SUBSCRIPTION_LIST_TAG:
return R.string.subscriptions_list_label;
default:
return 0;
}
}
private static @DrawableRes int getDrawable(String tag) {
switch (tag) {
case HomeFragment.TAG:
return R.drawable.ic_home;
case QueueFragment.TAG:
return R.drawable.ic_playlist_play;
case InboxFragment.TAG:
return R.drawable.ic_inbox;
case AllEpisodesFragment.TAG:
return R.drawable.ic_feed;
case CompletedDownloadsFragment.TAG:
return R.drawable.ic_download;
case PlaybackHistoryFragment.TAG:
return R.drawable.ic_history;
case SubscriptionFragment.TAG:
return R.drawable.ic_subscriptions;
case AddFeedFragment.TAG:
return R.drawable.ic_add;
default:
return 0;
}
}
public List<String> getFragmentTags() { public List<String> getFragmentTags() {
return Collections.unmodifiableList(fragmentTags); return Collections.unmodifiableList(fragmentTags);
} }
@ -207,7 +154,7 @@ public class NavListAdapter extends RecyclerView.Adapter<NavListAdapter.Holder>
holder.itemView.setOnCreateContextMenuListener(null); holder.itemView.setOnCreateContextMenuListener(null);
if (viewType == VIEW_TYPE_NAV) { if (viewType == VIEW_TYPE_NAV) {
bindNavView(getLabel(fragmentTags.get(position)), position, (NavHolder) holder); bindNavView(NavigationNames.getLabel(fragmentTags.get(position)), position, (NavHolder) holder);
} else if (viewType == VIEW_TYPE_SECTION_DIVIDER) { } else if (viewType == VIEW_TYPE_SECTION_DIVIDER) {
bindSectionDivider((DividerHolder) holder); bindSectionDivider((DividerHolder) holder);
} else { } else {
@ -292,7 +239,7 @@ public class NavListAdapter extends RecyclerView.Adapter<NavListAdapter.Holder>
} }
} }
holder.image.setImageResource(getDrawable(fragmentTags.get(position))); holder.image.setImageResource(NavigationNames.getDrawable(fragmentTags.get(position)));
} }
private void bindSectionDivider(DividerHolder holder) { private void bindSectionDivider(DividerHolder holder) {

View File

@ -0,0 +1,106 @@
package de.danoeh.antennapod.ui.screen.drawer;
import androidx.annotation.DrawableRes;
import androidx.annotation.StringRes;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.ui.screen.AddFeedFragment;
import de.danoeh.antennapod.ui.screen.AllEpisodesFragment;
import de.danoeh.antennapod.ui.screen.InboxFragment;
import de.danoeh.antennapod.ui.screen.PlaybackHistoryFragment;
import de.danoeh.antennapod.ui.screen.download.CompletedDownloadsFragment;
import de.danoeh.antennapod.ui.screen.home.HomeFragment;
import de.danoeh.antennapod.ui.screen.queue.QueueFragment;
import de.danoeh.antennapod.ui.screen.subscriptions.SubscriptionFragment;
public abstract class NavigationNames {
public static @DrawableRes int getDrawable(String tag) {
switch (tag) {
case HomeFragment.TAG:
return R.drawable.ic_home;
case QueueFragment.TAG:
return R.drawable.ic_playlist_play;
case InboxFragment.TAG:
return R.drawable.ic_inbox;
case AllEpisodesFragment.TAG:
return R.drawable.ic_feed;
case CompletedDownloadsFragment.TAG:
return R.drawable.ic_download;
case PlaybackHistoryFragment.TAG:
return R.drawable.ic_history;
case SubscriptionFragment.TAG:
return R.drawable.ic_subscriptions;
case AddFeedFragment.TAG:
return R.drawable.ic_add;
default:
return 0;
}
}
public static @StringRes int getLabel(String tag) {
switch (tag) {
case HomeFragment.TAG:
return R.string.home_label;
case QueueFragment.TAG:
return R.string.queue_label;
case InboxFragment.TAG:
return R.string.inbox_label;
case AllEpisodesFragment.TAG:
return R.string.episodes_label;
case SubscriptionFragment.TAG:
return R.string.subscriptions_label;
case CompletedDownloadsFragment.TAG:
return R.string.downloads_label;
case PlaybackHistoryFragment.TAG:
return R.string.playback_history_label;
case AddFeedFragment.TAG:
return R.string.add_feed_label;
case NavListAdapter.SUBSCRIPTION_LIST_TAG:
return R.string.subscriptions_list_label;
default:
return 0;
}
}
public static int getBottomNavigationItemId(String tag) {
switch (tag) {
case QueueFragment.TAG:
return R.id.bottom_navigation_queue;
case InboxFragment.TAG:
return R.id.bottom_navigation_inbox;
case AllEpisodesFragment.TAG:
return R.id.bottom_navigation_episodes;
case CompletedDownloadsFragment.TAG:
return R.id.bottom_navigation_downloads;
case PlaybackHistoryFragment.TAG:
return R.id.bottom_navigation_history;
case AddFeedFragment.TAG:
return R.id.bottom_navigation_addfeed;
case SubscriptionFragment.TAG:
return R.id.bottom_navigation_subscriptions;
case HomeFragment.TAG: // fall-through
default:
return R.id.bottom_navigation_home;
}
}
public static String getBottomNavigationFragmentTag(int id) {
if (id == R.id.bottom_navigation_queue) {
return QueueFragment.TAG;
} else if (id == R.id.bottom_navigation_inbox) {
return InboxFragment.TAG;
} else if (id == R.id.bottom_navigation_episodes) {
return AllEpisodesFragment.TAG;
} else if (id == R.id.bottom_navigation_downloads) {
return CompletedDownloadsFragment.TAG;
} else if (id == R.id.bottom_navigation_history) {
return PlaybackHistoryFragment.TAG;
} else if (id == R.id.bottom_navigation_addfeed) {
return AddFeedFragment.TAG;
} else if (id == R.id.bottom_navigation_subscriptions) {
return SubscriptionFragment.TAG;
} else if (id == R.id.bottom_navigation_home) {
return HomeFragment.TAG;
}
return null;
}
}

View File

@ -23,7 +23,6 @@ import androidx.viewpager2.widget.ViewPager2;
import com.google.android.material.appbar.MaterialToolbar; import com.google.android.material.appbar.MaterialToolbar;
import com.google.android.material.bottomsheet.BottomSheetBehavior; import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.android.material.elevation.SurfaceColors;
import de.danoeh.antennapod.model.feed.Feed; import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.playback.service.PlaybackController; import de.danoeh.antennapod.playback.service.PlaybackController;
@ -121,8 +120,6 @@ public class AudioPlayerFragment extends Fragment implements
getChildFragmentManager().beginTransaction() getChildFragmentManager().beginTransaction()
.replace(R.id.playerFragment, externalPlayerFragment, ExternalPlayerFragment.TAG) .replace(R.id.playerFragment, externalPlayerFragment, ExternalPlayerFragment.TAG)
.commit(); .commit();
root.findViewById(R.id.playerFragment).setBackgroundColor(
SurfaceColors.getColorForElevation(getContext(), 8 * getResources().getDisplayMetrics().density));
butPlaybackSpeed = root.findViewById(R.id.butPlaybackSpeed); butPlaybackSpeed = root.findViewById(R.id.butPlaybackSpeed);
txtvPlaybackSpeed = root.findViewById(R.id.txtvPlaybackSpeed); txtvPlaybackSpeed = root.findViewById(R.id.txtvPlaybackSpeed);

View File

@ -6,6 +6,8 @@ import androidx.annotation.NonNull;
import androidx.core.widget.NestedScrollView; import androidx.core.widget.NestedScrollView;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.ui.common.ThemeUtils;
/** /**
* Workaround for app:liftOnScroll flickering when in SwipeRefreshLayout * Workaround for app:liftOnScroll flickering when in SwipeRefreshLayout
@ -16,8 +18,9 @@ public class LiftOnScrollListener extends RecyclerView.OnScrollListener
private boolean animatingToScrolled = false; private boolean animatingToScrolled = false;
public LiftOnScrollListener(View appBar) { public LiftOnScrollListener(View appBar) {
animator = ValueAnimator.ofFloat(0, appBar.getContext().getResources().getDisplayMetrics().density * 8); int colorLifted = ThemeUtils.getColorFromAttr(appBar.getContext(), R.attr.colorSurfaceContainer);
animator.addUpdateListener(animation -> appBar.setElevation((float) animation.getAnimatedValue())); animator = ValueAnimator.ofArgb(colorLifted & 0x00ffffff, colorLifted);
animator.addUpdateListener(animation -> appBar.setBackgroundColor((int) animation.getAnimatedValue()));
} }
@Override @Override

View File

@ -3,45 +3,66 @@
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" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="horizontal" android:orientation="vertical"
tools:viewBindingIgnore="true"> tools:viewBindingIgnore="true">
<androidx.fragment.app.FragmentContainerView <LinearLayout
android:id="@+id/navDrawerFragment" android:layout_width="match_parent"
android:layout_width="300dp" android:layout_height="0dp"
android:layout_height="match_parent" android:orientation="horizontal"
android:layout_gravity="start" android:layout_weight="1">
android:orientation="vertical" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/navDrawerFragment"
android:layout_width="300dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:orientation="vertical" />
<View
android:layout_width="1dp"
android:layout_height="match_parent"
android:background="?android:attr/listDivider" />
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/overview_coordinator_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/main_content_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentTop="true"
android:foreground="?android:windowContentOverlay"
tools:background="@android:color/holo_red_dark" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/audioplayerFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:attr/colorBackground"
android:elevation="8dp"
android:visibility="gone"
app:layout_behavior="de.danoeh.antennapod.ui.view.LockableBottomSheetBehavior" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</LinearLayout>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottomNavigationView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:labelVisibilityMode="labeled" />
<View <View
android:layout_width="1dp" android:id="@+id/bottom_padding"
android:layout_height="match_parent"
android:background="?android:attr/listDivider" />
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/overview_coordinator_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="0dp"
android:background="?attr/colorSurfaceContainer" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/main_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentTop="true"
android:foreground="?android:windowContentOverlay"
tools:background="@android:color/holo_red_dark" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/audioplayerFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:attr/colorBackground"
android:elevation="8dp"
android:visibility="gone"
app:layout_behavior="de.danoeh.antennapod.ui.view.LockableBottomSheetBehavior" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</LinearLayout> </LinearLayout>

View File

@ -41,6 +41,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="56dp" android:layout_height="56dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
app:cardCornerRadius="28dp" app:cardCornerRadius="28dp"
app:cardElevation="0dp"> app:cardElevation="0dp">
@ -49,7 +50,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="horizontal"
android:background="?attr/colorPrimaryContainer"> android:background="?attr/colorSurfaceContainer">
<ImageView <ImageView
android:id="@+id/searchButton" android:id="@+id/searchButton"

View File

@ -13,12 +13,13 @@
android:layout_gravity="top" android:layout_gravity="top"
android:elevation="8dp" android:elevation="8dp"
android:outlineProvider="none" android:outlineProvider="none"
android:background="?attr/colorSurfaceContainer"
tools:layout_height="@dimen/external_player_height" /> tools:layout_height="@dimen/external_player_height" />
<RelativeLayout <RelativeLayout
android:id="@+id/playerContent"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent">
android:fitsSystemWindows="true">
<com.google.android.material.appbar.MaterialToolbar <com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar" android:id="@+id/toolbar"

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingHorizontal="16dp"
android:paddingVertical="4dp">
<ImageView
android:id="@+id/coverImage"
android:layout_width="@dimen/thumbnail_length_navlist"
android:layout_height="@dimen/thumbnail_length_navlist"
android:importantForAccessibility="no"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:scaleType="centerInside"
tools:src="@drawable/ic_download_black" />
<TextView
android:id="@+id/titleLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:ellipsize="end"
android:lines="1"
android:singleLine="true"
android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/text_size_navdrawer"
tools:text="Navigation item title" />
</LinearLayout>

View File

@ -6,8 +6,7 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:clipToPadding="false" android:clipToPadding="false">
android:padding="4dp">
<androidx.cardview.widget.CardView <androidx.cardview.widget.CardView
android:id="@+id/card" android:id="@+id/card"
@ -15,8 +14,10 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:clickable="true" android:clickable="true"
android:foreground="?android:attr/selectableItemBackground" android:foreground="?android:attr/selectableItemBackground"
android:layout_margin="4dp"
app:cardBackgroundColor="?attr/colorSurfaceContainer"
app:cardCornerRadius="12dp" app:cardCornerRadius="12dp"
app:cardElevation="0dp"> app:cardElevation="1dp">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -1,46 +1,68 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout <LinearLayout
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" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer_layout" android:id="@+id/main_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"
tools:ignore="InconsistentLayout" tools:ignore="InconsistentLayout"
tools:viewBindingIgnore="true"> tools:viewBindingIgnore="true">
<!-- InconsistentLayout: Tablet layout does not have a drawer --> <!-- InconsistentLayout: Tablet layout does not have a drawer -->
<!-- viewBindingIgnore: Configurations for main.xml must <!-- viewBindingIgnore: Configurations for main.xml must
agree on the root element's ID --> agree on the root element's ID -->
<androidx.coordinatorlayout.widget.CoordinatorLayout <androidx.drawerlayout.widget.DrawerLayout
android:id="@+id/overview_coordinator_layout" android:id="@+id/drawer_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="0dp"
android:layout_weight="1">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/overview_coordinator_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/main_content_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentTop="true"
android:foreground="?android:windowContentOverlay"
tools:background="@android:color/holo_red_dark" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/audioplayerFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:attr/colorBackground"
android:elevation="8dp"
android:visibility="gone"
android:fitsSystemWindows="false"
app:layout_behavior="de.danoeh.antennapod.ui.view.LockableBottomSheetBehavior" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<androidx.fragment.app.FragmentContainerView <androidx.fragment.app.FragmentContainerView
android:id="@+id/main_view" android:id="@+id/navDrawerFragment"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_alignParentTop="true" android:layout_gravity="start"
android:foreground="?android:windowContentOverlay" android:orientation="vertical" />
tools:background="@android:color/holo_red_dark" />
<androidx.fragment.app.FragmentContainerView </androidx.drawerlayout.widget.DrawerLayout>
android:id="@+id/audioplayerFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:attr/colorBackground"
android:elevation="8dp"
android:visibility="gone"
app:layout_behavior="de.danoeh.antennapod.ui.view.LockableBottomSheetBehavior" />
</androidx.coordinatorlayout.widget.CoordinatorLayout> <com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottomNavigationView"
<androidx.fragment.app.FragmentContainerView
android:id="@+id/navDrawerFragment"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="wrap_content"
android:layout_gravity="start" app:labelVisibilityMode="labeled" />
android:orientation="vertical" />
</androidx.drawerlayout.widget.DrawerLayout> <View
android:id="@+id/bottom_padding"
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="?attr/colorSurfaceContainer" />
</LinearLayout>

View File

@ -17,4 +17,16 @@
<item name="view_type_subscription_grid_with_title" type="id"/> <item name="view_type_subscription_grid_with_title" type="id"/>
<item name="view_type_subscription_grid_without_title" type="id"/> <item name="view_type_subscription_grid_without_title" type="id"/>
<item name="view_type_subscription_list" type="id"/> <item name="view_type_subscription_list" type="id"/>
<!-- Bottom navigation -->
<item name="bottom_navigation_home" type="id"/>
<item name="bottom_navigation_queue" type="id"/>
<item name="bottom_navigation_inbox" type="id"/>
<item name="bottom_navigation_episodes" type="id"/>
<item name="bottom_navigation_downloads" type="id"/>
<item name="bottom_navigation_history" type="id"/>
<item name="bottom_navigation_addfeed" type="id"/>
<item name="bottom_navigation_subscriptions" type="id"/>
<item name="bottom_navigation_more" type="id"/>
<item name="bottom_navigation_settings" type="id"/>
</resources> </resources>

View File

@ -21,7 +21,7 @@ project.ext {
recyclerViewVersion = "1.2.1" recyclerViewVersion = "1.2.1"
viewPager2Version = "1.1.0-beta01" viewPager2Version = "1.1.0-beta01"
workManagerVersion = "2.7.1" workManagerVersion = "2.7.1"
googleMaterialVersion = "1.7.0" googleMaterialVersion = "1.12.0"
// Third-party // Third-party
commonslangVersion = "3.6" commonslangVersion = "3.6"

View File

@ -62,6 +62,7 @@ public abstract class UserPreferences {
public static final String PREF_FILTER_FEED = "prefSubscriptionsFilter"; public static final String PREF_FILTER_FEED = "prefSubscriptionsFilter";
public static final String PREF_SUBSCRIPTION_TITLE = "prefSubscriptionTitle"; public static final String PREF_SUBSCRIPTION_TITLE = "prefSubscriptionTitle";
public static final String PREF_BACK_OPENS_DRAWER = "prefBackButtonOpensDrawer"; public static final String PREF_BACK_OPENS_DRAWER = "prefBackButtonOpensDrawer";
public static final String PREF_BOTTOM_NAVIGATION = "prefBottomNavigation";
public static final String PREF_QUEUE_KEEP_SORTED = "prefQueueKeepSorted"; public static final String PREF_QUEUE_KEEP_SORTED = "prefQueueKeepSorted";
public static final String PREF_QUEUE_KEEP_SORTED_ORDER = "prefQueueKeepSortedOrder"; public static final String PREF_QUEUE_KEEP_SORTED_ORDER = "prefQueueKeepSortedOrder";
@ -758,6 +759,14 @@ public abstract class UserPreferences {
return prefs.getBoolean(PREF_BACK_OPENS_DRAWER, false); return prefs.getBoolean(PREF_BACK_OPENS_DRAWER, false);
} }
public static boolean isBottomNavigationEnabled() {
return prefs.getBoolean(PREF_BOTTOM_NAVIGATION, false);
}
public static void setBottomNavigationEnabled(boolean enabled) {
prefs.edit().putBoolean(PREF_BOTTOM_NAVIGATION, enabled).apply();
}
public static boolean timeRespectsSpeed() { public static boolean timeRespectsSpeed() {
return prefs.getBoolean(PREF_TIME_RESPECTS_SPEED, false); return prefs.getBoolean(PREF_TIME_RESPECTS_SPEED, false);
} }

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000000"
android:pathData="M12,16A2,2 0 0,1 14,18A2,2 0 0,1 12,20A2,2 0 0,1 10,18A2,2 0 0,1 12,16M12,10A2,2 0 0,1 14,12A2,2 0 0,1 12,14A2,2 0 0,1 10,12A2,2 0 0,1 12,10M12,4A2,2 0 0,1 14,6A2,2 0 0,1 12,8A2,2 0 0,1 10,6A2,2 0 0,1 12,4Z" />
</vector>

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?attr/action_icon_color"
android:pathData="M20.71,7.04C21.1,6.65 21.1,6 20.71,5.63L18.37,3.29C18,2.9 17.35,2.9 16.96,3.29L15.12,5.12L18.87,8.87M3,17.25V21H6.75L17.81,9.93L14.06,6.18L3,17.25Z" />
</vector>

View File

@ -3,7 +3,5 @@
<style name="Theme.AntennaPod.Dynamic.Light" parent="Theme.Base.AntennaPod.Dynamic.Light"> <style name="Theme.AntennaPod.Dynamic.Light" parent="Theme.Base.AntennaPod.Dynamic.Light">
<item name="android:statusBarColor">@android:color/transparent</item> <item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:windowLightStatusBar">true</item> <item name="android:windowLightStatusBar">true</item>
<!-- To make icons visible -->
<item name="android:navigationBarColor">@color/grey600</item>
</style> </style>
</resources> </resources>

View File

@ -3,8 +3,6 @@
<style name="Theme.AntennaPod.Dynamic.Light" parent="Theme.Base.AntennaPod.Dynamic.Light"> <style name="Theme.AntennaPod.Dynamic.Light" parent="Theme.Base.AntennaPod.Dynamic.Light">
<item name="android:statusBarColor">@android:color/transparent</item> <item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:windowLightStatusBar">true</item> <item name="android:windowLightStatusBar">true</item>
<item name="android:navigationBarColor">@color/background_light</item>
<item name="android:navigationBarDividerColor">@color/navigation_bar_divider_light</item>
<item name="android:windowLightNavigationBar">true</item> <item name="android:windowLightNavigationBar">true</item>
</style> </style>
</resources> </resources>

View File

@ -11,14 +11,13 @@
<color name="feed_text_bg">#55333333</color> <color name="feed_text_bg">#55333333</color>
<!-- Theme colors --> <!-- Theme colors -->
<color name="background_light">#FFFFFF</color> <color name="background_light">#f9fcff</color>
<color name="background_elevated_light">#EFEEEE</color> <color name="background_elevated_light">#EFEEEE</color>
<color name="background_darktheme">#21272b</color> <color name="background_darktheme">#21272b</color>
<color name="background_elevated_darktheme">#2D3337</color> <color name="background_elevated_darktheme">#2D3337</color>
<color name="non_square_icon_background">#22777777</color> <color name="non_square_icon_background">#22777777</color>
<color name="seek_background_light">#90000000</color> <color name="seek_background_light">#90000000</color>
<color name="seek_background_dark">#905B5B5B</color> <color name="seek_background_dark">#905B5B5B</color>
<color name="navigation_bar_divider_light">#1F000000</color>
<color name="accent_light">#0078C2</color> <color name="accent_light">#0078C2</color>
<color name="accent_dark">#3D8BFF</color> <color name="accent_dark">#3D8BFF</color>

View File

@ -27,6 +27,7 @@
<item name="android:splitMotionEvents">false</item> <item name="android:splitMotionEvents">false</item>
<item name="android:fitsSystemWindows">false</item> <item name="android:fitsSystemWindows">false</item>
<item name="android:windowContentTransitions">true</item> <item name="android:windowContentTransitions">true</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
<item name="toolbarStyle">@style/Style.AntennaPod.Toolbar</item> <item name="toolbarStyle">@style/Style.AntennaPod.Toolbar</item>
<item name="preferenceTheme">@style/AppPreferenceThemeOverlay</item> <item name="preferenceTheme">@style/AppPreferenceThemeOverlay</item>
</style> </style>
@ -44,6 +45,8 @@
<item name="android:colorBackground">@color/background_light</item> <item name="android:colorBackground">@color/background_light</item>
<item name="colorSurface">@color/background_light</item> <item name="colorSurface">@color/background_light</item>
<item name="colorSurfaceVariant">#D3DCE0</item> <item name="colorSurfaceVariant">#D3DCE0</item>
<item name="colorSurfaceContainer">#EBEEF3</item>
<item name="colorSecondaryContainer">#D3DCE0</item>
</style> </style>
<style name="Theme.AntennaPod.Dynamic.Dark" parent="Theme.Base.AntennaPod.Dynamic.Dark"> <style name="Theme.AntennaPod.Dynamic.Dark" parent="Theme.Base.AntennaPod.Dynamic.Dark">
@ -70,7 +73,7 @@
<item name="android:statusBarColor">@android:color/transparent</item> <item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:windowLightStatusBar" tools:targetApi="m">false</item> <item name="android:windowLightStatusBar" tools:targetApi="m">false</item>
<item name="android:windowContentTransitions">true</item> <item name="android:windowContentTransitions">true</item>
<item name="android:navigationBarColor">@color/background_darktheme</item> <item name="android:navigationBarColor">@android:color/transparent</item>
<item name="toolbarStyle">@style/Style.AntennaPod.Toolbar</item> <item name="toolbarStyle">@style/Style.AntennaPod.Toolbar</item>
<item name="preferenceTheme">@style/AppPreferenceThemeOverlay</item> <item name="preferenceTheme">@style/AppPreferenceThemeOverlay</item>
</style> </style>
@ -88,6 +91,8 @@
<item name="android:colorBackground">@color/background_darktheme</item> <item name="android:colorBackground">@color/background_darktheme</item>
<item name="colorSurface">@color/background_darktheme</item> <item name="colorSurface">@color/background_darktheme</item>
<item name="colorSurfaceVariant">#2F3B4F</item> <item name="colorSurfaceVariant">#2F3B4F</item>
<item name="colorSurfaceContainer">#1C2024</item>
<item name="colorSecondaryContainer">#2F3B4F</item>
</style> </style>
<style name="Theme.AntennaPod.Dynamic.TrueBlack" parent="Theme.AntennaPod.Dynamic.Dark"> <style name="Theme.AntennaPod.Dynamic.TrueBlack" parent="Theme.AntennaPod.Dynamic.Dark">

View File

@ -454,8 +454,10 @@
<string name="pref_black_theme_message">Use full black for the dark theme</string> <string name="pref_black_theme_message">Use full black for the dark theme</string>
<string name="pref_tinted_theme_title">Dynamic colors</string> <string name="pref_tinted_theme_title">Dynamic colors</string>
<string name="pref_tinted_theme_message">Adapt app colors based on the wallpaper</string> <string name="pref_tinted_theme_message">Adapt app colors based on the wallpaper</string>
<string name="pref_nav_drawer_items_title">Set navigation drawer items</string> <string name="bottom_navigation">Bottom navigation</string>
<string name="pref_nav_drawer_items_sum">Change which items appear in the navigation drawer</string> <string name="bottom_navigation_summary">Beta feature: Access the most important screens from everywhere, in a single tap</string>
<string name="pref_nav_drawer_items_title">Customize navigation</string>
<string name="pref_nav_drawer_items_sum">Change which items appear in the navigation drawer or bottom navigation</string>
<string name="pref_nav_drawer_feed_order_title">Set subscription order</string> <string name="pref_nav_drawer_feed_order_title">Set subscription order</string>
<string name="pref_nav_drawer_feed_order_sum">Change the order of your subscriptions</string> <string name="pref_nav_drawer_feed_order_sum">Change the order of your subscriptions</string>
<string name="pref_nav_drawer_feed_counter_title">Set subscription counter</string> <string name="pref_nav_drawer_feed_counter_title">Set subscription counter</string>

View File

@ -80,10 +80,14 @@
android:title="@string/pref_default_page" android:title="@string/pref_default_page"
android:summary="@string/pref_default_page_sum" android:summary="@string/pref_default_page_sum"
android:defaultValue="HomeFragment"/> android:defaultValue="HomeFragment"/>
<SwitchPreferenceCompat
android:title="@string/bottom_navigation"
android:summary="@string/bottom_navigation_summary"
android:key="prefBottomNavigation" />
<Preference <Preference
android:key="prefHiddenDrawerItems" android:key="prefHiddenDrawerItems"
android:summary="@string/pref_nav_drawer_items_sum" android:summary="@string/pref_nav_drawer_items_sum"
android:title="@string/pref_nav_drawer_items_title"/> android:title="@string/pref_nav_drawer_items_title"/>
<SwitchPreferenceCompat <SwitchPreferenceCompat
android:key="prefBackButtonOpensDrawer" android:key="prefBackButtonOpensDrawer"
android:title="@string/pref_back_button_opens_drawer" android:title="@string/pref_back_button_opens_drawer"