Fix bug in image visualization

This commit is contained in:
Antonio Cappiello 2020-11-27 09:06:50 +01:00
parent a0f417fa94
commit 18fae806a6
36 changed files with 431 additions and 150 deletions

View file

@ -42,7 +42,7 @@ public class AlbumAdapter extends RecyclerView.Adapter<AlbumAdapter.ViewHolder>
holder.textArtistName.setText(album.getArtistName()); holder.textArtistName.setText(album.getArtistName());
CustomGlideRequest.Builder CustomGlideRequest.Builder
.from(context, album.getPrimary(), album.getBlurHash(), CustomGlideRequest.PRIMARY) .from(context, album.getPrimary(), album.getBlurHash(), CustomGlideRequest.PRIMARY, CustomGlideRequest.TOP_QUALITY)
.build() .build()
.into(holder.cover); .into(holder.cover);
} }

View file

@ -41,7 +41,7 @@ public class AlbumArtistPageAdapter extends RecyclerView.Adapter<AlbumArtistPage
holder.textAlbumName.setText(album.getTitle()); holder.textAlbumName.setText(album.getTitle());
CustomGlideRequest.Builder CustomGlideRequest.Builder
.from(context, album.getPrimary(), album.getBlurHash(), CustomGlideRequest.PRIMARY) .from(context, album.getPrimary(), album.getBlurHash(), CustomGlideRequest.PRIMARY, CustomGlideRequest.LOW_QUALITY)
.build() .build()
.into(holder.cover); .into(holder.cover);
} }

View file

@ -42,9 +42,9 @@ public class AlbumCatalogueAdapter extends RecyclerView.Adapter<AlbumCatalogueAd
holder.textArtistName.setText(album.getArtistName()); holder.textArtistName.setText(album.getArtistName());
CustomGlideRequest.Builder CustomGlideRequest.Builder
.from(context, album.getPrimary(), album.getBlurHash(), CustomGlideRequest.PRIMARY) .from(context, album.getPrimary(), album.getBlurHash(), CustomGlideRequest.PRIMARY, CustomGlideRequest.LOW_QUALITY)
.build() .build()
// .override(300) .override(300)
.into(holder.cover); .into(holder.cover);
} }

View file

@ -41,7 +41,7 @@ public class ArtistAdapter extends RecyclerView.Adapter<ArtistAdapter.ViewHolder
holder.textArtistName.setText(artist.getName()); holder.textArtistName.setText(artist.getName());
CustomGlideRequest.Builder CustomGlideRequest.Builder
.from(context, artist.getPrimary(), artist.getPrimaryBlurHash(), CustomGlideRequest.PRIMARY) .from(context, artist.getPrimary(), artist.getPrimaryBlurHash(), CustomGlideRequest.PRIMARY, CustomGlideRequest.TOP_QUALITY)
.build() .build()
.into(holder.cover); .into(holder.cover);
} }

View file

@ -41,7 +41,7 @@ public class ArtistCatalogueAdapter extends RecyclerView.Adapter<ArtistCatalogue
holder.textArtistName.setText(artist.getName()); holder.textArtistName.setText(artist.getName());
CustomGlideRequest.Builder CustomGlideRequest.Builder
.from(context, artist.getPrimary(), artist.getPrimaryBlurHash(), CustomGlideRequest.PRIMARY) .from(context, artist.getPrimary(), artist.getPrimaryBlurHash(), CustomGlideRequest.PRIMARY, CustomGlideRequest.TOP_QUALITY)
.build() .build()
.into(holder.cover); .into(holder.cover);
} }

View file

@ -44,7 +44,7 @@ public class DiscoverSongAdapter extends RecyclerView.Adapter<DiscoverSongAdapte
holder.textAlbum.setText(song.getAlbumName()); holder.textAlbum.setText(song.getAlbumName());
CustomGlideRequest.Builder CustomGlideRequest.Builder
.from(context, song.getPrimary(), song.getPrimary(), CustomGlideRequest.PRIMARY) .from(context, song.getPrimary(), song.getPrimary(), CustomGlideRequest.PRIMARY, CustomGlideRequest.TOP_QUALITY)
.build() .build()
.into(holder.cover); .into(holder.cover);
} }

View file

@ -46,7 +46,7 @@ public class RecentMusicAdapter extends RecyclerView.Adapter<RecentMusicAdapter.
holder.textAlbum.setText(song.getAlbumName()); holder.textAlbum.setText(song.getAlbumName());
CustomGlideRequest.Builder CustomGlideRequest.Builder
.from(context, song.getPrimary(), song.getPrimary(), CustomGlideRequest.PRIMARY) .from(context, song.getPrimary(), song.getPrimary(), CustomGlideRequest.PRIMARY, CustomGlideRequest.TOP_QUALITY)
.build() .build()
.into(holder.cover); .into(holder.cover);
} }

View file

@ -48,7 +48,7 @@ public class SongResultSearchAdapter extends RecyclerView.Adapter<SongResultSear
holder.songDuration.setText(Util.getReadableDurationString(song.getDuration())); holder.songDuration.setText(Util.getReadableDurationString(song.getDuration()));
CustomGlideRequest.Builder CustomGlideRequest.Builder
.from(context, song.getPrimary(), song.getPrimary(), CustomGlideRequest.PRIMARY) .from(context, song.getPrimary(), song.getPrimary(), CustomGlideRequest.PRIMARY, CustomGlideRequest.TOP_QUALITY)
.build() .build()
.into(holder.cover); .into(holder.cover);
} }

View file

@ -37,6 +37,9 @@ public interface AlbumDao {
@Delete @Delete
void delete(Album album); void delete(Album album);
@Query("SELECT title FROM album WHERE title LIKE :query || '%' GROUP BY title LIMIT :number") @Query("SELECT title FROM album WHERE title LIKE :query || '%' OR title like '% ' || :query || '%' GROUP BY title LIMIT :number")
List<String> searchSuggestions(String query, int number); List<String> searchSuggestions(String query, int number);
@Query("DELETE FROM album")
void deleteAll();
} }

