From 3a0154deea8f5ef91418b4ebe14de03a2a0ebaf8 Mon Sep 17 00:00:00 2001 From: adrian Date: Mon, 26 Jan 2026 14:21:13 -0300 Subject: [PATCH 1/4] Add new tab in MainActivity and migration for listing downloads. --- .../adapter/MusicLibraryPagerAdapter.java | 6 ++++- .../fragments/library/DownloadsFragment.java | 25 +++++++++++++++++++ .../adrianvictor/geleia/model/Category.java | 3 ++- .../geleia/util/PreferenceUtil.java | 13 +++++++++- .../main/res/layout/downloads_fragment.xml | 6 +++++ app/src/main/res/values/strings.xml | 1 + 6 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/org/adrianvictor/geleia/fragments/library/DownloadsFragment.java create mode 100644 app/src/main/res/layout/downloads_fragment.xml diff --git a/app/src/main/java/org/adrianvictor/geleia/adapter/MusicLibraryPagerAdapter.java b/app/src/main/java/org/adrianvictor/geleia/adapter/MusicLibraryPagerAdapter.java index 2979ee5c..23c1162d 100644 --- a/app/src/main/java/org/adrianvictor/geleia/adapter/MusicLibraryPagerAdapter.java +++ b/app/src/main/java/org/adrianvictor/geleia/adapter/MusicLibraryPagerAdapter.java @@ -10,6 +10,9 @@ import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentPagerAdapter; +import org.adrianvictor.geleia.activities.UnreachableActivity; +import org.adrianvictor.geleia.fragments.OfflineFragment; +import org.adrianvictor.geleia.fragments.library.DownloadsFragment; import org.adrianvictor.geleia.fragments.library.FavoritesFragment; import org.adrianvictor.geleia.model.Category; import org.adrianvictor.geleia.fragments.library.AlbumsFragment; @@ -157,7 +160,8 @@ public class MusicLibraryPagerAdapter extends FragmentPagerAdapter { ARTISTS(ArtistsFragment.class), GENRES(GenresFragment.class), PLAYLISTS(PlaylistsFragment.class), - FAVORITES(FavoritesFragment.class); + FAVORITES(FavoritesFragment.class), + DOWNLOADS(DownloadsFragment.class); private final Class mFragmentClass; diff --git a/app/src/main/java/org/adrianvictor/geleia/fragments/library/DownloadsFragment.java b/app/src/main/java/org/adrianvictor/geleia/fragments/library/DownloadsFragment.java new file mode 100644 index 00000000..2e052c2d --- /dev/null +++ b/app/src/main/java/org/adrianvictor/geleia/fragments/library/DownloadsFragment.java @@ -0,0 +1,25 @@ +package org.adrianvictor.geleia.fragments.library; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; + +import org.adrianvictor.geleia.R; + +public class DownloadsFragment extends Fragment { + public static DownloadsFragment newInstance() { + return new DownloadsFragment(); + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + return inflater.inflate(R.layout.downloads_fragment, container, false); + } + +} diff --git a/app/src/main/java/org/adrianvictor/geleia/model/Category.java b/app/src/main/java/org/adrianvictor/geleia/model/Category.java index 91e54717..3c897e81 100644 --- a/app/src/main/java/org/adrianvictor/geleia/model/Category.java +++ b/app/src/main/java/org/adrianvictor/geleia/model/Category.java @@ -10,7 +10,8 @@ public enum Category { ARTISTS(R.string.artists), GENRES(R.string.genres), PLAYLISTS(R.string.playlists), - FAVORITES(R.string.favorites); + FAVORITES(R.string.favorites), + DOWNLOADS(R.string.downloads); @StringRes public final int title; diff --git a/app/src/main/java/org/adrianvictor/geleia/util/PreferenceUtil.java b/app/src/main/java/org/adrianvictor/geleia/util/PreferenceUtil.java index 71ff53d5..ecb3dd68 100644 --- a/app/src/main/java/org/adrianvictor/geleia/util/PreferenceUtil.java +++ b/app/src/main/java/org/adrianvictor/geleia/util/PreferenceUtil.java @@ -110,9 +110,20 @@ public final class PreferenceUtil { } }; + private static final PreferenceMigration Migration3 = new PreferenceMigration(2, 3) { + @Override + public void migrate(SharedPreferences preferences) { + String currentCategories = preferences.getString(CATEGORIES, ""); + if (!currentCategories.toUpperCase().contains(Category.DOWNLOADS.toString())) { + preferences.edit().putString(CATEGORIES, currentCategories + "." + Category.DOWNLOADS.toString()).commit(); + } + } + }; + private static final List migrations = Arrays.asList( Migration1, - Migration2 + Migration2, + Migration3 ); private static PreferenceUtil instance; diff --git a/app/src/main/res/layout/downloads_fragment.xml b/app/src/main/res/layout/downloads_fragment.xml new file mode 100644 index 00000000..77d9ef65 --- /dev/null +++ b/app/src/main/res/layout/downloads_fragment.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bf686d10..235eb77f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -247,6 +247,7 @@ Downloads Downloading songs + Downloads Downloading %d song Downloading %d songs From cd0971bf145ffd4f27e2b6a8999223835c299f82 Mon Sep 17 00:00:00 2001 From: adrian Date: Mon, 26 Jan 2026 15:21:45 -0300 Subject: [PATCH 2/4] Change DownloadsFragment to extend AbsLibraryPagerRecyclerViewCustomGridSizeFragment and add DownloadsAdapter. --- .../geleia/adapter/DownloadsAdapter.java | 69 ++++++++++ .../fragments/library/DownloadsFragment.java | 129 ++++++++++++++++-- .../geleia/util/PreferenceUtil.java | 13 +- .../main/res/layout/downloads_fragment.xml | 9 +- 4 files changed, 192 insertions(+), 28 deletions(-) create mode 100644 app/src/main/java/org/adrianvictor/geleia/adapter/DownloadsAdapter.java diff --git a/app/src/main/java/org/adrianvictor/geleia/adapter/DownloadsAdapter.java b/app/src/main/java/org/adrianvictor/geleia/adapter/DownloadsAdapter.java new file mode 100644 index 00000000..844798d4 --- /dev/null +++ b/app/src/main/java/org/adrianvictor/geleia/adapter/DownloadsAdapter.java @@ -0,0 +1,69 @@ +package org.adrianvictor.geleia.adapter; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import org.adrianvictor.geleia.R; +import org.adrianvictor.geleia.model.Song; + +import java.util.ArrayList; +import java.util.List; + +public class DownloadsAdapter extends RecyclerView.Adapter { + private final List mSongs; + private final int mLayoutId; + + public DownloadsAdapter(int layoutId) { + mLayoutId = layoutId; + this.mSongs = new ArrayList<>(); + } + + public void swapDataSet(List newSongs) { + mSongs.clear(); + if (newSongs != null) { + mSongs.addAll(newSongs); + } + notifyDataSetChanged(); + } + + @NonNull + @Override + public DownloadsAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()).inflate(mLayoutId, parent, false); + return new ViewHolder(view); + } + + @Override + public void onBindViewHolder(@NonNull DownloadsAdapter.ViewHolder holder, int position) { + final Song song = mSongs.get(position); + + holder.title.setText(song.title); + holder.artist.setText(song.artistName); + + // TODO: Load album cover into holder.cover using a library like Glide or Picasso + } + + @Override + public int getItemCount() { + return mSongs.size(); + } + + public static class ViewHolder extends RecyclerView.ViewHolder { + public final TextView title; + public final TextView artist; + public final ImageView cover; + + public ViewHolder(View itemView) { + super(itemView); + title = itemView.findViewById(R.id.title); + artist = itemView.findViewById(R.id.text); + cover = itemView.findViewById(R.id.image); + } + } +} diff --git a/app/src/main/java/org/adrianvictor/geleia/fragments/library/DownloadsFragment.java b/app/src/main/java/org/adrianvictor/geleia/fragments/library/DownloadsFragment.java index 2e052c2d..58f379b6 100644 --- a/app/src/main/java/org/adrianvictor/geleia/fragments/library/DownloadsFragment.java +++ b/app/src/main/java/org/adrianvictor/geleia/fragments/library/DownloadsFragment.java @@ -1,25 +1,124 @@ package org.adrianvictor.geleia.fragments.library; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; +import androidx.recyclerview.widget.GridLayoutManager; -import org.adrianvictor.geleia.R; -public class DownloadsFragment extends Fragment { - public static DownloadsFragment newInstance() { - return new DownloadsFragment(); - } +import org.adrianvictor.geleia.adapter.DownloadsAdapter; - @Nullable +import org.adrianvictor.geleia.model.Song; +import org.adrianvictor.geleia.model.SortMethod; +import org.adrianvictor.geleia.model.SortOrder; + +import java.util.ArrayList; +import java.util.List; + +public class DownloadsFragment extends AbsLibraryPagerRecyclerViewCustomGridSizeFragment { + @NonNull @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - return inflater.inflate(R.layout.downloads_fragment, container, false); + protected DownloadsAdapter createAdapter() { + return new DownloadsAdapter(getItemLayoutRes()); } + @NonNull + @Override + protected GridLayoutManager createLayoutManager() { + return new GridLayoutManager(getActivity(), getGridSize()); + } + + @NonNull + @Override + protected Void createQuery() { + return null; + } + + @Override + protected void loadItems(int index) { + List dummySongs = new ArrayList<>(); + + Song song1 = new Song(); + song1.title = "Dummy Song Title"; + song1.artistName = "A. Dummy Artist"; + + dummySongs.add(song1); + + Song song2 = new Song(); + song2.title = "Another Test Song"; + song2.artistName = "The Testers"; + dummySongs.add(song2); + + getActivity().runOnUiThread(() -> { + getAdapter().swapDataSet(dummySongs); + }); + } + + @Override + protected int loadGridSize() { + return 1; + } + + @Override + protected void saveGridSize(int gridColumns) { + + } + + @Override + protected int loadGridSizeLand() { + return 1; + } + + @Override + protected void saveGridSizeLand(int gridColumns) { + + } + + @Override + protected void saveUsePalette(boolean usePalette) { + + } + + @Override + protected boolean loadUsePalette() { + return false; + } + + @Override + protected void setUsePalette(boolean usePalette) { + + } + + @Override + protected void setGridSize(int gridSize) { + + } + + @Override + protected SortMethod loadSortMethod() { + return SortMethod.ADDED; + } + + @Override + protected void saveSortMethod(SortMethod sortMethod) { + + } + + @Override + protected void setSortMethod(SortMethod sortMethod) { + + } + + @Override + protected SortOrder loadSortOrder() { + return SortOrder.ASCENDING; + } + + @Override + protected void saveSortOrder(SortOrder sortOrder) { + + } + + @Override + protected void setSortOrder(SortOrder sortOrder) { + + } } diff --git a/app/src/main/java/org/adrianvictor/geleia/util/PreferenceUtil.java b/app/src/main/java/org/adrianvictor/geleia/util/PreferenceUtil.java index ecb3dd68..71ff53d5 100644 --- a/app/src/main/java/org/adrianvictor/geleia/util/PreferenceUtil.java +++ b/app/src/main/java/org/adrianvictor/geleia/util/PreferenceUtil.java @@ -110,20 +110,9 @@ public final class PreferenceUtil { } }; - private static final PreferenceMigration Migration3 = new PreferenceMigration(2, 3) { - @Override - public void migrate(SharedPreferences preferences) { - String currentCategories = preferences.getString(CATEGORIES, ""); - if (!currentCategories.toUpperCase().contains(Category.DOWNLOADS.toString())) { - preferences.edit().putString(CATEGORIES, currentCategories + "." + Category.DOWNLOADS.toString()).commit(); - } - } - }; - private static final List migrations = Arrays.asList( Migration1, - Migration2, - Migration3 + Migration2 ); private static PreferenceUtil instance; diff --git a/app/src/main/res/layout/downloads_fragment.xml b/app/src/main/res/layout/downloads_fragment.xml index 77d9ef65..811958e9 100644 --- a/app/src/main/res/layout/downloads_fragment.xml +++ b/app/src/main/res/layout/downloads_fragment.xml @@ -1,6 +1,13 @@ + android:layout_height="match_parent" + tools:context="org.adrianvictor.geleia.fragments.library.DownloadsFragment"> + \ No newline at end of file From 1943344585f4246709c153d8fb917e11543089bf Mon Sep 17 00:00:00 2001 From: adrian Date: Mon, 26 Jan 2026 16:51:58 -0300 Subject: [PATCH 3/4] Implement barebones logic to list songs in the downloads page. --- .../geleia/adapter/DownloadsAdapter.java | 2 +- .../geleia/database/CacheDao.java | 3 ++ .../fragments/library/DownloadsFragment.java | 29 ++++++++++--------- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/org/adrianvictor/geleia/adapter/DownloadsAdapter.java b/app/src/main/java/org/adrianvictor/geleia/adapter/DownloadsAdapter.java index 844798d4..25cf4c79 100644 --- a/app/src/main/java/org/adrianvictor/geleia/adapter/DownloadsAdapter.java +++ b/app/src/main/java/org/adrianvictor/geleia/adapter/DownloadsAdapter.java @@ -46,7 +46,7 @@ public class DownloadsAdapter extends RecyclerView.Adapter getAll(); + @Query("SELECT * FROM songs LEFT JOIN cache USING(id) WHERE songs.id IN (:ids)") List getSongs(List ids); diff --git a/app/src/main/java/org/adrianvictor/geleia/fragments/library/DownloadsFragment.java b/app/src/main/java/org/adrianvictor/geleia/fragments/library/DownloadsFragment.java index 58f379b6..f6f259f5 100644 --- a/app/src/main/java/org/adrianvictor/geleia/fragments/library/DownloadsFragment.java +++ b/app/src/main/java/org/adrianvictor/geleia/fragments/library/DownloadsFragment.java @@ -3,9 +3,10 @@ package org.adrianvictor.geleia.fragments.library; import androidx.annotation.NonNull; import androidx.recyclerview.widget.GridLayoutManager; - +import org.adrianvictor.geleia.App; import org.adrianvictor.geleia.adapter.DownloadsAdapter; +import org.adrianvictor.geleia.database.Cache; import org.adrianvictor.geleia.model.Song; import org.adrianvictor.geleia.model.SortMethod; import org.adrianvictor.geleia.model.SortOrder; @@ -34,24 +35,24 @@ public class DownloadsFragment extends AbsLibraryPagerRecyclerViewCustomGridSize @Override protected void loadItems(int index) { - List dummySongs = new ArrayList<>(); + new Thread(() -> { + List cachedEntries = App.getDatabase().cacheDao().getAll(); - Song song1 = new Song(); - song1.title = "Dummy Song Title"; - song1.artistName = "A. Dummy Artist"; + List songIds = new ArrayList<>(); - dummySongs.add(song1); + for (Cache entry : cachedEntries) { + songIds.add(entry.id); + } - Song song2 = new Song(); - song2.title = "Another Test Song"; - song2.artistName = "The Testers"; - dummySongs.add(song2); + final List downloadedSongs = App.getDatabase().cacheDao().getSongs(songIds); - getActivity().runOnUiThread(() -> { - getAdapter().swapDataSet(dummySongs); - }); + if (getActivity() != null) { + getActivity().runOnUiThread(() -> { + getAdapter().swapDataSet(downloadedSongs); + }); + } + }).start(); } - @Override protected int loadGridSize() { return 1; From 49aa6e8048f8461784c591778321ba7e06fdf9d4 Mon Sep 17 00:00:00 2001 From: Adrian Victor Date: Thu, 5 Mar 2026 14:30:15 -0300 Subject: [PATCH 4/4] Move metadata images to correct location and expand app description. --- metadata/en-US/full_description.txt | 15 +++++++++++++++ .../en-US/{ => images}/phoneScreenshots/1.png | Bin .../en-US/{ => images}/phoneScreenshots/2.png | Bin .../en-US/{ => images}/phoneScreenshots/3.png | Bin .../en-US/{ => images}/phoneScreenshots/4.png | Bin .../en-US/{ => images}/phoneScreenshots/5.png | Bin .../en-US/{ => images/phoneScreenshots}/icon.png | Bin 7 files changed, 15 insertions(+) rename metadata/en-US/{ => images}/phoneScreenshots/1.png (100%) rename metadata/en-US/{ => images}/phoneScreenshots/2.png (100%) rename metadata/en-US/{ => images}/phoneScreenshots/3.png (100%) rename metadata/en-US/{ => images}/phoneScreenshots/4.png (100%) rename metadata/en-US/{ => images}/phoneScreenshots/5.png (100%) rename metadata/en-US/{ => images/phoneScreenshots}/icon.png (100%) diff --git a/metadata/en-US/full_description.txt b/metadata/en-US/full_description.txt index 62c58c1e..2a3d80a1 100644 --- a/metadata/en-US/full_description.txt +++ b/metadata/en-US/full_description.txt @@ -1 +1,16 @@ This is a native music player for Android devices that connects to Jellyfin media servers. The code is based on Gelli's archived repository, which is based on an old version of Phonograph. Jamfish is made for personal use, but contributions are welcome! Please open an issue to discuss larger changes before submitting a pull request. + +Features +- Basic library navigation +- Download songs to internal storage individually or through batch actions +- Gapless playback +- Sort albums and songs by different fields +- Search media for partial matches +- Media service integration with notification +- Favorites and playlists +- Playback history reporting +- Filter content by library + +Requisites: +- A Jellfin server +- Android 4.4 or later \ No newline at end of file diff --git a/metadata/en-US/phoneScreenshots/1.png b/metadata/en-US/images/phoneScreenshots/1.png similarity index 100% rename from metadata/en-US/phoneScreenshots/1.png rename to metadata/en-US/images/phoneScreenshots/1.png diff --git a/metadata/en-US/phoneScreenshots/2.png b/metadata/en-US/images/phoneScreenshots/2.png similarity index 100% rename from metadata/en-US/phoneScreenshots/2.png rename to metadata/en-US/images/phoneScreenshots/2.png diff --git a/metadata/en-US/phoneScreenshots/3.png b/metadata/en-US/images/phoneScreenshots/3.png similarity index 100% rename from metadata/en-US/phoneScreenshots/3.png rename to metadata/en-US/images/phoneScreenshots/3.png diff --git a/metadata/en-US/phoneScreenshots/4.png b/metadata/en-US/images/phoneScreenshots/4.png similarity index 100% rename from metadata/en-US/phoneScreenshots/4.png rename to metadata/en-US/images/phoneScreenshots/4.png diff --git a/metadata/en-US/phoneScreenshots/5.png b/metadata/en-US/images/phoneScreenshots/5.png similarity index 100% rename from metadata/en-US/phoneScreenshots/5.png rename to metadata/en-US/images/phoneScreenshots/5.png diff --git a/metadata/en-US/icon.png b/metadata/en-US/images/phoneScreenshots/icon.png similarity index 100% rename from metadata/en-US/icon.png rename to metadata/en-US/images/phoneScreenshots/icon.png