diff options
author | seeto <65185819+peakvalleytech@users.noreply.github.com> | 2021-08-23 14:46:38 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-08-23 23:46:38 +0200 |
commit | eacc90af299220c60fd504ef7970775aa344b44c (patch) | |
tree | 371df8b11c5c1e14515e662963ce3f61438a8bac /app | |
parent | 91967409cb9399637f7a7b6eef52c039c1145ef3 (diff) | |
download | AntennaPod-eacc90af299220c60fd504ef7970775aa344b44c.zip |
Feed multi select (#5261)
Diffstat (limited to 'app')
13 files changed, 739 insertions, 293 deletions
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/EpisodeItemListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/EpisodeItemListAdapter.java index 674b5f86e..f6b47d8e5 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/EpisodeItemListAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/EpisodeItemListAdapter.java @@ -76,21 +76,19 @@ public class EpisodeItemListAdapter extends SelectableAdapter<EpisodeItemViewHol int position = ArrayUtils.indexOf(ids, item.getId()); activity.loadChildFragment(ItemPagerFragment.newInstance(ids, position)); } else { - toggleSelection(pos); + toggleSelection(holder.getBindingAdapterPosition()); } }); holder.itemView.setOnCreateContextMenuListener(this); holder.itemView.setOnLongClickListener(v -> { - longPressedItem = item; - longPressedPosition = pos; + longPressedItem = getItem(holder.getBindingAdapterPosition()); + longPressedPosition = holder.getBindingAdapterPosition(); return false; }); if (inActionMode()) { holder.secondaryActionButton.setVisibility(View.GONE); - holder.selectCheckBox.setOnClickListener(v -> { - toggleSelection(pos); - }); + holder.selectCheckBox.setOnClickListener(v -> toggleSelection(holder.getBindingAdapterPosition())); holder.selectCheckBox.setChecked(isSelected(pos)); holder.selectCheckBox.setVisibility(View.VISIBLE); } else { diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java deleted file mode 100644 index 0af6960d7..000000000 --- a/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java +++ /dev/null @@ -1,152 +0,0 @@ -package de.danoeh.antennapod.adapter; - -import android.content.Context; - -import androidx.core.text.TextUtilsCompat; -import androidx.core.view.ViewCompat; -import androidx.fragment.app.Fragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.BaseAdapter; -import android.widget.ImageView; -import android.widget.TextView; - -import java.lang.ref.WeakReference; -import java.text.NumberFormat; -import java.util.Locale; - -import de.danoeh.antennapod.R; -import de.danoeh.antennapod.activity.MainActivity; -import de.danoeh.antennapod.model.feed.Feed; -import de.danoeh.antennapod.core.feed.LocalFeedUpdater; -import de.danoeh.antennapod.core.storage.NavDrawerData; -import de.danoeh.antennapod.fragment.FeedItemlistFragment; -import de.danoeh.antennapod.fragment.SubscriptionFragment; -import jp.shts.android.library.TriangleLabelView; - -/** - * Adapter for subscriptions - */ -public class SubscriptionsAdapter extends BaseAdapter implements AdapterView.OnItemClickListener { - /** the position in the view that holds the add item; 0 is the first, -1 is the last position */ - private static final String TAG = "SubscriptionsAdapter"; - - private final WeakReference<MainActivity> mainActivityRef; - private final ItemAccess itemAccess; - - public SubscriptionsAdapter(MainActivity mainActivity, ItemAccess itemAccess) { - this.mainActivityRef = new WeakReference<>(mainActivity); - this.itemAccess = itemAccess; - } - - @Override - public int getCount() { - return itemAccess.getCount(); - } - - @Override - public Object getItem(int position) { - return itemAccess.getItem(position); - } - - @Override - public boolean hasStableIds() { - return true; - } - - @Override - public long getItemId(int position) { - return ((NavDrawerData.DrawerItem) getItem(position)).id; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - Holder holder; - - if (convertView == null) { - holder = new Holder(); - - LayoutInflater layoutInflater = - (LayoutInflater) mainActivityRef.get().getSystemService(Context.LAYOUT_INFLATER_SERVICE); - convertView = layoutInflater.inflate(R.layout.subscription_item, parent, false); - holder.feedTitle = convertView.findViewById(R.id.txtvTitle); - holder.imageView = convertView.findViewById(R.id.imgvCover); - holder.count = convertView.findViewById(R.id.triangleCountView); - - - convertView.setTag(holder); - } else { - holder = (Holder) convertView.getTag(); - } - - final NavDrawerData.DrawerItem drawerItem = (NavDrawerData.DrawerItem) getItem(position); - if (drawerItem == null) { - return null; - } - - holder.feedTitle.setText(drawerItem.getTitle()); - holder.imageView.setContentDescription(drawerItem.getTitle()); - holder.feedTitle.setVisibility(View.VISIBLE); - - // Fix TriangleLabelView corner for RTL - if (TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault()) - == ViewCompat.LAYOUT_DIRECTION_RTL) { - holder.count.setCorner(TriangleLabelView.Corner.TOP_LEFT); - } - - if (drawerItem.getCounter() > 0) { - holder.count.setPrimaryText(NumberFormat.getInstance().format(drawerItem.getCounter())); - holder.count.setVisibility(View.VISIBLE); - } else { - holder.count.setVisibility(View.GONE); - } - - if (drawerItem.type == NavDrawerData.DrawerItem.Type.FEED) { - Feed feed = ((NavDrawerData.FeedDrawerItem) drawerItem).feed; - boolean textAndImageCombined = feed.isLocalFeed() - && LocalFeedUpdater.getDefaultIconUrl(convertView.getContext()).equals(feed.getImageUrl()); - new CoverLoader(mainActivityRef.get()) - .withUri(feed.getImageUrl()) - .withPlaceholderView(holder.feedTitle, textAndImageCombined) - .withCoverView(holder.imageView) - .load(); - } else { - new CoverLoader(mainActivityRef.get()) - .withResource(R.drawable.ic_folder) - .withPlaceholderView(holder.feedTitle, true) - .withCoverView(holder.imageView) - .load(); - } - return convertView; - } - - @Override - public void onItemClick(AdapterView<?> parent, View view, int position, long id) { - final NavDrawerData.DrawerItem drawerItem = (NavDrawerData.DrawerItem) getItem(position); - if (drawerItem == null) { - return; - } - if (drawerItem.type == NavDrawerData.DrawerItem.Type.FEED) { - Feed feed = ((NavDrawerData.FeedDrawerItem) drawerItem).feed; - Fragment fragment = FeedItemlistFragment.newInstance(feed.getId()); - mainActivityRef.get().loadChildFragment(fragment); - } else if (drawerItem.type == NavDrawerData.DrawerItem.Type.FOLDER) { - Fragment fragment = SubscriptionFragment.newInstance(drawerItem.getTitle()); - mainActivityRef.get().loadChildFragment(fragment); - } - } - - static class Holder { - public TextView feedTitle; - public ImageView imageView; - public TriangleLabelView count; - } - - public interface ItemAccess { - int getCount(); - - NavDrawerData.DrawerItem getItem(int position); - } -} diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsRecyclerAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsRecyclerAdapter.java new file mode 100644 index 000000000..689f4fcbc --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsRecyclerAdapter.java @@ -0,0 +1,245 @@ +package de.danoeh.antennapod.adapter; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.view.ContextMenu; +import android.view.LayoutInflater; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CheckBox; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.core.text.TextUtilsCompat; +import androidx.core.view.ViewCompat; +import androidx.fragment.app.Fragment; +import androidx.recyclerview.widget.RecyclerView; + +import java.lang.ref.WeakReference; +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.activity.MainActivity; +import de.danoeh.antennapod.core.feed.LocalFeedUpdater; +import de.danoeh.antennapod.core.storage.NavDrawerData; +import de.danoeh.antennapod.fragment.FeedItemlistFragment; +import de.danoeh.antennapod.fragment.SubscriptionFragment; +import de.danoeh.antennapod.model.feed.Feed; +import jp.shts.android.library.TriangleLabelView; + +/** + * Adapter for subscriptions + */ +public class SubscriptionsRecyclerAdapter extends SelectableAdapter<SubscriptionsRecyclerAdapter.SubscriptionViewHolder> + implements View.OnCreateContextMenuListener { + private final WeakReference<MainActivity> mainActivityRef; + private List<NavDrawerData.DrawerItem> listItems; + private Feed selectedFeed = null; + int longPressedPosition = 0; // used to init actionMode + + public SubscriptionsRecyclerAdapter(MainActivity mainActivity) { + super(mainActivity); + this.mainActivityRef = new WeakReference<>(mainActivity); + this.listItems = new ArrayList<>(); + setHasStableIds(true); + } + + public Object getItem(int position) { + return listItems.get(position); + } + + public Feed getSelectedFeed() { + return selectedFeed; + } + + @NonNull + @Override + public SubscriptionViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View itemView = LayoutInflater.from(mainActivityRef.get()).inflate(R.layout.subscription_item, parent, false); + return new SubscriptionViewHolder(itemView); + } + + @Override + public void onBindViewHolder(@NonNull SubscriptionViewHolder holder, int position) { + NavDrawerData.DrawerItem drawerItem = listItems.get(position); + boolean isFeed = drawerItem.type == NavDrawerData.DrawerItem.Type.FEED; + holder.bind(drawerItem); + holder.itemView.setOnCreateContextMenuListener(this); + if (inActionMode()) { + if (isFeed) { + holder.selectCheckbox.setVisibility(View.VISIBLE); + holder.selectView.setVisibility(View.VISIBLE); + } + holder.selectCheckbox.setChecked((isSelected(position))); + holder.selectCheckbox.setOnCheckedChangeListener((buttonView, isChecked) + -> setSelected(holder.getBindingAdapterPosition(), isChecked)); + holder.imageView.setAlpha(0.6f); + holder.count.setVisibility(View.GONE); + } else { + holder.selectView.setVisibility(View.GONE); + holder.imageView.setAlpha(1.0f); + } + + holder.itemView.setOnLongClickListener(v -> { + if (!inActionMode()) { + if (isFeed) { + selectedFeed = ((NavDrawerData.FeedDrawerItem) getItem(holder.getBindingAdapterPosition())).feed; + longPressedPosition = holder.getBindingAdapterPosition(); + } else { + selectedFeed = null; + } + } + return false; + }); + + holder.itemView.setOnClickListener(v -> { + if (isFeed) { + if (inActionMode()) { + holder.selectCheckbox.setChecked(!isSelected(holder.getBindingAdapterPosition())); + } else { + Fragment fragment = FeedItemlistFragment + .newInstance(((NavDrawerData.FeedDrawerItem) drawerItem).feed.getId()); + mainActivityRef.get().loadChildFragment(fragment); + } + } else if (!inActionMode()) { + Fragment fragment = SubscriptionFragment.newInstance(drawerItem.getTitle()); + mainActivityRef.get().loadChildFragment(fragment); + } + }); + + } + + @Override + public int getItemCount() { + return listItems.size(); + } + + @Override + public long getItemId(int position) { + return listItems.get(position).id; + } + + @Override + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { + if (selectedFeed != null && !inActionMode()) { + MenuInflater inflater = mainActivityRef.get().getMenuInflater(); + inflater.inflate(R.menu.nav_feed_context, menu); + menu.setHeaderTitle(selectedFeed.getTitle()); + } + } + + public boolean onContextItemSelected(MenuItem item) { + if (item.getItemId() == R.id.multi_select) { + startSelectMode(longPressedPosition); + return true; + } + return false; + } + + public List<Feed> getSelectedItems() { + List<Feed> items = new ArrayList<>(); + for (int i = 0; i < getItemCount(); i++) { + if (isSelected(i)) { + NavDrawerData.DrawerItem drawerItem = listItems.get(i); + if (drawerItem.type == NavDrawerData.DrawerItem.Type.FEED) { + Feed feed = ((NavDrawerData.FeedDrawerItem) drawerItem).feed; + items.add(feed); + } + } + } + return items; + } + + public void setItems(List<NavDrawerData.DrawerItem> listItems) { + this.listItems = listItems; + } + + @Override + public void setSelected(int pos, boolean selected) { + NavDrawerData.DrawerItem drawerItem = listItems.get(pos); + if (drawerItem.type == NavDrawerData.DrawerItem.Type.FEED) { + super.setSelected(pos, selected); + } + } + + public class SubscriptionViewHolder extends RecyclerView.ViewHolder { + private final TextView feedTitle; + private final ImageView imageView; + private final TriangleLabelView count; + private final FrameLayout selectView; + private final CheckBox selectCheckbox; + + public SubscriptionViewHolder(@NonNull View itemView) { + super(itemView); + feedTitle = itemView.findViewById(R.id.txtvTitle); + imageView = itemView.findViewById(R.id.imgvCover); + count = itemView.findViewById(R.id.triangleCountView); + selectView = itemView.findViewById(R.id.selectView); + selectCheckbox = itemView.findViewById(R.id.selectCheckBox); + } + + public void bind(NavDrawerData.DrawerItem drawerItem) { + feedTitle.setText(drawerItem.getTitle()); + imageView.setContentDescription(drawerItem.getTitle()); + feedTitle.setVisibility(View.VISIBLE); + if (TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault()) + == ViewCompat.LAYOUT_DIRECTION_RTL) { + count.setCorner(TriangleLabelView.Corner.TOP_LEFT); + } + + if (drawerItem.getCounter() > 0) { + count.setPrimaryText(NumberFormat.getInstance().format(drawerItem.getCounter())); + count.setVisibility(View.VISIBLE); + } else { + count.setVisibility(View.GONE); + } + + if (drawerItem.type == NavDrawerData.DrawerItem.Type.FEED) { + Feed feed = ((NavDrawerData.FeedDrawerItem) drawerItem).feed; + boolean textAndImageCombind = feed.isLocalFeed() + && LocalFeedUpdater.getDefaultIconUrl(itemView.getContext()).equals(feed.getImageUrl()); + new CoverLoader(mainActivityRef.get()) + .withUri(feed.getImageUrl()) + .withPlaceholderView(feedTitle, textAndImageCombind) + .withCoverView(imageView) + .load(); + } else { + new CoverLoader(mainActivityRef.get()) + .withResource(R.drawable.ic_folder) + .withPlaceholderView(feedTitle, true) + .withCoverView(imageView) + .load(); + } + } + } + + public static float convertDpToPixel(Context context, float dp) { + return dp * context.getResources().getDisplayMetrics().density; + } + + public static class GridDividerItemDecorator extends RecyclerView.ItemDecoration { + @Override + public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { + super.onDraw(c, parent, state); + } + + @Override + public void getItemOffsets(@NonNull Rect outRect, + @NonNull View view, + @NonNull RecyclerView parent, + @NonNull RecyclerView.State state) { + super.getItemOffsets(outRect, view, parent, state); + Context context = parent.getContext(); + int insetOffset = (int) convertDpToPixel(context, 1f); + outRect.set(insetOffset, insetOffset, insetOffset, insetOffset); + } + } +} diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/RemoveFeedDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/RemoveFeedDialog.java index f42f0870d..c4ae5e058 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/RemoveFeedDialog.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/RemoveFeedDialog.java @@ -4,6 +4,10 @@ import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; import android.util.Log; + +import java.util.Collections; +import java.util.List; + import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.dialog.ConfirmationDialog; import de.danoeh.antennapod.model.feed.Feed; @@ -16,10 +20,17 @@ public class RemoveFeedDialog { private static final String TAG = "RemoveFeedDialog"; public static void show(Context context, Feed feed, Runnable onSuccess) { - int messageId = feed.isLocalFeed() ? R.string.feed_delete_confirmation_local_msg - : R.string.feed_delete_confirmation_msg; - String message = context.getString(messageId, feed.getTitle()); + List<Feed> feeds = Collections.singletonList(feed); + String message = getMessageId(context, feeds); + showDialog(context, feeds, message, onSuccess); + } + + public static void show(Context context, List<Feed> feeds, Runnable onSuccess) { + String message = getMessageId(context, feeds); + showDialog(context, feeds, message, onSuccess); + } + private static void showDialog(Context context, List<Feed> feeds, String message, Runnable onSuccess) { ConfirmationDialog dialog = new ConfirmationDialog(context, R.string.remove_feed_label, message) { @Override public void onConfirmButtonPressed(DialogInterface clickedDialog) { @@ -31,12 +42,17 @@ public class RemoveFeedDialog { progressDialog.setCancelable(false); progressDialog.show(); - Completable.fromCallable(() -> DBWriter.deleteFeed(context, feed.getId()).get()) + Completable.fromCallable(() -> { + for (Feed feed : feeds) { + DBWriter.deleteFeed(context, feed.getId()).get(); + } + return null; + }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( () -> { - Log.d(TAG, "Feed was deleted"); + Log.d(TAG, "Feed(s) deleted"); if (onSuccess != null) { onSuccess.run(); } @@ -49,4 +65,17 @@ public class RemoveFeedDialog { }; dialog.createNewDialog().show(); } + + private static String getMessageId(Context context, List<Feed> feeds) { + if (feeds.size() == 1) { + if (feeds.get(0).isLocalFeed()) { + return context.getString(R.string.feed_delete_confirmation_local_msg); + } else { + return context.getString(R.string.feed_delete_confirmation_msg, feeds.get(0).getTitle()); + } + } else { + return context.getString(R.string.feed_delete_confirmation_msg_batch); + } + + } } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java index 9fcdc6f02..b720f1167 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java @@ -7,41 +7,44 @@ import android.content.SharedPreferences; import android.os.Bundle; import android.os.Handler; import android.os.Looper; -import android.widget.ProgressBar; -import androidx.annotation.NonNull; -import androidx.annotation.StringRes; -import androidx.appcompat.widget.Toolbar; -import androidx.fragment.app.Fragment; -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; - import android.util.Log; -import android.view.ContextMenu; import android.view.LayoutInflater; -import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.GridView; +import android.widget.ProgressBar; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.annotation.StringRes; +import androidx.appcompat.widget.Toolbar; +import androidx.fragment.app.Fragment; +import androidx.recyclerview.widget.GridLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; + import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.joanzapata.iconify.Iconify; +import com.leinardi.android.speeddial.SpeedDialView; + +import de.danoeh.antennapod.dialog.TagSettingsDialog; +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; +import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.concurrent.Callable; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; -import de.danoeh.antennapod.adapter.SubscriptionsAdapter; +import de.danoeh.antennapod.adapter.SubscriptionsRecyclerAdapter; import de.danoeh.antennapod.core.dialog.ConfirmationDialog; import de.danoeh.antennapod.core.event.DownloadEvent; import de.danoeh.antennapod.core.event.FeedListUpdateEvent; import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent; import de.danoeh.antennapod.core.menuhandler.MenuItemUtils; -import de.danoeh.antennapod.dialog.TagSettingsDialog; -import de.danoeh.antennapod.model.feed.Feed; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.service.download.DownloadService; import de.danoeh.antennapod.core.storage.DBReader; @@ -49,24 +52,24 @@ import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.storage.DownloadRequester; import de.danoeh.antennapod.core.storage.NavDrawerData; import de.danoeh.antennapod.core.util.download.AutoUpdateManager; -import de.danoeh.antennapod.dialog.RemoveFeedDialog; -import de.danoeh.antennapod.dialog.SubscriptionsFilterDialog; import de.danoeh.antennapod.dialog.FeedSortDialog; +import de.danoeh.antennapod.dialog.RemoveFeedDialog; import de.danoeh.antennapod.dialog.RenameFeedDialog; +import de.danoeh.antennapod.dialog.SubscriptionsFilterDialog; +import de.danoeh.antennapod.fragment.actions.FeedMultiSelectActionHandler; +import de.danoeh.antennapod.model.feed.Feed; import de.danoeh.antennapod.view.EmptyViewHandler; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; -import org.greenrobot.eventbus.EventBus; -import org.greenrobot.eventbus.Subscribe; -import org.greenrobot.eventbus.ThreadMode; /** * Fragment for displaying feed subscriptions */ -public class SubscriptionFragment extends Fragment implements Toolbar.OnMenuItemClickListener { - +public class SubscriptionFragment extends Fragment + implements Toolbar.OnMenuItemClickListener, + SubscriptionsRecyclerAdapter.OnSelectModeListener { public static final String TAG = "SubscriptionFragment"; private static final String PREFS = "SubscriptionFragment"; private static final String PREF_NUM_COLUMNS = "columns"; @@ -80,23 +83,24 @@ public class SubscriptionFragment extends Fragment implements Toolbar.OnMenuItem R.id.subscription_num_columns_4, R.id.subscription_num_columns_5}; - private GridView subscriptionGridLayout; - private List<NavDrawerData.DrawerItem> listItems; - private SubscriptionsAdapter subscriptionAdapter; + private RecyclerView subscriptionRecycler; + private SubscriptionsRecyclerAdapter subscriptionAdapter; private FloatingActionButton subscriptionAddButton; private ProgressBar progressBar; private EmptyViewHandler emptyView; private TextView feedsFilteredMsg; private Toolbar toolbar; private String displayedFolder = null; - - private Feed selectedFeed = null; private boolean isUpdatingFeeds = false; private boolean displayUpArrow; private Disposable disposable; private SharedPreferences prefs; + private SpeedDialView speedDialView; + + private List<NavDrawerData.DrawerItem> listItems; + public static SubscriptionFragment newInstance(String folderTitle) { SubscriptionFragment fragment = new SubscriptionFragment(); Bundle args = new Bundle(); @@ -139,9 +143,15 @@ public class SubscriptionFragment extends Fragment implements Toolbar.OnMenuItem } } - subscriptionGridLayout = root.findViewById(R.id.subscriptions_grid); - subscriptionGridLayout.setNumColumns(prefs.getInt(PREF_NUM_COLUMNS, getDefaultNumOfColumns())); - registerForContextMenu(subscriptionGridLayout); + subscriptionRecycler = root.findViewById(R.id.subscriptions_grid); + GridLayoutManager gridLayoutManager = new GridLayoutManager(getContext(), + prefs.getInt(PREF_NUM_COLUMNS, getDefaultNumOfColumns()), + RecyclerView.VERTICAL, + false); + subscriptionRecycler.setLayoutManager(gridLayoutManager); + subscriptionRecycler.addItemDecoration(new SubscriptionsRecyclerAdapter.GridDividerItemDecorator()); + gridLayoutManager.setSpanCount(prefs.getInt(PREF_NUM_COLUMNS, getDefaultNumOfColumns())); + registerForContextMenu(subscriptionRecycler); subscriptionAddButton = root.findViewById(R.id.subscriptions_add); progressBar = root.findViewById(R.id.progLoading); @@ -155,6 +165,25 @@ public class SubscriptionFragment extends Fragment implements Toolbar.OnMenuItem new Handler(Looper.getMainLooper()).postDelayed(() -> swipeRefreshLayout.setRefreshing(false), getResources().getInteger(R.integer.swipe_to_refresh_duration_in_ms)); }); + + speedDialView = root.findViewById(R.id.fabSD); + speedDialView.inflate(R.menu.nav_feed_action_speeddial); + speedDialView.setOnChangeListener(new SpeedDialView.OnChangeListener() { + @Override + public boolean onMainActionSelected() { + return false; + } + + @Override + public void onToggleChanged(boolean isOpen) { + } + }); + speedDialView.setOnActionSelectedListener(actionItem -> { + new FeedMultiSelectActionHandler((MainActivity) getActivity(), subscriptionAdapter.getSelectedItems()) + .handleAction(actionItem.getId()); + return true; + }); + return root; } @@ -204,7 +233,9 @@ public class SubscriptionFragment extends Fragment implements Toolbar.OnMenuItem } private void setColumnNumber(int columns) { - subscriptionGridLayout.setNumColumns(columns); + GridLayoutManager gridLayoutManager = (GridLayoutManager) subscriptionRecycler.getLayoutManager(); + gridLayoutManager.setSpanCount(columns); + subscriptionAdapter.notifyDataSetChanged(); prefs.edit().putInt(PREF_NUM_COLUMNS, columns).apply(); refreshToolbarState(); } @@ -214,18 +245,16 @@ public class SubscriptionFragment extends Fragment implements Toolbar.OnMenuItem emptyView.setIcon(R.drawable.ic_folder); emptyView.setTitle(R.string.no_subscriptions_head_label); emptyView.setMessage(R.string.no_subscriptions_label); - emptyView.attachToListView(subscriptionGridLayout); + emptyView.attachToRecyclerView(subscriptionRecycler); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - - subscriptionAdapter = new SubscriptionsAdapter((MainActivity) getActivity(), itemAccess); - subscriptionGridLayout.setAdapter(subscriptionAdapter); - subscriptionGridLayout.setOnItemClickListener(subscriptionAdapter); + subscriptionAdapter = new SubscriptionsRecyclerAdapter((MainActivity) getActivity()); + subscriptionAdapter.setOnSelectModeListener(this); + subscriptionRecycler.setAdapter(subscriptionAdapter); setupEmptyView(); - subscriptionAddButton.setOnClickListener(view -> { if (getActivity() instanceof MainActivity) { ((MainActivity) getActivity()).loadChildFragment(new AddFeedFragment()); @@ -247,6 +276,10 @@ public class SubscriptionFragment extends Fragment implements Toolbar.OnMenuItem if (disposable != null) { disposable.dispose(); } + + if (subscriptionAdapter != null) { + subscriptionAdapter.endSelectMode(); + } } private void loadSubscriptions() { @@ -271,6 +304,7 @@ public class SubscriptionFragment extends Fragment implements Toolbar.OnMenuItem .subscribe( result -> { listItems = result; + subscriptionAdapter.setItems(result); subscriptionAdapter.notifyDataSetChanged(); emptyView.updateVisibility(); progressBar.setVisibility(View.GONE); // Keep hidden to avoid flickering while refreshing @@ -293,33 +327,12 @@ public class SubscriptionFragment extends Fragment implements Toolbar.OnMenuItem } @Override - public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { - super.onCreateContextMenu(menu, v, menuInfo); - if (menuInfo == null) { - return; - } - AdapterView.AdapterContextMenuInfo adapterInfo = (AdapterView.AdapterContextMenuInfo) menuInfo; - int position = adapterInfo.position; - - NavDrawerData.DrawerItem selectedObject = (NavDrawerData.DrawerItem) subscriptionAdapter.getItem(position); - - if (selectedObject.type == NavDrawerData.DrawerItem.Type.FEED) { - MenuInflater inflater = requireActivity().getMenuInflater(); - inflater.inflate(R.menu.nav_feed_context, menu); - selectedFeed = ((NavDrawerData.FeedDrawerItem) selectedObject).feed; - } - menu.setHeaderTitle(selectedObject.getTitle()); - } - - @Override public boolean onContextItemSelected(MenuItem item) { - if (selectedFeed == null) { + Feed feed = subscriptionAdapter.getSelectedFeed(); + if (feed == null) { return false; } - - Feed feed = selectedFeed; - selectedFeed = null; - final int itemId = item.getItemId(); + int itemId = item.getItemId(); if (itemId == R.id.remove_all_new_flags_item) { displayConfirmationDialog( R.string.remove_all_new_flags_label, @@ -335,6 +348,9 @@ public class SubscriptionFragment extends Fragment implements Toolbar.OnMenuItem } else if (itemId == R.id.remove_item) { RemoveFeedDialog.show(getContext(), feed, null); return true; + } else if (itemId == R.id.multi_select) { + speedDialView.setVisibility(View.VISIBLE); + return subscriptionAdapter.onContextItemSelected(item); } return super.onContextItemSelected(item); } @@ -376,23 +392,23 @@ public class SubscriptionFragment extends Fragment implements Toolbar.OnMenuItem private final MenuItemUtils.UpdateRefreshMenuItemChecker updateRefreshMenuItemChecker = () -> DownloadService.isRunning && DownloadRequester.getInstance().isDownloadingFeeds(); - private final SubscriptionsAdapter.ItemAccess itemAccess = new SubscriptionsAdapter.ItemAccess() { - @Override - public int getCount() { - if (listItems != null) { - return listItems.size(); - } else { - return 0; - } - } + @Override + public void onEndSelectMode() { + speedDialView.close(); + speedDialView.setVisibility(View.GONE); + subscriptionAdapter.setItems(listItems); + subscriptionAdapter.notifyDataSetChanged(); + } - @Override - public NavDrawerData.DrawerItem getItem(int position) { - if (listItems != null && 0 <= position && position < listItems.size()) { - return listItems.get(position); - } else { - return null; + @Override + public void onStartSelectMode() { + List<NavDrawerData.DrawerItem> feedsOnly = new ArrayList<>(); + for (NavDrawerData.DrawerItem item : listItems) { + if (item.type == NavDrawerData.DrawerItem.Type.FEED) { + feedsOnly.add(item); } } - }; + subscriptionAdapter.setItems(feedsOnly); + subscriptionAdapter.notifyDataSetChanged(); + } } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/actions/FeedMultiSelectActionHandler.java b/app/src/main/java/de/danoeh/antennapod/fragment/actions/FeedMultiSelectActionHandler.java new file mode 100644 index 000000000..f160b2241 --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/fragment/actions/FeedMultiSelectActionHandler.java @@ -0,0 +1,139 @@ +package de.danoeh.antennapod.fragment.actions; + +import android.util.Log; + +import androidx.annotation.PluralsRes; +import androidx.core.util.Consumer; + +import com.google.android.material.snackbar.Snackbar; + +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.util.List; +import java.util.Locale; + +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.activity.MainActivity; +import de.danoeh.antennapod.core.storage.DBWriter; +import de.danoeh.antennapod.dialog.RemoveFeedDialog; +import de.danoeh.antennapod.fragment.preferences.dialog.PreferenceListDialog; +import de.danoeh.antennapod.fragment.preferences.dialog.PreferenceSwitchDialog; +import de.danoeh.antennapod.model.feed.Feed; +import de.danoeh.antennapod.model.feed.FeedPreferences; + +public class FeedMultiSelectActionHandler { + private static final String TAG = "FeedSelectHandler"; + private final MainActivity activity; + private final List<Feed> selectedItems; + + public FeedMultiSelectActionHandler(MainActivity activity, List<Feed> selectedItems) { + this.activity = activity; + this.selectedItems = selectedItems; + } + + public void handleAction(int id) { + if (id == R.id.remove_item) { + RemoveFeedDialog.show(activity, selectedItems, null); + } else if (id == R.id.keep_updated) { + keepUpdatedPrefHandler(); + } else if (id == R.id.autodownload) { + autoDownloadPrefHandler(); + } else if (id == R.id.autoDeleteDownload) { + autoDeleteEpisodesPrefHandler(); + } else if (id == R.id.playback_speed) { + playbackSpeedPrefHandler(); + } else { + Log.e(TAG, "Unrecognized speed dial action item. Do nothing. id=" + id); + } + } + + private void autoDownloadPrefHandler() { + PreferenceSwitchDialog preferenceSwitchDialog = new PreferenceSwitchDialog(activity, + activity.getString(R.string.auto_download_settings_label), + activity.getString(R.string.auto_download_label)); + preferenceSwitchDialog.setOnPreferenceChangedListener(new PreferenceSwitchDialog.OnPreferenceChangedListener() { + @Override + public void preferenceChanged(boolean enabled) { + saveFeedPreferences(feedPreferences -> feedPreferences.setAutoDownload(enabled)); + } + }); + preferenceSwitchDialog.openDialog(); + } + + private static final DecimalFormat SPEED_FORMAT = + new DecimalFormat("0.00", DecimalFormatSymbols.getInstance(Locale.US)); + + private void playbackSpeedPrefHandler() { + final String[] speeds = activity.getResources().getStringArray(R.array.playback_speed_values); + String[] values = new String[speeds.length + 1]; + values[0] = SPEED_FORMAT.format(FeedPreferences.SPEED_USE_GLOBAL); + + String[] entries = new String[speeds.length + 1]; + entries[0] = activity.getString(R.string.feed_auto_download_global); + + System.arraycopy(speeds, 0, values, 1, speeds.length); + System.arraycopy(speeds, 0, entries, 1, speeds.length); + + PreferenceListDialog preferenceListDialog = new PreferenceListDialog(activity, + activity.getString(R.string.playback_speed)); + preferenceListDialog.openDialog(entries); + preferenceListDialog.setOnPreferenceChangedListener(pos -> { + saveFeedPreferences(feedPreferences -> { + feedPreferences.setFeedPlaybackSpeed(Float.parseFloat((String) values[pos])); + }); + + }); + } + + private void autoDeleteEpisodesPrefHandler() { + PreferenceListDialog preferenceListDialog = new PreferenceListDialog(activity, + "Auto delete episodes"); + String[] items = activity.getResources().getStringArray(R.array.spnAutoDeleteItems); + String[] values = activity.getResources().getStringArray(R.array.spnAutoDeleteValues); + preferenceListDialog.openDialog(items); + preferenceListDialog.setOnPreferenceChangedListener(which -> { + FeedPreferences.AutoDeleteAction autoDeleteAction = null; + switch (values[which]) { + case "global": + autoDeleteAction = FeedPreferences.AutoDeleteAction.GLOBAL; + break; + case "always": + autoDeleteAction = FeedPreferences.AutoDeleteAction.YES; + break; + case "never": + autoDeleteAction = FeedPreferences.AutoDeleteAction.NO; + break; + default: + } + FeedPreferences.AutoDeleteAction finalAutoDeleteAction = autoDeleteAction; + saveFeedPreferences(feedPreferences -> { + feedPreferences.setAutoDeleteAction(finalAutoDeleteAction); + }); + }); + } + + private void keepUpdatedPrefHandler() { + PreferenceSwitchDialog preferenceSwitchDialog = new PreferenceSwitchDialog(activity, + activity.getString(R.string.kept_updated), + activity.getString(R.string.keep_updated_summary)); + preferenceSwitchDialog.setOnPreferenceChangedListener(keepUpdated -> { + saveFeedPreferences(feedPreferences -> { + feedPreferences.setKeepUpdated(keepUpdated); + }); + }); + preferenceSwitchDialog.openDialog(); + } + + private void showMessage(@PluralsRes int msgId, int numItems) { + activity.showSnackbarAbovePlayer(activity.getResources() + .getQuantityString(msgId, numItems, numItems), Snackbar.LENGTH_LONG); + } + + private void saveFeedPreferences(Consumer<FeedPreferences> preferencesConsumer) { + for (Feed feed : selectedItems) { + preferencesConsumer.accept(feed.getPreferences()); + DBWriter.setFeedPreferences(feed.getPreferences()); + } + showMessage(R.plurals.updated_feeds_batch_label, selectedItems.size()); + } +} diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/dialog/PreferenceListDialog.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/dialog/PreferenceListDialog.java new file mode 100644 index 000000000..d5c487d1d --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/dialog/PreferenceListDialog.java @@ -0,0 +1,49 @@ +package de.danoeh.antennapod.fragment.preferences.dialog; + +import android.content.Context; + +import androidx.appcompat.app.AlertDialog; + +import de.danoeh.antennapod.R; + +public class PreferenceListDialog { + protected Context context; + private String title; + private OnPreferenceChangedListener onPreferenceChangedListener; + private int selectedPos = 0; + + public PreferenceListDialog(Context context, String title) { + this.context = context; + this.title = title; + } + + public interface OnPreferenceChangedListener { + /** + * Notified when user confirms preference + * + * @param pos The index of the item that was selected + */ + + void preferenceChanged(int pos); + } + + public void openDialog(String[] items) { + + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle(title); + builder.setSingleChoiceItems(items, selectedPos, (dialog, which) -> { + selectedPos = which; + }); + builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> { + if (onPreferenceChangedListener != null && selectedPos >= 0) { + onPreferenceChangedListener.preferenceChanged(selectedPos); + } + }); + builder.setNegativeButton(R.string.cancel_label, null); + builder.create().show(); + } + + public void setOnPreferenceChangedListener(OnPreferenceChangedListener onPreferenceChangedListener) { + this.onPreferenceChangedListener = onPreferenceChangedListener; + } +} diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/dialog/PreferenceSwitchDialog.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/dialog/PreferenceSwitchDialog.java new file mode 100644 index 000000000..cc7c92194 --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/dialog/PreferenceSwitchDialog.java @@ -0,0 +1,57 @@ +package de.danoeh.antennapod.fragment.preferences.dialog; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; + +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.widget.SwitchCompat; + +import de.danoeh.antennapod.R; + +public class PreferenceSwitchDialog { + protected Context context; + private String title; + private String text; + private OnPreferenceChangedListener onPreferenceChangedListener; + + public PreferenceSwitchDialog(Context context, String title, String text) { + this.context = context; + this.title = title; + this.text = text; + } + + public interface OnPreferenceChangedListener { + /** + * Notified when user confirms preference + * + * @param enabled The preference + */ + + void preferenceChanged(boolean enabled); + } + + public void openDialog() { + + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle(title); + + LayoutInflater inflater = LayoutInflater.from(this.context); + View layout = inflater.inflate(R.layout.dialog_switch_preference, null, false); + SwitchCompat switchButton = layout.findViewById(R.id.dialogSwitch); + switchButton.setText(text); + builder.setView(layout); + + builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> { + if (onPreferenceChangedListener != null) { + onPreferenceChangedListener.preferenceChanged(switchButton.isChecked()); + } + }); + builder.setNegativeButton(R.string.cancel_label, null); + builder.create().show(); + } + + public void setOnPreferenceChangedListener(OnPreferenceChangedListener onPreferenceChangedListener) { + this.onPreferenceChangedListener = onPreferenceChangedListener; + } +} diff --git a/app/src/main/res/layout/dialog_switch_preference.xml b/app/src/main/res/layout/dialog_switch_preference.xml new file mode 100644 index 000000000..87f321c86 --- /dev/null +++ b/app/src/main/res/layout/dialog_switch_preference.xml @@ -0,0 +1,15 @@ +<?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="match_parent" + android:padding="24dp"> + + <androidx.appcompat.widget.SwitchCompat + android:id="@+id/dialogSwitch" + android:layout_width="match_parent" + android:layout_height="wrap_content" + tools:text="Switch" /> + +</LinearLayout> diff --git a/app/src/main/res/layout/fragment_subscriptions.xml b/app/src/main/res/layout/fragment_subscriptions.xml index 61d33f534..6dd112eed 100644 --- a/app/src/main/res/layout/fragment_subscriptions.xml +++ b/app/src/main/res/layout/fragment_subscriptions.xml @@ -1,33 +1,34 @@ <?xml version="1.0" encoding="utf-8"?> -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" - android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="match_parent"> +<RelativeLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> <androidx.appcompat.widget.Toolbar - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:minHeight="?attr/actionBarSize" - android:theme="?attr/actionBarTheme" - android:layout_alignParentTop="true" - app:title="@string/subscriptions_label" - app:navigationIcon="?homeAsUpIndicator" - android:id="@+id/toolbar"/> + android:id="@+id/toolbar" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:minHeight="?attr/actionBarSize" + android:theme="?attr/actionBarTheme" + android:layout_alignParentTop="true" + app:title="@string/subscriptions_label" + app:navigationIcon="?homeAsUpIndicator" /> <TextView - android:id="@+id/feeds_filtered_message" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_below="@id/toolbar" - android:background="?android:attr/selectableItemBackground" - android:gravity="start" - android:paddingStart="8dp" - android:paddingTop="4dp" - android:paddingEnd="8dp" - android:paddingBottom="8dp" - android:textColor="?android:attr/textColorSecondary" - android:textSize="@dimen/text_size_small" /> + android:id="@+id/feeds_filtered_message" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_below="@id/toolbar" + android:background="?android:attr/selectableItemBackground" + android:gravity="start" + android:paddingStart="8dp" + android:paddingTop="4dp" + android:paddingEnd="8dp" + android:paddingBottom="8dp" + android:textColor="?android:attr/textColorSecondary" + android:textSize="@dimen/text_size_small" /> <androidx.swiperefreshlayout.widget.SwipeRefreshLayout android:id="@+id/swipeRefresh" @@ -35,35 +36,36 @@ android:layout_height="match_parent" android:layout_below="@id/feeds_filtered_message"> - <GridView - android:id="@+id/subscriptions_grid" - android:layout_width="match_parent" - android:numColumns="3" - android:horizontalSpacing="2dp" - android:verticalSpacing="2dp" - android:layout_height="match_parent" - android:layout_gravity="center_horizontal" - android:paddingBottom="88dp" - android:clipToPadding="false"/> + <androidx.recyclerview.widget.RecyclerView + android:id="@+id/subscriptions_grid" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_gravity="center_horizontal" + android:paddingBottom="88dp" + android:clipToPadding="false" /> </androidx.swiperefreshlayout.widget.SwipeRefreshLayout> <ProgressBar - android:id="@+id/progLoading" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_centerInParent="true" - android:indeterminateOnly="true" - android:visibility="visible"/> + android:id="@+id/progLoading" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerInParent="true" + android:indeterminateOnly="true" + android:visibility="visible" /> <com.google.android.material.floatingactionbutton.FloatingActionButton - android:id="@+id/subscriptions_add" - android:layout_width="56dp" - android:layout_height="56dp" - android:layout_margin="16dp" - android:layout_alignParentBottom="true" - android:layout_alignParentEnd="true" - android:layout_alignParentRight="true" - android:contentDescription="@string/add_feed_label" - app:srcCompat="@drawable/ic_add"/> + android:id="@+id/subscriptions_add" + android:layout_width="56dp" + android:layout_height="56dp" + android:layout_margin="16dp" + android:layout_alignParentBottom="true" + android:layout_alignParentEnd="true" + android:layout_alignParentRight="true" + android:contentDescription="@string/add_feed_label" + app:srcCompat="@drawable/ic_add" /> + + <include + layout="@layout/multi_select_speed_dial" /> + </RelativeLayout> diff --git a/app/src/main/res/layout/subscription_item.xml b/app/src/main/res/layout/subscription_item.xml index ec918fdac..66628740f 100644 --- a/app/src/main/res/layout/subscription_item.xml +++ b/app/src/main/res/layout/subscription_item.xml @@ -1,11 +1,11 @@ -<?xml version='1.0' encoding='utf-8'?> +<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:squareImageView="http://schemas.android.com/apk/de.danoeh.antennapod" xmlns:tools="http://schemas.android.com/tools" - android:layout_width="match_parent" - android:layout_height="match_parent" + android:layout_width="wrap_content" + android:layout_height="wrap_content" android:foreground="?attr/selectableItemBackground"> <de.danoeh.antennapod.ui.common.SquareImageView @@ -15,7 +15,7 @@ android:background="@color/non_square_icon_background" android:scaleType="fitCenter" squareImageView:direction="width" - tools:src="@mipmap/ic_launcher_round" /> + tools:src="@tools:sample/avatars" /> <com.joanzapata.iconify.widget.IconTextView android:id="@+id/txtvTitle" @@ -46,4 +46,20 @@ app:primaryTextColor="?attr/colorOnSecondary" app:primaryTextSize="12sp" /> + <FrameLayout + android:id="@+id/selectView" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:background="@drawable/ic_checkbox_background"> + + <CheckBox + android:id="@+id/selectCheckBox" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:minWidth="0dp" + android:minHeight="0dp" + android:layout_margin="8dp" /> + + </FrameLayout> + </RelativeLayout> diff --git a/app/src/main/res/menu/nav_feed_action_speeddial.xml b/app/src/main/res/menu/nav_feed_action_speeddial.xml new file mode 100644 index 000000000..2dfa002bb --- /dev/null +++ b/app/src/main/res/menu/nav_feed_action_speeddial.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item + android:id="@+id/remove_item" + android:menuCategory="container" + android:title="@string/remove_feed_label" + android:icon="@drawable/ic_delete"/> + <item + android:id="@+id/keep_updated" + android:menuCategory="container" + android:title="@string/keep_updated" + android:icon="@drawable/ic_refresh"/> + <item + android:id="@+id/autodownload" + android:menuCategory="container" + android:title="@string/auto_download_label" + android:icon="@drawable/ic_download"/> + <item + android:id="@+id/autoDeleteDownload" + android:menuCategory="container" + android:title="@string/auto_delete_label" + android:icon="@drawable/ic_delete_auto"/> + <item + android:id="@+id/playback_speed" + android:menuCategory="container" + android:title="@string/playback_speed" + android:icon="@drawable/ic_playback_speed"/> +</menu> diff --git a/app/src/main/res/menu/nav_feed_context.xml b/app/src/main/res/menu/nav_feed_context.xml index 4ded52caf..e2b990023 100644 --- a/app/src/main/res/menu/nav_feed_context.xml +++ b/app/src/main/res/menu/nav_feed_context.xml @@ -21,4 +21,8 @@ android:menuCategory="container" android:title="@string/remove_feed_label" /> + <item + android:id="@+id/multi_select" + android:menuCategory="container" + android:title="@string/multi_select" /> </menu> |