View file

@ -34,6 +34,9 @@ public interface ArtistDao {
@Delete @Delete
void delete(Artist artist); void delete(Artist artist);
@Query("SELECT name FROM artist WHERE name LIKE :query || '%' GROUP BY name LIMIT :number") @Query("SELECT name FROM artist WHERE name LIKE :query || '%' OR name like '% ' || :query || '%' GROUP BY name LIMIT :number")
List<String> searchSuggestions(String query, int number); List<String> searchSuggestions(String query, int number);
@Query("DELETE FROM artist")
void deleteAll();
} }

View file

@ -7,8 +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.Genre; import com.cappielloantonio.play.model.Genre;
import java.util.List; import java.util.List;
@ -35,4 +33,7 @@ public interface GenreDao {
@Delete @Delete
void delete(Genre genre); void delete(Genre genre);
@Query("DELETE FROM genre")
void deleteAll();
} }

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.Artist;
import com.cappielloantonio.play.model.Playlist; import com.cappielloantonio.play.model.Playlist;
import java.util.List; import java.util.List;
@ -28,4 +27,7 @@ public interface PlaylistDao {
@Delete @Delete
void delete(Playlist playlist); void delete(Playlist playlist);
@Query("DELETE FROM playlist")
void deleteAll();
} }

View file

@ -18,6 +18,9 @@ public interface SongDao {
@Query("SELECT * FROM song") @Query("SELECT * FROM song")
LiveData<List<Song>> getAll(); LiveData<List<Song>> getAll();
@Query("SELECT * FROM song")
List<Song> getAllList();
@Query("SELECT * FROM song WHERE title LIKE '%' || :title || '%'") @Query("SELECT * FROM song WHERE title LIKE '%' || :title || '%'")
LiveData<List<Song>> searchSong(String title); LiveData<List<Song>> searchSong(String title);
@ -60,12 +63,15 @@ public interface SongDao {
@Delete @Delete
void delete(Song song); void delete(Song song);
@Query("DELETE FROM song")
void deleteAll();
@Update @Update
public void update(Song song); public void update(Song song);
@Query("SELECT * FROM song ORDER BY RANDOM() LIMIT :number") @Query("SELECT * FROM song ORDER BY RANDOM() LIMIT :number")
List<Song> random(int number); List<Song> random(int number);
@Query("SELECT title FROM song WHERE title LIKE :query || '%' GROUP BY title LIMIT :number") @Query("SELECT title FROM song WHERE title LIKE :query || '%' OR title like '% ' || :query || '%' GROUP BY title LIMIT :number")
List<String> searchSuggestions(String query, int number); List<String> searchSuggestions(String query, int number);
} }

View file

@ -8,7 +8,6 @@ import androidx.room.OnConflictStrategy;
import androidx.room.Query; import androidx.room.Query;
import androidx.room.Update; import androidx.room.Update;
import com.cappielloantonio.play.model.Song;
import com.cappielloantonio.play.model.SongGenreCross; import com.cappielloantonio.play.model.SongGenreCross;
import java.util.List; import java.util.List;
@ -32,4 +31,7 @@ public interface SongGenreCrossDao {
@Update @Update
void update(SongGenreCross songGenreCross); void update(SongGenreCross songGenreCross);
@Query("DELETE FROM song_genre_cross")
void deleteAll();
} }

View file

