Add artist page

This commit is contained in:
Antonio Cappiello 2020-11-22 19:11:38 +01:00
parent 8b7e383dc2
commit c2be2711b9
44 changed files with 1028 additions and 191 deletions

View file

@ -36,27 +36,40 @@ android {
dependencies { dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"]) implementation fileTree(dir: "libs", include: ["*.jar"])
implementation 'androidx.core:core-ktx:1.3.2' // Jellyfin
implementation 'com.github.jellyfin.jellyfin-apiclient-java:android:0.7.7'
// Kotlin
implementation 'org.jetbrains.kotlin:kotlin-stdlib:1.4.10' implementation 'org.jetbrains.kotlin:kotlin-stdlib:1.4.10'
// AndroidX
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4' implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0" implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0"
implementation 'pub.devrel:easypermissions:3.0.0'
implementation 'androidx.preference:preference-ktx:1.1.1' implementation 'androidx.preference:preference-ktx:1.1.1'
implementation 'com.android.volley:volley:1.1.1'
implementation "com.paulrybitskyi.persistentsearchview:persistentsearchview:1.1.3"
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.1' implementation 'androidx.navigation:navigation-fragment-ktx:2.3.1'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.1' implementation 'androidx.navigation:navigation-ui-ktx:2.3.1'
implementation 'androidx.recyclerview:recyclerview:1.1.0' implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation 'com.github.bumptech.glide:glide:4.11.0'
implementation "androidx.room:room-runtime:2.2.5" implementation "androidx.room:room-runtime:2.2.5"
implementation 'com.github.jellyfin.jellyfin-apiclient-java:android:0.7.7'
implementation "androidx.cardview:cardview:1.0.0" implementation "androidx.cardview:cardview:1.0.0"
implementation 'org.apache.commons:commons-lang3:3.11'
implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation 'androidx.legacy:legacy-support-v4:1.0.0'
// Android Material
implementation 'com.google.android.material:material:1.2.1'
// VolleyRequest
implementation 'com.android.volley:volley:1.1.1'
// SearchBar
implementation "com.paulrybitskyi.persistentsearchview:persistentsearchview:1.1.3"
// Permission
implementation 'pub.devrel:easypermissions:3.0.0'
// Glide
implementation 'com.github.bumptech.glide:glide:4.11.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0' annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
annotationProcessor "androidx.room:room-compiler:2.2.5" annotationProcessor "androidx.room:room-compiler:2.2.5"
testImplementation 'junit:junit:4.13.1' testImplementation 'junit:junit:4.13.1'

View file

@ -0,0 +1,76 @@
package com.cappielloantonio.play.adapter;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import com.cappielloantonio.play.R;
import com.cappielloantonio.play.model.Album;
import java.util.List;
public class AlbumArtistPageAdapter extends RecyclerView.Adapter<AlbumArtistPageAdapter.ViewHolder> {
private static final String TAG = "RecentMusicAdapter";
private List<Album> albums;
private LayoutInflater mInflater;
private Context context;
private ItemClickListener itemClickListener;
public AlbumArtistPageAdapter(Context context, List<Album> albums) {
this.context = context;
this.mInflater = LayoutInflater.from(context);
this.albums = albums;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = mInflater.inflate(R.layout.item_artist_page_album, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
Album album = albums.get(position);
holder.textAlbumName.setText(album.getTitle());
}
@Override
public int getItemCount() {
return albums.size();
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
TextView textAlbumName;
ViewHolder(View itemView) {
super(itemView);
textAlbumName = itemView.findViewById(R.id.album_name_label);
itemView.setOnClickListener(this);
}
@Override
public void onClick(View view) {
if (itemClickListener != null) itemClickListener.onItemClick(view, getAdapterPosition());
}
}
public void setItems(List<Album> albums) {
this.albums = albums;
notifyDataSetChanged();
}
public void setClickListener(ItemClickListener itemClickListener) {
this.itemClickListener = itemClickListener;
}
public interface ItemClickListener {
void onItemClick(View view, int position);
}
}

View file

@ -61,6 +61,10 @@ public class ArtistCatalogueAdapter extends RecyclerView.Adapter<ArtistCatalogue
} }
} }
public Artist getItem(int position) {
return artists.get(position);
}
public void setItems(List<Artist> artists) { public void setItems(List<Artist> artists) {
this.artists = artists; this.artists = artists;
notifyDataSetChanged(); notifyDataSetChanged();

View file

@ -1,6 +1,7 @@
package com.cappielloantonio.play.adapter; package com.cappielloantonio.play.adapter;
import android.content.Context; import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -10,21 +11,26 @@ import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.viewpager.widget.PagerAdapter; import androidx.viewpager.widget.PagerAdapter;
import com.cappielloantonio.play.App;
import com.cappielloantonio.play.R; import com.cappielloantonio.play.R;
import com.cappielloantonio.play.model.Artist;
import com.cappielloantonio.play.model.Song; import com.cappielloantonio.play.model.Song;
import com.cappielloantonio.play.repository.SongRepository;
import java.util.List; import java.util.List;
public class DiscoverSongAdapter extends PagerAdapter { public class DiscoverSongAdapter extends PagerAdapter {
private static final String TAG = "DiscoverSongAdapter";
private List<Song> songs; private List<Song> songs;
private LayoutInflater layoutInflater; private LayoutInflater layoutInflater;
private Context context; private Context context;
private View view;
public DiscoverSongAdapter(Context context, List<Song> models) { public DiscoverSongAdapter(Context context, List<Song> songs) {
this.context = context; this.context = context;
this.songs = models; this.songs = songs;
Log.d(TAG, "DiscoverSongAdapter: " + songs.toString());
} }
@Override @Override
@ -41,30 +47,31 @@ public class DiscoverSongAdapter extends PagerAdapter {
@Override @Override
public Object instantiateItem(@NonNull ViewGroup container, final int position) { public Object instantiateItem(@NonNull ViewGroup container, final int position) {
layoutInflater = LayoutInflater.from(context); layoutInflater = LayoutInflater.from(context);
View view = layoutInflater.inflate(R.layout.item_home_discover_song, container, false); view = layoutInflater.inflate(R.layout.item_home_discover_song, container, false);
TextView title;
TextView desc;
title = view.findViewById(R.id.title_discover_song_label);
desc = view.findViewById(R.id.artist_discover_song_label);
TextView title = view.findViewById(R.id.title_discover_song_label);
TextView desc = view.findViewById(R.id.artist_discover_song_label);
title.setText(songs.get(position).getTitle()); title.setText(songs.get(position).getTitle());
desc.setText(songs.get(position).getAlbumName()); desc.setText(songs.get(position).getAlbumName());
view.setOnClickListener(v -> Toast.makeText(context, songs.get(position).getTitle(), Toast.LENGTH_SHORT).show()); view.setOnClickListener(v -> {
SongRepository songRepository = new SongRepository(App.getInstance());
songRepository.update(songs.get(position));
});
container.addView(view, 0); container.addView(view, 0);
return view; return view;
} }
public void setItems(List<Song> songs) { public void setItems(List<Song> songs) {
Log.d(TAG, "setItems: CHANGING");
this.songs = songs; this.songs = songs;
notifyDataSetChanged(); notifyDataSetChanged();
} }
@Override @Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) { public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
container.removeView((View)object); container.removeView((View) object);
} }
} }

View file

@ -8,9 +8,11 @@ import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.cappielloantonio.play.App;
import com.cappielloantonio.play.R; import com.cappielloantonio.play.R;
import com.cappielloantonio.play.model.Artist; import com.cappielloantonio.play.model.Artist;
import com.cappielloantonio.play.model.Song; import com.cappielloantonio.play.model.Song;
import com.cappielloantonio.play.repository.SongRepository;
import com.cappielloantonio.play.util.Util; import com.cappielloantonio.play.util.Util;
import java.util.List; import java.util.List;
@ -23,7 +25,6 @@ public class RecentMusicAdapter extends RecyclerView.Adapter<RecentMusicAdapter.
private List<Song> songs; private List<Song> songs;
private LayoutInflater mInflater; private LayoutInflater mInflater;
private Context context; private Context context;
private ItemClickListener itemClickListener;
public RecentMusicAdapter(Context context, List<Song> songs) { public RecentMusicAdapter(Context context, List<Song> songs) {
this.context = context; this.context = context;
@ -65,7 +66,8 @@ public class RecentMusicAdapter extends RecyclerView.Adapter<RecentMusicAdapter.
@Override @Override
public void onClick(View view) { public void onClick(View view) {
if (itemClickListener != null) itemClickListener.onItemClick(view, getAdapterPosition()); SongRepository songRepository = new SongRepository(App.getInstance());
songRepository.update(songs.get(getAdapterPosition()));
} }
} }
@ -73,12 +75,4 @@ public class RecentMusicAdapter extends RecyclerView.Adapter<RecentMusicAdapter.
this.songs = songs; this.songs = songs;
notifyDataSetChanged(); notifyDataSetChanged();
} }
public void setClickListener(ItemClickListener itemClickListener) {
this.itemClickListener = itemClickListener;
}
public interface ItemClickListener {
void onItemClick(View view, int position);
}
} }

View file

@ -8,8 +8,10 @@ import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.cappielloantonio.play.App;
import com.cappielloantonio.play.R; import com.cappielloantonio.play.R;
import com.cappielloantonio.play.model.Song; import com.cappielloantonio.play.model.Song;
import com.cappielloantonio.play.repository.SongRepository;
import com.cappielloantonio.play.util.Util; import com.cappielloantonio.play.util.Util;
import java.util.List; import java.util.List;
@ -22,7 +24,6 @@ public class SongResultSearchAdapter extends RecyclerView.Adapter<SongResultSear
private List<Song> songs; private List<Song> songs;
private LayoutInflater mInflater; private LayoutInflater mInflater;
private Context context; private Context context;
private ItemClickListener itemClickListener;
public SongResultSearchAdapter(Context context, List<Song> songs) { public SongResultSearchAdapter(Context context, List<Song> songs) {
this.context = context; this.context = context;
@ -67,8 +68,8 @@ public class SongResultSearchAdapter extends RecyclerView.Adapter<SongResultSear
@Override @Override
public void onClick(View view) { public void onClick(View view) {
if (itemClickListener != null) SongRepository songRepository = new SongRepository(App.getInstance());
itemClickListener.onItemClick(view, getAdapterPosition()); songRepository.update(songs.get(getAdapterPosition()));
} }
} }
@ -80,12 +81,4 @@ public class SongResultSearchAdapter extends RecyclerView.Adapter<SongResultSear
public Song getItem(int id) { public Song getItem(int id) {
return songs.get(id); return songs.get(id);
} }
public void setClickListener(ItemClickListener itemClickListener) {
this.itemClickListener = itemClickListener;
}
public interface ItemClickListener {
void onItemClick(View view, int position);
}
} }

