Repository: AntennaPod/AntennaPod (language: java)

Issue:
Display podcast tags at the top of the Subscriptions screen
# Checklist

- [x ] I have used the search function to see if someone else has already submitted the same feature request.
- [x ] I will only create one feature request per issue.
- [x ] I will describe the problem with as much detail as possible.

# System info

**App version**: 2.2.1
**Source**: F-Droid

# Feature description

Currently I have around 40 subscriptions of podcasts with a vast range of topics: Local audiobooks, history, linux, politics, travel, languages etc. 
Therefore, it's hard to find straight forward the right podcast.  Unfortunately the sorting and the filter feature are hardly helpful here.



**Suggested solution**:
Categories, as I have already mentioned a few above, which the user could create and to which the user could add podcasts.


**Screenshots / Drawings / Technical details**:

There could be two sort of tiles now: Category tiles on top and podcast tiles below, which have not yet been categorized.

![MockUp_small](https://user-images.githubusercontent.com/43318127/127771255-f46ef271-7f66-4282-b88f-9790109107d7.png)       ![MockUp2_small](https://user-images.githubusercontent.com/43318127/127776861-a9aee853-86d1-466a-97d2-679da568ff24.png)




The user could add a new category with some custom name and icon via the context menu of the three dots on the top right.
Podcasts could be assigned a category or more categories by
* drag and drop
* long press on a podcast, add to category: A cateogry chooser would open
* In the opened categories folder, there could be a button "add podcast" or "add existing podcast"

If the user clicks on the category, it opens and presents all containing podcasts. 











Discussion hints:
Oh, as I just checked the changelog, could it be that this feature has been implemented in 2.3.0 as of
> Support for tagging (grouping) subscriptions

but I haven't seen it yet as F-Droid hasn't rolled out the latest version yet?
> but I haven't seen it yet as F-Droid hasn't rolled out the latest version yet?

Yup, see #5305.

> could it be that this feature has been implemented in 2.3.0 as of

Indeed tags are now implemented, but I like your suggestion to display them on top, above uncategorised podcasts.
Great you like it! I just have added a second mock-up with a search box added. I think this could be very useful too! :)
> but I like your suggestion to display them on top, above uncategorised podcasts.

For the subscriptions screen, we had this mock-up some time ago: https://github.com/AntennaPod/AntennaPod/issues/1711#issuecomment-727600455. In the side drawer, I would still keep them at the bottom, though.

<img width="250" src="https://user-images.githubusercontent.com/11229646/99189611-4f8f4600-2762-11eb-8800-9ae6256f9425.png" />
> For the subscriptions screen, we had this mock-up some time ago: #1711 (comment).

Ah, that's right! :)

> In the side drawer, I would still keep them at the bottom, though.

I still think it's nice to be able to disable the general list and display only the folders. But I guess that's for another issue.
> Oh, as I just checked the changelog, could it be that this feature has been implemented in 2.3.0 as of
> 
> > Support for tagging (grouping) subscriptions

I am now on 2.3.0 via F-Droid and I wanted to check out the current implementation to see what could be improved or what kind of suggestion I could make -- in case there's any space for improvement, but I absolutely don't find a way to create tags. I feel like I clicked and long pressed every button and tile and looked through all options.

So, if I am not completely blind, I already have one suggestion: This feature needs to be more discoverable. :)
You can find it on the settings screen of each podcast
> You can find it on the settings screen of each podcast

Thanks, I found it. I looked more for the keyword "tags" or "categories" but it's called "folders".  I see that these folders are now added as further tiles at the end of the list of podcasts. Making some distinction between "folders" other than just having a different icon would indeed improve the visual clearance.

 In my mockups I had added the "folders" on top as I thought one usually have quiet less "folders" than "podcasts" and as such, would not need to scroll so much to go to the "folders" or in the other way around  would only need to scroll very few to go to all podcasts.


>  looked more for the keyword "tags" or "categories" but it's called "folders"

Yeah, we should correct that.


What do you think of my mock-up that ByteHamster shared before?
> What do you think of my mock-up that ByteHamster shared before?

I like it overall, though I am not a great friend of horizontal scrolling. In the the mock up it looks like you need to do that as the tags are only within one line and fade out.
The touch area like it's drawn there is already small in height, it requires more action and may interfere with gestures and thus and thus could be uncomfortable.

How about  the following: 
* show a toggle button `tags ^`  which, when clicked on, reveals all available tags + a toggle tag `Show all`
* when clicked on again, the list of all tags vanish except for those, which are selected. So, you know what you are looking at.
* the list of tags displayed don't fade out but wrap around into the next line(s)

It would be rather similar to what you have now. 




> though I am not a great friend of horizontal scrolling


Yeah, I was thinking the same this morning, but wanted to hear your thoughts. I like your suggestion! Sounds like more implementation work, though.
@ericschdt I tried to put your suggestion in a set of mock-ups:
https://design.penpot.app/#/view/e357a320-7535-11eb-8a88-eb0147e048f5/7accff30-fa1e-11eb-a745-7b5f72f6725e?index=2
You can click on "Interactions" > "Show interactions" to see where you can click. It will show more or less the behaviour. You can also navigate with keyboard arrows. The first two screens cover the scenario where there's only a few tags.

I didn't implement it properly in the mock-ups, but I think tags should be sorted alphabetically (in the groups of inactive and selected tags).

Note also that I made the interactive mock-ups so that when clicking a tag, it collapses the list. But maybe that's not a good idea (e.g. if you want to select a couple of tags, you'd have to toggle the full list every time). Instead, the list should only collapse when scrolling, and when clicking the 'collapse' icon.

Curious to hear what you think.
@ByteHamster @keunes. I'm almost done with a prototype. I will submit it soon.
Thinking about this more, I am no longer really a fan of having an expanding list. My original idea why I suggested a tag bar was the following:

> https://github.com/AntennaPod/AntennaPod/issues/1711#issuecomment-727240650
> How about displaying the tags directly above the subscriptions list? Clicking could then filter (with a nice animation) in place. That makes it faster to switch and (at least in my opinion) easier to discover.

The tag bar is supposed to be a quick way to switch between tags. If the tag bar is not quick and needs something like 4 taps to change to another tag (expand, de-select one, select the other, collapse), we can just make it a dialog and have more screen space for the actual subscriptions. @keunes @Matth7878 - you were involved in the discussion in the old issue (#1711). Opinions about this?
@ByteHamster I don't know, I liked the mockup with the thin tag bar but it's true you don't see many chips.
Soooo pleased this functionality now exists.

It's true that a horizontal scrolling list would be frustrating. Because you wouldn't be able to review all/many folders in one look.

But having some mechanism at the top is very important. The bottom is the wrong place vs the function. Hiding the means of organisation at the bottom feels backwards, literally and metaphorically.

Could the left hand side hamburger also please have the folders at the top too?

It might make sense for the foldered subs that are hidden from the main list to also be hidden in the left hand menu. But if not, folders at the top is the same necessary logic as for the top of the Sub list itself.
I'd like to add my vote here to have some way of choosing a tag at the top of the subscriptions list. The tag feature is now so hidden/hard to get to, that I never use it, and don't really understand how others are supposed to use it either. 

The only people interested in this feature, I would think, are people with a lot of subscriptions – meaning that anyone interested in using it would have to scroll so far down to get to it, that it's really uncomfortable to use.

I hope you'll look more into how to make it available from the top of the subscriptions list. In the meantime I'd think that the easiest way would to just keep everything as it already is, but sorting the tags at the top of the list instead of the bottom.

Thanks!
@ByteHamster Am I still working on this?
If you want to start working on it again, feel free to re-open your PR :)
@ByteHamster Ah,I finally have the time to get back to you. I already have it implemented in my own fork. If I have time, I will try to see if I can port it over. In the meantime, i suggest you and others really hammer in what exactly you want the feature to look and act. That way I can modify my port.
This issue has been mentioned on **AntennaPod Forum**. There might be relevant details there:

https://forum.antennapod.org/t/tag-icons/3347/3

I know I'm not the only one waiting for this. You said you already had it implemented in a fork back in january, @peakvalleytech, have you had any progress in moving it over to upstream since then? 

As I said before, there's no need to change anything except for just moving the sorting of the tags from the bottom to the top.

Thanks!
Just changing the sort order breaks other user's workflow (favorite subscriptions in main list, everything else moved to folders and hidden from main list). Refer to the mock-up above to see how a solution might look.
I'm sorry, I don't understand this. First, I don't see any way to hide subs that are in tags/folders from the main list? I've looked. Second, I don't understand how moving them from the bottom to the top would break anything for anyone, except if you have a really good muscle memory. Sure, a new implementation might be preferable, but since it's been two years I at least think that just sorting them on top would be good enough until a better way can be developed.

Just my 2 cents. Asked about an update because someone I recommended AP to asked if there's a good way to reach favorites when you have hundreds of subs :) 
When adding a tag there is a checkbox that allow to hide subscription from main list.
It's not perfect but it is possible to select all subscriptions, apply a default tag and check don't show in main list.
This way you would see only chips in main list.
> I know I'm not the only one waiting for this. You said you already had it implemented in a fork back in january, @peakvalleytech, have you had any progress in moving it over to upstream since then?


