Support reordering drawer items (#7310)

This commit is contained in:
ByteHamster 2024-08-08 22:26:07 +02:00 committed by GitHub
parent 895987e5aa
commit f7faf3c8c3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 442 additions and 421 deletions

View File

@ -1,20 +1,15 @@
package de.test.antennapod.ui;
import android.content.Intent;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.espresso.contrib.DrawerActions;
import androidx.test.espresso.intent.rule.IntentsTestRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.ui.screen.preferences.PreferenceActivity;
import de.danoeh.antennapod.ui.screen.download.CompletedDownloadsFragment;
import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.storage.preferences.UserPreferences;
import de.danoeh.antennapod.ui.screen.AllEpisodesFragment;
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.preferences.PreferenceActivity;
import de.test.antennapod.EspressoTestUtils;
import org.junit.After;
import org.junit.Before;
@ -23,13 +18,10 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Collections;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.action.ViewActions.longClick;
import static androidx.test.espresso.action.ViewActions.swipeUp;
import static androidx.test.espresso.intent.Intents.intended;
import static androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent;
@ -40,10 +32,7 @@ import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static de.test.antennapod.EspressoTestUtils.onDrawerItem;
import static de.test.antennapod.EspressoTestUtils.waitForView;
import static de.test.antennapod.NthMatcher.first;
import static org.hamcrest.Matchers.allOf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
* User interface tests for MainActivity drawer.
@ -78,7 +67,7 @@ public class NavigationDrawerTest {
@Test
public void testClickNavDrawer() throws Exception {
uiTestUtils.addLocalFeedData(false);
UserPreferences.setHiddenDrawerItems(new ArrayList<>());
UserPreferences.setDrawerItemOrder(Collections.emptyList(), Collections.emptyList());
activityRule.launchActivity(new Intent());
// home
@ -147,85 +136,4 @@ public class NavigationDrawerTest {
onView(withText(R.string.settings_label)).perform(click());
intended(hasComponent(PreferenceActivity.class.getName()));
}
@Test
public void testDrawerPreferencesHideSomeElements() {
UserPreferences.setHiddenDrawerItems(new ArrayList<>());
activityRule.launchActivity(new Intent());
openNavDrawer();
onDrawerItem(withText(R.string.queue_label)).perform(longClick());
onView(withText(R.string.episodes_label)).perform(click());
onView(withId(R.id.contentPanel)).perform(swipeUp());
onView(withText(R.string.playback_history_label)).perform(click());
onView(withText(R.string.confirm_label)).perform(click());
List<String> hidden = UserPreferences.getHiddenDrawerItems();
assertEquals(2, hidden.size());
assertTrue(hidden.contains(AllEpisodesFragment.TAG));
assertTrue(hidden.contains(PlaybackHistoryFragment.TAG));
}
@Test
public void testDrawerPreferencesUnhideSomeElements() {
List<String> hidden = Arrays.asList(PlaybackHistoryFragment.TAG, CompletedDownloadsFragment.TAG);
UserPreferences.setHiddenDrawerItems(hidden);
activityRule.launchActivity(new Intent());
openNavDrawer();
onView(first(withText(R.string.queue_label))).perform(longClick());
onView(withText(R.string.queue_label)).perform(click());
onView(withId(R.id.contentPanel)).perform(swipeUp());
onView(withText(R.string.downloads_label)).perform(click());
onView(withText(R.string.confirm_label)).perform(click());
hidden = UserPreferences.getHiddenDrawerItems();
assertEquals(2, hidden.size());
assertTrue(hidden.contains(QueueFragment.TAG));
assertTrue(hidden.contains(PlaybackHistoryFragment.TAG));
}
@Test
public void testDrawerPreferencesHideAllElements() {
UserPreferences.setHiddenDrawerItems(new ArrayList<>());
activityRule.launchActivity(new Intent());
String[] titles = activityRule.getActivity().getResources().getStringArray(R.array.nav_drawer_titles);
openNavDrawer();
onView(first(withText(R.string.queue_label))).perform(longClick());
for (int i = 0; i < titles.length; i++) {
String title = titles[i];
onView(allOf(withText(title), isDisplayed())).perform(click());
if (i == 3) {
onView(withId(R.id.contentPanel)).perform(swipeUp());
}
}
onView(withText(R.string.confirm_label)).perform(click());
List<String> hidden = UserPreferences.getHiddenDrawerItems();
assertEquals(titles.length, hidden.size());
for (String tag : NavDrawerFragment.NAV_DRAWER_TAGS) {
assertTrue(hidden.contains(tag));
}
}
@Test
public void testDrawerPreferencesHideCurrentElement() {
UserPreferences.setHiddenDrawerItems(new ArrayList<>());
activityRule.launchActivity(new Intent());
openNavDrawer();
onView(withText(R.string.downloads_label)).perform(click());
openNavDrawer();
onView(first(withText(R.string.queue_label))).perform(longClick());
onView(withId(R.id.contentPanel)).perform(swipeUp());
onView(first(withText(R.string.downloads_label))).perform(click());
onView(withText(R.string.confirm_label)).perform(click());
List<String> hidden = UserPreferences.getHiddenDrawerItems();
assertEquals(1, hidden.size());
assertTrue(hidden.contains(CompletedDownloadsFragment.TAG));
}
}

View File

@ -139,7 +139,7 @@ public class MainActivity extends CastEnabledActivity {
loadFragment(UserPreferences.getDefaultPage(), null);
} else {
String lastFragment = NavDrawerFragment.getLastNavFragment(this);
if (ArrayUtils.contains(NavDrawerFragment.NAV_DRAWER_TAGS, lastFragment)) {
if (ArrayUtils.contains(getResources().getStringArray(R.array.nav_drawer_section_tags), lastFragment)) {
loadFragment(lastFragment, null);
} else {
try {

View File

@ -1,50 +1,86 @@
package de.danoeh.antennapod.ui.screen.drawer;
import android.content.Context;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import androidx.annotation.NonNull;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.storage.preferences.UserPreferences;
import de.danoeh.antennapod.ui.screen.drawer.NavDrawerFragment;
import de.danoeh.antennapod.ui.screen.preferences.ReorderDialog;
import de.danoeh.antennapod.ui.screen.preferences.ReorderDialogItem;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class DrawerPreferencesDialog {
public static void show(Context context, Runnable callback) {
final List<String> hiddenDrawerItems = UserPreferences.getHiddenDrawerItems();
final String[] navTitles = context.getResources().getStringArray(R.array.nav_drawer_titles);
boolean[] checked = new boolean[NavDrawerFragment.NAV_DRAWER_TAGS.length];
for (int i = 0; i < NavDrawerFragment.NAV_DRAWER_TAGS.length; i++) {
String tag = NavDrawerFragment.NAV_DRAWER_TAGS[i];
if (!hiddenDrawerItems.contains(tag)) {
checked[i] = true;
}
}
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(context);
builder.setTitle(R.string.drawer_preferences);
builder.setMultiChoiceItems(navTitles, checked, (dialog, which, isChecked) -> {
if (isChecked) {
hiddenDrawerItems.remove(NavDrawerFragment.NAV_DRAWER_TAGS[which]);
} else {
hiddenDrawerItems.add(NavDrawerFragment.NAV_DRAWER_TAGS[which]);
}
});
builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> {
UserPreferences.setHiddenDrawerItems(hiddenDrawerItems);
public class DrawerPreferencesDialog extends ReorderDialog {
private static final String TAG_HIDDEN = "hidden";
private static final String TAG_SHOWN = "shown";
private final Runnable onSettingsChanged;
if (hiddenDrawerItems.contains(UserPreferences.getDefaultPage())) {
for (String tag : NavDrawerFragment.NAV_DRAWER_TAGS) {
if (!hiddenDrawerItems.contains(tag)) {
UserPreferences.setDefaultPage(tag);
break;
}
public DrawerPreferencesDialog(Context context, Runnable onSettingsChanged) {
super(context);
this.onSettingsChanged = onSettingsChanged;
}
@Override
protected int getTitle() {
return R.string.drawer_preferences;
}
@NonNull
protected List<ReorderDialogItem> getInitialItems() {
ArrayList<ReorderDialogItem> settingsDialogItems = new ArrayList<>();
settingsDialogItems.add(new ReorderDialogItem(ReorderDialogItem.ViewType.Header,
TAG_SHOWN, context.getString(R.string.section_shown)));
final List<String> drawerItemOrder = UserPreferences.getVisibleDrawerItemOrder();
for (String tag : drawerItemOrder) {
settingsDialogItems.add(new ReorderDialogItem(ReorderDialogItem.ViewType.Section,
tag, context.getString(NavListAdapter.getLabel(tag))));
}
settingsDialogItems.add(new ReorderDialogItem(ReorderDialogItem.ViewType.Header,
TAG_HIDDEN, context.getString(R.string.section_hidden)));
final List<String> hiddenDrawerItems = UserPreferences.getHiddenDrawerItems();
for (String sectionTag : hiddenDrawerItems) {
settingsDialogItems.add(new ReorderDialogItem(ReorderDialogItem.ViewType.Section,
sectionTag, context.getString(NavListAdapter.getLabel(sectionTag))));
}
return settingsDialogItems;
}
@Override
protected boolean onItemMove(int fromPosition, int toPosition) {
if (toPosition == 0 || fromPosition == 0) {
return false;
}
return super.onItemMove(fromPosition, toPosition);
}
@Override
protected void onReset() {
UserPreferences.setDrawerItemOrder(Collections.emptyList(), Collections.emptyList());
if (onSettingsChanged != null) {
onSettingsChanged.run();
}
}
@Override
protected void onConfirmed() {
final List<String> hiddenDrawerItems = getTagsAfterHeader(TAG_HIDDEN);
UserPreferences.setDrawerItemOrder(hiddenDrawerItems, getTagsWithoutHeaders());
if (hiddenDrawerItems.contains(UserPreferences.getDefaultPage())) {
for (String tag : context.getResources().getStringArray(R.array.nav_drawer_section_tags)) {
if (!hiddenDrawerItems.contains(tag)) {
UserPreferences.setDefaultPage(tag);
break;
}
}
}
if (callback != null) {
callback.run();
}
});
builder.setNegativeButton(R.string.cancel_label, null);
builder.create().show();
if (onSettingsChanged != null) {
onSettingsChanged.run();
}
}
}

View File

@ -34,13 +34,6 @@ import com.google.android.material.shape.MaterialShapeDrawable;
import com.google.android.material.shape.ShapeAppearanceModel;
import de.danoeh.antennapod.net.download.service.episode.autodownload.EpisodeCleanupAlgorithmFactory;
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.queue.QueueFragment;
import de.danoeh.antennapod.ui.screen.download.CompletedDownloadsFragment;
import de.danoeh.antennapod.ui.screen.subscriptions.SubscriptionFragment;
import org.apache.commons.lang3.StringUtils;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
@ -85,18 +78,6 @@ public class NavDrawerFragment extends Fragment implements SharedPreferences.OnS
public static final String PREF_NAME = "NavDrawerPrefs";
public static final String TAG = "NavDrawerFragment";
public static final String[] NAV_DRAWER_TAGS = {
HomeFragment.TAG,
QueueFragment.TAG,
InboxFragment.TAG,
AllEpisodesFragment.TAG,
SubscriptionFragment.TAG,
CompletedDownloadsFragment.TAG,
PlaybackHistoryFragment.TAG,
AddFeedFragment.TAG,
NavListAdapter.SUBSCRIPTION_LIST_TAG
};
private NavDrawerData navDrawerData;
private int reclaimableSpace = 0;
private List<NavDrawerData.DrawerItem> flatItemList;
@ -396,7 +377,7 @@ public class NavDrawerFragment extends Fragment implements SharedPreferences.OnS
@Override
public boolean onItemLongClick(int position) {
if (position < navAdapter.getFragmentTags().size()) {
DrawerPreferencesDialog.show(getContext(), () -> {
new DrawerPreferencesDialog(getContext(), () -> {
navAdapter.notifyDataSetChanged();
if (UserPreferences.getHiddenDrawerItems().contains(getLastNavFragment(getContext()))) {
new MainActivityStarter(getContext())
@ -404,7 +385,7 @@ public class NavDrawerFragment extends Fragment implements SharedPreferences.OnS
.withDrawerOpen()
.start();
}
});
}).show();
return true;
} else {
contextPressedItem = flatItemList.get(position - navAdapter.getSubscriptionOffset());

View File

@ -7,9 +7,6 @@ import android.os.Build;
import android.view.ContextMenu;
import android.view.InputDevice;
import android.view.LayoutInflater;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.preference.PreferenceManager;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
@ -17,32 +14,34 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.bumptech.glide.load.resource.bitmap.FitCenter;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.bitmap.FitCenter;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.bumptech.glide.request.RequestOptions;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.ui.common.ImagePlaceholder;
import de.danoeh.antennapod.ui.screen.preferences.PreferenceActivity;
import de.danoeh.antennapod.ui.screen.AllEpisodesFragment;
import de.danoeh.antennapod.ui.screen.download.CompletedDownloadsFragment;
import de.danoeh.antennapod.ui.screen.InboxFragment;
import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.storage.preferences.UserPreferences;
import de.danoeh.antennapod.storage.database.NavDrawerData;
import de.danoeh.antennapod.storage.preferences.UserPreferences;
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.PlaybackHistoryFragment;
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.queue.QueueFragment;
import de.danoeh.antennapod.ui.screen.subscriptions.SubscriptionFragment;
import de.danoeh.antennapod.ui.screen.home.HomeFragment;
import org.apache.commons.lang3.ArrayUtils;
import java.lang.ref.WeakReference;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -63,7 +62,6 @@ public class NavListAdapter extends RecyclerView.Adapter<NavListAdapter.Holder>
public static final String SUBSCRIPTION_LIST_TAG = "SubscriptionList";
private final List<String> fragmentTags = new ArrayList<>();
private final String[] titles;
private final ItemAccess itemAccess;
private final WeakReference<Activity> activity;
public boolean showSubscriptionList = true;
@ -71,24 +69,20 @@ public class NavListAdapter extends RecyclerView.Adapter<NavListAdapter.Holder>
public NavListAdapter(ItemAccess itemAccess, Activity context) {
this.itemAccess = itemAccess;
this.activity = new WeakReference<>(context);
titles = context.getResources().getStringArray(R.array.nav_drawer_titles);
loadItems();
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
prefs.registerOnSharedPreferenceChangeListener(this);
}
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (UserPreferences.PREF_HIDDEN_DRAWER_ITEMS.equals(key)) {
if (UserPreferences.PREF_HIDDEN_DRAWER_ITEMS.equals(key)
|| UserPreferences.PREF_DRAWER_ITEM_ORDER.equals(key)) {
loadItems();
}
}
private void loadItems() {
List<String> newTags = new ArrayList<>(Arrays.asList(NavDrawerFragment.NAV_DRAWER_TAGS));
List<String> hiddenFragments = UserPreferences.getHiddenDrawerItems();
newTags.removeAll(hiddenFragments);
List<String> newTags = UserPreferences.getVisibleDrawerItemOrder();
if (newTags.contains(SUBSCRIPTION_LIST_TAG)) {
// we never want SUBSCRIPTION_LIST_TAG to be in 'tags'
@ -106,12 +100,32 @@ public class NavListAdapter extends RecyclerView.Adapter<NavListAdapter.Holder>
notifyDataSetChanged();
}
public String getLabel(String tag) {
int index = ArrayUtils.indexOf(NavDrawerFragment.NAV_DRAWER_TAGS, tag);
return titles[index];
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 @DrawableRes int getDrawable(String tag) {
private static @DrawableRes int getDrawable(String tag) {
switch (tag) {
case HomeFragment.TAG:
return R.drawable.ic_home;
@ -224,7 +238,7 @@ public class NavListAdapter extends RecyclerView.Adapter<NavListAdapter.Holder>
}
}
private void bindNavView(String title, int position, NavHolder holder) {
private void bindNavView(@StringRes int title, int position, NavHolder holder) {
Activity context = activity.get();
if (context == null) {
return;

View File

@ -139,7 +139,7 @@ public class HomeFragment extends Fragment implements Toolbar.OnMenuItemClickLis
@Override
public boolean onMenuItemClick(MenuItem item) {
if (item.getItemId() == R.id.homesettings_items) {
HomeSectionsSettingsDialog.open(getContext(), this::populateSectionList);
new HomeSectionsSettingsDialog(getContext(), this::populateSectionList).show();
return true;
} else if (item.getItemId() == R.id.refresh_item) {
FeedUpdateManager.getInstance().runOnceOrAsk(requireContext());

View File

@ -1,148 +0,0 @@
package de.danoeh.antennapod.ui.screen.home.settingsdialog;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.util.Consumer;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import de.danoeh.antennapod.databinding.ChooseHomeScreenOrderDialogEntryBinding;
import de.danoeh.antennapod.databinding.ChooseHomeScreenOrderDialogHeaderBinding;
class HomeScreenSettingDialogAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int HEADER_VIEW = 0;
private static final int ITEM_VIEW = 1;
private final List<HomeScreenSettingsDialogItem> settingsDialogItems;
@Nullable private Consumer<ItemViewHolder> dragListener;
public HomeScreenSettingDialogAdapter(@NonNull List<HomeScreenSettingsDialogItem> dialogItems) {
settingsDialogItems = dialogItems;
}
public void setDragListener(@Nullable Consumer<ItemViewHolder> dragListener) {
this.dragListener = dragListener;
}
@NonNull
public List<String> getOrderedSectionTags() {
List<String> orderedSectionTags = new ArrayList<>();
for (HomeScreenSettingsDialogItem item: settingsDialogItems) {
if (item.getViewType() == HomeScreenSettingsDialogItem.ViewType.Header) {
continue;
}
orderedSectionTags.add(item.getTitle());
}
return orderedSectionTags;
}
public List<String> getHiddenSectionTags() {
List<String> hiddenSections = new ArrayList<>();
for (int i = settingsDialogItems.size() - 1; i >= 0; i--) {
HomeScreenSettingsDialogItem item = settingsDialogItems.get(i);
if (item.getViewType() == HomeScreenSettingsDialogItem.ViewType.Header) {
return hiddenSections;
}
hiddenSections.add(item.getTitle());
}
Collections.reverse(hiddenSections);
return hiddenSections;
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
if (viewType == HEADER_VIEW) {
ChooseHomeScreenOrderDialogHeaderBinding binding = ChooseHomeScreenOrderDialogHeaderBinding.inflate(
inflater, parent, false);
return new HeaderViewHolder(binding.getRoot(), binding.headerLabel);
}
ChooseHomeScreenOrderDialogEntryBinding binding = ChooseHomeScreenOrderDialogEntryBinding.inflate(
inflater, parent, false);
return new ItemViewHolder(binding.getRoot(), binding.sectionLabel, binding.dragHandle);
}
@Override
public int getItemViewType(int position) {
HomeScreenSettingsDialogItem.ViewType viewType = settingsDialogItems.get(position).getViewType();
boolean isHeader = viewType == HomeScreenSettingsDialogItem.ViewType.Header;
return isHeader ? HEADER_VIEW : ITEM_VIEW;
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
String title = settingsDialogItems.get(position).getTitle();
if (holder instanceof HeaderViewHolder) {
HeaderViewHolder headerViewHolder = (HeaderViewHolder) holder;
headerViewHolder.categoryLabel.setText(title);
} else if (holder instanceof ItemViewHolder) {
ItemViewHolder itemViewHolder = (ItemViewHolder) holder;
String sectionName = HomePreferences.getNameFromTag(itemViewHolder.nameLabel.getContext(), title);
itemViewHolder.nameLabel.setText(sectionName);
itemViewHolder.dragImage.setOnTouchListener((view, motionEvent) -> {
if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
if (dragListener != null) {
dragListener.accept(itemViewHolder);
}
}
return true;
});
}
}
@Override
public int getItemCount() {
return settingsDialogItems.size();
}
public boolean onItemMove(int fromPosition, int toPosition) {
if (fromPosition < toPosition) {
for (int i = fromPosition; i < toPosition; i++) {
Collections.swap(settingsDialogItems, i, i + 1);
}
} else {
for (int i = fromPosition; i > toPosition; i--) {
Collections.swap(settingsDialogItems, i, i - 1);
}
}
notifyItemMoved(fromPosition, toPosition);
return true;
}
static class ItemViewHolder extends RecyclerView.ViewHolder {
private final TextView nameLabel;
private final ImageView dragImage;
ItemViewHolder(@NonNull View itemView, TextView nameLabel, ImageView dragImage) {
super(itemView);
this.nameLabel = nameLabel;
this.dragImage = dragImage;
}
}
static class HeaderViewHolder extends RecyclerView.ViewHolder {
private final TextView categoryLabel;
HeaderViewHolder(@NonNull View itemView, @NonNull TextView categoryLabel) {
super(itemView);
this.categoryLabel = categoryLabel;
}
}
}

View File

@ -1,82 +1,71 @@
package de.danoeh.antennapod.ui.screen.home.settingsdialog;
import android.content.Context;
import android.view.LayoutInflater;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.databinding.ChooseHomeScreenOrderDialogBinding;
import de.danoeh.antennapod.ui.screen.preferences.ReorderDialog;
import de.danoeh.antennapod.ui.screen.preferences.ReorderDialogItem;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class HomeSectionsSettingsDialog {
public static void open(Context context, Runnable onSettingsChanged) {
LayoutInflater layoutInflater = LayoutInflater.from(context);
ChooseHomeScreenOrderDialogBinding viewBinding = ChooseHomeScreenOrderDialogBinding.inflate(layoutInflater);
public class HomeSectionsSettingsDialog extends ReorderDialog {
private final Runnable onSettingsChanged;
private static final String TAG_HIDDEN = "hidden";
private static final String TAG_SHOWN = "shown";
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(context);
builder.setTitle(R.string.configure_home);
builder.setView(viewBinding.getRoot());
RecyclerView recyclerView = viewBinding.recyclerView;
List<HomeScreenSettingsDialogItem> dialogItems = initialItemsSettingsDialog(context);
HomeScreenSettingDialogAdapter adapter = new HomeScreenSettingDialogAdapter(dialogItems);
configureRecyclerView(recyclerView, adapter, context);
builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> {
final List<String> sectionOrder = adapter.getOrderedSectionTags();
final List<String> hiddenSections = adapter.getHiddenSectionTags();
HomePreferences.saveChanges(context, hiddenSections, sectionOrder);
onSettingsChanged.run();
});
builder.setNegativeButton(R.string.cancel_label, null);
builder.setNeutralButton(R.string.reset, (dialog, which) -> {
HomePreferences.saveChanges(context, Collections.emptyList(), Collections.emptyList());
onSettingsChanged.run();
});
builder.show();
public HomeSectionsSettingsDialog(Context context, Runnable onSettingsChanged) {
super(context);
this.onSettingsChanged = onSettingsChanged;
}
private static void configureRecyclerView(RecyclerView recyclerView,
HomeScreenSettingDialogAdapter adapter, Context context) {
ItemTouchCallback itemMoveCallback = new ItemTouchCallback() {
@Override
protected boolean onItemMove(int fromPosition, int toPosition) {
return adapter.onItemMove(fromPosition, toPosition);
}
};
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(itemMoveCallback);
itemTouchHelper.attachToRecyclerView(recyclerView);
adapter.setDragListener(itemTouchHelper::startDrag);
recyclerView.setLayoutManager(new LinearLayoutManager(context));
recyclerView.setAdapter(adapter);
@Override
protected int getTitle() {
return R.string.configure_home;
}
@NonNull
private static List<HomeScreenSettingsDialogItem> initialItemsSettingsDialog(@NonNull Context context) {
protected List<ReorderDialogItem> getInitialItems() {
final List<String> sectionTags = HomePreferences.getSortedSectionTags(context);
final List<String> hiddenSectionTags = HomePreferences.getHiddenSectionTags(context);
ArrayList<HomeScreenSettingsDialogItem> settingsDialogItems = new ArrayList<>();
ArrayList<ReorderDialogItem> settingsDialogItems = new ArrayList<>();
settingsDialogItems.add(new ReorderDialogItem(ReorderDialogItem.ViewType.Header,
TAG_SHOWN, context.getString(R.string.section_shown)));
for (String sectionTag: sectionTags) {
settingsDialogItems.add(new HomeScreenSettingsDialogItem(
HomeScreenSettingsDialogItem.ViewType.Section, sectionTag));
}
String hiddenText = context.getString(R.string.section_hidden);
settingsDialogItems.add(new HomeScreenSettingsDialogItem(
HomeScreenSettingsDialogItem.ViewType.Header, hiddenText));
for (String sectionTag: hiddenSectionTags) {
settingsDialogItems.add(new HomeScreenSettingsDialogItem(
HomeScreenSettingsDialogItem.ViewType.Section, sectionTag));
settingsDialogItems.add(new ReorderDialogItem(ReorderDialogItem.ViewType.Section, sectionTag,
HomePreferences.getNameFromTag(context, sectionTag)));
}
settingsDialogItems.add(new ReorderDialogItem(ReorderDialogItem.ViewType.Header,
TAG_HIDDEN, context.getString(R.string.section_hidden)));
for (String sectionTag: hiddenSectionTags) {
settingsDialogItems.add(new ReorderDialogItem(ReorderDialogItem.ViewType.Section, sectionTag,
HomePreferences.getNameFromTag(context, sectionTag)));
}
return settingsDialogItems;
}
@Override
protected boolean onItemMove(int fromPosition, int toPosition) {
if (toPosition == 0 || fromPosition == 0) {
return false;
}
return super.onItemMove(fromPosition, toPosition);
}
@Override
protected void onReset() {
HomePreferences.saveChanges(context, Collections.emptyList(), Collections.emptyList());
onSettingsChanged.run();
}
@Override
protected void onConfirmed() {
final List<String> sectionOrder = getTagsWithoutHeaders();
final List<String> hiddenSections = getTagsAfterHeader(TAG_HIDDEN);
HomePreferences.saveChanges(context, hiddenSections, sectionOrder);
onSettingsChanged.run();
}
}

View File

@ -0,0 +1,111 @@
package de.danoeh.antennapod.ui.screen.preferences;
import android.content.Context;
import android.view.LayoutInflater;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.databinding.ReorderDialogBinding;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public abstract class ReorderDialog {
protected final Context context;
protected final List<ReorderDialogItem> dialogItems;
private final ReorderDialogAdapter adapter;
public ReorderDialog(Context context) {
this.context = context;
dialogItems = getInitialItems();
adapter = new ReorderDialogAdapter(dialogItems);
}
public void show() {
LayoutInflater layoutInflater = LayoutInflater.from(context);
ReorderDialogBinding viewBinding = ReorderDialogBinding.inflate(layoutInflater);
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(context);
builder.setTitle(getTitle());
builder.setView(viewBinding.getRoot());
configureRecyclerView(viewBinding.recyclerView);
builder.setPositiveButton(android.R.string.ok, (dialog, which) -> onConfirmed());
builder.setNegativeButton(R.string.cancel_label, null);
builder.setNeutralButton(R.string.reset, (dialog, which) -> onReset());
builder.show();
}
private void configureRecyclerView(RecyclerView recyclerView) {
ReorderItemTouchCallback itemMoveCallback = new ReorderItemTouchCallback() {
@Override
protected boolean onItemMove(int fromPosition, int toPosition) {
return ReorderDialog.this.onItemMove(fromPosition, toPosition);
}
};
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(itemMoveCallback);
itemTouchHelper.attachToRecyclerView(recyclerView);
adapter.setDragListener(itemTouchHelper::startDrag);
LinearLayoutManager layoutManager = new LinearLayoutManager(context);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapter);
recyclerView.addItemDecoration(new DividerItemDecoration(context, layoutManager.getOrientation()));
}
protected boolean onItemMove(int fromPosition, int toPosition) {
if (fromPosition < toPosition) {
for (int i = fromPosition; i < toPosition; i++) {
Collections.swap(dialogItems, i, i + 1);
}
} else {
for (int i = fromPosition; i > toPosition; i--) {
Collections.swap(dialogItems, i, i - 1);
}
}
adapter.notifyItemMoved(fromPosition, toPosition);
return true;
}
@NonNull
public List<String> getTagsWithoutHeaders() {
List<String> orderedSectionTags = new ArrayList<>();
for (ReorderDialogItem item : dialogItems) {
if (item.getViewType() == ReorderDialogItem.ViewType.Header) {
continue;
}
orderedSectionTags.add(item.getTag());
}
return orderedSectionTags;
}
@NonNull
public List<String> getTagsAfterHeader(String tag) {
List<String> itemsAfterHeader = new ArrayList<>();
int i = 0;
while (dialogItems.get(i).getViewType() != ReorderDialogItem.ViewType.Header
|| !dialogItems.get(i).getTag().equals(tag)) {
i++;
}
i++;
while (i < dialogItems.size() && dialogItems.get(i).getViewType() != ReorderDialogItem.ViewType.Header) {
itemsAfterHeader.add(dialogItems.get(i).getTag());
i++;
}
return itemsAfterHeader;
}
protected abstract @StringRes int getTitle();
@NonNull
protected abstract List<ReorderDialogItem> getInitialItems();
protected abstract void onReset();
protected abstract void onConfirmed();
}

View File

@ -0,0 +1,96 @@
package de.danoeh.antennapod.ui.screen.preferences;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.util.Consumer;
import androidx.recyclerview.widget.RecyclerView;
import de.danoeh.antennapod.databinding.ReorderDialogEntryBinding;
import de.danoeh.antennapod.databinding.ReorderDialogHeaderBinding;
import java.util.List;
public class ReorderDialogAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int HEADER_VIEW = 0;
private static final int ITEM_VIEW = 1;
private final List<ReorderDialogItem> settingsDialogItems;
@Nullable private Consumer<ItemViewHolder> dragListener;
public ReorderDialogAdapter(@NonNull List<ReorderDialogItem> dialogItems) {
settingsDialogItems = dialogItems;
}
public void setDragListener(@Nullable Consumer<ItemViewHolder> dragListener) {
this.dragListener = dragListener;
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
if (viewType == HEADER_VIEW) {
ReorderDialogHeaderBinding binding = ReorderDialogHeaderBinding.inflate(inflater, parent, false);
return new HeaderViewHolder(binding.getRoot(), binding.headerLabel);
}
ReorderDialogEntryBinding binding = ReorderDialogEntryBinding.inflate(inflater, parent, false);
return new ItemViewHolder(binding.getRoot(), binding.sectionLabel, binding.dragHandle);
}
@Override
public int getItemViewType(int position) {
ReorderDialogItem.ViewType viewType = settingsDialogItems.get(position).getViewType();
boolean isHeader = viewType == ReorderDialogItem.ViewType.Header;
return isHeader ? HEADER_VIEW : ITEM_VIEW;
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
if (holder instanceof HeaderViewHolder) {
HeaderViewHolder headerViewHolder = (HeaderViewHolder) holder;
headerViewHolder.categoryLabel.setText(settingsDialogItems.get(position).getTitle());
} else if (holder instanceof ItemViewHolder) {
ItemViewHolder itemViewHolder = (ItemViewHolder) holder;
itemViewHolder.nameLabel.setText(settingsDialogItems.get(position).getTitle());
itemViewHolder.dragImage.setOnTouchListener((view, motionEvent) -> {
if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
if (dragListener != null) {
dragListener.accept(itemViewHolder);
}
}
return true;
});
}
}
@Override
public int getItemCount() {
return settingsDialogItems.size();
}
public static class ItemViewHolder extends RecyclerView.ViewHolder {
private final TextView nameLabel;
private final ImageView dragImage;
ItemViewHolder(@NonNull View itemView, TextView nameLabel, ImageView dragImage) {
super(itemView);
this.nameLabel = nameLabel;
this.dragImage = dragImage;
}
}
public static class HeaderViewHolder extends RecyclerView.ViewHolder {
private final TextView categoryLabel;
HeaderViewHolder(@NonNull View itemView, @NonNull TextView categoryLabel) {
super(itemView);
this.categoryLabel = categoryLabel;
}
}
}

View File

@ -1,16 +1,18 @@
package de.danoeh.antennapod.ui.screen.home.settingsdialog;
package de.danoeh.antennapod.ui.screen.preferences;
final class HomeScreenSettingsDialogItem {
enum ViewType {
public final class ReorderDialogItem {
public enum ViewType {
Section,
Header
}
private final ViewType viewType;
private final String tag;
private final String title;
HomeScreenSettingsDialogItem(ViewType viewType, String title) {
public ReorderDialogItem(ViewType viewType, String tag, String title) {
this.viewType = viewType;
this.tag = tag;
this.title = title;
}
@ -21,4 +23,8 @@ final class HomeScreenSettingsDialogItem {
public String getTitle() {
return title;
}
public String getTag() {
return tag;
}
}

View File

@ -1,10 +1,10 @@
package de.danoeh.antennapod.ui.screen.home.settingsdialog;
package de.danoeh.antennapod.ui.screen.preferences;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;
public abstract class ItemTouchCallback extends ItemTouchHelper.Callback {
public abstract class ReorderItemTouchCallback extends ItemTouchHelper.Callback {
@Override
public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;

View File

@ -65,7 +65,7 @@ public class UserInterfacePreferencesFragment extends PreferenceFragmentCompat {
findPreference(UserPreferences.PREF_HIDDEN_DRAWER_ITEMS)
.setOnPreferenceClickListener(preference -> {
DrawerPreferencesDialog.show(getContext(), null);
new DrawerPreferencesDialog(getContext(), null).show();
return true;
});

View File

@ -9,21 +9,22 @@
<ImageView
android:id="@+id/dragHandle"
android:layout_width="48dp"
android:layout_height="48dp"
android:paddingStart="16dp"
android:layout_height="40dp"
android:paddingStart="8dp"
android:paddingEnd="0dp"
android:paddingVertical="8dp"
android:paddingVertical="12dp"
android:layout_gravity="center_vertical"
android:contentDescription="@string/sort"
android:src="?attr/dragview_background" />
<TextView
android:id="@+id/sectionLabel"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_margin="8dp"
android:layout_gravity="center_vertical"
android:textColor="?android:attr/textColorPrimary"
android:textSize="18sp" />
android:textSize="16sp" />
</LinearLayout>

View File

@ -6,14 +6,15 @@
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingHorizontal="16dp"
android:paddingVertical="16dp">
android:paddingVertical="4dp"
android:background="?attr/colorSurfaceVariant">
<TextView
android:id="@+id/headerLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:textColor="?android:attr/textColorPrimary"
android:textSize="18sp" />
android:layout_margin="4dp"
android:textColor="?attr/colorOnSurfaceVariant"
android:textSize="16sp" />
</LinearLayout>

View File

@ -49,6 +49,7 @@ public abstract class UserPreferences {
public static final String PREF_THEME_BLACK = "prefThemeBlack";
public static final String PREF_TINTED_COLORS = "prefTintedColors";
public static final String PREF_HIDDEN_DRAWER_ITEMS = "prefHiddenDrawerItems";
public static final String PREF_DRAWER_ITEM_ORDER = "prefDrawerItemOrder";
public static final String PREF_DRAWER_FEED_ORDER = "prefDrawerFeedOrder";
public static final String PREF_DRAWER_FEED_COUNTER = "prefDrawerFeedIndicator";
public static final String PREF_EXPANDED_NOTIFICATION = "prefExpandNotify";
@ -194,6 +195,33 @@ public abstract class UserPreferences {
return new ArrayList<>(Arrays.asList(TextUtils.split(hiddenItems, ",")));
}
public static List<String> getVisibleDrawerItemOrder() {
String itemOrderStr = prefs.getString(PREF_DRAWER_ITEM_ORDER, "");
List<String> itemOrderTags = new ArrayList<>(Arrays.asList(TextUtils.split(itemOrderStr, ",")));
List<String> hiddenItemTags = getHiddenDrawerItems();
String[] sectionTags = context.getResources().getStringArray(R.array.nav_drawer_section_tags);
Arrays.sort(sectionTags, (String a, String b) -> Integer.signum(
indexOfOrMaxValue(itemOrderTags, a) - indexOfOrMaxValue(itemOrderTags, b)));
List<String> finalItemTags = new ArrayList<>();
for (String sectionTag: sectionTags) {
if (hiddenItemTags.contains(sectionTag)) {
continue;
}
finalItemTags.add(sectionTag);
}
return finalItemTags;
}
private static int indexOfOrMaxValue(List<String> haystack, String needle) {
int index = haystack.indexOf(needle);
return index == -1 ? Integer.MAX_VALUE : index;
}
public static void setDrawerItemOrder(List<String> hiddenItems, List<String> visibleItemsOrder) {
prefs.edit().putString(PREF_HIDDEN_DRAWER_ITEMS, TextUtils.join(",", hiddenItems)).apply();
prefs.edit().putString(PREF_DRAWER_ITEM_ORDER, TextUtils.join(",", visibleItemsOrder)).apply();
}
public static List<Integer> getFullNotificationButtons() {
String[] buttons = TextUtils.split(
prefs.getString(PREF_FULL_NOTIFICATION_BUTTONS,
@ -621,11 +649,6 @@ public abstract class UserPreferences {
prefs.edit().putBoolean(PREF_GPODNET_NOTIFICATIONS, true).apply();
}
public static void setHiddenDrawerItems(List<String> items) {
String str = TextUtils.join(",", items);
prefs.edit().putString(PREF_HIDDEN_DRAWER_ITEMS, str).apply();
}
public static void setFullNotificationButtons(List<Integer> items) {
String str = TextUtils.join(",", items);
prefs.edit().putString(PREF_FULL_NOTIFICATION_BUTTONS, str).apply();

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="nav_drawer_section_tags">
<item>HomeFragment</item>
<item>QueueFragment</item>
<item>NewEpisodesFragment</item>
<item>EpisodesFragment</item>
<item>SubscriptionFragment</item>
<item>DownloadsFragment</item>
<item>PlaybackHistoryFragment</item>
<item>AddFeedFragment</item>
<item>SubscriptionList</item>
</string-array>
</resources>

View File

@ -70,6 +70,7 @@
<string name="open_settings">Open settings</string>
<string name="configure_home">Configure Home Screen</string>
<string name="section_hidden">Hidden</string>
<string name="section_shown">Shown</string>
<!-- Download Statistics fragment -->
<string name="total_size_downloaded_podcasts">Total size of episodes on the device</string>

View File

@ -194,18 +194,6 @@
<item>-2</item>
</string-array>
<string-array name="nav_drawer_titles">
<item>@string/home_label</item>
<item>@string/queue_label</item>
<item>@string/inbox_label</item>
<item>@string/episodes_label</item>
<item>@string/subscriptions_label</item>
<item>@string/downloads_label</item>
<item>@string/playback_history_label</item>
<item>@string/add_feed_label</item>
<item>@string/subscriptions_list_label</item>
</string-array>
<string-array name="nav_drawer_feed_order_options">
<item>@string/drawer_feed_order_unplayed_episodes</item>
<item>@string/drawer_feed_order_alphabetical</item>