diff options
Diffstat (limited to 'app/src')
8 files changed, 356 insertions, 207 deletions
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/CoverLoader.java b/app/src/main/java/de/danoeh/antennapod/adapter/CoverLoader.java index d782d4ed5..aeaf526be 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/CoverLoader.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/CoverLoader.java @@ -21,6 +21,7 @@ import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.core.glide.ApGlideSettings; public class CoverLoader { + private int resource = 0; private String uri; private String fallbackUri; private TextView txtvPlaceholder; @@ -37,6 +38,11 @@ public class CoverLoader { return this; } + public CoverLoader withResource(int resource) { + this.resource = resource; + return this; + } + public CoverLoader withFallbackUri(String uri) { fallbackUri = uri; return this; @@ -66,6 +72,12 @@ public class CoverLoader { } public void load() { + if (resource != 0) { + imgvCover.setImageResource(resource); + CoverTarget.setPlaceholderVisibility(txtvPlaceholder, textAndImageCombined); + return; + } + RequestOptions options = new RequestOptions() .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) .fitCenter() @@ -106,15 +118,7 @@ public class CoverLoader { @Override public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) { - TextView txtvPlaceholder = placeholder.get(); - if (txtvPlaceholder != null) { - if (textAndImageCombined) { - int bgColor = txtvPlaceholder.getContext().getResources().getColor(R.color.feed_text_bg); - txtvPlaceholder.setBackgroundColor(bgColor); - } else { - txtvPlaceholder.setVisibility(View.INVISIBLE); - } - } + setPlaceholderVisibility(placeholder.get(), textAndImageCombined); ImageView ivCover = cover.get(); ivCover.setImageDrawable(resource); } @@ -124,5 +128,16 @@ public class CoverLoader { ImageView ivCover = cover.get(); ivCover.setImageDrawable(placeholder); } + + static void setPlaceholderVisibility(TextView placeholder, boolean textAndImageCombined) { + if (placeholder != null) { + if (textAndImageCombined) { + int bgColor = placeholder.getContext().getResources().getColor(R.color.feed_text_bg); + placeholder.setBackgroundColor(bgColor); + } else { + placeholder.setVisibility(View.INVISIBLE); + } + } + } } }
\ No newline at end of file diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java index 7c8943f36..8bfcf66cc 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java @@ -1,20 +1,20 @@ package de.danoeh.antennapod.adapter; import android.app.Activity; -import android.content.Context; import android.content.SharedPreferences; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; +import android.view.LayoutInflater; +import androidx.annotation.NonNull; import androidx.preference.PreferenceManager; import android.util.TypedValue; -import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView; import androidx.appcompat.app.AlertDialog; +import androidx.recyclerview.widget.RecyclerView; import com.bumptech.glide.Glide; import com.bumptech.glide.request.RequestOptions; import com.joanzapata.iconify.Iconify; @@ -23,6 +23,7 @@ import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.glide.ApGlideSettings; import de.danoeh.antennapod.core.preferences.UserPreferences; +import de.danoeh.antennapod.core.storage.NavDrawerData; import de.danoeh.antennapod.fragment.AddFeedFragment; import de.danoeh.antennapod.fragment.DownloadsFragment; import de.danoeh.antennapod.fragment.EpisodesFragment; @@ -30,6 +31,7 @@ import de.danoeh.antennapod.fragment.NavDrawerFragment; import de.danoeh.antennapod.fragment.PlaybackHistoryFragment; import de.danoeh.antennapod.fragment.QueueFragment; import de.danoeh.antennapod.fragment.SubscriptionFragment; +import de.danoeh.antennapod.ui.common.ThemeUtils; import org.apache.commons.lang3.ArrayUtils; import java.lang.ref.WeakReference; @@ -42,10 +44,9 @@ import java.util.List; /** * BaseAdapter for the navigation drawer */ -public class NavListAdapter extends BaseAdapter +public class NavListAdapter extends RecyclerView.Adapter<NavListAdapter.Holder> implements SharedPreferences.OnSharedPreferenceChangeListener { - private static final int VIEW_TYPE_COUNT = 3; public static final int VIEW_TYPE_NAV = 0; public static final int VIEW_TYPE_SECTION_DIVIDER = 1; private static final int VIEW_TYPE_SUBSCRIPTION = 2; @@ -56,7 +57,7 @@ public class NavListAdapter extends BaseAdapter */ public static final String SUBSCRIPTION_LIST_TAG = "SubscriptionList"; - private static List<String> tags; + private static List<String> fragmentTags; private static String[] titles; private final ItemAccess itemAccess; @@ -96,7 +97,7 @@ public class NavListAdapter extends BaseAdapter showSubscriptionList = false; } - tags = newTags; + fragmentTags = newTags; notifyDataSetChanged(); } @@ -140,12 +141,11 @@ public class NavListAdapter extends BaseAdapter } public List<String> getTags() { - return Collections.unmodifiableList(tags); + return Collections.unmodifiableList(fragmentTags); } - @Override - public int getCount() { + public int getItemCount() { int baseCount = getSubscriptionOffset(); if (showSubscriptionList) { baseCount += itemAccess.getCount(); @@ -154,25 +154,18 @@ public class NavListAdapter extends BaseAdapter } @Override - public Object getItem(int position) { + public long getItemId(int position) { int viewType = getItemViewType(position); - if (viewType == VIEW_TYPE_NAV) { - return getLabel(tags.get(position)); - } else if (viewType == VIEW_TYPE_SECTION_DIVIDER) { - return ""; + if (viewType == VIEW_TYPE_SUBSCRIPTION) { + return itemAccess.getItem(position - getSubscriptionOffset()).id; } else { - return itemAccess.getItem(position); + return -position - 1; //TODO } } @Override - public long getItemId(int position) { - return position; - } - - @Override public int getItemViewType(int position) { - if (0 <= position && position < tags.size()) { + if (0 <= position && position < fragmentTags.size()) { return VIEW_TYPE_NAV; } else if (position < getSubscriptionOffset()) { return VIEW_TYPE_SECTION_DIVIDER; @@ -181,69 +174,68 @@ public class NavListAdapter extends BaseAdapter } } - @Override - public int getViewTypeCount() { - return VIEW_TYPE_COUNT; - } - public int getSubscriptionOffset() { - return tags.size() > 0 ? tags.size() + 1 : 0; + return fragmentTags.size() > 0 ? fragmentTags.size() + 1 : 0; } + @NonNull + @Override + public Holder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + LayoutInflater inflater = LayoutInflater.from(activity.get()); + if (viewType == VIEW_TYPE_NAV) { + return new NavHolder(inflater.inflate(R.layout.nav_listitem, parent, false)); + } else if (viewType == VIEW_TYPE_SECTION_DIVIDER) { + return new DividerHolder(inflater.inflate(R.layout.nav_section_item, parent, false)); + } else { + return new FeedHolder(inflater.inflate(R.layout.nav_listitem, parent, false)); + } + } @Override - public View getView(int position, View convertView, ViewGroup parent) { + public void onBindViewHolder(@NonNull Holder holder, int position) { int viewType = getItemViewType(position); - View v; if (viewType == VIEW_TYPE_NAV) { - v = getNavView((String) getItem(position), position, convertView, parent); + bindNavView(getLabel(fragmentTags.get(position)), position, (NavHolder) holder); } else if (viewType == VIEW_TYPE_SECTION_DIVIDER) { - v = getSectionDividerView(convertView, parent); + bindSectionDivider((DividerHolder) holder); } else { - v = getFeedView(position, convertView, parent); + int itemPos = position - getSubscriptionOffset(); + NavDrawerData.DrawerItem item = itemAccess.getItem(itemPos); + bindListItem(item, (FeedHolder) holder); + if (item.type == NavDrawerData.DrawerItem.Type.FEED) { + bindFeedView((NavDrawerData.FeedDrawerItem) item, (FeedHolder) holder); + } else { + bindFolderView((NavDrawerData.FolderDrawerItem) item, (FeedHolder) holder); + } } - if (v != null && viewType != VIEW_TYPE_SECTION_DIVIDER) { + if (viewType != VIEW_TYPE_SECTION_DIVIDER) { TypedValue typedValue = new TypedValue(); if (position == itemAccess.getSelectedItemIndex()) { - v.getContext().getTheme().resolveAttribute(R.attr.drawer_activated_color, typedValue, true); - v.setBackgroundResource(typedValue.resourceId); + activity.get().getTheme().resolveAttribute(R.attr.drawer_activated_color, typedValue, true); + holder.itemView.setBackgroundResource(typedValue.resourceId); } else { - v.getContext().getTheme().resolveAttribute(android.R.attr.windowBackground, typedValue, true); - v.setBackgroundResource(typedValue.resourceId); + activity.get().getTheme().resolveAttribute(android.R.attr.windowBackground, typedValue, true); + holder.itemView.setBackgroundResource(typedValue.resourceId); } + + holder.itemView.setOnClickListener(v -> itemAccess.onItemClick(position)); + holder.itemView.setOnLongClickListener(v -> itemAccess.onItemLongClick(position)); } - return v; } - private View getNavView(String title, int position, View convertView, ViewGroup parent) { + private void bindNavView(String title, int position, NavHolder holder) { Activity context = activity.get(); if(context == null) { - return null; - } - NavHolder holder; - if (convertView == null) { - holder = new NavHolder(); - LayoutInflater inflater = (LayoutInflater) context - .getSystemService(Context.LAYOUT_INFLATER_SERVICE); - - convertView = inflater.inflate(R.layout.nav_listitem, parent, false); - - holder.image = convertView.findViewById(R.id.imgvCover); - holder.title = convertView.findViewById(R.id.txtvTitle); - holder.count = convertView.findViewById(R.id.txtvCount); - convertView.setTag(holder); - } else { - holder = (NavHolder) convertView.getTag(); + return; } - holder.title.setText(title); // reset for re-use holder.count.setVisibility(View.GONE); holder.count.setOnClickListener(null); - String tag = tags.get(position); + String tag = fragmentTags.get(position); if (tag.equals(QueueFragment.TAG)) { int queueSize = itemAccess.getQueueSize(); if (queueSize > 0) { @@ -282,58 +274,43 @@ public class NavListAdapter extends BaseAdapter } } - holder.image.setImageDrawable(getDrawable(tags.get(position))); - - return convertView; + holder.image.setImageDrawable(getDrawable(fragmentTags.get(position))); } - private View getSectionDividerView(View convertView, ViewGroup parent) { + private void bindSectionDivider(DividerHolder holder) { Activity context = activity.get(); if(context == null) { - return null; + return; } - LayoutInflater inflater = (LayoutInflater) context - .getSystemService(Context.LAYOUT_INFLATER_SERVICE); - - convertView = inflater.inflate(R.layout.nav_section_item, parent, false); - TextView feedsFilteredMsg = convertView.findViewById(R.id.nav_feeds_filtered_message); if (UserPreferences.getSubscriptionsFilter().isEnabled() && showSubscriptionList) { - convertView.setEnabled(true); - feedsFilteredMsg.setText("{md-info-outline} " + context.getString(R.string.subscriptions_are_filtered)); - Iconify.addIcons(feedsFilteredMsg); - feedsFilteredMsg.setVisibility(View.VISIBLE); + holder.itemView.setEnabled(true); + holder.feedsFilteredMsg.setText("{md-info-outline} " + + context.getString(R.string.subscriptions_are_filtered)); + Iconify.addIcons(holder.feedsFilteredMsg); + holder.feedsFilteredMsg.setVisibility(View.VISIBLE); } else { - convertView.setEnabled(false); - feedsFilteredMsg.setVisibility(View.GONE); + holder.itemView.setEnabled(false); + holder.feedsFilteredMsg.setVisibility(View.GONE); } + } - return convertView; + private void bindListItem(NavDrawerData.DrawerItem item, FeedHolder holder) { + if (item.getCounter() > 0) { + holder.count.setVisibility(View.VISIBLE); + holder.count.setText(NumberFormat.getInstance().format(item.getCounter())); + } else { + holder.count.setVisibility(View.GONE); + } + holder.title.setText(item.getTitle()); + holder.itemView.setPadding(item.getLayer() * 50, 0, 0, 0); // TODO } - private View getFeedView(int position, View convertView, ViewGroup parent) { + private void bindFeedView(NavDrawerData.FeedDrawerItem drawerItem, FeedHolder holder) { + Feed feed = drawerItem.feed; Activity context = activity.get(); - if(context == null) { - return null; - } - int feedPos = position - getSubscriptionOffset(); - Feed feed = itemAccess.getItem(feedPos); - - FeedHolder holder; - if (convertView == null) { - holder = new FeedHolder(); - LayoutInflater inflater = (LayoutInflater) context - .getSystemService(Context.LAYOUT_INFLATER_SERVICE); - - convertView = inflater.inflate(R.layout.nav_listitem, parent, false); - - holder.image = convertView.findViewById(R.id.imgvCover); - holder.title = convertView.findViewById(R.id.txtvTitle); - holder.failure = convertView.findViewById(R.id.itxtvFailure); - holder.count = convertView.findViewById(R.id.txtvCount); - convertView.setTag(holder); - } else { - holder = (FeedHolder) convertView.getTag(); + if (context == null) { + return; } Glide.with(context) @@ -346,9 +323,7 @@ public class NavListAdapter extends BaseAdapter .dontAnimate()) .into(holder.image); - holder.title.setText(feed.getTitle()); - - if(feed.hasLastUpdateFailed()) { + if (feed.hasLastUpdateFailed()) { RelativeLayout.LayoutParams p = (RelativeLayout.LayoutParams) holder.title.getLayoutParams(); p.addRule(RelativeLayout.LEFT_OF, R.id.itxtvFailure); holder.failure.setVisibility(View.VISIBLE); @@ -357,39 +332,86 @@ public class NavListAdapter extends BaseAdapter p.addRule(RelativeLayout.LEFT_OF, R.id.txtvCount); holder.failure.setVisibility(View.GONE); } - int counter = itemAccess.getFeedCounter(feed.getId()); - if(counter > 0) { - holder.count.setVisibility(View.VISIBLE); - holder.count.setText(NumberFormat.getInstance().format(counter)); - } else { + } + + private void bindFolderView(NavDrawerData.FolderDrawerItem folder, FeedHolder holder) { + Activity context = activity.get(); + if (context == null) { + return; + } + if (folder.isOpen) { holder.count.setVisibility(View.GONE); } - return convertView; + Glide.with(context).clear(holder.image); + holder.image.setImageResource(ThemeUtils.getDrawableFromAttr(context, R.attr.ic_folder)); + holder.failure.setVisibility(View.GONE); } - static class NavHolder { - ImageView image; - TextView title; - TextView count; + static class Holder extends RecyclerView.ViewHolder { + public Holder(@NonNull View itemView) { + super(itemView); + } } - static class FeedHolder { - ImageView image; - TextView title; - IconTextView failure; - TextView count; + static class DividerHolder extends Holder { + final TextView feedsFilteredMsg; + + public DividerHolder(@NonNull View itemView) { + super(itemView); + feedsFilteredMsg = itemView.findViewById(R.id.nav_feeds_filtered_message); + } + } + + static class NavHolder extends Holder { + final ImageView image; + final TextView title; + final TextView count; + + public NavHolder(@NonNull View itemView) { + super(itemView); + image = itemView.findViewById(R.id.imgvCover); + title = itemView.findViewById(R.id.txtvTitle); + count = itemView.findViewById(R.id.txtvCount); + } + } + + static class FeedHolder extends Holder { + final ImageView image; + final TextView title; + final IconTextView failure; + final TextView count; + + public FeedHolder(@NonNull View itemView) { + super(itemView); + image = itemView.findViewById(R.id.imgvCover); + title = itemView.findViewById(R.id.txtvTitle); + failure = itemView.findViewById(R.id.itxtvFailure); + count = itemView.findViewById(R.id.txtvCount); + } } public interface ItemAccess { int getCount(); - Feed getItem(int position); + + NavDrawerData.DrawerItem getItem(int position); + int getSelectedItemIndex(); + int getQueueSize(); + int getNumberOfNewItems(); + int getNumberOfDownloadedItems(); + int getReclaimableItems(); + int getFeedCounter(long feedId); + int getFeedCounterSum(); + + void onItemClick(int position); + + boolean onItemLongClick(int position); } } diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java index f7d6358de..1d85cdaff 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java @@ -21,7 +21,9 @@ import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.core.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.ui.common.ThemeUtils; import jp.shts.android.library.TriangleLabelView; /** @@ -60,7 +62,7 @@ public class SubscriptionsAdapter extends BaseAdapter implements AdapterView.OnI @Override public long getItemId(int position) { - return itemAccess.getItem(position).getId(); + return ((NavDrawerData.DrawerItem) getItem(position)).id; } @Override @@ -83,11 +85,13 @@ public class SubscriptionsAdapter extends BaseAdapter implements AdapterView.OnI holder = (Holder) convertView.getTag(); } - final Feed feed = (Feed) getItem(position); - if (feed == null) return null; + final NavDrawerData.DrawerItem drawerItem = (NavDrawerData.DrawerItem) getItem(position); + if (drawerItem == null) { + return null; + } - holder.feedTitle.setText(feed.getTitle()); - holder.imageView.setContentDescription(feed.getTitle()); + holder.feedTitle.setText(drawerItem.getTitle()); + holder.imageView.setContentDescription(drawerItem.getTitle()); holder.feedTitle.setVisibility(View.VISIBLE); // Fix TriangleLabelView corner for RTL @@ -96,30 +100,43 @@ public class SubscriptionsAdapter extends BaseAdapter implements AdapterView.OnI holder.count.setCorner(TriangleLabelView.Corner.TOP_LEFT); } - int count = itemAccess.getFeedCounter(feed.getId()); - if(count > 0) { - holder.count.setPrimaryText( - NumberFormat.getInstance().format(itemAccess.getFeedCounter(feed.getId()))); + if (drawerItem.getCounter() > 0) { + holder.count.setPrimaryText(NumberFormat.getInstance().format(drawerItem.getCounter())); holder.count.setVisibility(View.VISIBLE); } else { holder.count.setVisibility(View.GONE); } - 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(); - + 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(ThemeUtils.getDrawableFromAttr(mainActivityRef.get(), R.attr.ic_folder)) + .withPlaceholderView(holder.feedTitle, true) + .withCoverView(holder.imageView) + .load(); + } return convertView; } @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { - Fragment fragment = FeedItemlistFragment.newInstance(getItemId(position)); - mainActivityRef.get().loadChildFragment(fragment); + 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); + } } static class Holder { @@ -130,7 +147,9 @@ public class SubscriptionsAdapter extends BaseAdapter implements AdapterView.OnI public interface ItemAccess { int getCount(); - Feed getItem(int position); + + NavDrawerData.DrawerItem getItem(int position); + int getFeedCounter(long feedId); } } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java index e24c89478..c000107a7 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java @@ -9,6 +9,7 @@ import android.view.View; import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; import androidx.appcompat.widget.Toolbar; import androidx.fragment.app.Fragment; import androidx.preference.ListPreference; @@ -27,6 +28,7 @@ import de.danoeh.antennapod.core.feed.VolumeAdaptionSetting; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBWriter; +import de.danoeh.antennapod.databinding.EditTextDialogBinding; import de.danoeh.antennapod.dialog.AuthenticationDialog; import de.danoeh.antennapod.dialog.EpisodeFilterDialog; import de.danoeh.antennapod.dialog.FeedPreferenceSkipDialog; @@ -39,6 +41,8 @@ import org.greenrobot.eventbus.EventBus; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; +import java.util.Arrays; +import java.util.HashSet; import java.util.Locale; import static de.danoeh.antennapod.core.feed.FeedPreferences.SPEED_USE_GLOBAL; @@ -105,6 +109,7 @@ public class FeedSettingsFragment extends Fragment { private static final CharSequence PREF_CATEGORY_AUTO_DOWNLOAD = "autoDownloadCategory"; private static final String PREF_FEED_PLAYBACK_SPEED = "feedPlaybackSpeed"; private static final String PREF_AUTO_SKIP = "feedAutoSkip"; + private static final String PREF_TAGS = "tags"; private static final DecimalFormat SPEED_FORMAT = new DecimalFormat("0.00", DecimalFormatSymbols.getInstance(Locale.US)); @@ -160,6 +165,7 @@ public class FeedSettingsFragment extends Fragment { setupPlaybackSpeedPreference(); setupFeedAutoSkipPreference(); setupEpisodeNotificationPreference(); + setupTags(); updateAutoDeleteSummary(); updateVolumeReductionValue(); @@ -395,6 +401,26 @@ public class FeedSettingsFragment extends Fragment { } } + private void setupTags() { + findPreference(PREF_TAGS).setOnPreferenceClickListener(preference -> { + EditTextDialogBinding alertViewBinding = EditTextDialogBinding.inflate(getLayoutInflater()); + alertViewBinding.urlEditText.setText(feed.getPreferences().getTagsAsString()); + new AlertDialog.Builder(getContext()) + .setView(alertViewBinding.getRoot()) + .setTitle(R.string.feed_folders_label) + .setPositiveButton(android.R.string.ok, (d, input) -> { + String foldersString = alertViewBinding.urlEditText.getText().toString(); + feedPreferences.getTags().clear(); + feedPreferences.getTags().addAll(new HashSet<>(Arrays.asList( + foldersString.split(FeedPreferences.TAG_SEPARATOR)))); + DBWriter.setFeedPreferences(feedPreferences); + }) + .setNegativeButton(R.string.cancel_label, null) + .show(); + return true; + }); + } + private void setupEpisodeNotificationPreference() { SwitchPreferenceCompat pref = findPreference("episodeNotification"); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/NavDrawerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/NavDrawerFragment.java index be74678d3..e8c04336f 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/NavDrawerFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/NavDrawerFragment.java @@ -13,14 +13,16 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; -import android.widget.ListView; import android.widget.ProgressBar; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.appcompat.app.AlertDialog; +import androidx.core.util.Pair; import androidx.fragment.app.Fragment; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import com.google.android.material.bottomsheet.BottomSheetBehavior; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; @@ -34,6 +36,7 @@ import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBWriter; +import de.danoeh.antennapod.core.storage.NavDrawerData; import de.danoeh.antennapod.dialog.RemoveFeedDialog; import de.danoeh.antennapod.dialog.SubscriptionsFilterDialog; import de.danoeh.antennapod.dialog.RenameFeedDialog; @@ -46,10 +49,12 @@ import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; +import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; -public class NavDrawerFragment extends Fragment implements AdapterView.OnItemClickListener, - AdapterView.OnItemLongClickListener, SharedPreferences.OnSharedPreferenceChangeListener { +public class NavDrawerFragment extends Fragment implements SharedPreferences.OnSharedPreferenceChangeListener { @VisibleForTesting public static final String PREF_LAST_FRAGMENT_TAG = "prefLastFragmentTag"; @VisibleForTesting @@ -66,12 +71,14 @@ public class NavDrawerFragment extends Fragment implements AdapterView.OnItemCli NavListAdapter.SUBSCRIPTION_LIST_TAG }; - private DBReader.NavDrawerData navDrawerData; + private NavDrawerData navDrawerData; + private List<NavDrawerData.DrawerItem> flatItemList; private int selectedNavListIndex = -1; private int position = -1; private NavListAdapter navAdapter; private Disposable disposable; private ProgressBar progressBar; + private Set<String> openFolders = new HashSet<>(); @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @@ -80,11 +87,11 @@ public class NavDrawerFragment extends Fragment implements AdapterView.OnItemCli View root = inflater.inflate(R.layout.nav_list, container, false); progressBar = root.findViewById(R.id.progressBar); - ListView navList = root.findViewById(R.id.nav_list); + RecyclerView navList = root.findViewById(R.id.nav_list); navAdapter = new NavListAdapter(itemAccess, getActivity()); + navAdapter.setHasStableIds(true); navList.setAdapter(navAdapter); - navList.setOnItemClickListener(this); - navList.setOnItemLongClickListener(this); + navList.setLayoutManager(new LinearLayoutManager(getContext())); registerForContextMenu(navList); updateSelection(); @@ -103,9 +110,10 @@ public class NavDrawerFragment extends Fragment implements AdapterView.OnItemCli } else if (StringUtils.isNumeric(lastNavFragment)) { // last fragment was not a list, but a feed long feedId = Long.parseLong(lastNavFragment); if (navDrawerData != null) { - List<Feed> feeds = navDrawerData.feeds; - for (int i = 0; i < feeds.size(); i++) { - if (feeds.get(i).getId() == feedId) { + List<NavDrawerData.DrawerItem> items = flatItemList; + for (int i = 0; i < items.size(); i++) { + if (items.get(i).type == NavDrawerData.DrawerItem.Type.FEED + && ((NavDrawerData.FeedDrawerItem) items.get(i)).feed.getId() == feedId) { selectedNavListIndex = navAdapter.getSubscriptionOffset() + i; break; } @@ -145,8 +153,11 @@ public class NavDrawerFragment extends Fragment implements AdapterView.OnItemCli } MenuInflater inflater = getActivity().getMenuInflater(); inflater.inflate(R.menu.nav_feed_context, menu); - Feed feed = navDrawerData.feeds.get(position - navAdapter.getSubscriptionOffset()); - menu.setHeaderTitle(feed.getTitle()); + + NavDrawerData.DrawerItem drawerItem = flatItemList.get(position - navAdapter.getSubscriptionOffset()); + if (drawerItem.type == NavDrawerData.DrawerItem.Type.FEED) { + menu.setHeaderTitle(((NavDrawerData.FeedDrawerItem) drawerItem).feed.getTitle()); + } // episodes are not loaded, so we cannot check if the podcast has new or unplayed ones! } @@ -157,7 +168,14 @@ public class NavDrawerFragment extends Fragment implements AdapterView.OnItemCli if (position < 0) { return false; } - Feed feed = navDrawerData.feeds.get(position - navAdapter.getSubscriptionOffset()); + NavDrawerData.DrawerItem drawerItem = flatItemList.get(position - navAdapter.getSubscriptionOffset()); + if (drawerItem.type == NavDrawerData.DrawerItem.Type.FEED) { + return onFeedContextMenuClicked(((NavDrawerData.FeedDrawerItem) drawerItem).feed, item); + } + return false; + } + + private boolean onFeedContextMenuClicked(Feed feed, MenuItem item) { switch (item.getItemId()) { case R.id.remove_all_new_flags_item: ConfirmationDialog removeAllNewFlagsConfirmationDialog = new ConfirmationDialog(getContext(), @@ -270,17 +288,17 @@ public class NavDrawerFragment extends Fragment implements AdapterView.OnItemCli private final NavListAdapter.ItemAccess itemAccess = new NavListAdapter.ItemAccess() { @Override public int getCount() { - if (navDrawerData != null) { - return navDrawerData.feeds.size(); + if (flatItemList != null) { + return flatItemList.size(); } else { return 0; } } @Override - public Feed getItem(int position) { - if (navDrawerData != null && 0 <= position && position < navDrawerData.feeds.size()) { - return navDrawerData.feeds.get(position); + public NavDrawerData.DrawerItem getItem(int position) { + if (flatItemList != null && 0 <= position && position < flatItemList.size()) { + return flatItemList.get(position); } else { return null; } @@ -328,15 +346,82 @@ public class NavDrawerFragment extends Fragment implements AdapterView.OnItemCli return sum; } + @Override + public void onItemClick(int position) { + int viewType = navAdapter.getItemViewType(position); + if (viewType != NavListAdapter.VIEW_TYPE_SECTION_DIVIDER) { + if (position < navAdapter.getSubscriptionOffset()) { + String tag = navAdapter.getTags().get(position); + if (getActivity() instanceof MainActivity) { + ((MainActivity) getActivity()).loadFragment(tag, null); + ((MainActivity) getActivity()).getBottomSheet().setState(BottomSheetBehavior.STATE_COLLAPSED); + } else { + showMainActivity(tag); + } + } else { + int pos = position - navAdapter.getSubscriptionOffset(); + NavDrawerData.DrawerItem clickedItem = flatItemList.get(pos); + + if (clickedItem.type == NavDrawerData.DrawerItem.Type.FEED) { + long feedId = ((NavDrawerData.FeedDrawerItem) clickedItem).feed.getId(); + if (getActivity() instanceof MainActivity) { + ((MainActivity) getActivity()).loadFeedFragmentById(feedId, null); + ((MainActivity) getActivity()).getBottomSheet() + .setState(BottomSheetBehavior.STATE_COLLAPSED); + } else { + Intent intent = new Intent(getActivity(), MainActivity.class); + intent.putExtra(MainActivity.EXTRA_FEED_ID, feedId); + startActivity(intent); + } + } else { + NavDrawerData.FolderDrawerItem folder = ((NavDrawerData.FolderDrawerItem) clickedItem); + if (openFolders.contains(folder.name)) { + openFolders.remove(folder.name); + } else { + openFolders.add(folder.name); + } + + disposable = Observable.fromCallable(() -> makeFlatDrawerData(navDrawerData.items, 0)) + .subscribeOn(Schedulers.computation()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + result -> { + flatItemList = result; + navAdapter.notifyDataSetChanged(); + }, error -> Log.e(TAG, Log.getStackTraceString(error))); + } + } + } else if (UserPreferences.getSubscriptionsFilter().isEnabled() + && navAdapter.showSubscriptionList) { + SubscriptionsFilterDialog.showDialog(requireContext()); + } + } + + @Override + public boolean onItemLongClick(int position) { + if (position < navAdapter.getTags().size()) { + showDrawerPreferencesDialog(); + return true; + } else { + NavDrawerFragment.this.position = position; + return false; + } + } }; private void loadData() { - disposable = Observable.fromCallable(DBReader::getNavDrawerData) + progressBar.setVisibility(View.VISIBLE); + disposable = Observable.fromCallable( + () -> { + NavDrawerData data = DBReader.getNavDrawerData(); + return new Pair<>(data, makeFlatDrawerData(data.items, 0)); + }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( result -> { - navDrawerData = result; + navDrawerData = result.first; + flatItemList = result.second; updateSelection(); // Selected item might be a feed navAdapter.notifyDataSetChanged(); progressBar.setVisibility(View.GONE); // Stays hidden once there is something in the list @@ -346,45 +431,20 @@ public class NavDrawerFragment extends Fragment implements AdapterView.OnItemCli }); } - @Override - public void onItemClick(AdapterView<?> parent, View view, int position, long id) { - int viewType = parent.getAdapter().getItemViewType(position); - if (viewType != NavListAdapter.VIEW_TYPE_SECTION_DIVIDER) { - if (position < navAdapter.getSubscriptionOffset()) { - String tag = navAdapter.getTags().get(position); - if (getActivity() instanceof MainActivity) { - ((MainActivity) getActivity()).loadFragment(tag, null); - ((MainActivity) getActivity()).getBottomSheet().setState(BottomSheetBehavior.STATE_COLLAPSED); - } else { - showMainActivity(tag); - } - } else { - int pos = position - navAdapter.getSubscriptionOffset(); - long feedId = navDrawerData.feeds.get(pos).getId(); - if (getActivity() instanceof MainActivity) { - ((MainActivity) getActivity()).loadFeedFragmentById(feedId, null); - ((MainActivity) getActivity()).getBottomSheet().setState(BottomSheetBehavior.STATE_COLLAPSED); - } else { - Intent intent = new Intent(getActivity(), MainActivity.class); - intent.putExtra(MainActivity.EXTRA_FEED_ID, feedId); - startActivity(intent); + private List<NavDrawerData.DrawerItem> makeFlatDrawerData(List<NavDrawerData.DrawerItem> items, int layer) { + List<NavDrawerData.DrawerItem> flatItems = new ArrayList<>(); + for (NavDrawerData.DrawerItem item : items) { + item.setLayer(layer); + flatItems.add(item); + if (item.type == NavDrawerData.DrawerItem.Type.FOLDER) { + NavDrawerData.FolderDrawerItem folder = ((NavDrawerData.FolderDrawerItem) item); + folder.isOpen = openFolders.contains(folder.name); + if (folder.isOpen) { + flatItems.addAll(makeFlatDrawerData(((NavDrawerData.FolderDrawerItem) item).children, layer + 1)); } } - } else if (UserPreferences.getSubscriptionsFilter().isEnabled() - && navAdapter.showSubscriptionList) { - SubscriptionsFilterDialog.showDialog(requireContext()); - } - } - - @Override - public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { - if (position < navAdapter.getTags().size()) { - showDrawerPreferencesDialog(); - return true; - } else { - this.position = position; - return false; } + return flatItems; } public static void saveLastNavFragment(Context context, String tag) { 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 9bbc03fba..3c529d941 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java @@ -44,6 +44,7 @@ import de.danoeh.antennapod.core.service.download.DownloadService; import de.danoeh.antennapod.core.storage.DBReader; 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; @@ -77,7 +78,7 @@ public class SubscriptionFragment extends Fragment implements Toolbar.OnMenuItem R.id.subscription_num_columns_5}; private GridView subscriptionGridLayout; - private DBReader.NavDrawerData navDrawerData; + private NavDrawerData navDrawerData; private SubscriptionsAdapter subscriptionAdapter; private FloatingActionButton subscriptionAddButton; private ProgressBar progressBar; @@ -360,16 +361,16 @@ public class SubscriptionFragment extends Fragment implements Toolbar.OnMenuItem @Override public int getCount() { if (navDrawerData != null) { - return navDrawerData.feeds.size(); + return navDrawerData.items.size(); } else { return 0; } } @Override - public Feed getItem(int position) { - if (navDrawerData != null && 0 <= position && position < navDrawerData.feeds.size()) { - return navDrawerData.feeds.get(position); + public NavDrawerData.DrawerItem getItem(int position) { + if (navDrawerData != null && 0 <= position && position < navDrawerData.items.size()) { + return navDrawerData.items.get(position); } else { return null; } diff --git a/app/src/main/res/layout/nav_list.xml b/app/src/main/res/layout/nav_list.xml index ed850cc86..70a71a453 100644 --- a/app/src/main/res/layout/nav_list.xml +++ b/app/src/main/res/layout/nav_list.xml @@ -57,7 +57,7 @@ android:background="?android:attr/listDivider" tools:background="@android:color/holo_red_dark" /> - <ListView + <androidx.recyclerview.widget.RecyclerView android:id="@+id/nav_list" android:layout_width="match_parent" android:layout_height="wrap_content" diff --git a/app/src/main/res/xml/feed_settings.xml b/app/src/main/res/xml/feed_settings.xml index 13288fbda..8a63ac8e9 100644 --- a/app/src/main/res/xml/feed_settings.xml +++ b/app/src/main/res/xml/feed_settings.xml @@ -23,6 +23,12 @@ android:title="@string/authentication_label" android:summary="@string/authentication_descr"/> + <Preference + android:key="tags" + android:icon="?attr/ic_folder" + android:title="@string/feed_folders_label" + android:summary="@string/feed_folders_summary"/> + <ListPreference android:key="feedPlaybackSpeed" android:icon="?attr/ic_settings_speed" |