Yes, that is true. I already have a fully implemented solution in my fork (https://github.com/ApexPod/ApexPod). It's been a while, so I don't remember the implementation entirely. I would have to look at the code again. But I can send you a demo of how it works, if you want.

There has been no progress in moving it upstream due to some politics. The maintainers, if i recalled, wanted it to be done by the book, which involved numerous meetings for designs and brainstorming. I told them I didn't have the time, as I am only a volunteer. 

Users benefitting from/interested in this request, in Google Play reviews:
[1](https://play.google.com/console/developers/8008695526664634386/app/4974638472012894302/user-feedback/review-details?reviewId=2eec8c4d-04f8-48d1-839b-1f336e9594ea&corpus=PUBLIC_REVIEWS)
> Would be great add tags/ folders to group podcasts

And on social media:
[1](https://mastodon.social/@bjoern/113753033433042601)
> first impression: I like the bottom navigation a lot! 🚀 
>
> Only drawback so far, but maybe I miss something. On the sidebar I could filter my podcasts by tags which I use a lot to organize my podcasts. With the bottom navigation tags seems to be completely useless, at least I couldn't find a way to access them. It would be great if tags could be added to the bottom navigation to have easy access to some kind of filtered view of my podcast subscriptions.

[2](https://so.denk-nach-mcfly.de/objects/e65a5b3c-358e-42db-9f7f-ca2eea212359)
> Bottom Menu doesn't feel so „modern“ to me, more „retro“ (which isn't bad at all!)
>
> In the drawer I have ALL the views listed, PLUS all subscribed podcasts in Folders/Tags in dynamic order and with counts. That's very powerful for me!
> 
> In the bottom button menu bar, I can only place 5 "Most Favorite" quick switch views, one of these 5 is currently taken by the overflow „More“ entry which feels clumsy.
This issue has been mentioned on **AntennaPod Forum**. There might be relevant details there:

https://forum.antennapod.org/t/organization-of-podcasts/6340/3

This issue has been mentioned on **AntennaPod Forum**. There might be relevant details there:

https://forum.antennapod.org/t/option-to-move-tags-at-the-top/7019/2


Pull request: Tags above subscriptions screen
### Description

Tags above subscriptions screen, remembers the last selected one
Closes #5318

<img width="200" alt="Screenshot_20250830-145355" src="https://github.com/user-attachments/assets/10e15860-51e7-4321-a5f1-1dc436b3790e" />

### Checklist
<!-- 
  To help us keep the issue tracker clean and work as efficient as possible,
  please make sure that you have done all of the following.
  You can tick the boxes below by placing an x inside the brackets like this: [x]
-->
- [x] I have read the contribution guidelines: https://github.com/AntennaPod/AntennaPod/blob/develop/CONTRIBUTING.md#submit-a-pull-request
- [x] I have performed a self-review of my code
- [x] I have run the automated code checks using `./gradlew checkstyle spotbugsPlayDebug spotbugsDebug :app:lintPlayDebug`
- [x] My code follows the style guidelines of the AntennaPod project: https://github.com/AntennaPod/AntennaPod/wiki/Code-style
- [x] I have mentioned the corresponding issue and the relevant keyword (e.g., "Closes: #xy") in the description (see https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue)
- [x] If it is a core feature, I have added automated tests


Test command: ./gradlew :net:download:service:testFreeDebugUnitTest --tests "de.danoeh.antennapod.net.download.service.episode.autodownload.DbReaderTest"

Fix patch:
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/SelectSubscriptionActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/SelectSubscriptionActivity.java
index 2b11080565b..749799b236b 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/SelectSubscriptionActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/SelectSubscriptionActivity.java
@@ -27,10 +27,8 @@
 import de.danoeh.antennapod.ui.appstartintent.MainActivityStarter;
 import de.danoeh.antennapod.ui.common.ThemeSwitcher;
 import de.danoeh.antennapod.storage.database.DBReader;
-import de.danoeh.antennapod.storage.database.NavDrawerData;
 import de.danoeh.antennapod.databinding.SubscriptionSelectionActivityBinding;
 import de.danoeh.antennapod.model.feed.Feed;
-import de.danoeh.antennapod.storage.preferences.UserPreferences;
 import io.reactivex.rxjava3.core.Observable;
 import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
 import io.reactivex.rxjava3.disposables.Disposable;
@@ -74,20 +72,6 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
 
     }
 
-    public List<Feed> getFeedItems(List<NavDrawerData.DrawerItem> items, List<Feed> result) {
-        for (NavDrawerData.DrawerItem item : items) {
-            if (item.type == NavDrawerData.DrawerItem.Type.TAG) {
-                getFeedItems(((NavDrawerData.TagDrawerItem) item).getChildren(), result);
-            } else {
-                Feed feed = ((NavDrawerData.FeedDrawerItem) item).feed;
-                if (!result.contains(feed)) {
-                    result.add(feed);
-                }
-            }
-        }
-        return result;
-    }
-
     private void addShortcut(Feed feed, Bitmap bitmap) {
         Intent intent = new Intent(this, MainActivity.class);
         intent.setAction(Intent.ACTION_MAIN);
@@ -140,12 +124,7 @@ private void loadSubscriptions() {
         if (disposable != null) {
             disposable.dispose();
         }
-        disposable = Observable.fromCallable(
-                () -> {
-                    NavDrawerData data = DBReader.getNavDrawerData(UserPreferences.getSubscriptionsFilter(),
-                            UserPreferences.getFeedOrder(), UserPreferences.getFeedCounterSetting());
-                    return getFeedItems(data.items, new ArrayList<>());
-                })
+        disposable = Observable.fromCallable(DBReader::getFeedList)
                 .subscribeOn(Schedulers.io())
                 .observeOn(AndroidSchedulers.mainThread())
                 .subscribe(
diff --git a/app/src/main/java/de/danoeh/antennapod/ui/screen/drawer/DrawerItem.java b/app/src/main/java/de/danoeh/antennapod/ui/screen/drawer/DrawerItem.java
new file mode 100644
index 00000000000..dbf2f6d44c3
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/ui/screen/drawer/DrawerItem.java
@@ -0,0 +1,58 @@
+package de.danoeh.antennapod.ui.screen.drawer;
+
+import androidx.annotation.NonNull;
+import de.danoeh.antennapod.model.feed.Feed;
+import de.danoeh.antennapod.storage.database.NavDrawerData;
+
+public class DrawerItem {
+    private final Feed feed;
+    private final NavDrawerData.TagItem tag;
+    private int counter;
+    private final int layer;
+
+    public DrawerItem(@NonNull Feed feed, int counter, int layer) {
+        this.tag = null;
+        this.feed = feed;
+        this.counter = counter;
+        this.layer = layer;
+    }
+
+    public DrawerItem(@NonNull NavDrawerData.TagItem tag) {
+        this.feed = null;
+        this.tag = tag;
+        this.counter = 0;
+        this.layer = 0;
+    }
+
+    public boolean isFeed() {
+        return feed != null;
+    }
+
+    public Feed asFeed() {
+        return feed;
+    }
+
+    public NavDrawerData.TagItem asTag() {
+        return tag;
+    }
+
+    public void setCounter(int counter) {
+        this.counter = counter;
+    }
+
+    public int getCounter() {
+        return counter;
+    }
+
+    public int getLayer() {
+        return layer;
+    }
+
+    public String getTitle() {
+        return isFeed() ? feed.getTitle() : tag.getTitle();
+    }
+
+    public long getId() {
+        return isFeed() ? feed.getId() : tag.getId();
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/de/danoeh/antennapod/ui/screen/drawer/NavDrawerFragment.java b/app/src/main/java/de/danoeh/antennapod/ui/screen/drawer/NavDrawerFragment.java
index 74089b9b8a7..083acebac7a 100644
--- a/app/src/main/java/de/danoeh/antennapod/ui/screen/drawer/NavDrawerFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/ui/screen/drawer/NavDrawerFragment.java
@@ -46,6 +46,7 @@
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import de.danoeh.antennapod.R;
@@ -80,8 +81,8 @@ public class NavDrawerFragment extends Fragment implements SharedPreferences.OnS
 
     private NavDrawerData navDrawerData;
     private int reclaimableSpace = 0;
-    private List<NavDrawerData.DrawerItem> flatItemList;
-    private NavDrawerData.DrawerItem contextPressedItem = null;
+    private List<DrawerItem> flatItemList;
+    private DrawerItem contextPressedItem = null;
     private NavListAdapter navAdapter;
     private Disposable disposable;
     private ProgressBar progressBar;
@@ -162,11 +163,12 @@ public void onDestroyView() {
     public void onCreateContextMenu(@NonNull ContextMenu menu, @NonNull View v, ContextMenu.ContextMenuInfo menuInfo) {
         super.onCreateContextMenu(menu, v, menuInfo);
         MenuInflater inflater = getActivity().getMenuInflater();
-        menu.setHeaderTitle(contextPressedItem.getTitle());
-        if (contextPressedItem.type == NavDrawerData.DrawerItem.Type.FEED) {
+        if (contextPressedItem.isFeed()) {
+            menu.setHeaderTitle(contextPressedItem.asFeed().getTitle());
             inflater.inflate(R.menu.nav_feed_context, menu);
             // episodes are not loaded, so we cannot check if the podcast has new or unplayed ones!
         } else {
+            menu.setHeaderTitle(contextPressedItem.asTag().getTitle());
             inflater.inflate(R.menu.nav_folder_context, menu);
         }
         MenuItemUtils.setOnClickListeners(menu, this::onContextItemSelected);
@@ -174,15 +176,15 @@ public void onCreateContextMenu(@NonNull ContextMenu menu, @NonNull View v, Cont
 
     @Override
     public boolean onContextItemSelected(@NonNull MenuItem item) {
-        NavDrawerData.DrawerItem pressedItem = contextPressedItem;
+        DrawerItem pressedItem = contextPressedItem;
         contextPressedItem = null;
         if (pressedItem == null) {
             return false;
         }
-        if (pressedItem.type == NavDrawerData.DrawerItem.Type.FEED) {
-            return onFeedContextMenuClicked(((NavDrawerData.FeedDrawerItem) pressedItem).feed, item);
+        if (pressedItem.isFeed()) {
+            return onFeedContextMenuClicked(pressedItem.asFeed(), item);
         } else {
-            return onTagContextMenuClicked(pressedItem, item);
+            return onTagContextMenuClicked(pressedItem.asTag(), item);
         }
     }
 
@@ -204,7 +206,7 @@ private boolean onFeedContextMenuClicked(Feed feed, MenuItem item) {
         return super.onContextItemSelected(item);
     }
 
-    private boolean onTagContextMenuClicked(NavDrawerData.DrawerItem drawerItem, MenuItem item) {
+    private boolean onTagContextMenuClicked(NavDrawerData.TagItem drawerItem, MenuItem item) {
         final int itemId = item.getItemId();
         if (itemId == R.id.rename_folder_item) {
             new RenameFeedDialog(getActivity(), drawerItem).show();
@@ -216,10 +218,8 @@ private boolean onTagContextMenuClicked(NavDrawerData.DrawerItem drawerItem, Men
 
                 @Override
                 public void onConfirmButtonPressed(DialogInterface dialog) {
-                    List<NavDrawerData.DrawerItem> feeds = ((NavDrawerData.TagDrawerItem) drawerItem).getChildren();
-
-                    for (NavDrawerData.DrawerItem feed : feeds) {
-                        FeedPreferences preferences = ((NavDrawerData.FeedDrawerItem) feed).feed.getPreferences();
+                    for (Feed feed : drawerItem.getFeeds()) {
+                        FeedPreferences preferences = feed.getPreferences();
                         preferences.getTags().remove(drawerItem.getTitle());
                         DBWriter.setFeedPreferences(preferences);
                     }
@@ -272,7 +272,7 @@ public int getCount() {
         }
 
         @Override
-        public NavDrawerData.DrawerItem getItem(int position) {
+        public DrawerItem getItem(int position) {
             if (flatItemList != null && 0 <= position && position < flatItemList.size()) {
                 return flatItemList.get(position);
             } else {
@@ -288,11 +288,11 @@ public boolean isSelected(int position) {
             } else if (StringUtils.isNumeric(lastNavFragment)) { // last fragment was not a list, but a feed
                 long feedId = Long.parseLong(lastNavFragment);
                 if (navDrawerData != null) {
-                    NavDrawerData.DrawerItem itemToCheck = flatItemList.get(
+                    DrawerItem itemToCheck = flatItemList.get(
                             position - navAdapter.getSubscriptionOffset());
-                    if (itemToCheck.type == NavDrawerData.DrawerItem.Type.FEED) {
+                    if (itemToCheck.isFeed()) {
                         // When the same feed is displayed multiple times, it should be highlighted multiple times.
-                        return ((NavDrawerData.FeedDrawerItem) itemToCheck).feed.getId() == feedId;
+                        return itemToCheck.asFeed().getId() == feedId;
                     }
                 }
             }
@@ -341,15 +341,15 @@ public void onItemClick(int position) {
                     ((MainActivity) getActivity()).getBottomSheet().setState(BottomSheetBehavior.STATE_COLLAPSED);
                 } else {
                     int pos = position - navAdapter.getSubscriptionOffset();
-                    NavDrawerData.DrawerItem clickedItem = flatItemList.get(pos);
+                    DrawerItem clickedItem = flatItemList.get(pos);
 
-                    if (clickedItem.type == NavDrawerData.DrawerItem.Type.FEED) {
-                        long feedId = ((NavDrawerData.FeedDrawerItem) clickedItem).feed.getId();
+                    if (clickedItem.isFeed()) {
+                        long feedId = clickedItem.asFeed().getId();
                         ((MainActivity) getActivity()).loadFeedFragmentById(feedId, null);
                         ((MainActivity) getActivity()).getBottomSheet()
                                 .setState(BottomSheetBehavior.STATE_COLLAPSED);
                     } else {
-                        NavDrawerData.TagDrawerItem folder = ((NavDrawerData.TagDrawerItem) clickedItem);
+                        NavDrawerData.TagItem folder = clickedItem.asTag();
                         if (openFolders.contains(folder.getTitle())) {
                             openFolders.remove(folder.getTitle());
                         } else {
@@ -361,7 +361,8 @@ public void onItemClick(int position) {
                                 .putStringSet(PREF_OPEN_FOLDERS, openFolders)
                                 .apply();
 
-                        disposable = Observable.fromCallable(() -> makeFlatDrawerData(navDrawerData.items, 0))
+                        disposable = Observable.fromCallable(() -> makeFlatDrawerData(navDrawerData.feeds,
+                                        navDrawerData.tags, navDrawerData.feedCounters))
                                 .subscribeOn(Schedulers.computation())
                                 .observeOn(AndroidSchedulers.mainThread())
                                 .subscribe(
@@ -409,7 +410,7 @@ private void loadData() {
                     NavDrawerData data = DBReader.getNavDrawerData(UserPreferences.getSubscriptionsFilter(),
                             UserPreferences.getFeedOrder(), UserPreferences.getFeedCounterSetting());
                     reclaimableSpace = EpisodeCleanupAlgorithmFactory.build().getReclaimableItems();
-                    return new Pair<>(data, makeFlatDrawerData(data.items, 0));
+                    return new Pair<>(data, makeFlatDrawerData(data.feeds, data.tags, data.feedCounters));
                 })
                 .subscribeOn(Schedulers.io())
                 .observeOn(AndroidSchedulers.mainThread())
@@ -425,23 +426,38 @@ private void loadData() {
                         });
     }
 
-    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.TAG) {
-                NavDrawerData.TagDrawerItem folder = ((NavDrawerData.TagDrawerItem) item);
-                folder.setOpen(openFolders.contains(folder.getTitle()));
-                if (folder.isOpen()) {
-                    flatItems.addAll(makeFlatDrawerData(
-                            ((NavDrawerData.TagDrawerItem) item).getChildren(), layer + 1));
+    private List<DrawerItem> makeFlatDrawerData(List<Feed> feeds, List<NavDrawerData.TagItem> tags,
+                                                @Nullable java.util.Map<Long, Integer> feedCounters) {
+        List<DrawerItem> flatItems = new ArrayList<>();
+        for (Feed feed : feeds) {
+            flatItems.add(new DrawerItem(feed, feedCounter(feed, feedCounters), 0));
+        }
+        for (NavDrawerData.TagItem tag : tags) {
+            if (FeedPreferences.TAG_ROOT.equals(tag.getTitle())) {
+                continue;
+            }
+            DrawerItem tagItem = new DrawerItem(tag);
+            flatItems.add(tagItem);
+            int counter = 0;
+            tag.setOpen(openFolders.contains(tag.getTitle()));
+            for (Feed feed : tag.getFeeds()) {
+                counter += feedCounter(feed, feedCounters);
+                if (tag.isOpen()) {
+                    flatItems.add(new DrawerItem(feed, feedCounter(feed, feedCounters), 1));
                 }
             }
+            tagItem.setCounter(counter);
         }
         return flatItems;
     }
 
+    private int feedCounter(Feed feed, Map<Long, Integer> feedCounters) {
+        if (navDrawerData == null || feedCounters == null) {
+            return 0;
+        }
+        return feedCounters.containsKey(feed.getId()) ? feedCounters.get(feed.getId()) : 0;
+    }
+
     public static void saveLastNavFragment(Context context, String tag) {
         Log.d(TAG, "saveLastNavFragment(tag: " + tag + ")");
         SharedPreferences prefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
diff --git a/app/src/main/java/de/danoeh/antennapod/ui/screen/drawer/NavListAdapter.java b/app/src/main/java/de/danoeh/antennapod/ui/screen/drawer/NavListAdapter.java
index 763b0ca1f25..47783f88d7a 100644
--- a/app/src/main/java/de/danoeh/antennapod/ui/screen/drawer/NavListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/ui/screen/drawer/NavListAdapter.java
@@ -107,7 +107,7 @@ public int getItemCount() {
     public long getItemId(int position) {
         int viewType = getItemViewType(position);
         if (viewType == VIEW_TYPE_SUBSCRIPTION) {
-            return itemAccess.getItem(position - getSubscriptionOffset()).id;
+            return itemAccess.getItem(position - getSubscriptionOffset()).getId();
         } else if (viewType == VIEW_TYPE_NAV) {
             return -Math.abs((long) fragmentTags.get(position).hashCode()) - 1; // Folder IDs are >0
         } else {
@@ -154,12 +154,12 @@ public void onBindViewHolder(@NonNull Holder holder, int position) {
             bindSectionDivider((DividerHolder) holder);
         } else {
             int itemPos = position - getSubscriptionOffset();
-            NavDrawerData.DrawerItem item = itemAccess.getItem(itemPos);
+            DrawerItem item = itemAccess.getItem(itemPos);
             bindListItem(item, (FeedHolder) holder);
-            if (item.type == NavDrawerData.DrawerItem.Type.FEED) {
-                bindFeedView((NavDrawerData.FeedDrawerItem) item, (FeedHolder) holder);
+            if (item.isFeed()) {
+                bindFeedView(item.asFeed(), (FeedHolder) holder);
             } else {
-                bindTagView((NavDrawerData.TagDrawerItem) item, (FeedHolder) holder);
+                bindTagView(item.asTag(), (FeedHolder) holder);
             }
             holder.itemView.setOnCreateContextMenuListener(itemAccess);
         }
@@ -231,7 +231,7 @@ private void bindSectionDivider(DividerHolder holder) {
         }
     }
 
-    private void bindListItem(NavDrawerData.DrawerItem item, FeedHolder holder) {
+    private void bindListItem(DrawerItem item, FeedHolder holder) {
         if (item.getCounter() > 0) {
             holder.count.setVisibility(View.VISIBLE);
             holder.count.setText(NumberFormat.getInstance().format(item.getCounter()));
@@ -243,8 +243,7 @@ private void bindListItem(NavDrawerData.DrawerItem item, FeedHolder holder) {
         holder.itemView.setPadding(item.getLayer() * padding, 0, 0, 0);
     }
 
-    private void bindFeedView(NavDrawerData.FeedDrawerItem drawerItem, FeedHolder holder) {
-        Feed feed = drawerItem.feed;
+    private void bindFeedView(Feed feed, FeedHolder holder) {
         Activity context = activity.get();
         if (context == null) {
             return;
@@ -264,7 +263,7 @@ private void bindFeedView(NavDrawerData.FeedDrawerItem drawerItem, FeedHolder ho
         holder.failure.setVisibility(feed.hasLastUpdateFailed() ? View.VISIBLE : View.GONE);
     }
 
-    private void bindTagView(NavDrawerData.TagDrawerItem tag, FeedHolder holder) {
+    private void bindTagView(NavDrawerData.TagItem tag, FeedHolder holder) {
         Activity context = activity.get();
         if (context == null) {
             return;
@@ -323,7 +322,7 @@ public FeedHolder(@NonNull View itemView) {
     public interface ItemAccess extends View.OnCreateContextMenuListener {
         int getCount();
 
-        NavDrawerData.DrawerItem getItem(int position);
+        DrawerItem getItem(int position);
 
         boolean isSelected(int position);
 
diff --git a/app/src/main/java/de/danoeh/antennapod/ui/screen/feed/RenameFeedDialog.java b/app/src/main/java/de/danoeh/antennapod/ui/screen/feed/RenameFeedDialog.java
index 4ce9632f348..4339b63aaae 100644
--- a/app/src/main/java/de/danoeh/antennapod/ui/screen/feed/RenameFeedDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/ui/screen/feed/RenameFeedDialog.java
@@ -3,8 +3,6 @@
 import android.app.Activity;
 
 import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.List;
 
 import android.view.LayoutInflater;
 import androidx.appcompat.app.AlertDialog;
@@ -20,16 +18,16 @@ public class RenameFeedDialog {
 
     private final WeakReference<Activity> activityRef;
     private Feed feed = null;
-    private NavDrawerData.DrawerItem drawerItem = null;
+    private NavDrawerData.TagItem tag = null;
 
     public RenameFeedDialog(Activity activity, Feed feed) {
         this.activityRef = new WeakReference<>(activity);
         this.feed = feed;
     }
 
-    public RenameFeedDialog(Activity activity, NavDrawerData.DrawerItem drawerItem) {
+    public RenameFeedDialog(Activity activity, NavDrawerData.TagItem drawerItem) {
         this.activityRef = new WeakReference<>(activity);
-        this.drawerItem = drawerItem;
+        this.tag = drawerItem;
     }
 
     public void show() {
@@ -39,7 +37,7 @@ public void show() {
         }
 
         final EditTextDialogBinding binding = EditTextDialogBinding.inflate(LayoutInflater.from(activity));
-        String title = feed != null ? feed.getTitle() : drawerItem.getTitle();
+        String title = feed != null ? feed.getTitle() : tag.getTitle();
 
         binding.textInput.setText(title);
         AlertDialog dialog = new MaterialAlertDialogBuilder(activity)
@@ -64,17 +62,11 @@ public void show() {
     }
 
     private void renameTag(String title) {
-        if (NavDrawerData.DrawerItem.Type.TAG == drawerItem.type) {
-            List<FeedPreferences> feedPreferences = new ArrayList<>();
-            for (NavDrawerData.DrawerItem item : ((NavDrawerData.TagDrawerItem) drawerItem).getChildren()) {
-                feedPreferences.add(((NavDrawerData.FeedDrawerItem) item).feed.getPreferences());
-            }
-
-            for (FeedPreferences preferences : feedPreferences) {
-                preferences.getTags().remove(drawerItem.getTitle());
-                preferences.getTags().add(title);
-                DBWriter.setFeedPreferences(preferences);
-            }
+        for (Feed feed : tag.getFeeds()) {
+            FeedPreferences preferences = feed.getPreferences();
+            preferences.getTags().remove(tag.getTitle());
+            preferences.getTags().add(title);
+            DBWriter.setFeedPreferences(preferences);
         }
     }
 
diff --git a/app/src/main/java/de/danoeh/antennapod/ui/screen/feed/preferences/TagSettingsDialog.java b/app/src/main/java/de/danoeh/antennapod/ui/screen/feed/preferences/TagSettingsDialog.java
index 5e5d59527c7..3c89da0d1f5 100644
--- a/app/src/main/java/de/danoeh/antennapod/ui/screen/feed/preferences/TagSettingsDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/ui/screen/feed/preferences/TagSettingsDialog.java
@@ -7,33 +7,30 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.widget.ArrayAdapter;
-
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.fragment.app.DialogFragment;
 import androidx.recyclerview.widget.GridLayoutManager;
-
 import com.google.android.material.dialog.MaterialAlertDialogBuilder;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
 import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.ui.SimpleChipAdapter;
-import de.danoeh.antennapod.storage.database.DBReader;
-import de.danoeh.antennapod.storage.database.DBWriter;
-import de.danoeh.antennapod.storage.database.NavDrawerData;
 import de.danoeh.antennapod.databinding.EditTagsDialogBinding;
 import de.danoeh.antennapod.model.feed.FeedCounter;
 import de.danoeh.antennapod.model.feed.FeedOrder;
 import de.danoeh.antennapod.model.feed.FeedPreferences;
+import de.danoeh.antennapod.storage.database.DBReader;
+import de.danoeh.antennapod.storage.database.DBWriter;
+import de.danoeh.antennapod.storage.database.NavDrawerData;
+import de.danoeh.antennapod.ui.SimpleChipAdapter;
 import de.danoeh.antennapod.ui.view.ItemOffsetDecoration;
-import io.reactivex.rxjava3.core.Observable;
 import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
+import io.reactivex.rxjava3.core.Observable;
 import io.reactivex.rxjava3.schedulers.Schedulers;
 
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
 public class TagSettingsDialog extends DialogFragment {
     public static final String TAG = "TagSettingsDialog";
     private static final String ARG_FEED_PREFERENCES = "feed_preferences";
@@ -113,10 +110,9 @@ private void loadTags() {
         Observable.fromCallable(
                 () -> {
                     NavDrawerData data = DBReader.getNavDrawerData(null, FeedOrder.ALPHABETICAL, FeedCounter.SHOW_NONE);
-                    List<NavDrawerData.DrawerItem> items = data.items;
-                    List<String> folders = new ArrayList<String>();
-                    for (NavDrawerData.DrawerItem item : items) {
-                        if (item.type == NavDrawerData.DrawerItem.Type.TAG) {
+                    List<String> folders = new ArrayList<>();
+                    for (NavDrawerData.TagItem item : data.tags) {
+                        if (!FeedPreferences.TAG_ROOT.equals(item.getTitle())) {
                             folders.add(item.getTitle());
                         }
                     }
diff --git a/app/src/main/java/de/danoeh/antennapod/ui/screen/subscriptions/SubscriptionFragment.java b/app/src/main/java/de/danoeh/antennapod/ui/screen/subscriptions/SubscriptionFragment.java
index 1f00251d02e..307436babf6 100644
--- a/app/src/main/java/de/danoeh/antennapod/ui/screen/subscriptions/SubscriptionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/ui/screen/subscriptions/SubscriptionFragment.java
@@ -11,7 +11,6 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ProgressBar;
-
 import androidx.annotation.NonNull;
 import androidx.core.util.Pair;
 import androidx.fragment.app.Fragment;
@@ -19,43 +18,40 @@
 import androidx.recyclerview.widget.LinearLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
 import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
-
 import com.google.android.material.appbar.MaterialToolbar;
 import com.google.android.material.floatingactionbutton.FloatingActionButton;
-
-import de.danoeh.antennapod.model.feed.FeedPreferences;
-import de.danoeh.antennapod.storage.database.DBWriter;
-import de.danoeh.antennapod.ui.common.ConfirmationDialog;
-import de.danoeh.antennapod.ui.screen.AddFeedFragment;
-import de.danoeh.antennapod.ui.screen.SearchFragment;
-import de.danoeh.antennapod.net.download.serviceinterface.FeedUpdateManager;
-import de.danoeh.antennapod.ui.view.FloatingSelectMenu;
-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 de.danoeh.antennapod.R;
 import de.danoeh.antennapod.activity.MainActivity;
-import de.danoeh.antennapod.ui.MenuItemUtils;
-import de.danoeh.antennapod.storage.database.DBReader;
-import de.danoeh.antennapod.storage.database.NavDrawerData;
-import de.danoeh.antennapod.ui.screen.feed.RenameFeedDialog;
 import de.danoeh.antennapod.event.FeedListUpdateEvent;
 import de.danoeh.antennapod.event.FeedUpdateRunningEvent;
 import de.danoeh.antennapod.event.UnreadItemsUpdateEvent;
 import de.danoeh.antennapod.model.feed.Feed;
+import de.danoeh.antennapod.model.feed.FeedPreferences;
+import de.danoeh.antennapod.net.download.serviceinterface.FeedUpdateManager;
+import de.danoeh.antennapod.storage.database.DBReader;
+import de.danoeh.antennapod.storage.database.DBWriter;
+import de.danoeh.antennapod.storage.database.NavDrawerData;
 import de.danoeh.antennapod.storage.preferences.UserPreferences;
+import de.danoeh.antennapod.ui.MenuItemUtils;
+import de.danoeh.antennapod.ui.common.ConfirmationDialog;
+import de.danoeh.antennapod.ui.screen.AddFeedFragment;
+import de.danoeh.antennapod.ui.screen.SearchFragment;
+import de.danoeh.antennapod.ui.screen.feed.RenameFeedDialog;
 import de.danoeh.antennapod.ui.statistics.StatisticsFragment;
 import de.danoeh.antennapod.ui.view.EmptyViewHandler;
+import de.danoeh.antennapod.ui.view.FloatingSelectMenu;
+import de.danoeh.antennapod.ui.view.ItemOffsetDecoration;
 import de.danoeh.antennapod.ui.view.LiftOnScrollListener;
-import io.reactivex.rxjava3.core.Observable;
 import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
+import io.reactivex.rxjava3.core.Observable;
 import io.reactivex.rxjava3.disposables.Disposable;
 import io.reactivex.rxjava3.schedulers.Schedulers;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
+
+import java.util.List;
+import java.util.Locale;
 
 /**
  * Fragment for displaying feed subscriptions
@@ -66,6 +62,7 @@ public class SubscriptionFragment extends Fragment
     public static final String TAG = "SubscriptionFragment";
     private static final String PREFS = "SubscriptionFragment";
     private static final String PREF_NUM_COLUMNS = "columns";
+    private static final String PREF_LAST_TAG = "last_tag";
     private static final String KEY_UP_ARROW = "up_arrow";
     private static final String ARGUMENT_FOLDER = "folder";
 
@@ -79,12 +76,13 @@ public class SubscriptionFragment extends Fragment
 
     private RecyclerView subscriptionRecycler;
     private SubscriptionsRecyclerAdapter subscriptionAdapter;
+    private RecyclerView tagsRecycler;
+    private SubscriptionTagAdapter tagAdapter;
     private EmptyViewHandler emptyView;
     private View feedsFilteredMsg;
     private MaterialToolbar toolbar;
     private SwipeRefreshLayout swipeRefreshLayout;
     private ProgressBar progressBar;
-    private String displayedFolder = null;
     private boolean displayUpArrow;
 
     private Disposable disposable;
@@ -94,7 +92,7 @@ public class SubscriptionFragment extends Fragment
     private FloatingActionButton subscriptionAddButton;
     private FloatingSelectMenu floatingSelectMenu;
     private RecyclerView.ItemDecoration itemDecoration;
-    private List<NavDrawerData.DrawerItem> listItems;
+    private List<Feed> feeds;
 
     public static SubscriptionFragment newInstance(String folderTitle) {
         SubscriptionFragment fragment = new SubscriptionFragment();
@@ -134,13 +132,6 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container,
         }
         refreshToolbarState();
 
-        if (getArguments() != null) {
-            displayedFolder = getArguments().getString(ARGUMENT_FOLDER, null);
-            if (displayedFolder != null) {
-                toolbar.setTitle(displayedFolder);
-            }
-        }
-
         subscriptionRecycler = root.findViewById(R.id.subscriptions_grid);
         registerForContextMenu(subscriptionRecycler);
         subscriptionRecycler.addOnScrollListener(new LiftOnScrollListener(root.findViewById(R.id.appbar)));
@@ -186,6 +177,28 @@ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMen
             return true;
         });
 
+        tagsRecycler = root.findViewById(R.id.tags_recycler);
+        tagsRecycler.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false));
+        tagsRecycler.addItemDecoration(new ItemOffsetDecoration(getContext(), 4, 0));
+        registerForContextMenu(tagsRecycler);
+        tagAdapter = new SubscriptionTagAdapter(getActivity()) {
+            @Override
+            public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
+                super.onCreateContextMenu(menu, v, menuInfo);
+                MenuItemUtils.setOnClickListeners(menu, SubscriptionFragment.this::onTagContextItemSelected);
+            }
+
+            @Override
+            protected void onTagClick(NavDrawerData.TagItem tag) {
+                tagAdapter.setSelectedTag(tag.getTitle());
+                loadSubscriptionsAndTags();
+            }
+        };
+        tagAdapter.setSelectedTag(prefs.getString(PREF_LAST_TAG, FeedPreferences.TAG_ROOT));
+        tagsRecycler.setAdapter(tagAdapter);
+        if (getArguments() != null) {
+            tagAdapter.setSelectedTag(getArguments().getString(ARGUMENT_FOLDER, null));
+        }
         return root;
     }
 
@@ -282,13 +295,14 @@ private void setupEmptyView() {
     public void onStart() {
         super.onStart();
         EventBus.getDefault().register(this);
-        loadSubscriptions();
+        loadSubscriptionsAndTags();
     }
 
     @Override
     public void onPause() {
         super.onPause();
         scrollPosition = getScrollPosition();
+        prefs.edit().putString(PREF_LAST_TAG, tagAdapter.getSelectedTag()).apply();
     }
 
     @Override
@@ -303,44 +317,45 @@ public void onStop() {
         }
     }
 
-    private void loadSubscriptions() {
+    private void loadSubscriptionsAndTags() {
         if (disposable != null) {
             disposable.dispose();
         }
         emptyView.hide();
         disposable = Observable.fromCallable(
-                () -> {
-                    NavDrawerData data = DBReader.getNavDrawerData(UserPreferences.getSubscriptionsFilter(),
-                            UserPreferences.getFeedOrder(), UserPreferences.getFeedCounterSetting());
-                    List<NavDrawerData.DrawerItem> items = data.items;
-                    for (NavDrawerData.DrawerItem item : items) {
-                        if (item.type == NavDrawerData.DrawerItem.Type.TAG
-                                && item.getTitle().equals(displayedFolder)) {
-                            return ((NavDrawerData.TagDrawerItem) item).getChildren();
-                        }
-                    }
-                    return items;
-                })
+                () -> DBReader.getNavDrawerData(UserPreferences.getSubscriptionsFilter(),
+                        UserPreferences.getFeedOrder(), UserPreferences.getFeedCounterSetting()))
                 .subscribeOn(Schedulers.io())
                 .observeOn(AndroidSchedulers.mainThread())
                 .subscribe(
                     result -> {
-                        final boolean firstLoaded = listItems == null || listItems.isEmpty();
-                        if (listItems != null && listItems.size() > result.size()) {
+                        List<Feed> openedFolderFeeds = result.feeds;
+                        for (NavDrawerData.TagItem tag : result.tags) {
+                            if (tag.getTitle().equals(tagAdapter.getSelectedTag())) {
+                                openedFolderFeeds = tag.getFeeds();
+                                break;
+                            }
+                        }
+
+                        final boolean firstLoaded = feeds == null || feeds.isEmpty();
+                        if (feeds != null && feeds.size() > openedFolderFeeds.size()) {
                             // We have fewer items. This can result in items being selected that are no longer visible.
                             subscriptionAdapter.endSelectMode();
                         }
-                        listItems = result;
+                        feeds = openedFolderFeeds;
                         progressBar.setVisibility(View.GONE);
-                        subscriptionAdapter.setItems(result);
+                        subscriptionAdapter.setItems(feeds, result.feedCounters);
                         if (firstLoaded) {
                             restoreScrollPosition(scrollPosition);
                         }
                         emptyView.updateVisibility();
+                        if (tagAdapter != null) {
+                            tagAdapter.setTags(result.tags);
+                            tagsRecycler.setVisibility(result.tags.size() > 1 ? View.VISIBLE : View.GONE);
+                        }
                     }, error -> {
                         Log.e(TAG, Log.getStackTraceString(error));
                     });
-
         updateFilterVisibility();
     }
 
@@ -358,75 +373,71 @@ private int getDefaultNumOfColumns() {
         return getResources().getInteger(R.integer.subscriptions_default_num_of_columns);
     }
 
-    @Override
-    public boolean onContextItemSelected(MenuItem item) {
-        NavDrawerData.DrawerItem drawerItem = subscriptionAdapter.getSelectedItem();
-        if (drawerItem == null) {
+    private boolean onTagContextItemSelected(MenuItem item) {
+        NavDrawerData.TagItem selectedTag = tagAdapter.getLongPressedItem();
+        if (selectedTag == null) {
             return false;
         }
         int itemId = item.getItemId();
-        if (drawerItem.type == NavDrawerData.DrawerItem.Type.TAG) {
-            if (itemId == R.id.rename_folder_item) {
-                new RenameFeedDialog(getActivity(), drawerItem).show();
-                return true;
-            } else if (itemId == R.id.delete_folder_item) {
-                ConfirmationDialog dialog = new ConfirmationDialog(
-                        getContext(), R.string.delete_tag_label,
-                        getString(R.string.delete_tag_confirmation, drawerItem.getTitle())) {
-
-                    @Override
-                    public void onConfirmButtonPressed(DialogInterface dialog) {
-                        List<NavDrawerData.DrawerItem> feeds = ((NavDrawerData.TagDrawerItem) drawerItem).getChildren();
-
-                        for (NavDrawerData.DrawerItem feed : feeds) {
-                            FeedPreferences preferences = ((NavDrawerData.FeedDrawerItem) feed).feed.getPreferences();
-                            preferences.getTags().remove(drawerItem.getTitle());
-                            DBWriter.setFeedPreferences(preferences);
-                        }
+        if (itemId == R.id.rename_folder_item) {
+            new RenameFeedDialog(getActivity(), selectedTag).show();
+            return true;
+        } else if (itemId == R.id.delete_folder_item) {
+            ConfirmationDialog dialog = new ConfirmationDialog(getContext(), R.string.delete_tag_label,
+                    getString(R.string.delete_tag_confirmation, selectedTag.getTitle())) {
+
+                @Override
+                public void onConfirmButtonPressed(DialogInterface dialog) {
+                    tagAdapter.setSelectedTag(FeedPreferences.TAG_ROOT);
+                    for (Feed feed : selectedTag.getFeeds()) {
+                        FeedPreferences preferences = feed.getPreferences();
+                        preferences.getTags().remove(selectedTag.getTitle());
+                        DBWriter.setFeedPreferences(preferences);
                     }
-                };
-                dialog.createNewDialog().show();
+                }
+            };
+            dialog.createNewDialog().show();
 
-                return true;
-            }
+            return true;
         }
+        return false;
+    }
 
-        Feed feed = ((NavDrawerData.FeedDrawerItem) drawerItem).feed;
-        if (itemId == R.id.multi_select) {
+    @Override
+    public boolean onContextItemSelected(@NonNull MenuItem item) {
+        Feed selectedFeed = subscriptionAdapter.getSelectedItem();
+        if (selectedFeed == null) {
+            return false;
+        }
+        if (item.getItemId() == R.id.multi_select) {
             return subscriptionAdapter.onContextItemSelected(item);
         }
-        return FeedMenuHandler.onMenuItemClicked(this, item.getItemId(), feed, this::loadSubscriptions);
+        return FeedMenuHandler.onMenuItemClicked(this, item.getItemId(), selectedFeed, this::loadSubscriptionsAndTags);
     }
 
     @Subscribe(threadMode = ThreadMode.MAIN)
     public void onFeedListChanged(FeedListUpdateEvent event) {
-        loadSubscriptions();
+        loadSubscriptionsAndTags();
     }
 
     @Subscribe(threadMode = ThreadMode.MAIN)
     public void onUnreadItemsChanged(UnreadItemsUpdateEvent event) {
-        loadSubscriptions();
+        loadSubscriptionsAndTags();
     }
 
     @Override
     public void onEndSelectMode() {
         floatingSelectMenu.setVisibility(View.GONE);
         subscriptionAddButton.setVisibility(View.VISIBLE);
-        subscriptionAdapter.setItems(listItems);
+        tagsRecycler.setVisibility(tagAdapter.getItemCount() > 1 ? View.VISIBLE : View.GONE);
         updateFilterVisibility();
     }
 
     @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);
         floatingSelectMenu.setVisibility(View.VISIBLE);
         subscriptionAddButton.setVisibility(View.GONE);
+        tagsRecycler.setVisibility(tagAdapter.getItemCount() > 1 ? View.INVISIBLE : View.GONE);
         updateFilterVisibility();
     }
 
diff --git a/app/src/main/java/de/danoeh/antennapod/ui/screen/subscriptions/SubscriptionTagAdapter.java b/app/src/main/java/de/danoeh/antennapod/ui/screen/subscriptions/SubscriptionTagAdapter.java
new file mode 100644
index 00000000000..11328323630
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/ui/screen/subscriptions/SubscriptionTagAdapter.java
@@ -0,0 +1,116 @@
+package de.danoeh.antennapod.ui.screen.subscriptions;
+
+import android.app.Activity;
+import android.os.Build;
+import android.view.ContextMenu;
+import android.view.InputDevice;
+import android.view.LayoutInflater;
+import android.view.MenuInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+import com.google.android.material.chip.Chip;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.model.feed.FeedPreferences;
+import de.danoeh.antennapod.storage.database.NavDrawerData;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+
+public class SubscriptionTagAdapter extends RecyclerView.Adapter<SubscriptionTagAdapter.TagViewHolder>
+        implements View.OnCreateContextMenuListener {
+    private final WeakReference<Activity> activityRef;
+    private List<NavDrawerData.TagItem> tags = new ArrayList<>();
+    private String selectedTag = null;
+    private NavDrawerData.TagItem longPressedItem = null;
+
+    public SubscriptionTagAdapter(Activity activity) {
+        this.activityRef = new WeakReference<>(activity);
+    }
+
+    public void setTags(List<NavDrawerData.TagItem> tags) {
+        this.tags = tags;
+        notifyDataSetChanged();
+    }
+
+    public void setSelectedTag(String tag) {
+        this.selectedTag = tag;
+        notifyDataSetChanged();
+    }
+
+    public String getSelectedTag() {
+        return selectedTag;
+    }
+
+    @NonNull
+    @Override
+    public TagViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+        View view = LayoutInflater.from(parent.getContext())
+                .inflate(R.layout.item_tag_chip, parent, false);
+        return new TagViewHolder(view);
+    }
+
+    @Override
+    public void onBindViewHolder(@NonNull TagViewHolder holder, int position) {
+        NavDrawerData.TagItem tag = tags.get(position);
+        if (FeedPreferences.TAG_ROOT.equals(tag.getTitle())) {
+            holder.chip.setText(R.string.tag_all);
+        } else {
+            String title = tag.getTitle();
+            if (title.length() > 20) {
+                title = title.substring(0, 19) + "…";
+            }
+            holder.chip.setText(title);
+        }
+        holder.chip.setChecked(tag.getTitle().equals(selectedTag));
+        holder.chip.setOnClickListener(v -> onTagClick(tag));
+        holder.chip.setOnTouchListener((v, e) -> {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+                if (e.isFromSource(InputDevice.SOURCE_MOUSE)
+                        &&  e.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
+                    longPressedItem = tag;
+                }
+            }
+            return false;
+        });
+        holder.chip.setOnLongClickListener(v -> {
+            longPressedItem = tag;
+            return false;
+        });
+        holder.chip.setOnCreateContextMenuListener(this);
+    }
+
+    @Override
+    public int getItemCount() {
+        return tags != null ? tags.size() : 0;
+    }
+
+    @Override
+    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
+        if (longPressedItem == null || FeedPreferences.TAG_ROOT.equals(longPressedItem.getTitle())) {
+            return;
+        }
+        MenuInflater inflater = activityRef.get().getMenuInflater();
+        inflater.inflate(R.menu.nav_folder_context, menu);
+        menu.setHeaderTitle(longPressedItem.getTitle());
+    }
+
+    protected void onTagClick(NavDrawerData.TagItem tag) {
+    }
+
+    public NavDrawerData.TagItem getLongPressedItem() {
+        return longPressedItem;
+    }
+
+    public static class TagViewHolder extends RecyclerView.ViewHolder {
+        public final Chip chip;
+
+        public TagViewHolder(@NonNull View itemView) {
+            super(itemView);
+            chip = itemView.findViewById(R.id.tag_chip);
+        }
+    }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/ui/screen/subscriptions/SubscriptionViewHolder.java b/app/src/main/java/de/danoeh/antennapod/ui/screen/subscriptions/SubscriptionViewHolder.java
index 79b58f8d7b9..d85f9bb6478 100644
--- a/app/src/main/java/de/danoeh/antennapod/ui/screen/subscriptions/SubscriptionViewHolder.java
+++ b/app/src/main/java/de/danoeh/antennapod/ui/screen/subscriptions/SubscriptionViewHolder.java
@@ -13,7 +13,6 @@
 import androidx.recyclerview.widget.RecyclerView;
 import de.danoeh.antennapod.R;
 import de.danoeh.antennapod.model.feed.Feed;
-import de.danoeh.antennapod.storage.database.NavDrawerData;
 import de.danoeh.antennapod.storage.preferences.UserPreferences;
 import de.danoeh.antennapod.ui.CoverLoader;
 import de.danoeh.antennapod.ui.common.ThemeUtils;
@@ -45,35 +44,28 @@ public SubscriptionViewHolder(@NonNull View itemView, Activity mainActivity) {
         this.mainActivityRef = new WeakReference<>(mainActivity);
     }
 
-    public void bind(NavDrawerData.DrawerItem drawerItem, int columnCount) {
+    public void bind(Feed feed, int columnCount, int counter) {
         if (selectView != null) {
             Drawable drawable = AppCompatResources.getDrawable(selectView.getContext(),
                     R.drawable.ic_checkbox_background);
             selectView.setBackground(drawable); // Setting this in XML crashes API <= 21
         }
-        title.setText(drawerItem.getTitle());
-        fallbackTitle.setText(drawerItem.getTitle());
-        coverImage.setContentDescription(drawerItem.getTitle());
-        if (drawerItem.getCounter() > 0) {
-            count.setText(NumberFormat.getInstance().format(drawerItem.getCounter()));
+        title.setText(feed.getTitle());
+        fallbackTitle.setText(feed.getTitle());
+        coverImage.setContentDescription(feed.getTitle());
+        if (counter > 0) {
+            count.setText(NumberFormat.getInstance().format(counter));
             count.setVisibility(View.VISIBLE);
         } else {
             count.setVisibility(View.GONE);
         }
 
         CoverLoader coverLoader = new CoverLoader();
-        boolean textAndImageCombined;
-        if (drawerItem.type == NavDrawerData.DrawerItem.Type.FEED) {
-            Feed feed = ((NavDrawerData.FeedDrawerItem) drawerItem).feed;
-            textAndImageCombined = feed.isLocalFeed() && feed.getImageUrl() != null
-                    && feed.getImageUrl().startsWith(Feed.PREFIX_GENERATIVE_COVER);
-            coverLoader.withUri(feed.getImageUrl());
-            errorIcon.setVisibility(feed.hasLastUpdateFailed() ? View.VISIBLE : View.GONE);
-        } else {
-            textAndImageCombined = true;
-            coverLoader.withResource(R.drawable.ic_tag);
-            errorIcon.setVisibility(View.GONE);
-        }
+        boolean textAndImageCombined = feed.isLocalFeed() && feed.getImageUrl() != null
+                && feed.getImageUrl().startsWith(Feed.PREFIX_GENERATIVE_COVER);
+        coverLoader.withUri(feed.getImageUrl());
+        errorIcon.setVisibility(feed.hasLastUpdateFailed() ? View.VISIBLE : View.GONE);
+
         if (UserPreferences.shouldShowSubscriptionTitle() || columnCount == 1) {
             // No need for fallback title when already showing title
             fallbackTitle.setVisibility(View.GONE);
diff --git a/app/src/main/java/de/danoeh/antennapod/ui/screen/subscriptions/SubscriptionsRecyclerAdapter.java b/app/src/main/java/de/danoeh/antennapod/ui/screen/subscriptions/SubscriptionsRecyclerAdapter.java
index 1a243a250fd..0a35a015a13 100644
--- a/app/src/main/java/de/danoeh/antennapod/ui/screen/subscriptions/SubscriptionsRecyclerAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/ui/screen/subscriptions/SubscriptionsRecyclerAdapter.java
@@ -18,7 +18,6 @@
 import de.danoeh.antennapod.R;
 import de.danoeh.antennapod.activity.MainActivity;
 import de.danoeh.antennapod.model.feed.Feed;
-import de.danoeh.antennapod.storage.database.NavDrawerData;
 import de.danoeh.antennapod.storage.preferences.UserPreferences;
 import de.danoeh.antennapod.ui.SelectableAdapter;
 import de.danoeh.antennapod.ui.common.ThemeUtils;
@@ -27,6 +26,7 @@
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Adapter for subscriptions
@@ -34,8 +34,9 @@
 public class SubscriptionsRecyclerAdapter extends SelectableAdapter<SubscriptionViewHolder>
         implements View.OnCreateContextMenuListener {
     private final WeakReference<MainActivity> mainActivityRef;
-    private List<NavDrawerData.DrawerItem> listItems;
-    private NavDrawerData.DrawerItem selectedItem = null;
+    private List<Feed> listItems;
+    private Map<Long, Integer> feedCounters;
+    private Feed selectedItem = null;
     int longPressedPosition = 0; // used to init actionMode
     private int columnCount = 3;
 
@@ -43,6 +44,7 @@ public SubscriptionsRecyclerAdapter(MainActivity mainActivity) {
         super(mainActivity);
         this.mainActivityRef = new WeakReference<>(mainActivity);
         this.listItems = new ArrayList<>();
+        this.feedCounters = Map.of();
         setHasStableIds(true);
     }
 
@@ -54,7 +56,7 @@ public Object getItem(int position) {
         return listItems.get(position);
     }
 
-    public NavDrawerData.DrawerItem getSelectedItem() {
+    public Feed getSelectedItem() {
         return selectedItem;
     }
 
@@ -75,16 +77,13 @@ public SubscriptionViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int
 
     @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, columnCount);
+        Feed feed = listItems.get(position);
+        holder.bind(feed, columnCount, feedCounters.containsKey(feed.getId()) ? feedCounters.get(feed.getId()) : 0);
         holder.itemView.setOnCreateContextMenuListener(this);
         if (inActionMode()) {
             if (holder.selectView != null) {
-                if (isFeed) {
-                    holder.selectCheckbox.setVisibility(View.VISIBLE);
-                }
-                holder.selectView.setVisibility(isFeed ? View.VISIBLE : View.GONE);
+                holder.selectCheckbox.setVisibility(View.VISIBLE);
+                holder.selectView.setVisibility(View.VISIBLE);
                 holder.selectCheckbox.setChecked((isSelected(position)));
                 holder.selectCheckbox.setOnCheckedChangeListener((buttonView, isChecked)
                         -> setSelected(holder.getBindingAdapterPosition(), isChecked));
@@ -106,10 +105,8 @@ public void onBindViewHolder(@NonNull SubscriptionViewHolder holder, int positio
 
         holder.itemView.setOnLongClickListener(v -> {
             if (!inActionMode()) {
-                if (isFeed) {
-                    longPressedPosition = holder.getBindingAdapterPosition();
-                }
-                selectedItem = drawerItem;
+                longPressedPosition = holder.getBindingAdapterPosition();
+                selectedItem = feed;
             }
             return false;
         });
@@ -119,27 +116,19 @@ public void onBindViewHolder(@NonNull SubscriptionViewHolder holder, int positio
                 if (e.isFromSource(InputDevice.SOURCE_MOUSE)
                         &&  e.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
                     if (!inActionMode()) {
-                        if (isFeed) {
-                            longPressedPosition = holder.getBindingAdapterPosition();
-                        }
-                        selectedItem = drawerItem;
+                        longPressedPosition = holder.getBindingAdapterPosition();
+                        selectedItem = feed;
                     }
                 }
             }
             return false;
         });
         holder.itemView.setOnClickListener(v -> {
-            if (isFeed) {
-                if (inActionMode()) {
-                    setSelected(holder.getBindingAdapterPosition(), !isSelected(holder.getBindingAdapterPosition()));
-                    notifyItemChanged(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());
+            if (inActionMode()) {
+                setSelected(holder.getBindingAdapterPosition(), !isSelected(holder.getBindingAdapterPosition()));
+                notifyItemChanged(holder.getBindingAdapterPosition());
+            } else {
+                Fragment fragment = FeedItemlistFragment.newInstance(feed.getId());
                 mainActivityRef.get().loadChildFragment(fragment);
             }
         });
@@ -156,7 +145,7 @@ public long getItemId(int position) {
         if (position >= listItems.size()) {
             return RecyclerView.NO_ID; // Dummy views
         }
-        return listItems.get(position).id;
+        return listItems.get(position).getId();
     }
 
     @Override
@@ -165,12 +154,8 @@ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMen
             return;
         }
         MenuInflater inflater = mainActivityRef.get().getMenuInflater();
-        if (selectedItem.type == NavDrawerData.DrawerItem.Type.FEED) {
-            inflater.inflate(R.menu.nav_feed_context, menu);
-            menu.findItem(R.id.multi_select).setVisible(true);
-        } else {
-            inflater.inflate(R.menu.nav_folder_context, menu);
-        }
+        inflater.inflate(R.menu.nav_feed_context, menu);
+        menu.findItem(R.id.multi_select).setVisible(true);
         menu.setHeaderTitle(selectedItem.getTitle());
     }
 
@@ -186,29 +171,18 @@ 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);
-                }
+                items.add(listItems.get(i));
             }
         }
         return items;
     }
 
-    public void setItems(List<NavDrawerData.DrawerItem> listItems) {
+    public void setItems(List<Feed> listItems, Map<Long, Integer> feedCounters) {
         this.listItems = listItems;
+        this.feedCounters = feedCounters;
         notifyDataSetChanged();
     }
 
-    @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);
-        }
-    }
-
     @Override
     public int getItemViewType(int position) {
         if (columnCount == 1) {
diff --git a/app/src/main/java/de/danoeh/antennapod/ui/view/ItemOffsetDecoration.java b/app/src/main/java/de/danoeh/antennapod/ui/view/ItemOffsetDecoration.java
index 4c3adcbc1b5..2bfc0ba7f60 100644
--- a/app/src/main/java/de/danoeh/antennapod/ui/view/ItemOffsetDecoration.java
+++ b/app/src/main/java/de/danoeh/antennapod/ui/view/ItemOffsetDecoration.java
@@ -6,19 +6,24 @@
 import androidx.annotation.NonNull;
 import androidx.recyclerview.widget.RecyclerView;
 
-/**
- * Source: https://stackoverflow.com/a/30794046
- */
 public class ItemOffsetDecoration extends RecyclerView.ItemDecoration {
-    private final int itemOffset;
+    private final int itemOffsetHorizontal;
+    private final int itemOffsetVertical;
 
     public ItemOffsetDecoration(@NonNull Context context, int itemOffsetDp) {
-        itemOffset = (int) (itemOffsetDp * context.getResources().getDisplayMetrics().density);
+        itemOffsetHorizontal = (int) (itemOffsetDp * context.getResources().getDisplayMetrics().density);
+        itemOffsetVertical = (int) (itemOffsetDp * context.getResources().getDisplayMetrics().density);
+    }
+
+    public ItemOffsetDecoration(@NonNull Context context, int itemOffsetHorizontalDp, int itemOffsetVerticalDp) {
+        itemOffsetHorizontal = (int) (itemOffsetHorizontalDp * context.getResources().getDisplayMetrics().density);
+        itemOffsetVertical = (int) (itemOffsetVerticalDp * context.getResources().getDisplayMetrics().density);
     }
 
     @Override
-    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
+    public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent,
+                               @NonNull RecyclerView.State state) {
         super.getItemOffsets(outRect, view, parent, state);
-        outRect.set(itemOffset, itemOffset, itemOffset, itemOffset);
+        outRect.set(itemOffsetHorizontal, itemOffsetVertical, itemOffsetHorizontal, itemOffsetVertical);
     }
 }
diff --git a/app/src/main/res/layout/fragment_subscriptions.xml b/app/src/main/res/layout/fragment_subscriptions.xml
index 16c69bd0cbe..c18eb0b88ba 100644
--- a/app/src/main/res/layout/fragment_subscriptions.xml
+++ b/app/src/main/res/layout/fragment_subscriptions.xml
@@ -20,15 +20,32 @@
             app:navigationContentDescription="@string/toolbar_back_button_content_description"
             app:navigationIcon="?homeAsUpIndicator" />
 
-        <TextView
-            android:id="@+id/feeds_filtered_message"
+        <LinearLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:paddingHorizontal="16dp"
-            android:paddingVertical="4dp"
             android:layout_marginTop="-16dp"
-            android:background="?android:attr/selectableItemBackground"
-            android:text="@string/filtered_label" />
+            android:orientation="vertical">
+
+            <androidx.recyclerview.widget.RecyclerView
+                android:id="@+id/tags_recycler"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:paddingHorizontal="12dp"
+                android:clipToPadding="false"
+                android:visibility="visible"
+                tools:orientation="horizontal"
+                tools:listitem="@layout/item_tag_chip" />
+
+            <TextView
+                android:id="@+id/feeds_filtered_message"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:paddingHorizontal="16dp"
+                android:paddingVertical="4dp"
+                android:background="?android:attr/selectableItemBackground"
+                android:text="@string/filtered_label" />
+
+        </LinearLayout>
 
     </com.google.android.material.appbar.AppBarLayout>
 
diff --git a/app/src/main/res/layout/item_tag_chip.xml b/app/src/main/res/layout/item_tag_chip.xml
new file mode 100644
index 00000000000..5a88e67c11b
--- /dev/null
+++ b/app/src/main/res/layout/item_tag_chip.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<com.google.android.material.chip.Chip
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/tag_chip"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:checkable="true"
+    android:longClickable="true"
+    style="@style/Widget.Material3.Chip.Filter" />
diff --git a/storage/database/src/main/java/de/danoeh/antennapod/storage/database/DBReader.java b/storage/database/src/main/java/de/danoeh/antennapod/storage/database/DBReader.java
index 08f9dd5a348..9e1524fc2f1 100644
--- a/storage/database/src/main/java/de/danoeh/antennapod/storage/database/DBReader.java
+++ b/storage/database/src/main/java/de/danoeh/antennapod/storage/database/DBReader.java
@@ -21,7 +21,6 @@
 import de.danoeh.antennapod.model.feed.FeedItemFilter;
 import de.danoeh.antennapod.model.feed.FeedMedia;
 import de.danoeh.antennapod.model.feed.FeedOrder;
-import de.danoeh.antennapod.model.feed.FeedPreferences;
 import de.danoeh.antennapod.model.feed.SortOrder;
 import de.danoeh.antennapod.model.feed.SubscriptionsFilter;
 import de.danoeh.antennapod.model.download.DownloadResult;
@@ -737,32 +736,21 @@ public static NavDrawerData getNavDrawerData(@Nullable SubscriptionsFilter subsc
         final int numNewItems = getTotalEpisodeCount(new FeedItemFilter(FeedItemFilter.NEW));
         final int numDownloadedItems = getTotalEpisodeCount(new FeedItemFilter(FeedItemFilter.DOWNLOADED));
 
-        List<NavDrawerData.DrawerItem> items = new ArrayList<>();
-        Map<String, NavDrawerData.TagDrawerItem> folders = new HashMap<>();
+        Map<String, NavDrawerData.TagItem> tags = new HashMap<>();
         for (Feed feed : feeds) {
             for (String tag : feed.getPreferences().getTags()) {
-                int counter = feedCounters.containsKey(feed.getId()) ? feedCounters.get(feed.getId()) : 0;
-                NavDrawerData.FeedDrawerItem drawerItem = new NavDrawerData.FeedDrawerItem(feed, feed.getId(), counter);
-                if (FeedPreferences.TAG_ROOT.equals(tag)) {
-                    items.add(drawerItem);
-                    continue;
-                }
-                NavDrawerData.TagDrawerItem folder;
-                if (folders.containsKey(tag)) {
-                    folder = folders.get(tag);
-                } else {
-                    folder = new NavDrawerData.TagDrawerItem(tag);
-                    folders.put(tag, folder);
+                if (!tags.containsKey(tag)) {
+                    tags.put(tag, new NavDrawerData.TagItem(tag));
                 }
-                drawerItem.id |= folder.id;
-                folder.getChildren().add(drawerItem);
+                int counter = feedCounters.containsKey(feed.getId()) ? feedCounters.get(feed.getId()) : 0;
+                tags.get(tag).addFeed(feed, counter);
             }
         }
-        List<NavDrawerData.TagDrawerItem> foldersSorted = new ArrayList<>(folders.values());
-        Collections.sort(foldersSorted, (o1, o2) -> o1.getTitle().compareToIgnoreCase(o2.getTitle()));
-        items.addAll(foldersSorted);
+        List<NavDrawerData.TagItem> tagsSorted = new ArrayList<>(tags.values());
+        Collections.sort(tagsSorted, (o1, o2) -> o1.getTitle().compareToIgnoreCase(o2.getTitle()));
 
-        NavDrawerData result = new NavDrawerData(items, queueSize, numNewItems, numDownloadedItems, feedCounters);
+        NavDrawerData result = new NavDrawerData(feeds, tagsSorted,
+                queueSize, numNewItems, numDownloadedItems, feedCounters);
         adapter.close();
         return result;
     }
diff --git a/storage/database/src/main/java/de/danoeh/antennapod/storage/database/NavDrawerData.java b/storage/database/src/main/java/de/danoeh/antennapod/storage/database/NavDrawerData.java
index 5be8247526b..601ae7e87ca 100644
--- a/storage/database/src/main/java/de/danoeh/antennapod/storage/database/NavDrawerData.java
+++ b/storage/database/src/main/java/de/danoeh/antennapod/storage/database/NavDrawerData.java
@@ -7,60 +7,38 @@
 import java.util.Map;
 
 public class NavDrawerData {
-    public final List<DrawerItem> items;
+    public final List<Feed> feeds;
+    public final List<TagItem> tags;
     public final int queueSize;
     public final int numNewItems;
     public final int numDownloadedItems;
     public final Map<Long, Integer> feedCounters;
 
-    public NavDrawerData(List<DrawerItem> feeds,
+    public NavDrawerData(List<Feed> feeds,
+                         List<TagItem> tags,
                          int queueSize,
                          int numNewItems,
                          int numDownloadedItems,
                          Map<Long, Integer> feedIndicatorValues) {
-        this.items = feeds;
+        this.feeds = feeds;
+        this.tags = tags;
         this.queueSize = queueSize;
         this.numNewItems = numNewItems;
         this.numDownloadedItems = numDownloadedItems;
         this.feedCounters = feedIndicatorValues;
     }
 
-    public abstract static class DrawerItem {
-        public enum Type {
-            TAG, FEED
-        }
-
-        public final Type type;
-        private int layer;
-        public long id;
-
-        public DrawerItem(Type type, long id) {
-            this.type = type;
-            this.id = id;
-        }
-
-        public int getLayer() {
-            return layer;
-        }
-
-        public void setLayer(int layer) {
-            this.layer = layer;
-        }
-
-        public abstract String getTitle();
-
-        public abstract int getCounter();
-    }
-
-    public static class TagDrawerItem extends DrawerItem {
-        private final List<DrawerItem> children = new ArrayList<>();
+    public static class TagItem {
         private final String name;
-        private boolean isOpen;
+        private List<Feed> feeds = new ArrayList<>();
+        private int counter = 0;
+        private boolean isOpen = false;
+        private long id;
 
-        public TagDrawerItem(String name) {
-            // Keep IDs >0 but make room for many feeds
-            super(DrawerItem.Type.TAG, Math.abs((long) name.hashCode()) << 20);
+        public TagItem(String name) {
             this.name = name;
+            // Keep IDs >0 but make room for many feeds
+            this.id = (Math.abs((long) name.hashCode()) << 20);
         }
 
         public String getTitle() {
@@ -75,35 +53,21 @@ public void setOpen(final boolean open) {
             isOpen = open;
         }
 
-        public List<DrawerItem> getChildren() {
-            return children;
+        public List<Feed> getFeeds() {
+            return feeds;
         }
 
         public int getCounter() {
-            int sum = 0;
-            for (DrawerItem item : children) {
-                sum += item.getCounter();
-            }
-            return sum;
-        }
-    }
-
-    public static class FeedDrawerItem extends DrawerItem {
-        public final Feed feed;
-        public final int counter;
-
-        public FeedDrawerItem(Feed feed, long id, int counter) {
-            super(DrawerItem.Type.FEED, id);
-            this.feed = feed;
-            this.counter = counter;
+            return counter;
         }
 
-        public String getTitle() {
-            return feed.getTitle();
+        public void addFeed(Feed feed, int feedCounter) {
+            counter += feedCounter;
+            feeds.add(feed);
         }
 
-        public int getCounter() {
-            return counter;
+        public long getId() {
+            return id;
         }
     }
 }
diff --git a/ui/i18n/src/main/res/values/strings.xml b/ui/i18n/src/main/res/values/strings.xml
index 5eb3088e422..00a13e4cd3f 100644
--- a/ui/i18n/src/main/res/values/strings.xml
+++ b/ui/i18n/src/main/res/values/strings.xml
@@ -760,7 +760,8 @@
     <string name="authentication_descr">Change your username and password for this podcast and its episodes</string>
     <string name="feed_tags_label">Tags</string>
     <string name="feed_tags_summary">Change the tags of this podcast to help organize your subscriptions</string>
-    <string name="feed_folders_include_root">Show this podcast in main list</string>
+    <string name="feed_folders_include_root">Show this podcast in \"all\" tag</string>
+    <string name="tag_all">All</string>
     <string name="multi_feed_common_tags_info">Only common tags from all selected subscriptions are shown. Other tags stay unaffected.</string>
     <string name="auto_download_inbox_category">Automatically download episodes from the inbox</string>
     <string name="episode_filters_label">Episode filter</string>


Test patch:
diff --git a/net/download/service/src/test/java/de/danoeh/antennapod/net/download/service/episode/autodownload/DbReaderTest.java b/net/download/service/src/test/java/de/danoeh/antennapod/net/download/service/episode/autodownload/DbReaderTest.java
index 8f54609b013..16dce9143ba 100644
--- a/net/download/service/src/test/java/de/danoeh/antennapod/net/download/service/episode/autodownload/DbReaderTest.java
+++ b/net/download/service/src/test/java/de/danoeh/antennapod/net/download/service/episode/autodownload/DbReaderTest.java
@@ -343,7 +343,7 @@ public void testGetNavDrawerDataQueueEmptyNoUnreadItems() {
             DbTestUtils.saveFeedlist(numFeeds, numItems, true);
             NavDrawerData navDrawerData = DBReader.getNavDrawerData(
                     UserPreferences.getSubscriptionsFilter(), FeedOrder.COUNTER, FeedCounter.SHOW_NEW);
-            assertEquals(numFeeds, navDrawerData.items.size());
+            assertEquals(numFeeds, navDrawerData.feeds.size());
             assertEquals(0, navDrawerData.numNewItems);
             assertEquals(0, navDrawerData.queueSize);
         }
@@ -373,7 +373,7 @@ public void testGetNavDrawerDataQueueNotEmptyWithUnreadItems() {
 
             NavDrawerData navDrawerData = DBReader.getNavDrawerData(
                     UserPreferences.getSubscriptionsFilter(), FeedOrder.COUNTER, FeedCounter.SHOW_NEW);
-            assertEquals(numFeeds, navDrawerData.items.size());
+            assertEquals(numFeeds, navDrawerData.feeds.size());
             assertEquals(numNew, navDrawerData.numNewItems);
             assertEquals(numQueue, navDrawerData.queueSize);
         }