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..25cf4c79 --- /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 Glide + } + + @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/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/database/CacheDao.java b/app/src/main/java/org/adrianvictor/geleia/database/CacheDao.java index 8df0ded5..54ff3733 100644 --- a/app/src/main/java/org/adrianvictor/geleia/database/CacheDao.java +++ b/app/src/main/java/org/adrianvictor/geleia/database/CacheDao.java @@ -14,6 +14,9 @@ public interface CacheDao { @Insert(onConflict = OnConflictStrategy.REPLACE) void insertCache(Cache cache); + @Query("SELECT * FROM cache") + List 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 new file mode 100644 index 00000000..f6f259f5 --- /dev/null +++ b/app/src/main/java/org/adrianvictor/geleia/fragments/library/DownloadsFragment.java @@ -0,0 +1,125 @@ +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; + +import java.util.ArrayList; +import java.util.List; + +public class DownloadsFragment extends AbsLibraryPagerRecyclerViewCustomGridSizeFragment { + @NonNull + @Override + 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) { + new Thread(() -> { + List cachedEntries = App.getDatabase().cacheDao().getAll(); + + List songIds = new ArrayList<>(); + + for (Cache entry : cachedEntries) { + songIds.add(entry.id); + } + + final List downloadedSongs = App.getDatabase().cacheDao().getSongs(songIds); + + if (getActivity() != null) { + getActivity().runOnUiThread(() -> { + getAdapter().swapDataSet(downloadedSongs); + }); + } + }).start(); + } + @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/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/res/layout/downloads_fragment.xml b/app/src/main/res/layout/downloads_fragment.xml new file mode 100644 index 00000000..811958e9 --- /dev/null +++ b/app/src/main/res/layout/downloads_fragment.xml @@ -0,0 +1,13 @@ + + + + + \ 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 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