Merge pull request #6 from adrianvic/feature-download-playback

Fix metadata and add downloads tab with listing logic and UI updates
This commit is contained in:
天クマ 2026-03-06 08:45:15 -03:00 committed by GitHub
commit 30cc7f663c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 233 additions and 2 deletions

View file

@ -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<DownloadsAdapter.ViewHolder> {
private final List<Song> mSongs;
private final int mLayoutId;
public DownloadsAdapter(int layoutId) {
mLayoutId = layoutId;
this.mSongs = new ArrayList<>();
}
public void swapDataSet(List<Song> 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);
}
}
}

View file

@ -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<? extends Fragment> mFragmentClass;

View file

@ -14,6 +14,9 @@ public interface CacheDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
void insertCache(Cache cache);
@Query("SELECT * FROM cache")
List<Cache> getAll();
@Query("SELECT * FROM songs LEFT JOIN cache USING(id) WHERE songs.id IN (:ids)")
List<Song> getSongs(List<String> ids);

View file

@ -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<DownloadsAdapter, GridLayoutManager, Void> {
@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<Cache> cachedEntries = App.getDatabase().cacheDao().getAll();
List<String> songIds = new ArrayList<>();
for (Cache entry : cachedEntries) {
songIds.add(entry.id);
}
final List<Song> 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) {
}
}

View file

@ -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;

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="org.adrianvictor.geleia.fragments.library.DownloadsFragment">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="409dp"
android:layout_height="729dp"
tools:layout_editor_absoluteX="1dp"
tools:layout_editor_absoluteY="1dp" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -247,6 +247,7 @@
<string name="download_channel_name">Downloads</string>
<string name="downloading_songs">Downloading songs</string>
<string name="downloads">Downloads</string>
<plurals name="downloading_s_songs">
<item quantity="one">Downloading %d song</item>
<item quantity="other">Downloading %d songs</item>

View file

@ -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

View file

Before

Width:  |  Height:  |  Size: 650 KiB

After

Width:  |  Height:  |  Size: 650 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 340 KiB

After

Width:  |  Height:  |  Size: 340 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 165 KiB

After

Width:  |  Height:  |  Size: 165 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 159 KiB

After

Width:  |  Height:  |  Size: 159 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 109 KiB

After

Width:  |  Height:  |  Size: 109 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 174 KiB

After

Width:  |  Height:  |  Size: 174 KiB

Before After
Before After