@ -24,6 +24,10 @@ public class CustomGlideRequest {
public static final String PRIMARY = "PRIMARY"; public static final String PRIMARY = "PRIMARY";
public static final String BACKDROP = "BACKDROP"; public static final String BACKDROP = "BACKDROP";
public static final String TOP_QUALITY = "TOP";
public static final String MEDIUM_QUALITY = "MEDIUM";
public static final String LOW_QUALITY = "LOW";
public static final DiskCacheStrategy DEFAULT_DISK_CACHE_STRATEGY = DiskCacheStrategy.ALL; public static final DiskCacheStrategy DEFAULT_DISK_CACHE_STRATEGY = DiskCacheStrategy.ALL;
public static final int DEFAULT_IMAGE = R.drawable.ic_launcher_background; public static final int DEFAULT_IMAGE = R.drawable.ic_launcher_background;
@ -32,9 +36,9 @@ public class CustomGlideRequest {
private final Object item; private final Object item;
private final Context context; private final Context context;
private Builder(Context context, String item, String placeholder, String itemType) { private Builder(Context context, String item, String placeholder, String itemType, String quality) {
this.requestManager = Glide.with(context); this.requestManager = Glide.with(context);
this.item = item != null ? createUrl(item, itemType) : DEFAULT_IMAGE; this.item = item != null ? createUrl(item, itemType, quality) : DEFAULT_IMAGE;
this.context = context; this.context = context;
if (placeholder != null) { if (placeholder != null) {
@ -47,8 +51,8 @@ public class CustomGlideRequest {
} }
} }
public static Builder from(Context context, String item, String placeholder, String itemType) { public static Builder from(Context context, String item, String placeholder, String itemType, String quality) {
return new Builder(context, item, placeholder, itemType); return new Builder(context, item, placeholder, itemType, quality);
} }
public RequestBuilder<Drawable> build() { public RequestBuilder<Drawable> build() {
@ -67,8 +71,9 @@ public class CustomGlideRequest {
return options; return options;
} }
public static String createUrl(String item, String itemType) { public static String createUrl(String item, String itemType, String quality) {
ImageOptions options = new ImageOptions(); ImageOptions options = new ImageOptions();
switch(itemType) { switch(itemType) {
case PRIMARY: { case PRIMARY: {
options.setImageType(ImageType.Primary); options.setImageType(ImageType.Primary);
@ -79,9 +84,27 @@ public class CustomGlideRequest {
break; break;
} }
} }
options.setQuality(100);
options.setMaxHeight(500); switch(quality) {
options.setEnableImageEnhancers(true); case TOP_QUALITY: {
options.setQuality(100);
options.setMaxHeight(800);
options.setEnableImageEnhancers(true);
break;
}
case MEDIUM_QUALITY: {
options.setQuality(80);
options.setMaxHeight(500);
options.setEnableImageEnhancers(true);
break;
}
case LOW_QUALITY: {
options.setQuality(60);
options.setMaxHeight(300);
options.setEnableImageEnhancers(true);
break;
}
}
return App.getApiClientInstance(App.getInstance()).GetImageUrl(item, options); return App.getApiClientInstance(App.getInstance()).GetImageUrl(item, options);
} }

View file

@ -381,14 +381,14 @@ public class Song implements Parcelable {
this.added = added; this.added = added;
} }
public void setAdded(int playCount) {
this.playCount = playCount;
}
public void setLastPlay(long lastPlay) { public void setLastPlay(long lastPlay) {
this.lastPlay = lastPlay; this.lastPlay = lastPlay;
} }
public void setPlayCount(int playCount) {
this.playCount = playCount;
}
public void nowPlaying() { public void nowPlaying() {
this.playCount++; this.playCount++;
this.lastPlay = Instant.now().toEpochMilli(); this.lastPlay = Instant.now().toEpochMilli();

View file

@ -1,17 +1,12 @@
package com.cappielloantonio.play.repository; package com.cappielloantonio.play.repository;
import android.app.Application; import android.app.Application;
import android.util.Log;
import androidx.lifecycle.LiveData; 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.database.dao.SongDao;
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 com.paulrybitskyi.persistentsearchview.adapters.model.SuggestionItem;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -103,6 +98,12 @@ public class AlbumRepository {
thread.start(); thread.start();
} }
public void deleteAll() {
DeleteAllThreadSafe delete = new DeleteAllThreadSafe(albumDao);
Thread thread = new Thread(delete);
thread.start();
}
private static class ExistThreadSafe implements Runnable { private static class ExistThreadSafe implements Runnable {
private AlbumDao albumDao; private AlbumDao albumDao;
private Album album; private Album album;
@ -149,6 +150,7 @@ public class AlbumRepository {
@Override @Override
public void run() { public void run() {
albumDao.deleteAll();
albumDao.insertAll(albums); albumDao.insertAll(albums);
} }
} }
@ -189,4 +191,17 @@ public class AlbumRepository {
return suggestions; return suggestions;
} }
} }
private static class DeleteAllThreadSafe implements Runnable {
private AlbumDao albumDao;
public DeleteAllThreadSafe(AlbumDao albumDao) {
this.albumDao = albumDao;
}
@Override
public void run() {
albumDao.deleteAll();
}
}
} }

View file

@ -5,14 +5,10 @@ import android.app.Application;
import androidx.lifecycle.LiveData; 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.ArtistDao; import com.cappielloantonio.play.database.dao.ArtistDao;
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.Collection;
import java.util.List; import java.util.List;
public class ArtistRepository { public class ArtistRepository {
@ -95,6 +91,12 @@ public class ArtistRepository {
thread.start(); thread.start();
} }
public void deleteAll() {
DeleteAllThreadSafe delete = new DeleteAllThreadSafe(artistDao);
Thread thread = new Thread(delete);
thread.start();
}
private static class ExistThreadSafe implements Runnable { private static class ExistThreadSafe implements Runnable {
private ArtistDao artistDao; private ArtistDao artistDao;
private Artist artist; private Artist artist;
@ -141,6 +143,7 @@ public class ArtistRepository {
@Override @Override
public void run() { public void run() {
artistDao.deleteAll();
artistDao.insertAll(artists); artistDao.insertAll(artists);
} }
} }
@ -181,4 +184,17 @@ public class ArtistRepository {
return suggestions; return suggestions;
} }
} }
private static class DeleteAllThreadSafe implements Runnable {
private ArtistDao artistDao;
public DeleteAllThreadSafe(ArtistDao artistDao) {
this.artistDao = artistDao;
}
@Override
public void run() {
artistDao.deleteAll();
}
}
} }

View file