View file

@ -19,7 +19,7 @@ import com.cappielloantonio.play.model.Playlist;
import com.cappielloantonio.play.model.RecentSearch; import com.cappielloantonio.play.model.RecentSearch;
import com.cappielloantonio.play.model.Song; import com.cappielloantonio.play.model.Song;
@Database(entities = {Album.class, Artist.class, Genre.class, Playlist.class, Song.class, RecentSearch.class}, version = 3, exportSchema = false) @Database(entities = {Album.class, Artist.class, Genre.class, Playlist.class, Song.class, RecentSearch.class}, version = 4, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase { public abstract class AppDatabase extends RoomDatabase {
private static final String TAG = "AppDatabase"; private static final String TAG = "AppDatabase";

View file

@ -8,7 +8,6 @@ import androidx.room.OnConflictStrategy;
import androidx.room.Query; import androidx.room.Query;
import com.cappielloantonio.play.model.Album; import com.cappielloantonio.play.model.Album;
import com.cappielloantonio.play.model.Song;
import java.util.List; import java.util.List;
@ -17,9 +16,15 @@ public interface AlbumDao {
@Query("SELECT * FROM album") @Query("SELECT * FROM album")
LiveData<List<Album>> getAll(); LiveData<List<Album>> getAll();
@Query("SELECT * FROM album WHERE artistId = :artistId;")
LiveData<List<Album>> getArtistAlbums(String artistId);
@Query("SELECT * FROM album ORDER BY RANDOM() LIMIT :number;") @Query("SELECT * FROM album ORDER BY RANDOM() LIMIT :number;")
LiveData<List<Album>> getSample(int number); LiveData<List<Album>> getSample(int number);
@Query("SELECT * FROM album WHERE title LIKE '%' || :name || '%'")
LiveData<List<Album>> searchAlbum(String name);
@Query("SELECT EXISTS(SELECT * FROM album WHERE id = :id)") @Query("SELECT EXISTS(SELECT * FROM album WHERE id = :id)")
boolean exist(String id); boolean exist(String id);

View file

@ -7,7 +7,6 @@ import androidx.room.Insert;
import androidx.room.OnConflictStrategy; import androidx.room.OnConflictStrategy;
import androidx.room.Query; import androidx.room.Query;
import com.cappielloantonio.play.model.Album;
import com.cappielloantonio.play.model.Artist; import com.cappielloantonio.play.model.Artist;
import java.util.List; import java.util.List;
@ -20,6 +19,9 @@ public interface ArtistDao {
@Query("SELECT * FROM artist ORDER BY RANDOM() LIMIT :number;") @Query("SELECT * FROM artist ORDER BY RANDOM() LIMIT :number;")
LiveData<List<Artist>> getSample(int number); LiveData<List<Artist>> getSample(int number);
@Query("SELECT * FROM artist WHERE name LIKE '%' || :name || '%'")
LiveData<List<Artist>> searchArtist(String name);
@Query("SELECT EXISTS(SELECT * FROM artist WHERE id = :id)") @Query("SELECT EXISTS(SELECT * FROM artist WHERE id = :id)")
boolean exist(String id); boolean exist(String id);

View file

@ -6,6 +6,7 @@ import androidx.room.Delete;
import androidx.room.Insert; import androidx.room.Insert;
import androidx.room.OnConflictStrategy; import androidx.room.OnConflictStrategy;
import androidx.room.Query; import androidx.room.Query;
import androidx.room.Update;
import com.cappielloantonio.play.model.Song; import com.cappielloantonio.play.model.Song;
@ -22,15 +23,18 @@ public interface SongDao {
@Query("SELECT * FROM song ORDER BY RANDOM() LIMIT :number") @Query("SELECT * FROM song ORDER BY RANDOM() LIMIT :number")
LiveData<List<Song>> getDiscoverSample(int number); LiveData<List<Song>> getDiscoverSample(int number);
@Query("SELECT * FROM song ORDER BY RANDOM() LIMIT :number") @Query("SELECT * FROM song ORDER BY added DESC LIMIT :number")
LiveData<List<Song>> getRecentlyAddedSample(int number); LiveData<List<Song>> getRecentlyAddedSample(int number);
@Query("SELECT * FROM song ORDER BY RANDOM() LIMIT :number") @Query("SELECT * FROM song WHERE last_play != 0 ORDER BY last_play DESC LIMIT :number")
LiveData<List<Song>> getRecentlyPlayedSample(int number); LiveData<List<Song>> getRecentlyPlayedSample(int number);
@Query("SELECT * FROM song ORDER BY RANDOM() LIMIT :number") @Query("SELECT * FROM song WHERE play_count != 0 ORDER BY play_count DESC LIMIT :number")
LiveData<List<Song>> getMostPlayedSample(int number); LiveData<List<Song>> getMostPlayedSample(int number);
@Query("SELECT * FROM song WHERE play_count != 0 AND artistId = :artistID ORDER BY play_count DESC LIMIT :number")
LiveData<List<Song>> getArtistTopSongsSample(String artistID, int number);
@Query("SELECT EXISTS(SELECT * FROM song WHERE id = :id)") @Query("SELECT EXISTS(SELECT * FROM song WHERE id = :id)")
boolean exist(String id); boolean exist(String id);
@ -42,4 +46,10 @@ public interface SongDao {
@Delete @Delete
void delete(Song song); void delete(Song song);
@Update
public void update(Song song);
@Query("SELECT * FROM song ORDER BY RANDOM() LIMIT :number")
List<Song> random(int number);
} }

View file

@ -0,0 +1,40 @@
package com.cappielloantonio.play.helper.recyclerview;
import android.graphics.Rect;
import android.view.View;
import androidx.recyclerview.widget.RecyclerView;
public class ItemlDecoration extends RecyclerView.ItemDecoration {
private int spanCount;
private int spacing;
private boolean includeEdge;
public ItemlDecoration(int spanCount, int spacing, boolean includeEdge) {
this.spanCount = spanCount;
this.spacing = spacing;
this.includeEdge = includeEdge;
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
int position = parent.getChildAdapterPosition(view); // item position
int column = position % spanCount; // item column
if (includeEdge) {
outRect.left = spacing - column * spacing / spanCount; // spacing - column * ((1f / spanCount) * spacing)
outRect.right = (column + 1) * spacing / spanCount; // (column + 1) * ((1f / spanCount) * spacing)
if (position < spanCount) { // top edge
outRect.top = spacing;
}
outRect.bottom = spacing; // item bottom
} else {
outRect.left = column * spacing / spanCount; // column * ((1f / spanCount) * spacing)
outRect.right = spacing - (column + 1) * spacing / spanCount; // spacing - (column + 1) * ((1f / spanCount) * spacing)
if (position >= spanCount) {
outRect.top = spacing; // item top
}
}
}
}

View file

@ -36,14 +36,22 @@ public class Artist implements Parcelable {
@ColumnInfo(name = "primary") @ColumnInfo(name = "primary")
public String primary; public String primary;
@ColumnInfo(name = "blurHash") @ColumnInfo(name = "primary_blurHash")
public String blurHash; public String primaryBlurHash;
public Artist(@NonNull String id, String name, String primary, String blurHash) { @ColumnInfo(name = "backdrop")
public String backdrop;
@ColumnInfo(name = "backdrop_blurHash")
public String backdropBlurHash;
public Artist(@NonNull String id, String name, String primary, String primaryBlurHash, String backdrop, String backdropBlurHash) {
this.id = id; this.id = id;
this.name = name; this.name = name;
this.primary = primary; this.primary = primary;
this.blurHash = blurHash; this.primaryBlurHash = primaryBlurHash;
this.backdrop = backdrop;
this.backdropBlurHash = backdropBlurHash;
} }
@Ignore @Ignore
@ -51,11 +59,23 @@ public class Artist implements Parcelable {
this.id = itemDto.getId(); this.id = itemDto.getId();
this.name = itemDto.getName(); this.name = itemDto.getName();
this.primary = itemDto.getImageTags().containsKey(ImageType.Primary) ? id : null; this.primary = itemDto.getImageTags().getOrDefault(ImageType.Primary, null);
if (itemDto.getImageBlurHashes() != null && itemDto.getImageBlurHashes().get(ImageType.Primary) != null) { if (itemDto.getImageBlurHashes() != null && itemDto.getImageBlurHashes().get(ImageType.Primary) != null) {
this.blurHash = (String) itemDto.getImageBlurHashes().get(ImageType.Primary).values().toArray()[0]; this.primaryBlurHash = (String) itemDto.getImageBlurHashes().get(ImageType.Primary).values().toArray()[0];
} }
try {
this.backdrop = itemDto.getBackdropImageTags().get(0);
if (itemDto.getImageBlurHashes() != null && itemDto.getBackdropImageTags().get(0) != null) {
this.backdropBlurHash = (String) itemDto.getImageBlurHashes().get(ImageType.Backdrop).values().toArray()[0];
}
}
catch (IndexOutOfBoundsException exception) {
this.backdrop = null;
this.backdropBlurHash = null;
}
this.genres = new ArrayList<>(); this.genres = new ArrayList<>();
this.albums = new ArrayList<>(); this.albums = new ArrayList<>();
this.songs = new ArrayList<>(); this.songs = new ArrayList<>();
@ -92,12 +112,28 @@ public class Artist implements Parcelable {
this.primary = primary; this.primary = primary;
} }
public String getBlurHash() { public String getPrimaryBlurHash() {
return blurHash; return primaryBlurHash;
} }
public void setBlurHash(String blurHash) { public void setPrimaryBlurHash(String primaryBlurHash) {
this.blurHash = blurHash; this.primaryBlurHash = primaryBlurHash;
}
public String getBackdrop() {
return backdrop;
}
public void setBackdrop(String backdrop) {
this.backdrop = backdrop;
}
public String getBackdropBlurHash() {
return backdropBlurHash;
}
public void setBackdropBlurHash(String backdropBlurHash) {
this.backdropBlurHash = backdropBlurHash;
} }
@Override @Override
@ -130,7 +166,9 @@ public class Artist implements Parcelable {
dest.writeString(id); dest.writeString(id);
dest.writeString(name); dest.writeString(name);
dest.writeString(primary); dest.writeString(primary);
dest.writeString(blurHash); dest.writeString(primaryBlurHash);
dest.writeString(backdrop);
dest.writeString(backdropBlurHash);
} }
protected Artist(Parcel in) { protected Artist(Parcel in) {
@ -140,7 +178,9 @@ public class Artist implements Parcelable {
this.id = in.readString(); this.id = in.readString();
this.name = in.readString(); this.name = in.readString();
this.primary = in.readString(); this.primary = in.readString();
this.blurHash = in.readString(); this.primaryBlurHash = in.readString();
this.backdrop = in.readString();
this.backdropBlurHash = in.readString();
} }
public static final Parcelable.Creator<Artist> CREATOR = new Parcelable.Creator<Artist>() { public static final Parcelable.Creator<Artist> CREATOR = new Parcelable.Creator<Artist>() {

View file

@ -14,6 +14,7 @@ import org.jellyfin.apiclient.model.dto.MediaSourceInfo;
import org.jellyfin.apiclient.model.entities.ImageType; import org.jellyfin.apiclient.model.entities.ImageType;
import org.jellyfin.apiclient.model.entities.MediaStream; import org.jellyfin.apiclient.model.entities.MediaStream;
import java.time.Instant;
import java.util.UUID; import java.util.UUID;
@Entity(tableName = "song") @Entity(tableName = "song")
@ -83,7 +84,16 @@ public class Song implements Parcelable {
@ColumnInfo(name = "channels") @ColumnInfo(name = "channels")
private int channels; private int channels;
public Song(@NonNull String id, String title, int trackNumber, int discNumber, int year, long duration, String albumId, String albumName, String artistId, String artistName, String primary, String blurHash, boolean favorite, String path, long size, String container, String codec, int sampleRate, int bitRate, int bitDepth, int channels) { @ColumnInfo(name = "added")
private long added;
@ColumnInfo(name = "play_count")
private int playCount;
@ColumnInfo(name = "last_play")
private long lastPlay;
public Song(@NonNull String id, String title, int trackNumber, int discNumber, int year, long duration, String albumId, String albumName, String artistId, String artistName, String primary, String blurHash, boolean favorite, String path, long size, String container, String codec, int sampleRate, int bitRate, int bitDepth, int channels, long added, int playCount, long lastPlay) {
this.id = id; this.id = id;
this.title = title; this.title = title;
this.trackNumber = trackNumber; this.trackNumber = trackNumber;
@ -105,6 +115,9 @@ public class Song implements Parcelable {
this.bitRate = bitRate; this.bitRate = bitRate;
this.bitDepth = bitDepth; this.bitDepth = bitDepth;
this.channels = channels; this.channels = channels;
this.added = added;
this.playCount = playCount;
this.lastPlay = lastPlay;
} }
@Ignore @Ignore
@ -152,6 +165,10 @@ public class Song implements Parcelable {
this.channels = stream.getChannels() != null ? stream.getChannels() : 0; this.channels = stream.getChannels() != null ? stream.getChannels() : 0;
} }
} }
this.added = Instant.now().toEpochMilli();
this.playCount = 0;
this.lastPlay = 0;
} }
@Ignore @Ignore
@ -246,6 +263,18 @@ public class Song implements Parcelable {
return channels; return channels;
} }
public long getAdded() {
return added;
}
public int getPlayCount() {
return playCount;
}
public long getLastPlay() {
return lastPlay;
}
public void setId(@NonNull String id) { public void setId(@NonNull String id) {
this.id = id; this.id = id;
} }
@ -330,6 +359,23 @@ public class Song implements Parcelable {
this.channels = channels; this.channels = channels;
} }
public void setAdded(long added) {
this.added = added;
}
public void setAdded(int playCount) {
this.playCount = playCount;
}
public void setLastPlay(long lastPlay) {
this.lastPlay = lastPlay;
}
public void nowPlaying() {
this.playCount++;
this.lastPlay = Instant.now().toEpochMilli();
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) return true; if (this == o) return true;
@ -377,6 +423,9 @@ public class Song implements Parcelable {
dest.writeInt(this.bitRate); dest.writeInt(this.bitRate);
dest.writeInt(this.bitDepth); dest.writeInt(this.bitDepth);
dest.writeInt(this.channels); dest.writeInt(this.channels);
dest.writeLong(this.added);
dest.writeInt(this.playCount);
dest.writeLong(this.lastPlay);
} }
protected Song(Parcel in) { protected Song(Parcel in) {
@ -401,6 +450,9 @@ public class Song implements Parcelable {
this.bitRate = in.readInt(); this.bitRate = in.readInt();
this.bitDepth = in.readInt(); this.bitDepth = in.readInt();
this.channels = in.readInt(); this.channels = in.readInt();
this.added = in.readLong();
this.playCount = in.readInt();
this.lastPlay = in.readLong();
} }
public static final Creator<Song> CREATOR = new Creator<Song>() { public static final Creator<Song> CREATOR = new Creator<Song>() {

View file

@ -7,6 +7,8 @@ import androidx.lifecycle.LiveData;
import com.cappielloantonio.play.database.AppDatabase; import com.cappielloantonio.play.database.AppDatabase;
import com.cappielloantonio.play.database.dao.AlbumDao; import com.cappielloantonio.play.database.dao.AlbumDao;
import com.cappielloantonio.play.model.Album; import com.cappielloantonio.play.model.Album;
import com.cappielloantonio.play.model.Artist;
import com.cappielloantonio.play.model.Song;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -14,7 +16,10 @@ import java.util.List;
public class AlbumRepository { public class AlbumRepository {
private AlbumDao albumDao; private AlbumDao albumDao;
private LiveData<List<Album>> listLiveAlbums; private LiveData<List<Album>> listLiveAlbums;
private LiveData<List<Album>> artistListLiveAlbums;
private LiveData<List<Album>> listLiveSampleAlbum; private LiveData<List<Album>> listLiveSampleAlbum;
private LiveData<List<Album>> searchListLiveAlbum;
public AlbumRepository(Application application) { public AlbumRepository(Application application) {
AppDatabase database = AppDatabase.getInstance(application); AppDatabase database = AppDatabase.getInstance(application);
@ -26,11 +31,21 @@ public class AlbumRepository {
return listLiveAlbums; return listLiveAlbums;
} }
public LiveData<List<Album>> getArtistListLiveAlbums(String artistId) {
artistListLiveAlbums = albumDao.getArtistAlbums(artistId);
return artistListLiveAlbums;
}
public LiveData<List<Album>> getListLiveSampleAlbum() { public LiveData<List<Album>> getListLiveSampleAlbum() {
listLiveSampleAlbum = albumDao.getSample(10); listLiveSampleAlbum = albumDao.getSample(10);
return listLiveSampleAlbum; return listLiveSampleAlbum;
} }
public LiveData<List<Album>> searchListLiveAlbum(String name) {
searchListLiveAlbum = albumDao.searchAlbum(name);
return searchListLiveAlbum;
}
public boolean exist(Album album) { public boolean exist(Album album) {
boolean exist = false; boolean exist = false;

View file

@ -8,6 +8,7 @@ import com.cappielloantonio.play.database.AppDatabase;
import com.cappielloantonio.play.database.dao.ArtistDao; import com.cappielloantonio.play.database.dao.ArtistDao;
import com.cappielloantonio.play.model.Album; import com.cappielloantonio.play.model.Album;
import com.cappielloantonio.play.model.Artist; import com.cappielloantonio.play.model.Artist;
import com.cappielloantonio.play.model.Song;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -16,6 +17,8 @@ public class ArtistRepository {
private ArtistDao artistDao; private ArtistDao artistDao;
private LiveData<List<Artist>> listLiveArtists; private LiveData<List<Artist>> listLiveArtists;
private LiveData<List<Artist>> listLiveSampleArtist; private LiveData<List<Artist>> listLiveSampleArtist;
private LiveData<List<Artist>> searchListLiveArtist;
public ArtistRepository(Application application) { public ArtistRepository(Application application) {
AppDatabase database = AppDatabase.getInstance(application); AppDatabase database = AppDatabase.getInstance(application);
@ -32,6 +35,11 @@ public class ArtistRepository {
return listLiveSampleArtist; return listLiveSampleArtist;
} }
public LiveData<List<Artist>> searchListLiveArtist(String name) {
searchListLiveArtist = artistDao.searchArtist(name);
return searchListLiveArtist;
}
public boolean exist(Artist artist) { public boolean exist(Artist artist) {
boolean exist = false; boolean exist = false;

View file

@ -14,12 +14,11 @@ import java.util.List;
public class SongRepository { public class SongRepository {
private SongDao songDao; private SongDao songDao;
private LiveData<List<Song>> listLiveSongs;
private LiveData<List<Song>> searchListLiveSongs; private LiveData<List<Song>> searchListLiveSongs;
private LiveData<List<Song>> listLiveSampleDiscoverSongs;
private LiveData<List<Song>> listLiveSampleRecentlyAddedSongs; private LiveData<List<Song>> listLiveSampleRecentlyAddedSongs;
private LiveData<List<Song>> listLiveSampleRecentlyPlayedSongs; private LiveData<List<Song>> listLiveSampleRecentlyPlayedSongs;
private LiveData<List<Song>> listLiveSampleMostPlayedSongs; private LiveData<List<Song>> listLiveSampleMostPlayedSongs;
private LiveData<List<Song>> listLiveSampleArtistTopSongs;
public SongRepository(Application application) { public SongRepository(Application application) {
@ -32,11 +31,6 @@ public class SongRepository {
return searchListLiveSongs; return searchListLiveSongs;
} }
public LiveData<List<Song>> getListLiveDiscoverSampleSong() {
listLiveSampleDiscoverSongs = songDao.getDiscoverSample(5);
return listLiveSampleDiscoverSongs;
}
public LiveData<List<Song>> getListLiveRecentlyAddedSampleSong() { public LiveData<List<Song>> getListLiveRecentlyAddedSampleSong() {
listLiveSampleRecentlyAddedSongs = songDao.getRecentlyAddedSample(20); listLiveSampleRecentlyAddedSongs = songDao.getRecentlyAddedSample(20);
return listLiveSampleRecentlyAddedSongs; return listLiveSampleRecentlyAddedSongs;
@ -52,6 +46,11 @@ public class SongRepository {
return listLiveSampleMostPlayedSongs; return listLiveSampleMostPlayedSongs;
} }
public LiveData<List<Song>> getArtistListLiveTopSong(String artistID) {
listLiveSampleArtistTopSongs = songDao.getArtistTopSongsSample(artistID, 5);
return listLiveSampleArtistTopSongs;
}
public boolean exist(Song song) { public boolean exist(Song song) {
boolean exist = false; boolean exist = false;
@ -87,6 +86,31 @@ public class SongRepository {
thread.start(); thread.start();
} }
public void update(Song song) {
song.nowPlaying();
UpdateThreadSafe update = new UpdateThreadSafe(songDao, song);
Thread thread = new Thread(update);
thread.start();
}
public List<Song> getRandomSample(int number) {
List<Song> sample = new ArrayList<>();
PickRandomThreadSafe randomThread = new PickRandomThreadSafe(songDao, number);
Thread thread = new Thread(randomThread);
thread.start();
try {
thread.join();
sample = randomThread.getSample();
} catch (InterruptedException e) {
e.printStackTrace();
}
return sample;
}
private static class ExistThreadSafe implements Runnable { private static class ExistThreadSafe implements Runnable {
private SongDao songDao; private SongDao songDao;
private Song song; private Song song;
@ -151,4 +175,39 @@ public class SongRepository {
songDao.delete(song); songDao.delete(song);
} }
} }
private static class UpdateThreadSafe implements Runnable {
private SongDao songDao;
private Song song;
public UpdateThreadSafe(SongDao songDao, Song song) {
this.songDao = songDao;
this.song = song;
}
@Override
public void run() {
songDao.update(song);
}
}
private static class PickRandomThreadSafe implements Runnable {
private SongDao songDao;
private int elementNumber;
private List<Song> sample;
public PickRandomThreadSafe(SongDao songDao, int number) {
this.songDao = songDao;
this.elementNumber = number;
}
@Override
public void run() {
sample = songDao.random(elementNumber);
}
public List<Song> getSample() {
return sample;
}
}
} }

View file

@ -11,11 +11,9 @@ import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.GridLayoutManager;
import com.cappielloantonio.play.adapter.AlbumCatalogueAdapter; import com.cappielloantonio.play.adapter.AlbumCatalogueAdapter;
import com.cappielloantonio.play.adapter.ArtistCatalogueAdapter;
import com.cappielloantonio.play.databinding.FragmentAlbumCatalogueBinding; import com.cappielloantonio.play.databinding.FragmentAlbumCatalogueBinding;
import com.cappielloantonio.play.databinding.FragmentArtistCatalogueBinding; import com.cappielloantonio.play.helper.recyclerview.ItemlDecoration;
import com.cappielloantonio.play.viewmodel.AlbumCatalogueViewModel; import com.cappielloantonio.play.viewmodel.AlbumCatalogueViewModel;
import com.cappielloantonio.play.viewmodel.ArtistCatalogueViewModel;
import java.util.ArrayList; import java.util.ArrayList;
@ -46,6 +44,7 @@ public class AlbumCatalogueFragment extends Fragment {
private void initAlbumCatalogueView() { private void initAlbumCatalogueView() {
bind.albumCatalogueRecyclerView.setLayoutManager(new GridLayoutManager(requireContext(), 2)); bind.albumCatalogueRecyclerView.setLayoutManager(new GridLayoutManager(requireContext(), 2));
bind.albumCatalogueRecyclerView.addItemDecoration(new ItemlDecoration(2, 20, false));
bind.albumCatalogueRecyclerView.setHasFixedSize(true); bind.albumCatalogueRecyclerView.setHasFixedSize(true);
albumAdapter = new AlbumCatalogueAdapter(requireContext(), new ArrayList<>()); albumAdapter = new AlbumCatalogueAdapter(requireContext(), new ArrayList<>());

View file

@ -17,6 +17,7 @@ import com.cappielloantonio.play.adapter.ArtistAdapter;
import com.cappielloantonio.play.adapter.ArtistCatalogueAdapter; import com.cappielloantonio.play.adapter.ArtistCatalogueAdapter;
import com.cappielloantonio.play.adapter.RecentMusicAdapter; import com.cappielloantonio.play.adapter.RecentMusicAdapter;
import com.cappielloantonio.play.databinding.FragmentArtistCatalogueBinding; import com.cappielloantonio.play.databinding.FragmentArtistCatalogueBinding;
import com.cappielloantonio.play.helper.recyclerview.ItemlDecoration;
import com.cappielloantonio.play.model.Artist; import com.cappielloantonio.play.model.Artist;
import com.cappielloantonio.play.ui.activities.MainActivity; import com.cappielloantonio.play.ui.activities.MainActivity;
import com.cappielloantonio.play.viewmodel.ArtistCatalogueViewModel; import com.cappielloantonio.play.viewmodel.ArtistCatalogueViewModel;
@ -51,6 +52,7 @@ public class ArtistCatalogueFragment extends Fragment {
private void initArtistCatalogueView() { private void initArtistCatalogueView() {
bind.artistCatalogueRecyclerView.setLayoutManager(new GridLayoutManager(requireContext(), 2)); bind.artistCatalogueRecyclerView.setLayoutManager(new GridLayoutManager(requireContext(), 2));
bind.artistCatalogueRecyclerView.addItemDecoration(new ItemlDecoration(2, 20, false));
bind.artistCatalogueRecyclerView.setHasFixedSize(true); bind.artistCatalogueRecyclerView.setHasFixedSize(true);
artistAdapter = new ArtistCatalogueAdapter(requireContext(), new ArrayList<>()); artistAdapter = new ArtistCatalogueAdapter(requireContext(), new ArrayList<>());

View file

@ -0,0 +1,78 @@
package com.cappielloantonio.play.ui.fragment;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.cappielloantonio.play.R;
import com.cappielloantonio.play.adapter.AlbumArtistPageAdapter;
import com.cappielloantonio.play.adapter.RecentMusicAdapter;
import com.cappielloantonio.play.adapter.SongResultSearchAdapter;
import com.cappielloantonio.play.databinding.FragmentArtistPageBinding;
import com.cappielloantonio.play.databinding.FragmentHomeBinding;
import com.cappielloantonio.play.ui.activities.MainActivity;
import com.cappielloantonio.play.viewmodel.ArtistPageViewModel;
import com.cappielloantonio.play.viewmodel.HomeViewModel;
import java.util.ArrayList;
public class ArtistPageFragment extends Fragment {
private FragmentArtistPageBinding bind;
private MainActivity activity;
private ArtistPageViewModel artistPageViewModel;
private SongResultSearchAdapter songResultSearchAdapter;
private AlbumArtistPageAdapter albumArtistPageAdapter;
private String artistID;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
activity = (MainActivity) getActivity();
bind = FragmentArtistPageBinding.inflate(inflater, container, false);
View view = bind.getRoot();
artistPageViewModel = new ViewModelProvider(requireActivity()).get(ArtistPageViewModel.class);
init();
initTopSongsView();
initAlbumsView();
return view;
}
@Override
public void onDestroyView() {
super.onDestroyView();
bind = null;
}
private void init() {
artistID = getArguments().getString("artistID");
}
private void initTopSongsView() {
bind.mostStreamedSongRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
bind.mostStreamedSongRecyclerView.setHasFixedSize(true);
songResultSearchAdapter = new SongResultSearchAdapter(requireContext(), new ArrayList<>());
bind.mostStreamedSongRecyclerView.setAdapter(songResultSearchAdapter);
artistPageViewModel.getArtistTopSongList(artistID).observe(requireActivity(), songs -> songResultSearchAdapter.setItems(songs));
}
private void initAlbumsView() {
bind.albumsRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false));
bind.albumsRecyclerView.setHasFixedSize(true);
albumArtistPageAdapter = new AlbumArtistPageAdapter(requireContext(), new ArrayList<>());
bind.albumsRecyclerView.setAdapter(albumArtistPageAdapter);
artistPageViewModel.getAlbumList(artistID).observe(requireActivity(), songs -> albumArtistPageAdapter.setItems(songs));
}
}

View file

@ -44,6 +44,7 @@ public class FilterFragment extends Fragment {
private void setFilterChips() { private void setFilterChips() {
filterViewModel.getGenreList().observe(requireActivity(), genres -> { filterViewModel.getGenreList().observe(requireActivity(), genres -> {
bind.loadingProgressBar.setVisibility(View.GONE); bind.loadingProgressBar.setVisibility(View.GONE);
bind.filterContainer.setVisibility(View.VISIBLE);
for (Genre genre : genres) { for (Genre genre : genres) {
Chip mChip = (Chip) requireActivity().getLayoutInflater().inflate(R.layout.chip_search_filter_genre, null, false); Chip mChip = (Chip) requireActivity().getLayoutInflater().inflate(R.layout.chip_search_filter_genre, null, false);
mChip.setText(genre.getName()); mChip.setText(genre.getName());

View file

@ -15,6 +15,7 @@ import com.cappielloantonio.play.adapter.GenreAdapter;
import com.cappielloantonio.play.adapter.GenreCatalogueAdapter; import com.cappielloantonio.play.adapter.GenreCatalogueAdapter;
import com.cappielloantonio.play.databinding.FragmentArtistCatalogueBinding; import com.cappielloantonio.play.databinding.FragmentArtistCatalogueBinding;
import com.cappielloantonio.play.databinding.FragmentGenreCatalogueBinding; import com.cappielloantonio.play.databinding.FragmentGenreCatalogueBinding;
import com.cappielloantonio.play.helper.recyclerview.ItemlDecoration;
import com.cappielloantonio.play.viewmodel.ArtistCatalogueViewModel; import com.cappielloantonio.play.viewmodel.ArtistCatalogueViewModel;
import com.cappielloantonio.play.viewmodel.GenreCatalogueViewModel; import com.cappielloantonio.play.viewmodel.GenreCatalogueViewModel;
@ -47,11 +48,13 @@ public class GenreCatalogueFragment extends Fragment {
private void initArtistCatalogueView() { private void initArtistCatalogueView() {
bind.genreCatalogueRecyclerView.setLayoutManager(new GridLayoutManager(requireContext(), 2)); bind.genreCatalogueRecyclerView.setLayoutManager(new GridLayoutManager(requireContext(), 2));
bind.genreCatalogueRecyclerView.addItemDecoration(new ItemlDecoration(2, 20, false));
bind.genreCatalogueRecyclerView.setHasFixedSize(true); bind.genreCatalogueRecyclerView.setHasFixedSize(true);
genreCatalogueAdapter = new GenreCatalogueAdapter(requireContext(), new ArrayList<>()); genreCatalogueAdapter = new GenreCatalogueAdapter(requireContext(), new ArrayList<>());
genreCatalogueAdapter.setClickListener((view, position) -> Toast.makeText(requireContext(), "Click: " + position, Toast.LENGTH_SHORT).show()); genreCatalogueAdapter.setClickListener((view, position) -> Toast.makeText(requireContext(), "Click: " + position, Toast.LENGTH_SHORT).show());
bind.genreCatalogueRecyclerView.setAdapter(genreCatalogueAdapter); bind.genreCatalogueRecyclerView.setAdapter(genreCatalogueAdapter);
genreCatalogueViewModel.getGenreList().observe(requireActivity(), genres -> { genreCatalogueViewModel.getGenreList().observe(requireActivity(), genres -> {
bind.loadingProgressBar.setVisibility(View.GONE); bind.loadingProgressBar.setVisibility(View.GONE);
bind.genreCatalogueContainer.setVisibility(View.VISIBLE); bind.genreCatalogueContainer.setVisibility(View.VISIBLE);

View file

@ -9,22 +9,19 @@ import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import com.cappielloantonio.play.adapter.DiscoverSongAdapter; import com.cappielloantonio.play.adapter.DiscoverSongAdapter;
import com.cappielloantonio.play.adapter.RecentMusicAdapter; import com.cappielloantonio.play.adapter.RecentMusicAdapter;
import com.cappielloantonio.play.databinding.FragmentHomeBinding; import com.cappielloantonio.play.databinding.FragmentHomeBinding;
import com.cappielloantonio.play.model.Song;
import com.cappielloantonio.play.ui.activities.MainActivity; import com.cappielloantonio.play.ui.activities.MainActivity;
import com.cappielloantonio.play.util.PreferenceUtil; import com.cappielloantonio.play.util.PreferenceUtil;
import com.cappielloantonio.play.viewmodel.HomeViewModel; import com.cappielloantonio.play.viewmodel.HomeViewModel;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
public class HomeFragment extends Fragment implements RecentMusicAdapter.ItemClickListener { public class HomeFragment extends Fragment {
private static final String TAG = "CategoriesFragment"; private static final String TAG = "CategoriesFragment";
private FragmentHomeBinding bind; private FragmentHomeBinding bind;
@ -33,6 +30,7 @@ public class HomeFragment extends Fragment implements RecentMusicAdapter.ItemCli
private DiscoverSongAdapter discoverSongAdapter; private DiscoverSongAdapter discoverSongAdapter;
private RecentMusicAdapter recentlyAddedMusicAdapter; private RecentMusicAdapter recentlyAddedMusicAdapter;
private RecentMusicAdapter recentlyPlayedMusicAdapter;
private RecentMusicAdapter mostPlayedMusicAdapter; private RecentMusicAdapter mostPlayedMusicAdapter;
@Nullable @Nullable
@ -46,6 +44,7 @@ public class HomeFragment extends Fragment implements RecentMusicAdapter.ItemCli
init(); init();
initDiscoverSongSlideView(); initDiscoverSongSlideView();
initRecentAddedSongView();
initRecentPlayedSongView(); initRecentPlayedSongView();
initMostPlayedSongView(); initMostPlayedSongView();
@ -66,20 +65,27 @@ public class HomeFragment extends Fragment implements RecentMusicAdapter.ItemCli
} }
private void initDiscoverSongSlideView() { private void initDiscoverSongSlideView() {
discoverSongAdapter = new DiscoverSongAdapter(requireContext(), new ArrayList<>()); discoverSongAdapter = new DiscoverSongAdapter(requireContext(), homeViewModel.getDiscoverSongList());
bind.discoverSongViewPager.setAdapter(discoverSongAdapter); bind.discoverSongViewPager.setAdapter(discoverSongAdapter);
bind.discoverSongViewPager.setPageMargin(20); bind.discoverSongViewPager.setPageMargin(20);
homeViewModel.getDiscoverSongList().observe(requireActivity(), songs -> discoverSongAdapter.setItems(songs)); }
private void initRecentAddedSongView() {
bind.recentlyAddedTracksRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false));
bind.recentlyAddedTracksRecyclerView.setHasFixedSize(true);
recentlyAddedMusicAdapter = new RecentMusicAdapter(requireContext(), new ArrayList<>());
bind.recentlyAddedTracksRecyclerView.setAdapter(recentlyAddedMusicAdapter);
homeViewModel.getRecentlyAddedSongList().observe(requireActivity(), songs -> recentlyAddedMusicAdapter.setItems(songs));
} }
private void initRecentPlayedSongView() { private void initRecentPlayedSongView() {
bind.recentlyPlayedTracksRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false)); bind.recentlyPlayedTracksRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false));
bind.recentlyPlayedTracksRecyclerView.setHasFixedSize(true); bind.recentlyPlayedTracksRecyclerView.setHasFixedSize(true);
recentlyAddedMusicAdapter = new RecentMusicAdapter(requireContext(), new ArrayList<>()); recentlyPlayedMusicAdapter = new RecentMusicAdapter(requireContext(), new ArrayList<>());
recentlyAddedMusicAdapter.setClickListener(this); bind.recentlyPlayedTracksRecyclerView.setAdapter(recentlyPlayedMusicAdapter);
bind.recentlyPlayedTracksRecyclerView.setAdapter(recentlyAddedMusicAdapter); homeViewModel.getRecentlyPlayedSongList().observe(requireActivity(), songs -> recentlyPlayedMusicAdapter.setItems(songs));
homeViewModel.getRecentlyAddedSongList().observe(requireActivity(), songs -> recentlyAddedMusicAdapter.setItems(songs));
} }
private void initMostPlayedSongView() { private void initMostPlayedSongView() {
@ -87,13 +93,7 @@ public class HomeFragment extends Fragment implements RecentMusicAdapter.ItemCli
bind.mostPlayedTracksRecyclerView.setHasFixedSize(true); bind.mostPlayedTracksRecyclerView.setHasFixedSize(true);
mostPlayedMusicAdapter = new RecentMusicAdapter(requireContext(), new ArrayList<>()); mostPlayedMusicAdapter = new RecentMusicAdapter(requireContext(), new ArrayList<>());
mostPlayedMusicAdapter.setClickListener(this);
bind.mostPlayedTracksRecyclerView.setAdapter(mostPlayedMusicAdapter); bind.mostPlayedTracksRecyclerView.setAdapter(mostPlayedMusicAdapter);
homeViewModel.getMostPlayedSongList().observe(requireActivity(), songs -> mostPlayedMusicAdapter.setItems(songs)); homeViewModel.getMostPlayedSongList().observe(requireActivity(), songs -> mostPlayedMusicAdapter.setItems(songs));
} }
@Override
public void onItemClick(View view, int position) {
Toast.makeText(requireContext(), "Click: " + position, Toast.LENGTH_SHORT).show();
}
} }

View file

@ -107,8 +107,9 @@ public class LibraryFragment extends Fragment {
bind.playlistRecyclerView.setLayoutManager(new GridLayoutManager(requireContext(), 2)); bind.playlistRecyclerView.setLayoutManager(new GridLayoutManager(requireContext(), 2));
bind.playlistRecyclerView.setHasFixedSize(true); bind.playlistRecyclerView.setHasFixedSize(true);
playlistAdapter = new PlaylistAdapter(requireContext(), libraryViewModel.getPlaylist()); playlistAdapter = new PlaylistAdapter(requireContext(), new ArrayList<>());
playlistAdapter.setClickListener((view, position) -> Toast.makeText(requireContext(), "Playlist: " + position, Toast.LENGTH_SHORT).show()); playlistAdapter.setClickListener((view, position) -> Toast.makeText(requireContext(), "Playlist: " + position, Toast.LENGTH_SHORT).show());
bind.playlistRecyclerView.setAdapter(playlistAdapter); bind.playlistRecyclerView.setAdapter(playlistAdapter);
libraryViewModel.getPlaylistList().observe(requireActivity(), playlists -> playlistAdapter.setItems(playlists));
} }
} }

View file

@ -12,12 +12,17 @@ import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.lifecycle.Observer; import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import com.cappielloantonio.play.R; import com.cappielloantonio.play.R;
import com.cappielloantonio.play.adapter.AlbumCatalogueAdapter;
import com.cappielloantonio.play.adapter.ArtistCatalogueAdapter;
import com.cappielloantonio.play.adapter.RecentSearchAdapter; import com.cappielloantonio.play.adapter.RecentSearchAdapter;
import com.cappielloantonio.play.adapter.SongResultSearchAdapter; import com.cappielloantonio.play.adapter.SongResultSearchAdapter;
import com.cappielloantonio.play.databinding.FragmentSearchBinding; import com.cappielloantonio.play.databinding.FragmentSearchBinding;
import com.cappielloantonio.play.helper.recyclerview.ItemlDecoration;
import com.cappielloantonio.play.model.Artist;
import com.cappielloantonio.play.model.RecentSearch; import com.cappielloantonio.play.model.RecentSearch;
import com.cappielloantonio.play.model.Song; import com.cappielloantonio.play.model.Song;
import com.cappielloantonio.play.ui.activities.MainActivity; import com.cappielloantonio.play.ui.activities.MainActivity;
@ -35,6 +40,8 @@ public class SearchFragment extends Fragment {
private RecentSearchAdapter recentSearchAdapter; private RecentSearchAdapter recentSearchAdapter;
private SongResultSearchAdapter songResultSearchAdapter; private SongResultSearchAdapter songResultSearchAdapter;
private AlbumCatalogueAdapter albumResultSearchAdapter;
private ArtistCatalogueAdapter artistResultSearchAdapter;
@Nullable @Nullable
@Override @Override
@ -78,14 +85,37 @@ public class SearchFragment extends Fragment {
} }
private void initSearchResultView() { private void initSearchResultView() {
// Songs
bind.searchResultTracksRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext())); bind.searchResultTracksRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
bind.searchResultTracksRecyclerView.setHasFixedSize(true); bind.searchResultTracksRecyclerView.setHasFixedSize(true);
songResultSearchAdapter = new SongResultSearchAdapter(requireContext(), new ArrayList<>()); songResultSearchAdapter = new SongResultSearchAdapter(requireContext(), new ArrayList<>());
songResultSearchAdapter.setClickListener((view, position) -> {
Toast.makeText(requireContext(), "Song " + position, Toast.LENGTH_SHORT).show();
});
bind.searchResultTracksRecyclerView.setAdapter(songResultSearchAdapter); bind.searchResultTracksRecyclerView.setAdapter(songResultSearchAdapter);
// Albums
bind.searchResultAlbumRecyclerView.setLayoutManager(new GridLayoutManager(requireContext(), 2));
bind.searchResultAlbumRecyclerView.addItemDecoration(new ItemlDecoration(2, 20, false));
bind.searchResultAlbumRecyclerView.setHasFixedSize(true);
albumResultSearchAdapter = new AlbumCatalogueAdapter(requireContext(), new ArrayList<>());
albumResultSearchAdapter.setClickListener((view, position) -> {
Toast.makeText(requireContext(), "Album " + position, Toast.LENGTH_SHORT).show();
});
bind.searchResultAlbumRecyclerView.setAdapter(albumResultSearchAdapter);
// Artist
bind.searchResultArtistRecyclerView.setLayoutManager(new GridLayoutManager(requireContext(), 2));
bind.searchResultArtistRecyclerView.addItemDecoration(new ItemlDecoration(2, 20, false));
bind.searchResultArtistRecyclerView.setHasFixedSize(true);
artistResultSearchAdapter = new ArtistCatalogueAdapter(requireContext(), new ArrayList<>());
artistResultSearchAdapter.setClickListener((view, position) -> {
Toast.makeText(requireContext(), "Artist " + position, Toast.LENGTH_SHORT).show();
Bundle bundle = new Bundle();
bundle.putString("artistID", artistResultSearchAdapter.getItem(position).id);
activity.navController.navigate(R.id.action_searchFragment_to_artistPageFragment, bundle);
});
bind.searchResultArtistRecyclerView.setAdapter(artistResultSearchAdapter);
} }
private void initSearchView() { private void initSearchView() {
@ -118,5 +148,9 @@ public class SearchFragment extends Fragment {
private void performSearch(String query) { private void performSearch(String query) {
searchViewModel.searchSong(query).observe(requireActivity(), songs -> songResultSearchAdapter.setItems(songs)); searchViewModel.searchSong(query).observe(requireActivity(), songs -> songResultSearchAdapter.setItems(songs));
searchViewModel.searchAlbum(query).observe(requireActivity(), albums -> albumResultSearchAdapter.setItems(albums));
searchViewModel.searchArtist(query).observe(requireActivity(), artists -> artistResultSearchAdapter.setItems(artists));
bind.searchResultNestedScrollView.setVisibility(View.VISIBLE);
} }
} }

View file

@ -22,11 +22,10 @@ public class AlbumCatalogueViewModel extends AndroidViewModel {
super(application); super(application);
albumRepository = new AlbumRepository(application); albumRepository = new AlbumRepository(application);
albumList = albumRepository.getListLiveAlbums();
} }
public LiveData<List<Album>> getAlbumList() { public LiveData<List<Album>> getAlbumList() {
albumList = albumRepository.getListLiveAlbums();
return albumList; return albumList;
} }
} }

View file

@ -20,11 +20,10 @@ public class ArtistCatalogueViewModel extends AndroidViewModel {
super(application); super(application);
artistRepository = new ArtistRepository(application); artistRepository = new ArtistRepository(application);
artistList = artistRepository.getListLiveArtists();
} }
public LiveData<List<Artist>> getArtistList() { public LiveData<List<Artist>> getArtistList() {
artistList = artistRepository.getListLiveArtists();
return artistList; return artistList;
} }
} }

View file

@ -0,0 +1,39 @@
package com.cappielloantonio.play.viewmodel;
import android.app.Application;
import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import com.cappielloantonio.play.model.Album;
import com.cappielloantonio.play.model.Song;
import com.cappielloantonio.play.repository.AlbumRepository;
import com.cappielloantonio.play.repository.SongRepository;
import java.util.List;
public class ArtistPageViewModel extends AndroidViewModel {
private SongRepository songRepository;
private AlbumRepository albumRepository;
private LiveData<List<Song>> songList;
private LiveData<List<Album>> albumList;
public ArtistPageViewModel(@NonNull Application application) {
super(application);
songRepository = new SongRepository(application);
albumRepository = new AlbumRepository(application);
}
public LiveData<List<Album>> getAlbumList(String artistID) {
albumList = albumRepository.getArtistListLiveAlbums(artistID);
return albumList;
}
public LiveData<List<Song>> getArtistTopSongList(String artistID) {
songList = songRepository.getArtistListLiveTopSong(artistID);
return songList;
}
}

View file

@ -22,11 +22,10 @@ public class GenreCatalogueViewModel extends AndroidViewModel {
super(application); super(application);
genreRepository = new GenreRepository(application); genreRepository = new GenreRepository(application);
genreList = genreRepository.getListLiveGenres();
} }
public LiveData<List<Genre>> getGenreList() { public LiveData<List<Genre>> getGenreList() {
genreList = genreRepository.getListLiveGenres();
return genreList; return genreList;
} }
} }

View file

@ -5,18 +5,17 @@ import android.app.Application;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel; import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModel;
import com.cappielloantonio.play.model.Song; import com.cappielloantonio.play.model.Song;
import com.cappielloantonio.play.repository.SongRepository; import com.cappielloantonio.play.repository.SongRepository;
import java.util.ArrayList;
import java.util.List; import java.util.List;
public class HomeViewModel extends AndroidViewModel { public class HomeViewModel extends AndroidViewModel {
private static final String TAG = "HomeViewModel";
private SongRepository songRepository; private SongRepository songRepository;
private LiveData<List<Song>> dicoverSongSample; private List<Song> dicoverSongSample;
private LiveData<List<Song>> recentlyPlayedSongSample; private LiveData<List<Song>> recentlyPlayedSongSample;
private LiveData<List<Song>> recentlyAddedSongSample; private LiveData<List<Song>> recentlyAddedSongSample;
private LiveData<List<Song>> mostPlayedSongSample; private LiveData<List<Song>> mostPlayedSongSample;
@ -26,14 +25,14 @@ public class HomeViewModel extends AndroidViewModel {
songRepository = new SongRepository(application); songRepository = new SongRepository(application);
dicoverSongSample = songRepository.getListLiveDiscoverSampleSong(); dicoverSongSample = songRepository.getRandomSample(5);
recentlyPlayedSongSample = songRepository.getListLiveRecentlyPlayedSampleSong(); recentlyPlayedSongSample = songRepository.getListLiveRecentlyPlayedSampleSong();
recentlyAddedSongSample = songRepository.getListLiveRecentlyAddedSampleSong(); recentlyAddedSongSample = songRepository.getListLiveRecentlyAddedSampleSong();
mostPlayedSongSample = songRepository.getListLiveMostPlayedSampleSong(); mostPlayedSongSample = songRepository.getListLiveMostPlayedSampleSong();
} }
public LiveData<List<Song>> getDiscoverSongList() { public List<Song> getDiscoverSongList() {
return dicoverSongSample; return dicoverSongSample;
} }

View file

@ -13,6 +13,7 @@ import com.cappielloantonio.play.model.Playlist;
import com.cappielloantonio.play.repository.AlbumRepository; import com.cappielloantonio.play.repository.AlbumRepository;
import com.cappielloantonio.play.repository.ArtistRepository; import com.cappielloantonio.play.repository.ArtistRepository;
import com.cappielloantonio.play.repository.GenreRepository; import com.cappielloantonio.play.repository.GenreRepository;
import com.cappielloantonio.play.repository.PlaylistRepository;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -21,12 +22,14 @@ public class LibraryViewModel extends AndroidViewModel {
private AlbumRepository albumRepository; private AlbumRepository albumRepository;
private ArtistRepository artistRepository; private ArtistRepository artistRepository;
private GenreRepository genreRepository; private GenreRepository genreRepository;
private PlaylistRepository playlistRepository;
private LiveData<List<Album>> sampleAlbum; private LiveData<List<Album>> sampleAlbum;
private LiveData<List<Artist>> sampleArtist; private LiveData<List<Artist>> sampleArtist;
private LiveData<List<Genre>> sampleGenres; private LiveData<List<Genre>> sampleGenres;
private LiveData<List<Genre>> allGenres; private LiveData<List<Genre>> allGenres;
private LiveData<List<Playlist>> allPlaylist;
public LibraryViewModel(@NonNull Application application) { public LibraryViewModel(@NonNull Application application) {
super(application); super(application);
@ -34,6 +37,7 @@ public class LibraryViewModel extends AndroidViewModel {
albumRepository = new AlbumRepository(application); albumRepository = new AlbumRepository(application);
artistRepository = new ArtistRepository(application); artistRepository = new ArtistRepository(application);
genreRepository = new GenreRepository(application); genreRepository = new GenreRepository(application);
playlistRepository = new PlaylistRepository(application);
// Inizializzate all'interno del costruttore, in modo da rimanere immutabili per tutto il // Inizializzate all'interno del costruttore, in modo da rimanere immutabili per tutto il
// ciclo di vita dell'applicazione // ciclo di vita dell'applicazione
@ -47,6 +51,11 @@ public class LibraryViewModel extends AndroidViewModel {
return allGenres; return allGenres;
} }
public LiveData<List<Playlist>> getPlaylistList() {
allPlaylist = playlistRepository.getListLivePlaylists();
return allPlaylist;
}
public LiveData<List<Album>> getAlbumSample() { public LiveData<List<Album>> getAlbumSample() {
return sampleAlbum; return sampleAlbum;
} }
@ -58,19 +67,4 @@ public class LibraryViewModel extends AndroidViewModel {
public LiveData<List<Genre>> getGenreSample() { public LiveData<List<Genre>> getGenreSample() {
return sampleGenres; return sampleGenres;
} }
public ArrayList<Playlist> getPlaylist() {
ArrayList<Playlist> playlists = new ArrayList<>();
playlists.add(new Playlist("1", "sdad", "", ""));
playlists.add(new Playlist("2", "rwef", "", ""));
playlists.add(new Playlist("3", "khjf", "", ""));
playlists.add(new Playlist("4", "thfd", "", ""));
playlists.add(new Playlist("5", "jhku", "", ""));
playlists.add(new Playlist("6", "tuid", "", ""));
playlists.add(new Playlist("7", "hfrt", "", ""));
playlists.add(new Playlist("8", "qedg", "", ""));
playlists.add(new Playlist("9", "tugh", "", ""));
return playlists;
}
} }

View file

@ -6,9 +6,13 @@ import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel; import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import com.cappielloantonio.play.model.Album;
import com.cappielloantonio.play.model.Artist;
import com.cappielloantonio.play.model.Genre; import com.cappielloantonio.play.model.Genre;
import com.cappielloantonio.play.model.RecentSearch; import com.cappielloantonio.play.model.RecentSearch;
import com.cappielloantonio.play.model.Song; import com.cappielloantonio.play.model.Song;
import com.cappielloantonio.play.repository.AlbumRepository;
import com.cappielloantonio.play.repository.ArtistRepository;
import com.cappielloantonio.play.repository.GenreRepository; import com.cappielloantonio.play.repository.GenreRepository;
import com.cappielloantonio.play.repository.RecentSearchRepository; import com.cappielloantonio.play.repository.RecentSearchRepository;
import com.cappielloantonio.play.repository.SongRepository; import com.cappielloantonio.play.repository.SongRepository;
@ -17,18 +21,21 @@ import java.util.List;
public class SearchViewModel extends AndroidViewModel { public class SearchViewModel extends AndroidViewModel {
private SongRepository songRepository; private SongRepository songRepository;
private GenreRepository genreRepository; private AlbumRepository albumRepository;
private ArtistRepository artistRepository;
private RecentSearchRepository recentSearchRepository; private RecentSearchRepository recentSearchRepository;
private LiveData<List<Song>> searchSong; private LiveData<List<Song>> searchSong;
private LiveData<List<Genre>> allGenres; private LiveData<List<Album>> searchAlbum;
private LiveData<List<Artist>> searchArtist;
private LiveData<List<RecentSearch>> recentSearches; private LiveData<List<RecentSearch>> recentSearches;
public SearchViewModel(@NonNull Application application) { public SearchViewModel(@NonNull Application application) {
super(application); super(application);
songRepository = new SongRepository(application); songRepository = new SongRepository(application);
genreRepository = new GenreRepository(application); albumRepository = new AlbumRepository(application);
artistRepository = new ArtistRepository(application);
recentSearchRepository = new RecentSearchRepository(application); recentSearchRepository = new RecentSearchRepository(application);
} }
@ -37,9 +44,14 @@ public class SearchViewModel extends AndroidViewModel {
return searchSong; return searchSong;
} }
public LiveData<List<Genre>> getGenreList() { public LiveData<List<Album>> searchAlbum(String name) {
allGenres = genreRepository.getListLiveGenres(); searchAlbum = albumRepository.searchListLiveAlbum(name);
return allGenres; return searchAlbum;
}
public LiveData<List<Artist>> searchArtist(String name) {
searchArtist = artistRepository.searchListLiveArtist(name);
return searchArtist;
} }
public LiveData<List<RecentSearch>> getSearchList() { public LiveData<List<RecentSearch>> getSearchList() {

Binary file not shown.

View file

@ -3,4 +3,5 @@
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<font app:font="@font/regular" app:fontStyle="normal" app:fontWeight="400" /> <font app:font="@font/regular" app:fontStyle="normal" app:fontWeight="400" />
<font app:font="@font/bold" app:fontStyle="normal" app:fontWeight="700" /> <font app:font="@font/bold" app:fontStyle="normal" app:fontWeight="700" />
<font app:font="@font/extra_bold" app:fontStyle="normal" app:fontWeight="900" />
</font-family> </font-family>

View file

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout <RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"

View file

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.cardview.widget.CardView
android:id="@+id/card_view"
android:layout_width="0dp"
android:layout_height="0dp"
android:backgroundTint="@color/cardColor"
app:layout_constraintDimensionRatio="W, 4:5"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/open_sans_font_family"
android:text="Green Day"
android:textColor="@color/titleTextColor"
android:textSize="40sp"
android:textStyle="bold"
android:textFontWeight="900"
android:paddingBottom="32dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>
<!-- Label and button -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingStart="8dp"
android:paddingTop="8dp"
android:paddingEnd="8dp">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:fontFamily="@font/open_sans_font_family"
android:paddingStart="8dp"
android:paddingTop="12dp"
android:paddingEnd="8dp"
android:text="Most Streamed Songs"
android:textColor="@color/titleTextColor"
android:textSize="22sp"
android:textStyle="bold" />
<TextView
android:id="@+id/album_catalogue_text_view_clickable"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/open_sans_font_family"
android:paddingStart="8dp"
android:paddingTop="12dp"
android:paddingEnd="8dp"
android:text="See all"
android:textColor="@color/subtitleTextColor"
android:textSize="14sp" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/most_streamed_song_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:clipToPadding="false"
android:paddingStart="16dp"
android:paddingTop="8dp"
android:paddingEnd="8dp"
android:paddingBottom="8dp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="@font/open_sans_font_family"
android:paddingStart="16dp"
android:paddingTop="20dp"
android:paddingEnd="16dp"
android:text="Albums"
android:textColor="@color/titleTextColor"
android:textSize="22sp"
android:textStyle="bold" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/albums_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:clipToPadding="false"
android:paddingStart="16dp"
android:paddingTop="8dp"
android:paddingEnd="8dp"
android:paddingBottom="8dp" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>

View file

@ -1,38 +1,53 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout <RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent">
android:orientation="vertical"
android:gravity="center">
<ProgressBar <ProgressBar
android:id="@+id/loading_progress_bar" android:id="@+id/loading_progress_bar"
style="@style/Widget.AppCompat.ProgressBar.Horizontal" style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center"
android:indeterminate="true" android:indeterminate="true"
android:minWidth="128dp" android:minWidth="128dp"
app:layout_constraintBottom_toBottomOf="parent" android:layout_centerInParent="true"/>
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ScrollView <androidx.core.widget.NestedScrollView
android:id="@+id/filter_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/hide_bottom_view_on_scroll_behavior"
android:visibility="gone"
android:paddingTop="8dp">
<LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:clipToPadding="false" android:orientation="vertical"
android:paddingStart="16dp"
android:paddingTop="8dp"
android:paddingEnd="8dp"
android:paddingBottom="8dp"> android:paddingBottom="8dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="@font/open_sans_font_family"
android:paddingStart="16dp"
android:paddingTop="20dp"
android:paddingEnd="16dp"
android:text="Filter"
android:textColor="@color/titleTextColor"
android:textSize="22sp"
android:textStyle="bold" />
<com.google.android.material.chip.ChipGroup <com.google.android.material.chip.ChipGroup
android:id="@+id/filters_chips_group" android:id="@+id/filters_chips_group"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:singleSelection="false" /> app:singleSelection="false"
</ScrollView> android:paddingStart="16dp"
</LinearLayout> android:paddingTop="8dp"
android:paddingEnd="16dp"/>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</RelativeLayout>

View file

@ -17,6 +17,38 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="@font/open_sans_font_family"
android:paddingStart="16dp"
android:paddingTop="20dp"
android:paddingEnd="16dp"
android:text="Discover Music"
android:textColor="@color/titleTextColor"
android:textSize="22sp"
android:textStyle="bold" />
<!-- slideview -->
<androidx.viewpager.widget.ViewPager
android:id="@+id/discover_song_view_pager"
android:layout_width="match_parent"
android:layout_height="152dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:clipToPadding="false"
android:paddingStart="16dp"
android:paddingTop="8dp"
android:paddingEnd="16dp"
android:paddingBottom="8dp" />
</LinearLayout>
<!-- Recently added tracks -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<!-- Label and button --> <!-- Label and button -->
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -34,7 +66,7 @@
android:paddingStart="8dp" android:paddingStart="8dp"
android:paddingTop="12dp" android:paddingTop="12dp"
android:paddingEnd="8dp" android:paddingEnd="8dp"
android:text="Discover Music" android:text="Recently added tracks"
android:textColor="@color/titleTextColor" android:textColor="@color/titleTextColor"
android:textSize="22sp" android:textSize="22sp"
android:textStyle="bold" /> android:textStyle="bold" />
@ -53,17 +85,16 @@
</LinearLayout> </LinearLayout>
<!-- slideview --> <androidx.recyclerview.widget.RecyclerView
<androidx.viewpager.widget.ViewPager android:id="@+id/recently_added_tracks_recycler_view"
android:id="@+id/discover_song_view_pager"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="152dp" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginBottom="8dp" android:layout_marginBottom="8dp"
android:clipToPadding="false" android:clipToPadding="false"
android:paddingStart="16dp" android:paddingStart="16dp"
android:paddingTop="8dp" android:paddingTop="8dp"
android:paddingEnd="16dp" android:paddingEnd="8dp"
android:paddingBottom="8dp" /> android:paddingBottom="8dp" />
</LinearLayout> </LinearLayout>
@ -181,13 +212,13 @@
style="@style/Widget.MaterialComponents.Button.OutlinedButton" style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginTop="24dp"
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
android:text="Resync" android:text="Resync"
android:textAllCaps="false" android:textAllCaps="false"
android:textColor="@color/normalTextColor" android:textColor="@color/normalTextColor"
app:cornerRadius="24dp"/> app:cornerRadius="24dp" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
</androidx.core.widget.NestedScrollView> </androidx.core.widget.NestedScrollView>

View file

@ -90,13 +90,6 @@
android:paddingEnd="8dp" /> android:paddingEnd="8dp" />
</LinearLayout> </LinearLayout>
<!-- Search result -->
<LinearLayout
android:id="@+id/search_result_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<!-- Label --> <!-- Label -->
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -110,10 +103,83 @@
android:textSize="22sp" android:textSize="22sp"
android:textStyle="bold" /> android:textStyle="bold" />
<androidx.core.widget.NestedScrollView
android:id="@+id/search_result_nested_scroll_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone">
<!-- Search result -->
<LinearLayout
android:id="@+id/search_result_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/open_sans_font_family"
android:paddingStart="16dp"
android:paddingTop="8dp"
android:paddingEnd="8dp"
android:text="Songs"
android:textColor="@color/titleTextColor"
android:textSize="18sp"
android:textStyle="bold" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/search_result_tracks_recycler_view" android:id="@+id/search_result_tracks_recycler_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:clipToPadding="false"
android:overScrollMode="never"
android:paddingStart="16dp"
android:paddingTop="8dp"
android:paddingEnd="8dp"
android:paddingBottom="24dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/open_sans_font_family"
android:paddingStart="16dp"
android:paddingTop="8dp"
android:paddingEnd="8dp"
android:text="Albums"
android:textColor="@color/titleTextColor"
android:textSize="18sp"
android:textStyle="bold" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/search_result_album_recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:clipToPadding="false"
android:overScrollMode="never"
android:paddingStart="16dp"
android:paddingTop="8dp"
android:paddingEnd="8dp"
android:paddingBottom="16dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/open_sans_font_family"
android:paddingStart="16dp"
android:paddingEnd="8dp"
android:paddingTop="8dp"
android:text="Artists"
android:textColor="@color/titleTextColor"
android:textSize="18sp"
android:textStyle="bold" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/search_result_artist_recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp" android:layout_marginTop="4dp"
android:clipToPadding="false" android:clipToPadding="false"
android:overScrollMode="never" android:overScrollMode="never"
@ -122,5 +188,6 @@
android:paddingEnd="8dp" android:paddingEnd="8dp"
android:paddingBottom="56dp" /> android:paddingBottom="56dp" />
</LinearLayout> </LinearLayout>
</androidx.core.widget.NestedScrollView>
</LinearLayout> </LinearLayout>
</RelativeLayout> </RelativeLayout>

View file

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingEnd="8dp">
<androidx.cardview.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:id="@+id/card_view"
android:layout_width="256dp"
android:layout_height="256dp"
android:layout_gravity="center"
android:backgroundTint="@color/cardColor"
card_view:cardCornerRadius="4dp"
card_view:cardElevation="2dp"
card_view:cardPreventCornerOverlap="false"
card_view:cardUseCompatPadding="true" />
<TextView
android:id="@+id/album_name_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/open_sans_font_family"
android:maxWidth="172dp"
android:paddingStart="2dp"
android:paddingTop="8dp"
android:singleLine="false"
android:text="@string/label_placeholder"
android:textColor="@color/titleTextColor"
android:textSize="16sp"
android:textStyle="bold" />
</LinearLayout>

View file

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView <androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -16,8 +15,8 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="16dp" android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginTop="18dp" android:layout_marginTop="18dp"
android:layout_marginRight="16dp"
android:text="@string/label_placeholder" android:text="@string/label_placeholder"
android:textColor="@color/titleTextColor" android:textColor="@color/titleTextColor"
android:textSize="20sp" /> android:textSize="20sp" />

View file

@ -2,8 +2,7 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="54dp" android:layout_height="54dp"
android:orientation="vertical" android:orientation="vertical">
android:layout_marginBottom="4dp">
<androidx.cardview.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto" <androidx.cardview.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:id="@+id/card_view" android:id="@+id/card_view"

View file

@ -0,0 +1,68 @@
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingTop="4dp"
android:paddingBottom="4dp"
android:paddingEnd="4dp">
<androidx.cardview.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:id="@+id/imageView"
android:layout_width="52dp"
android:layout_height="52dp"
android:layout_gravity="center"
android:backgroundTint="@color/cardColor"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:tint="@color/subtitleTextColor"
card_view:cardCornerRadius="4dp"
card_view:cardElevation="2dp"
card_view:cardPreventCornerOverlap="false"
card_view:cardUseCompatPadding="false"/>
<TextView
android:id="@+id/song_title_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/open_sans_font_family"
android:paddingStart="12dp"
android:paddingTop="4dp"
android:text="@string/label_placeholder"
android:textColor="@color/titleTextColor"
android:textSize="14sp"
android:textStyle="bold"
app:layout_constraintStart_toEndOf="@+id/imageView"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/song_album_text_view"/>
<TextView
android:id="@+id/song_album_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/open_sans_font_family"
android:paddingStart="12dp"
android:paddingBottom="4dp"
android:text="@string/label_placeholder"
android:textColor="@color/subtitleTextColor"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/imageView"
app:layout_constraintTop_toBottomOf="@+id/song_title_text_view" />
<TextView
android:id="@+id/song_duration_text_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:fontFamily="@font/open_sans_font_family"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:text="@string/label_placeholder"
android:textColor="@color/subtitleTextColor"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -4,16 +4,17 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="horizontal"
android:paddingTop="4dp" android:paddingTop="2dp"
android:paddingBottom="4dp" android:paddingBottom="2dp"
android:paddingEnd="4dp"> android:paddingEnd="4dp">
<androidx.cardview.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto" <androidx.cardview.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:id="@+id/imageView" android:id="@+id/imageView"
android:layout_width="48dp" android:layout_width="48dp"
android:layout_height="48dp" android:layout_height="48dp"
android:layout_margin="2dp"
android:layout_gravity="center" android:layout_gravity="center"
android:backgroundTint="@color/colorAccent" android:backgroundTint="@color/cardColor"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"

View file

@ -81,6 +81,13 @@
<action <action
android:id="@+id/action_libraryFragment_to_genreCatalogueFragment" android:id="@+id/action_libraryFragment_to_genreCatalogueFragment"
app:destination="@id/genreCatalogueFragment" /> app:destination="@id/genreCatalogueFragment" />
<action
android:id="@+id/action_libraryFragment_to_artistPageFragment"
app:destination="@id/artistPageFragment">
<argument
android:name="artistID"
app:argType="string"/>
</action>
</fragment> </fragment>
<fragment <fragment
android:id="@+id/settingsFragment" android:id="@+id/settingsFragment"
@ -95,6 +102,13 @@
<action <action
android:id="@+id/action_searchFragment_to_filterFragment" android:id="@+id/action_searchFragment_to_filterFragment"
app:destination="@id/filterFragment" /> app:destination="@id/filterFragment" />
<action
android:id="@+id/action_searchFragment_to_artistPageFragment"
app:destination="@id/artistPageFragment">
<argument
android:name="artistID"
app:argType="string"/>
</action>
</fragment> </fragment>
<fragment <fragment
android:id="@+id/filterFragment" android:id="@+id/filterFragment"
@ -105,7 +119,15 @@
android:id="@+id/artistCatalogueFragment" android:id="@+id/artistCatalogueFragment"
android:name="com.cappielloantonio.play.ui.fragment.ArtistCatalogueFragment" android:name="com.cappielloantonio.play.ui.fragment.ArtistCatalogueFragment"
android:label="ArtistCatalogueFragment" android:label="ArtistCatalogueFragment"
tools:layout="@layout/fragment_artist_catalogue"/> tools:layout="@layout/fragment_artist_catalogue">
<action
android:id="@+id/action_artistCatalogueFragment_to_artistPageFragment"
app:destination="@id/artistPageFragment">
<argument
android:name="artistID"
app:argType="string"/>
</action>
</fragment>
<fragment <fragment
android:id="@+id/albumCatalogueFragment" android:id="@+id/albumCatalogueFragment"
android:name="com.cappielloantonio.play.ui.fragment.AlbumCatalogueFragment" android:name="com.cappielloantonio.play.ui.fragment.AlbumCatalogueFragment"
@ -116,4 +138,10 @@
android:name="com.cappielloantonio.play.ui.fragment.GenreCatalogueFragment" android:name="com.cappielloantonio.play.ui.fragment.GenreCatalogueFragment"
android:label="GenreCatalogueFragment" android:label="GenreCatalogueFragment"
tools:layout="@layout/fragment_genre_catalogue"/> tools:layout="@layout/fragment_genre_catalogue"/>
<fragment
android:id="@+id/artistPageFragment"
android:name="com.cappielloantonio.play.ui.fragment.ArtistPageFragment"
android:label="ArtistPageFragment"
tools:layout="@layout/fragment_artist_page"/>
</navigation> </navigation>