@ -7,9 +7,7 @@ import androidx.lifecycle.LiveData;
import com.cappielloantonio.play.database.AppDatabase; import com.cappielloantonio.play.database.AppDatabase;
import com.cappielloantonio.play.database.dao.GenreDao; import com.cappielloantonio.play.database.dao.GenreDao;
import com.cappielloantonio.play.database.dao.SongGenreCrossDao; import com.cappielloantonio.play.database.dao.SongGenreCrossDao;
import com.cappielloantonio.play.model.Album;
import com.cappielloantonio.play.model.Genre; import com.cappielloantonio.play.model.Genre;
import com.cappielloantonio.play.model.SongGenreCross;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -84,14 +82,20 @@ public class GenreRepository {
thread.start(); thread.start();
} }
public void insertPerGenre(ArrayList<SongGenreCross> songGenreCrosses) { public void delete(Genre genre) {
InsertPerGenreThreadSafe insertPerGenre = new InsertPerGenreThreadSafe(songGenreCrossDao, songGenreCrosses); DeleteThreadSafe delete = new DeleteThreadSafe(genreDao, genre);
Thread thread = new Thread(insertPerGenre); Thread thread = new Thread(delete);
thread.start(); thread.start();
} }
public void delete(Genre genre) { public void deleteAll() {
DeleteThreadSafe delete = new DeleteThreadSafe(genreDao, genre); DeleteAllGenreThreadSafe delete = new DeleteAllGenreThreadSafe(genreDao);
Thread thread = new Thread(delete);
thread.start();
}
public void deleteAllSongGenreCross() {
DeleteAllSongGenreCrossThreadSafe delete = new DeleteAllSongGenreCrossThreadSafe(songGenreCrossDao);
Thread thread = new Thread(delete); Thread thread = new Thread(delete);
thread.start(); thread.start();
} }
@ -160,25 +164,11 @@ public class GenreRepository {
@Override @Override
public void run() { public void run() {
genreDao.deleteAll();
genreDao.insertAll(genres); genreDao.insertAll(genres);
} }
} }
private static class InsertPerGenreThreadSafe implements Runnable {
private SongGenreCrossDao songGenreCrossDao;
private ArrayList<SongGenreCross> cross;
public InsertPerGenreThreadSafe(SongGenreCrossDao songGenreCrossDao, ArrayList<SongGenreCross> cross) {
this.songGenreCrossDao = songGenreCrossDao;
this.cross = cross;
}
@Override
public void run() {
songGenreCrossDao.insertAll(cross);
}
}
private static class DeleteThreadSafe implements Runnable { private static class DeleteThreadSafe implements Runnable {
private GenreDao genreDao; private GenreDao genreDao;
private Genre genre; private Genre genre;
@ -193,4 +183,30 @@ public class GenreRepository {
genreDao.delete(genre); genreDao.delete(genre);
} }
} }
private static class DeleteAllGenreThreadSafe implements Runnable {
private GenreDao genreDao;
public DeleteAllGenreThreadSafe(GenreDao genreDao) {
this.genreDao = genreDao;
}
@Override
public void run() {
genreDao.deleteAll();
}
}
private static class DeleteAllSongGenreCrossThreadSafe implements Runnable {
private SongGenreCrossDao songGenreCrossDao;
public DeleteAllSongGenreCrossThreadSafe(SongGenreCrossDao songGenreCrossDao) {
this.songGenreCrossDao = songGenreCrossDao;
}
@Override
public void run() {
songGenreCrossDao.deleteAll();
}
}
} }

View file

@ -61,6 +61,12 @@ public class PlaylistRepository {
thread.start(); thread.start();
} }
public void deleteAll() {
DeleteAllThreadSafe delete = new DeleteAllThreadSafe(playlistDao);
Thread thread = new Thread(delete);
thread.start();
}
private static class ExistThreadSafe implements Runnable { private static class ExistThreadSafe implements Runnable {
private PlaylistDao playlistDao; private PlaylistDao playlistDao;
private Playlist playlist; private Playlist playlist;
@ -107,6 +113,7 @@ public class PlaylistRepository {
@Override @Override
public void run() { public void run() {
playlistDao.deleteAll();
playlistDao.insertAll(playlists); playlistDao.insertAll(playlists);
} }
} }
@ -125,4 +132,17 @@ public class PlaylistRepository {
playlistDao.delete(playlist); playlistDao.delete(playlist);
} }
} }
private static class DeleteAllThreadSafe implements Runnable {
private PlaylistDao playlistDao;
public DeleteAllThreadSafe(PlaylistDao playlistDao) {
this.playlistDao = playlistDao;
}
@Override
public void run() {
playlistDao.deleteAll();
}
}
} }

View file

@ -5,17 +5,17 @@ import android.app.Application;
import androidx.lifecycle.LiveData; 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.SongDao; import com.cappielloantonio.play.database.dao.SongDao;
import com.cappielloantonio.play.model.Album; import com.cappielloantonio.play.database.dao.SongGenreCrossDao;
import com.cappielloantonio.play.model.Song; import com.cappielloantonio.play.model.Song;
import com.cappielloantonio.play.model.SongGenreCross;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.List; import java.util.List;
public class SongRepository { public class SongRepository {
private SongDao songDao; private SongDao songDao;
private SongGenreCrossDao songGenreCrossDao;
private LiveData<List<Song>> searchListLiveSongs; private LiveData<List<Song>> searchListLiveSongs;
private LiveData<List<Song>> listLiveSampleRecentlyAddedSongs; private LiveData<List<Song>> listLiveSampleRecentlyAddedSongs;
private LiveData<List<Song>> listLiveSampleRecentlyPlayedSongs; private LiveData<List<Song>> listLiveSampleRecentlyPlayedSongs;
@ -29,6 +29,7 @@ public class SongRepository {
public SongRepository(Application application) { public SongRepository(Application application) {
AppDatabase database = AppDatabase.getInstance(application); AppDatabase database = AppDatabase.getInstance(application);
songDao = database.songDao(); songDao = database.songDao();
songGenreCrossDao = database.songGenreCrossDao();
} }
public LiveData<List<Song>> searchListLiveSong(String title) { public LiveData<List<Song>> searchListLiveSong(String title) {
@ -93,6 +94,27 @@ public class SongRepository {
return suggestions; return suggestions;
} }
/*
* Funzione che ritorna l'intero set di canzoni.
* Utilizzato per l'aggiornamento del catalogo.
*/
public List<Song> getCatalogue() {
List<Song> catalogue = new ArrayList<>();
GetCatalogueThreadSafe getCatalogueThread = new GetCatalogueThreadSafe(songDao);
Thread thread = new Thread(getCatalogueThread);
thread.start();
try {
thread.join();
catalogue = getCatalogueThread.getCatalogue();
} catch (InterruptedException e) {
e.printStackTrace();
}
return catalogue;
}
public boolean exist(Song song) { public boolean exist(Song song) {
boolean exist = false; boolean exist = false;
@ -117,7 +139,7 @@ public class SongRepository {
} }
public void insertAll(ArrayList<Song> songs) { public void insertAll(ArrayList<Song> songs) {
InsertAllThreadSafe insertAll = new InsertAllThreadSafe(songDao, songs); InsertAllThreadSafe insertAll = new InsertAllThreadSafe(songDao, songGenreCrossDao, songs);
Thread thread = new Thread(insertAll); Thread thread = new Thread(insertAll);
thread.start(); thread.start();
} }
@ -136,6 +158,30 @@ public class SongRepository {
thread.start(); thread.start();
} }
public void getAll() {
GetCatalogueThreadSafe catalogue = new GetCatalogueThreadSafe(songDao);
Thread thread = new Thread(catalogue);
thread.start();
}
public void insertSongPerGenre(ArrayList<SongGenreCross> songGenreCrosses) {
InsertPerGenreThreadSafe insertPerGenre = new InsertPerGenreThreadSafe(songGenreCrossDao, songGenreCrosses);
Thread thread = new Thread(insertPerGenre);
thread.start();
}
public void deleteAllSong() {
DeleteAllSongThreadSafe delete = new DeleteAllSongThreadSafe(songDao);
Thread thread = new Thread(delete);
thread.start();
}
public void deleteAllSongGenreCross() {
DeleteAllSongGenreCrossThreadSafe delete = new DeleteAllSongGenreCrossThreadSafe(songGenreCrossDao);
Thread thread = new Thread(delete);
thread.start();
}
public List<Song> getRandomSample(int number) { public List<Song> getRandomSample(int number) {
List<Song> sample = new ArrayList<>(); List<Song> sample = new ArrayList<>();
@ -190,15 +236,19 @@ public class SongRepository {
private static class InsertAllThreadSafe implements Runnable { private static class InsertAllThreadSafe implements Runnable {
private SongDao songDao; private SongDao songDao;
private SongGenreCrossDao songGenreCrossDao;
private ArrayList<Song> songs; private ArrayList<Song> songs;
public InsertAllThreadSafe(SongDao songDao, ArrayList<Song> songs) { public InsertAllThreadSafe(SongDao songDao, SongGenreCrossDao songGenreCrossDao, ArrayList<Song> songs) {
this.songDao = songDao; this.songDao = songDao;
this.songGenreCrossDao = songGenreCrossDao;
this.songs = songs; this.songs = songs;
} }
@Override @Override
public void run() { public void run() {
songDao.deleteAll();
songGenreCrossDao.deleteAll();
songDao.insertAll(songs); songDao.insertAll(songs);
} }
} }
@ -274,4 +324,63 @@ public class SongRepository {
return suggestions; return suggestions;
} }
} }
private static class GetCatalogueThreadSafe implements Runnable {
private SongDao songDao;
private List<Song> catalogue = new ArrayList<>();
public GetCatalogueThreadSafe(SongDao songDao) {
this.songDao = songDao;
}
@Override
public void run() {
catalogue = songDao.getAllList();
}
public List<Song> getCatalogue() {
return catalogue;
}
}
private static class InsertPerGenreThreadSafe implements Runnable {
private SongGenreCrossDao songGenreCrossDao;
private ArrayList<SongGenreCross> cross;
public InsertPerGenreThreadSafe(SongGenreCrossDao songGenreCrossDao, ArrayList<SongGenreCross> cross) {
this.songGenreCrossDao = songGenreCrossDao;
this.cross = cross;
}
@Override
public void run() {
songGenreCrossDao.insertAll(cross);
}
}
private static class DeleteAllSongThreadSafe implements Runnable {
private SongDao songDao;
public DeleteAllSongThreadSafe(SongDao songDao) {
this.songDao = songDao;
}
@Override
public void run() {
songDao.deleteAll();
}
}
private static class DeleteAllSongGenreCrossThreadSafe implements Runnable {
private SongGenreCrossDao songGenreCrossDao;
public DeleteAllSongGenreCrossThreadSafe(SongGenreCrossDao songGenreCrossDao) {
this.songGenreCrossDao = songGenreCrossDao;
}
@Override
public void run() {
songGenreCrossDao.deleteAll();
}
}
} }

View file

@ -81,6 +81,16 @@ public class MainActivity extends BaseActivity {
}); });
} }
// True: VISIBLE
// False: GONE
public void setBottomNavigationBarVisibility(boolean visibility) {
if (visibility) {
bottomNavigationView.setVisibility(View.VISIBLE);
} else {
bottomNavigationView.setVisibility(View.GONE);
}
}
public void goToLogin() { public void goToLogin() {
if (Objects.requireNonNull(navController.getCurrentDestination()).getId() == R.id.landingFragment) if (Objects.requireNonNull(navController.getCurrentDestination()).getId() == R.id.landingFragment)
navController.navigate(R.id.action_landingFragment_to_loginFragment); navController.navigate(R.id.action_landingFragment_to_loginFragment);
@ -89,10 +99,7 @@ public class MainActivity extends BaseActivity {
public void goToSync() { public void goToSync() {
bottomNavigationView.setVisibility(View.GONE); bottomNavigationView.setVisibility(View.GONE);
if (Objects.requireNonNull(navController.getCurrentDestination()).getId() == R.id.landingFragment) { if (Objects.requireNonNull(navController.getCurrentDestination()).getId() == R.id.loginFragment) {
Bundle bundle = SyncUtil.getSyncBundle(true, true, true, true, true, false);
navController.navigate(R.id.action_landingFragment_to_syncFragment, bundle);
} else if (Objects.requireNonNull(navController.getCurrentDestination()).getId() == R.id.loginFragment) {
Bundle bundle = SyncUtil.getSyncBundle(true, true, true, true, true, false); Bundle bundle = SyncUtil.getSyncBundle(true, true, true, true, true, false);
navController.navigate(R.id.action_loginFragment_to_syncFragment, bundle); navController.navigate(R.id.action_loginFragment_to_syncFragment, bundle);
} else if (Objects.requireNonNull(navController.getCurrentDestination()).getId() == R.id.homeFragment) { } else if (Objects.requireNonNull(navController.getCurrentDestination()).getId() == R.id.homeFragment) {

View file

@ -48,7 +48,7 @@ public class AlbumPageFragment extends Fragment {
private void initBackCover() { private void initBackCover() {
CustomGlideRequest.Builder CustomGlideRequest.Builder
.from(requireContext(), albumPageViewModel.getAlbum().getPrimary(), albumPageViewModel.getAlbum().getBlurHash(), CustomGlideRequest.PRIMARY) .from(requireContext(), albumPageViewModel.getAlbum().getPrimary(), albumPageViewModel.getAlbum().getBlurHash(), CustomGlideRequest.PRIMARY, CustomGlideRequest.TOP_QUALITY)
.build() .build()
.into(bind.albumBackCoverImageView); .into(bind.albumBackCoverImageView);
} }

View file

@ -55,20 +55,17 @@ public class ArtistPageFragment extends Fragment {
artistPageViewModel.setArtist(getArguments().getParcelable("artist_object")); artistPageViewModel.setArtist(getArguments().getParcelable("artist_object"));
bind.artistNameLabel.setText(artistPageViewModel.getArtist().getName()); bind.artistNameLabel.setText(artistPageViewModel.getArtist().getName());
bind.mostStreamedSongTextViewClickable.setOnClickListener(new View.OnClickListener() { bind.mostStreamedSongTextViewClickable.setOnClickListener(v -> {
@Override Bundle bundle = new Bundle();
public void onClick(View v) { bundle.putString(Song.BY_ARTIST, Song.BY_ARTIST);
Bundle bundle = new Bundle(); bundle.putParcelable("artist_object", artistPageViewModel.getArtist());
bundle.putString(Song.BY_ARTIST, Song.BY_ARTIST); activity.navController.navigate(R.id.action_artistPageFragment_to_songListPageFragment, bundle);
bundle.putParcelable("artist_object", artistPageViewModel.getArtist());
activity.navController.navigate(R.id.action_artistPageFragment_to_songListPageFragment, bundle);
}
}); });
} }
private void initBackdrop() { private void initBackdrop() {
CustomGlideRequest.Builder CustomGlideRequest.Builder
.from(requireContext(), artistPageViewModel.getArtist().getBackdrop(), artistPageViewModel.getArtist().getBackdropBlurHash(), CustomGlideRequest.BACKDROP) .from(requireContext(), artistPageViewModel.getArtist().getBackdrop(), artistPageViewModel.getArtist().getBackdropBlurHash(), CustomGlideRequest.BACKDROP, CustomGlideRequest.TOP_QUALITY)
.build() .build()
.into(bind.artistBackdropImageView); .into(bind.artistBackdropImageView);
} }

View file

@ -58,14 +58,11 @@ public class GenreCatalogueFragment extends Fragment {
bind.genreCatalogueRecyclerView.setHasFixedSize(true); bind.genreCatalogueRecyclerView.setHasFixedSize(true);
genreCatalogueAdapter = new GenreCatalogueAdapter(requireContext(), new ArrayList<>()); genreCatalogueAdapter = new GenreCatalogueAdapter(requireContext(), new ArrayList<>());
genreCatalogueAdapter.setClickListener(new GenreCatalogueAdapter.ItemClickListener() { genreCatalogueAdapter.setClickListener((view, position) -> {
@Override Bundle bundle = new Bundle();
public void onItemClick(View view, int position) { bundle.putString(Song.BY_GENRE, Song.BY_GENRE);
Bundle bundle = new Bundle(); bundle.putParcelable("genre_object", genreCatalogueAdapter.getItem(position));
bundle.putString(Song.BY_GENRE, Song.BY_GENRE); activity.navController.navigate(R.id.action_genreCatalogueFragment_to_songListPageFragment, bundle);
bundle.putParcelable("genre_object", genreCatalogueAdapter.getItem(position));
activity.navController.navigate(R.id.action_genreCatalogueFragment_to_songListPageFragment, bundle);
}
}); });
bind.genreCatalogueRecyclerView.setAdapter(genreCatalogueAdapter); bind.genreCatalogueRecyclerView.setAdapter(genreCatalogueAdapter);

View file

@ -1,8 +1,6 @@
package com.cappielloantonio.play.ui.fragment; package com.cappielloantonio.play.ui.fragment;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log; import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
@ -11,12 +9,10 @@ import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import com.cappielloantonio.play.App; import com.cappielloantonio.play.App;
import com.cappielloantonio.play.R;
import com.cappielloantonio.play.databinding.FragmentSyncBinding; import com.cappielloantonio.play.databinding.FragmentSyncBinding;
import com.cappielloantonio.play.interfaces.MediaCallback; import com.cappielloantonio.play.interfaces.MediaCallback;
import com.cappielloantonio.play.model.Album; import com.cappielloantonio.play.model.Album;
@ -34,8 +30,6 @@ import com.cappielloantonio.play.ui.activities.MainActivity;
import com.cappielloantonio.play.util.PreferenceUtil; import com.cappielloantonio.play.util.PreferenceUtil;
import com.cappielloantonio.play.util.SyncUtil; import com.cappielloantonio.play.util.SyncUtil;
import com.cappielloantonio.play.viewmodel.SyncViewModel; import com.cappielloantonio.play.viewmodel.SyncViewModel;
import com.google.android.material.snackbar.BaseTransientBottomBar;
import com.google.android.material.snackbar.Snackbar;
import org.jellyfin.apiclient.model.dto.BaseItemDto; import org.jellyfin.apiclient.model.dto.BaseItemDto;
@ -181,7 +175,7 @@ public class SyncFragment extends Fragment {
private void syncSongs() { private void syncSongs() {
Log.d(TAG, "syncSongs"); Log.d(TAG, "syncSongs");
SyncUtil.getSongs(requireContext(), new MediaCallback() { SyncUtil.getSongs(requireContext(), syncViewModel.getCatalogue(), new MediaCallback() {
@Override @Override
public void onError(Exception exception) { public void onError(Exception exception) {
Log.e(TAG, "onError: " + exception.getMessage()); Log.e(TAG, "onError: " + exception.getMessage());
@ -191,6 +185,7 @@ public class SyncFragment extends Fragment {
@Override @Override
public void onLoadMedia(List<?> media) { public void onLoadMedia(List<?> media) {
SongRepository repository = new SongRepository(activity.getApplication()); SongRepository repository = new SongRepository(activity.getApplication());
repository.deleteAllSongGenreCross();
repository.insertAll((ArrayList<Song>) media); repository.insertAll((ArrayList<Song>) media);
animateProgressBar(true); animateProgressBar(true);
} }
@ -198,6 +193,8 @@ public class SyncFragment extends Fragment {
} }
private void syncSongsPerGenre(List<Genre> genres) { private void syncSongsPerGenre(List<Genre> genres) {
Log.d(TAG, "syncSongsPerGenre");
for (Genre genre : genres) { for (Genre genre : genres) {
SyncUtil.getSongsPerGenre(requireContext(), new MediaCallback() { SyncUtil.getSongsPerGenre(requireContext(), new MediaCallback() {
@Override @Override
@ -207,24 +204,30 @@ public class SyncFragment extends Fragment {
@Override @Override
public void onLoadMedia(List<?> media) { public void onLoadMedia(List<?> media) {
GenreRepository repository = new GenreRepository(App.getInstance()); SongRepository repository = new SongRepository(App.getInstance());
repository.insertPerGenre((ArrayList<SongGenreCross>) media); repository.insertSongPerGenre((ArrayList<SongGenreCross>) media);
} }
}, genre.id); }, genre.id);
} }
Log.d(TAG, "syncSongsPerGenre: set progress");
animateProgressBar(true); animateProgressBar(true);
PreferenceUtil.getInstance(requireContext()).setSongGenreSync(true); PreferenceUtil.getInstance(requireContext()).setSongGenreSync(true);
} }
private void animateProgressBar(boolean step) { private void animateProgressBar(boolean step) {
Log.d(TAG, "animateProgressBar: PROGRESS " + step);
syncViewModel.setProgress(step); syncViewModel.setProgress(step);
bind.loadingProgressBar.setProgress(syncViewModel.getProgressBarInfo(), true); bind.loadingProgressBar.setProgress(syncViewModel.getProgressBarInfo(), true);
countProgress(); countProgress();
} }
private void countProgress() { private void countProgress() {
Log.d(TAG, "countProgress = " + syncViewModel.getProgress());
Log.d(TAG, "progressbar = " + syncViewModel.getProgressBarInfo());
if (syncViewModel.getProgress() == syncViewModel.getStep()) { if (syncViewModel.getProgress() == syncViewModel.getStep()) {
if (syncViewModel.getProgressBarInfo() >= 100) if (syncViewModel.getProgressBarInfo() >= 100)
terminate(); terminate();

View file

@ -2,6 +2,7 @@ package com.cappielloantonio.play.util;
import android.content.Context; import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log;
import com.cappielloantonio.play.App; import com.cappielloantonio.play.App;
import com.cappielloantonio.play.interfaces.MediaCallback; import com.cappielloantonio.play.interfaces.MediaCallback;
@ -23,8 +24,11 @@ import org.jellyfin.apiclient.model.querying.ItemsResult;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map;
public class SyncUtil { public class SyncUtil {
private static final String TAG = "SyncUtil";
public static void getLibraries(Context context, MediaCallback callback) { public static void getLibraries(Context context, MediaCallback callback) {
String id = App.getApiClientInstance(context).getCurrentUserId(); String id = App.getApiClientInstance(context).getCurrentUserId();
@ -44,7 +48,7 @@ public class SyncUtil {
}); });
} }
public static void getSongs(Context context, MediaCallback callback) { public static void getSongs(Context context, Map<Integer, Song> currentCatalogue, MediaCallback callback) {
ItemQuery query = new ItemQuery(); ItemQuery query = new ItemQuery();
query.setIncludeItemTypes(new String[]{"Audio"}); query.setIncludeItemTypes(new String[]{"Audio"});
@ -59,7 +63,7 @@ public class SyncUtil {
ArrayList<Song> songs = new ArrayList<>(); ArrayList<Song> songs = new ArrayList<>();
for (BaseItemDto itemDto : result.getItems()) { for (BaseItemDto itemDto : result.getItems()) {
songs.add(new Song(itemDto)); songs.add(updateSongData(currentCatalogue, new Song(itemDto)));
} }
callback.onLoadMedia(songs); callback.onLoadMedia(songs);
@ -215,4 +219,18 @@ public class SyncUtil {
return bundle; return bundle;
} }
private static Song updateSongData(Map<Integer, Song> library, Song newSong) {
if(library.containsKey(newSong.hashCode())) {
Log.d(TAG, "updateSongData: " + newSong.getTitle());
Song oldSong = library.get(newSong.hashCode());
newSong.setFavorite(oldSong.isFavorite());
newSong.setAdded(oldSong.getAdded());
newSong.setLastPlay(oldSong.getLastPlay());
newSong.setPlayCount(oldSong.getPlayCount());
}
return newSong;
}
} }

View file

@ -2,20 +2,21 @@ package com.cappielloantonio.play.viewmodel;
import android.app.Application; import android.app.Application;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel; import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
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.List; import java.util.HashMap;
import java.util.Map;
public class SyncViewModel extends AndroidViewModel { public class SyncViewModel extends AndroidViewModel {
private static final String TAG = "SyncViewModel"; private static final String TAG = "SyncViewModel";
private SongRepository songRepository;
private boolean syncAlbum = false; private boolean syncAlbum = false;
private boolean syncArtist = false; private boolean syncArtist = false;
private boolean syncGenres = false; private boolean syncGenres = false;
@ -29,9 +30,14 @@ public class SyncViewModel extends AndroidViewModel {
public SyncViewModel(@NonNull Application application) { public SyncViewModel(@NonNull Application application) {
super(application); super(application);
songRepository = new SongRepository(application);
} }
public void setArguemnts(Bundle bundle) { public void setArguemnts(Bundle bundle) {
step = 0;
progress = 0;
syncAlbum = bundle.getBoolean("sync_album", false); syncAlbum = bundle.getBoolean("sync_album", false);
syncArtist = bundle.getBoolean("sync_artist", false); syncArtist = bundle.getBoolean("sync_artist", false);
syncGenres = bundle.getBoolean("sync_genres", false); syncGenres = bundle.getBoolean("sync_genres", false);
@ -90,4 +96,14 @@ public class SyncViewModel extends AndroidViewModel {
public int getProgressBarInfo() { public int getProgressBarInfo() {
return progress * (100 / step); return progress * (100 / step);
} }
public Map<Integer, Song> getCatalogue() {
Map<Integer, Song> map = new HashMap<>();
for(Song song: songRepository.getCatalogue()){
map.put(song.hashCode(), song);
}
return map;
}
} }

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:startColor="#00000000"
android:endColor="#FF000000"
android:angle="90"
android:dither="true" />
</shape>

View file

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView <androidx.core.widget.NestedScrollView 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">
@ -32,43 +31,53 @@
android:id="@+id/album_back_cover_image_view" android:id="@+id/album_back_cover_image_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"/> app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View <View
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@drawable/gradient_background_image" /> android:background="@drawable/gradient_backdrop_background_image" />
<TextView
android:id="@+id/album_title_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/open_sans_font_family"
android:textColor="@android:color/white"
android:textSize="40sp"
android:textStyle="bold"
android:textAlignment="center"
android:textFontWeight="900"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:paddingBottom="32dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>
<TextView
android:id="@+id/album_title_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="60dp"
android:elevation="4dp"
android:fontFamily="@font/open_sans_font_family"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:paddingBottom="32dp"
android:textAlignment="center"
android:textColor="@android:color/white"
android:textFontWeight="900"
android:textSize="40sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<View
android:layout_width="match_parent"
android:layout_height="86dp"
android:background="@drawable/gradient_backdrop_fading_down_background_image"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/card_view" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
<!-- Label and button --> <!-- Label and button -->
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="-54dp"
android:fontFamily="@font/open_sans_font_family" android:fontFamily="@font/open_sans_font_family"
android:paddingStart="16dp" android:paddingStart="16dp"
android:paddingTop="20dp"
android:paddingEnd="16dp" android:paddingEnd="16dp"
android:text="Song" android:text="Song"
android:textColor="@color/titleTextColor" android:textColor="@color/titleTextColor"

View file

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView <androidx.core.widget.NestedScrollView 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">
@ -32,43 +31,53 @@
android:id="@+id/artist_backdrop_image_view" android:id="@+id/artist_backdrop_image_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"/> app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View <View
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@drawable/gradient_background_image" /> android:background="@drawable/gradient_backdrop_background_image" />
<TextView
android:id="@+id/artist_name_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/open_sans_font_family"
android:textColor="@android:color/white"
android:textSize="40sp"
android:textStyle="bold"
android:textAlignment="center"
android:textFontWeight="900"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:paddingBottom="32dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>
<TextView
android:id="@+id/artist_name_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="60dp"
android:elevation="4dp"
android:fontFamily="@font/open_sans_font_family"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:paddingBottom="32dp"
android:textAlignment="center"
android:textColor="@android:color/white"
android:textFontWeight="900"
android:textSize="40sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<View
android:layout_width="match_parent"
android:layout_height="86dp"
android:background="@drawable/gradient_backdrop_fading_down_background_image"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/card_view" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
<!-- Label and button --> <!-- Label and button -->
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="-58dp"
android:orientation="horizontal" android:orientation="horizontal"
android:paddingStart="8dp" android:paddingStart="8dp"
android:paddingTop="8dp"
android:paddingEnd="8dp"> android:paddingEnd="8dp">
<TextView <TextView
@ -77,7 +86,6 @@
android:layout_weight="1" android:layout_weight="1"
android:fontFamily="@font/open_sans_font_family" android:fontFamily="@font/open_sans_font_family"
android:paddingStart="8dp" android:paddingStart="8dp"
android:paddingTop="12dp"
android:paddingEnd="8dp" android:paddingEnd="8dp"
android:text="Most Streamed Songs" android:text="Most Streamed Songs"
android:textColor="@color/titleTextColor" android:textColor="@color/titleTextColor"

View file

@ -167,7 +167,7 @@
android:paddingStart="8dp" android:paddingStart="8dp"
android:paddingTop="12dp" android:paddingTop="12dp"
android:paddingEnd="8dp" android:paddingEnd="8dp"
android:text="Music By Genre" android:text="Music by genre"
android:textColor="@color/titleTextColor" android:textColor="@color/titleTextColor"
android:textSize="22sp" android:textSize="22sp"
android:textStyle="bold" /> android:textStyle="bold" />

View file

@ -18,7 +18,7 @@
<View <View
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="172dp" android:layout_height="172dp"
android:background="@drawable/discover_background_image" /> android:background="@drawable/gradient_discover_background_image" />
<RelativeLayout <RelativeLayout
android:layout_width="match_parent" android:layout_width="match_parent"

View file

@ -63,7 +63,9 @@
tools:layout="@layout/fragment_home"> tools:layout="@layout/fragment_home">
<action <action
android:id="@+id/action_homeFragment_to_syncFragment" android:id="@+id/action_homeFragment_to_syncFragment"
app:destination="@id/syncFragment" /> app:destination="@id/syncFragment"
app:popUpTo="@id/homeFragment"
app:popUpToInclusive="true" />
<action <action
android:id="@+id/action_homeFragment_to_songListPageFragment" android:id="@+id/action_homeFragment_to_songListPageFragment"
app:destination="@id/songListPageFragment" /> app:destination="@id/songListPageFragment" />