diff --git a/app/build.gradle b/app/build.gradle index 57388bf0..98948d6e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -36,6 +36,7 @@ android { dependencies { implementation fileTree(dir: "libs", include: ["*.jar"]) + // Jellyfin implementation 'com.github.jellyfin.jellyfin-apiclient-java:android:0.7.7' @@ -48,13 +49,14 @@ dependencies { implementation 'androidx.constraintlayout:constraintlayout:2.0.4' implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0" implementation 'androidx.preference:preference-ktx:1.1.1' - implementation 'androidx.navigation:navigation-fragment-ktx:2.3.1' - implementation 'androidx.navigation:navigation-ui-ktx:2.3.1' + implementation 'androidx.navigation:navigation-fragment-ktx:2.3.2' + implementation 'androidx.navigation:navigation-ui-ktx:2.3.2' implementation 'androidx.recyclerview:recyclerview:1.1.0' implementation "androidx.room:room-runtime:2.2.5" implementation "androidx.cardview:cardview:1.0.0" implementation 'androidx.legacy:legacy-support-v4:1.0.0' - implementation 'androidx.paging:paging-runtime:2.1.2' + implementation 'androidx.paging:paging-runtime-ktx:2.1.2' + implementation "androidx.lifecycle:lifecycle-common-java8:2.2.0" // Android Material implementation 'com.google.android.material:material:1.2.1' @@ -72,7 +74,7 @@ dependencies { // Glide implementation 'com.github.bumptech.glide:glide:4.11.0' implementation "com.github.woltapp:blurhash:f41a23cc50" - + annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0' annotationProcessor "androidx.room:room-compiler:2.2.5" testImplementation 'junit:junit:4.13.1' diff --git a/app/src/main/java/com/cappielloantonio/play/adapter/PlayerNowPlayingSongAdapter.java b/app/src/main/java/com/cappielloantonio/play/adapter/PlayerNowPlayingSongAdapter.java new file mode 100644 index 00000000..bee305cd --- /dev/null +++ b/app/src/main/java/com/cappielloantonio/play/adapter/PlayerNowPlayingSongAdapter.java @@ -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.ImageView; + +import androidx.recyclerview.widget.RecyclerView; + +import com.cappielloantonio.play.App; +import com.cappielloantonio.play.R; +import com.cappielloantonio.play.glide.CustomGlideRequest; +import com.cappielloantonio.play.model.Song; +import com.cappielloantonio.play.repository.SongRepository; + +import java.util.ArrayList; +import java.util.List; + +public class PlayerNowPlayingSongAdapter extends RecyclerView.Adapter { + private static final String TAG = "DiscoverSongAdapter"; + + private List songs; + private LayoutInflater inflater; + private Context context; + + public PlayerNowPlayingSongAdapter(Context context) { + this.context = context; + this.inflater = LayoutInflater.from(context); + this.songs = new ArrayList<>(); + } + + @Override + public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View view = inflater.inflate(R.layout.item_player_now_playing_song, parent, false); + return new ViewHolder(view); + } + + @Override + public void onBindViewHolder(ViewHolder holder, int position) { + Song song = songs.get(position); + + CustomGlideRequest.Builder + .from(context, song.getPrimary(), song.getPrimary(), CustomGlideRequest.PRIMARY, CustomGlideRequest.TOP_QUALITY) + .build() + .into(holder.cover); + } + + @Override + public int getItemCount() { + return songs.size(); + } + + public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { + ImageView cover; + + ViewHolder(View itemView) { + super(itemView); + + cover = itemView.findViewById(R.id.discover_song_cover_image_view); + + itemView.setOnClickListener(this); + } + + @Override + public void onClick(View view) { + SongRepository songRepository = new SongRepository(App.getInstance()); + songRepository.increasePlayCount(songs.get(getAdapterPosition())); + } + } + + public void setItems(List songs) { + this.songs = songs; + notifyDataSetChanged(); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/cappielloantonio/play/adapter/PlayerSongQueueAdapter.java b/app/src/main/java/com/cappielloantonio/play/adapter/PlayerSongQueueAdapter.java new file mode 100644 index 00000000..159dda72 --- /dev/null +++ b/app/src/main/java/com/cappielloantonio/play/adapter/PlayerSongQueueAdapter.java @@ -0,0 +1,100 @@ +package com.cappielloantonio.play.adapter; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.fragment.app.FragmentManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.cappielloantonio.play.App; +import com.cappielloantonio.play.R; +import com.cappielloantonio.play.glide.CustomGlideRequest; +import com.cappielloantonio.play.model.Song; +import com.cappielloantonio.play.repository.QueueRepository; +import com.cappielloantonio.play.repository.SongRepository; +import com.cappielloantonio.play.ui.activities.MainActivity; + +import java.util.ArrayList; +import java.util.List; + +/** + * Adapter per i brani ritrovati nella ricerca + */ +public class PlayerSongQueueAdapter extends RecyclerView.Adapter { + private static final String TAG = "SongResultSearchAdapter"; + + private List songs; + private LayoutInflater mInflater; + private MainActivity mainActivity; + private Context context; + private FragmentManager fragmentManager; + + public PlayerSongQueueAdapter(MainActivity mainActivity, Context context, FragmentManager fragmentManager) { + this.mainActivity = mainActivity; + this.context = context; + this.fragmentManager = fragmentManager; + this.mInflater = LayoutInflater.from(context); + this.songs = new ArrayList<>(); + } + + @Override + public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View view = mInflater.inflate(R.layout.item_player_queue_song, parent, false); + return new ViewHolder(view); + } + + @Override + public void onBindViewHolder(ViewHolder holder, int position) { + Song song = songs.get(position); + + holder.songTitle.setText(song.getTitle()); + holder.songArtist.setText(song.getArtistName()); + + CustomGlideRequest.Builder + .from(context, song.getPrimary(), song.getBlurHash(), CustomGlideRequest.PRIMARY, CustomGlideRequest.TOP_QUALITY) + .build() + .into(holder.cover); + } + + @Override + public int getItemCount() { + return songs.size(); + } + + public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { + TextView songTitle; + TextView songArtist; + ImageView cover; + + ViewHolder(View itemView) { + super(itemView); + + songTitle = itemView.findViewById(R.id.queue_song_title_text_view); + songArtist = itemView.findViewById(R.id.queue_song_artist_text_view); + cover = itemView.findViewById(R.id.queue_song_cover_image_view); + + itemView.setOnClickListener(this); + } + + @Override + public void onClick(View view) { + SongRepository songRepository = new SongRepository(App.getInstance()); + QueueRepository queueRepository = new QueueRepository(App.getInstance()); + + songRepository.increasePlayCount(songs.get(getAdapterPosition())); + } + } + + public void setItems(List songs) { + this.songs = songs; + notifyDataSetChanged(); + } + + public Song getItem(int id) { + return songs.get(id); + } +} diff --git a/app/src/main/java/com/cappielloantonio/play/adapter/RecentMusicAdapter.java b/app/src/main/java/com/cappielloantonio/play/adapter/RecentMusicAdapter.java index 1fb727ff..339f5a99 100644 --- a/app/src/main/java/com/cappielloantonio/play/adapter/RecentMusicAdapter.java +++ b/app/src/main/java/com/cappielloantonio/play/adapter/RecentMusicAdapter.java @@ -16,7 +16,9 @@ import com.cappielloantonio.play.App; import com.cappielloantonio.play.R; import com.cappielloantonio.play.glide.CustomGlideRequest; import com.cappielloantonio.play.model.Song; +import com.cappielloantonio.play.repository.QueueRepository; import com.cappielloantonio.play.repository.SongRepository; +import com.cappielloantonio.play.ui.activities.MainActivity; import java.util.ArrayList; import java.util.List; @@ -29,10 +31,12 @@ public class RecentMusicAdapter extends RecyclerView.Adapter songs; private LayoutInflater mInflater; + private MainActivity mainActivity; private Context context; private FragmentManager fragmentManager; - public RecentMusicAdapter(Context context, FragmentManager fragmentManager) { + public RecentMusicAdapter(MainActivity mainActivity, Context context, FragmentManager fragmentManager) { + this.mainActivity = mainActivity; this.context = context; this.fragmentManager = fragmentManager; this.mInflater = LayoutInflater.from(context); @@ -82,7 +86,12 @@ public class RecentMusicAdapter extends RecyclerView.Adapter songs; private LayoutInflater mInflater; + private MainActivity mainActivity; private Context context; private FragmentManager fragmentManager; - public SongResultSearchAdapter(Context context, FragmentManager fragmentManager) { + public SongResultSearchAdapter(MainActivity mainActivity, Context context, FragmentManager fragmentManager) { + this.mainActivity = mainActivity; this.context = context; this.fragmentManager = fragmentManager; this.mInflater = LayoutInflater.from(context); @@ -86,7 +90,12 @@ public class SongResultSearchAdapter extends RecyclerView.Adapter> getAll(); + + @Query("SELECT * FROM song JOIN queue ON song.id = queue.song_id") + List getAllSimple(); + + @Insert(onConflict = OnConflictStrategy.REPLACE) + void insert(Queue songQueueObject); + + @Insert(onConflict = OnConflictStrategy.REPLACE) + void insertAll(List songQueueObject); + + @Delete + void delete(Queue songQueueObject); + + @Query("DELETE FROM queue") + void deleteAll(); + + @Query("SELECT COUNT(*) FROM queue;") + int count(); +} \ No newline at end of file diff --git a/app/src/main/java/com/cappielloantonio/play/model/Queue.java b/app/src/main/java/com/cappielloantonio/play/model/Queue.java new file mode 100644 index 00000000..8396c344 --- /dev/null +++ b/app/src/main/java/com/cappielloantonio/play/model/Queue.java @@ -0,0 +1,44 @@ +package com.cappielloantonio.play.model; + +import androidx.annotation.NonNull; +import androidx.room.ColumnInfo; +import androidx.room.Entity; +import androidx.room.Ignore; +import androidx.room.PrimaryKey; + +@Entity(tableName = "queue") +public class Queue { + @NonNull + @PrimaryKey(autoGenerate = true) + @ColumnInfo(name = "id") + private int id; + + @ColumnInfo(name = "song_id") + private String songID; + + public Queue(@NonNull int id, String songID) { + this.id = id; + this.songID = songID; + } + + @Ignore + public Queue(String songID) { + this.songID = songID; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getSongID() { + return songID; + } + + public void setSongID(String songID) { + this.songID = songID; + } +} diff --git a/app/src/main/java/com/cappielloantonio/play/repository/QueueRepository.java b/app/src/main/java/com/cappielloantonio/play/repository/QueueRepository.java new file mode 100644 index 00000000..1c57f61a --- /dev/null +++ b/app/src/main/java/com/cappielloantonio/play/repository/QueueRepository.java @@ -0,0 +1,197 @@ +package com.cappielloantonio.play.repository; + +import android.app.Application; + +import androidx.lifecycle.LiveData; + +import com.cappielloantonio.play.database.AppDatabase; +import com.cappielloantonio.play.database.dao.QueueDao; +import com.cappielloantonio.play.model.Queue; +import com.cappielloantonio.play.model.Song; +import com.cappielloantonio.play.util.QueueUtil; + +import java.util.ArrayList; +import java.util.List; + +public class QueueRepository { + private static final String TAG = "QueueRepository"; + + private QueueDao queueDao; + private LiveData> listLiveQueue; + + public QueueRepository(Application application) { + AppDatabase database = AppDatabase.getInstance(application); + queueDao = database.queueDao(); + } + + public LiveData> getLiveQueue() { + listLiveQueue = queueDao.getAll(); + return listLiveQueue; + } + + public List getSongs() { + List songs = new ArrayList<>(); + + GetSongsThreadSafe getSongs = new GetSongsThreadSafe(queueDao); + Thread thread = new Thread(getSongs); + thread.start(); + + try { + thread.join(); + songs = getSongs.getSongs(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + return songs; + } + + public void insert(Song song) { + InsertThreadSafe insert = new InsertThreadSafe(queueDao, song); + Thread thread = new Thread(insert); + thread.start(); + } + + public void insertAll(List songs) { + InsertAllThreadSafe insertAll = new InsertAllThreadSafe(queueDao, songs); + Thread thread = new Thread(insertAll); + thread.start(); + } + + public void insertAllAndStartNew(List songs) { + try { + final Thread delete = new Thread(new DeleteAllThreadSafe(queueDao)); + final Thread insertAll = new Thread(new InsertAllThreadSafe(queueDao, songs)); + + delete.start(); + delete.join(); + insertAll.start(); + insertAll.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + public void delete(Queue queueElement) { + DeleteThreadSafe delete = new DeleteThreadSafe(queueDao, queueElement); + Thread thread = new Thread(delete); + thread.start(); + } + + public void deleteAll() { + DeleteAllThreadSafe delete = new DeleteAllThreadSafe(queueDao); + Thread thread = new Thread(delete); + thread.start(); + } + + public int count() { + int count = 0; + + CountThreadSafe countThread = new CountThreadSafe(queueDao); + Thread thread = new Thread(countThread); + thread.start(); + + try { + thread.join(); + count = countThread.getCount(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + return count; + } + + private static class InsertThreadSafe implements Runnable { + private QueueDao queueDao; + private Song song; + + public InsertThreadSafe(QueueDao queueDao, Song song) { + this.queueDao = queueDao; + this.song = song; + } + + @Override + public void run() { + queueDao.insert(QueueUtil.getQueueElementFromSong(song)); + } + } + + private static class InsertAllThreadSafe implements Runnable { + private QueueDao queueDao; + private List songs; + + public InsertAllThreadSafe(QueueDao queueDao, List songs) { + this.queueDao = queueDao; + this.songs = songs; + } + + @Override + public void run() { + queueDao.insertAll(QueueUtil.getQueueElementsFromSongs(songs)); + } + } + + private static class DeleteThreadSafe implements Runnable { + private QueueDao queueDao; + private Queue queueElement; + + public DeleteThreadSafe(QueueDao queueDao, Queue queueElement) { + this.queueDao = queueDao; + this.queueElement = queueElement; + } + + @Override + public void run() { + queueDao.delete(queueElement); + } + } + + private static class DeleteAllThreadSafe implements Runnable { + private QueueDao queueDao; + + public DeleteAllThreadSafe(QueueDao queueDao) { + this.queueDao = queueDao; + } + + @Override + public void run() { + queueDao.deleteAll(); + } + } + + private static class CountThreadSafe implements Runnable { + private QueueDao queueDao; + private int count = 0; + + public CountThreadSafe(QueueDao queueDao) { + this.queueDao = queueDao; + } + + @Override + public void run() { + count = queueDao.count(); + } + + public int getCount() { + return count; + } + } + + private static class GetSongsThreadSafe implements Runnable { + private QueueDao queueDao; + private List songs; + + public GetSongsThreadSafe(QueueDao queueDao) { + this.queueDao = queueDao; + } + + @Override + public void run() { + songs = queueDao.getAllSimple(); + } + + public List getSongs() { + return songs; + } + } +} diff --git a/app/src/main/java/com/cappielloantonio/play/ui/activities/MainActivity.java b/app/src/main/java/com/cappielloantonio/play/ui/activities/MainActivity.java index 40cf9766..6ccde626 100644 --- a/app/src/main/java/com/cappielloantonio/play/ui/activities/MainActivity.java +++ b/app/src/main/java/com/cappielloantonio/play/ui/activities/MainActivity.java @@ -3,9 +3,12 @@ package com.cappielloantonio.play.ui.activities; import android.content.IntentFilter; import android.net.ConnectivityManager; import android.os.Bundle; +import android.util.Log; import android.view.View; +import androidx.annotation.NonNull; import androidx.fragment.app.FragmentManager; +import androidx.lifecycle.ViewModelProvider; import androidx.navigation.NavController; import androidx.navigation.fragment.NavHostFragment; import androidx.navigation.ui.NavigationUI; @@ -15,9 +18,12 @@ import com.cappielloantonio.play.R; import com.cappielloantonio.play.broadcast.receiver.ConnectivityStatusBroadcastReceiver; import com.cappielloantonio.play.databinding.ActivityMainBinding; import com.cappielloantonio.play.ui.activities.base.BaseActivity; +import com.cappielloantonio.play.ui.fragment.PlayerBottomSheetFragment; import com.cappielloantonio.play.util.PreferenceUtil; import com.cappielloantonio.play.util.SyncUtil; +import com.cappielloantonio.play.viewmodel.MainViewModel; import com.google.android.material.bottomnavigation.BottomNavigationView; +import com.google.android.material.bottomsheet.BottomSheetBehavior; import org.jellyfin.apiclient.interaction.EmptyResponse; import org.jellyfin.apiclient.interaction.Response; @@ -29,21 +35,27 @@ import java.util.Objects; public class MainActivity extends BaseActivity { private static final String TAG = "MainActivity"; - public ActivityMainBinding activityMainBinding; + public ActivityMainBinding bind; + private MainViewModel mainViewModel; private FragmentManager fragmentManager; private NavHostFragment navHostFragment; private BottomNavigationView bottomNavigationView; public NavController navController; + private BottomSheetBehavior bottomSheetBehavior; ConnectivityStatusBroadcastReceiver connectivityStatusBroadcastReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - activityMainBinding = ActivityMainBinding.inflate(getLayoutInflater()); - View view = activityMainBinding.getRoot(); + + bind = ActivityMainBinding.inflate(getLayoutInflater()); + View view = bind.getRoot(); setContentView(view); + + mainViewModel = new ViewModelProvider(this).get(MainViewModel.class); + connectivityStatusBroadcastReceiver = new ConnectivityStatusBroadcastReceiver(this); connectivityStatusReceiverManager(true); @@ -58,11 +70,9 @@ public class MainActivity extends BaseActivity { public void init() { fragmentManager = getSupportFragmentManager(); - bottomNavigationView = findViewById(R.id.bottom_navigation); - navHostFragment = (NavHostFragment) fragmentManager.findFragmentById(R.id.nav_host_fragment); - navController = navHostFragment.getNavController(); - NavigationUI.setupWithNavController(bottomNavigationView, navController); + initBottomSheet(); + initNavigation(); if (PreferenceUtil.getInstance(this).getToken() != null) { checkPreviousSession(); @@ -71,6 +81,42 @@ public class MainActivity extends BaseActivity { } } + private void initBottomSheet() { + bottomSheetBehavior = BottomSheetBehavior.from(findViewById(R.id.player_bottom_sheet)); + bottomSheetBehavior.addBottomSheetCallback(bottomSheetCallback); + fragmentManager.beginTransaction().replace(R.id.player_bottom_sheet, new PlayerBottomSheetFragment(), "PlayerBottomSheet").commit(); + + isBottomSheetInPeek(mainViewModel.isQueueLoaded()); + } + + public void isBottomSheetInPeek(Boolean isVisible) { + + Log.d(TAG, "isBottomSheetInPeek: " + isVisible); + + if (isVisible) { + bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED); + } else { + bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); + } + } + + private void initNavigation() { + bottomNavigationView = findViewById(R.id.bottom_navigation); + navHostFragment = (NavHostFragment) fragmentManager.findFragmentById(R.id.nav_host_fragment); + navController = navHostFragment.getNavController(); + navController.addOnDestinationChangedListener((controller, destination, arguments) -> { + if(bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED && ( + destination.getId() == R.id.homeFragment || + destination.getId() == R.id.libraryFragment || + destination.getId() == R.id.searchFragment || + destination.getId() == R.id.settingsFragment) + ) { + bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED); + } + }); + NavigationUI.setupWithNavController(bottomNavigationView, navController); + } + private void checkPreviousSession() { App.getApiClientInstance(getApplicationContext()).ChangeServerLocation(PreferenceUtil.getInstance(this).getServer()); App.getApiClientInstance(getApplicationContext()).SetAuthenticationInfo(PreferenceUtil.getInstance(this).getToken(), PreferenceUtil.getInstance(this).getUser()); @@ -94,8 +140,6 @@ public class MainActivity extends BaseActivity { }); } - // True: VISIBLE - // False: GONE public void setBottomNavigationBarVisibility(boolean visibility) { if (visibility) { bottomNavigationView.setVisibility(View.VISIBLE); @@ -104,13 +148,50 @@ public class MainActivity extends BaseActivity { } } + public void setBottomSheetVisibility(boolean visibility) { + if (visibility) { + findViewById(R.id.player_bottom_sheet).setVisibility(View.VISIBLE); + } else { + findViewById(R.id.player_bottom_sheet).setVisibility(View.GONE); + } + } + + private BottomSheetBehavior.BottomSheetCallback bottomSheetCallback = + new BottomSheetBehavior.BottomSheetCallback() { + @Override + public void onStateChanged(@NonNull View view, int state) { + switch (state) { + case BottomSheetBehavior.STATE_COLLAPSED: + PlayerBottomSheetFragment playerBottomSheetFragment = (PlayerBottomSheetFragment) getSupportFragmentManager().findFragmentByTag("PlayerBottomSheet"); + if(playerBottomSheetFragment == null) break; + + playerBottomSheetFragment.scrollOnTop(); + break; + case BottomSheetBehavior.STATE_HIDDEN: + mainViewModel.deleteQueue(); + break; + } + } + + @Override + public void onSlide(@NonNull View view, float slideOffset) { + PlayerBottomSheetFragment playerBottomSheetFragment = (PlayerBottomSheetFragment) getSupportFragmentManager().findFragmentByTag("PlayerBottomSheet"); + if(playerBottomSheetFragment == null) return; + + float condensedSlideOffset = Math.max(0.0f, Math.min(0.2f, slideOffset - 0.2f)) / 0.2f; + playerBottomSheetFragment.getPlayerHeader().setAlpha(1 - condensedSlideOffset); + playerBottomSheetFragment.getPlayerHeader().setVisibility(condensedSlideOffset > 0.99 ? View.GONE : View.VISIBLE); + } + }; + public void goToLogin() { if (Objects.requireNonNull(navController.getCurrentDestination()).getId() == R.id.landingFragment) navController.navigate(R.id.action_landingFragment_to_loginFragment); } public void goToSync() { - bottomNavigationView.setVisibility(View.GONE); + setBottomNavigationBarVisibility(false); + setBottomSheetVisibility(false); if (Objects.requireNonNull(navController.getCurrentDestination()).getId() == R.id.loginFragment) { Bundle bundle = SyncUtil.getSyncBundle(true, true, true, true, true, false); @@ -145,12 +226,19 @@ public class MainActivity extends BaseActivity { } private void connectivityStatusReceiverManager(boolean isActive) { - if(isActive) { + if (isActive) { IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); registerReceiver(connectivityStatusBroadcastReceiver, filter); - } - else { + } else { unregisterReceiver(connectivityStatusBroadcastReceiver); } } + + @Override + public void onBackPressed() { + if(bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED) + bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED); + else + super.onBackPressed(); + } } \ No newline at end of file diff --git a/app/src/main/java/com/cappielloantonio/play/ui/fragment/AlbumPageFragment.java b/app/src/main/java/com/cappielloantonio/play/ui/fragment/AlbumPageFragment.java index f0af8742..da5e738d 100644 --- a/app/src/main/java/com/cappielloantonio/play/ui/fragment/AlbumPageFragment.java +++ b/app/src/main/java/com/cappielloantonio/play/ui/fragment/AlbumPageFragment.java @@ -1,14 +1,18 @@ package com.cappielloantonio.play.ui.fragment; +import android.graphics.PorterDuff; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import androidx.annotation.Nullable; +import androidx.core.view.ViewCompat; import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; import androidx.recyclerview.widget.LinearLayoutManager; +import com.cappielloantonio.play.R; import com.cappielloantonio.play.adapter.SongResultSearchAdapter; import com.cappielloantonio.play.databinding.FragmentAlbumPageBinding; import com.cappielloantonio.play.glide.CustomGlideRequest; @@ -24,7 +28,14 @@ public class AlbumPageFragment extends Fragment { private SongResultSearchAdapter songResultSearchAdapter; @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + public void onActivityCreated(@Nullable Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + initAppBar(); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { activity = (MainActivity) getActivity(); bind = FragmentAlbumPageBinding.inflate(inflater, container, false); @@ -50,6 +61,29 @@ public class AlbumPageFragment extends Fragment { bind = null; } + private void init() { + albumPageViewModel.setAlbum(getArguments().getParcelable("album_object")); + } + + private void initAppBar() { + activity.setSupportActionBar(bind.animToolbar); + if (activity.getSupportActionBar() != null) + activity.getSupportActionBar().setDisplayHomeAsUpEnabled(true); + + bind.collapsingToolbar.setTitle(albumPageViewModel.getAlbum().getTitle()); + bind.animToolbar.setNavigationOnClickListener(v -> activity.navController.navigateUp()); + bind.collapsingToolbar.setCollapsedTitleTextColor(getResources().getColor(R.color.titleTextColor, null)); + + bind.appbar.addOnOffsetChangedListener((appBarLayout, verticalOffset) -> { + if ((bind.collapsingToolbar.getHeight() + verticalOffset) < (2 * ViewCompat.getMinimumHeight(bind.collapsingToolbar))) { + bind.animToolbar.getNavigationIcon().setColorFilter(getResources().getColor(R.color.titleTextColor, null), PorterDuff.Mode.SRC_ATOP); + } else { + bind.animToolbar.getNavigationIcon().setColorFilter(getResources().getColor(R.color.white, null), PorterDuff.Mode.SRC_ATOP); + } + }); + + } + private void initBackCover() { CustomGlideRequest.Builder .from(requireContext(), albumPageViewModel.getAlbum().getPrimary(), albumPageViewModel.getAlbum().getBlurHash(), CustomGlideRequest.PRIMARY, CustomGlideRequest.TOP_QUALITY) @@ -57,17 +91,11 @@ public class AlbumPageFragment extends Fragment { .into(bind.albumBackCoverImageView); } - private void init() { - albumPageViewModel.setAlbum(getArguments().getParcelable("album_object")); - - bind.albumTitleLabel.setText(albumPageViewModel.getAlbum().getTitle()); - } - private void initSongsView() { bind.songRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext())); bind.songRecyclerView.setHasFixedSize(true); - songResultSearchAdapter = new SongResultSearchAdapter(requireContext(), getChildFragmentManager()); + songResultSearchAdapter = new SongResultSearchAdapter(activity, requireContext(), getChildFragmentManager()); bind.songRecyclerView.setAdapter(songResultSearchAdapter); albumPageViewModel.getAlbumSongList().observe(requireActivity(), songs -> songResultSearchAdapter.setItems(songs)); } diff --git a/app/src/main/java/com/cappielloantonio/play/ui/fragment/ArtistPageFragment.java b/app/src/main/java/com/cappielloantonio/play/ui/fragment/ArtistPageFragment.java index 287782d6..77aa74ca 100644 --- a/app/src/main/java/com/cappielloantonio/play/ui/fragment/ArtistPageFragment.java +++ b/app/src/main/java/com/cappielloantonio/play/ui/fragment/ArtistPageFragment.java @@ -1,10 +1,13 @@ package com.cappielloantonio.play.ui.fragment; +import android.graphics.PorterDuff; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import androidx.annotation.Nullable; +import androidx.core.view.ViewCompat; import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; import androidx.recyclerview.widget.LinearLayoutManager; @@ -27,6 +30,13 @@ public class ArtistPageFragment extends Fragment { private SongResultSearchAdapter songResultSearchAdapter; private AlbumArtistPageAdapter albumArtistPageAdapter; + @Override + public void onActivityCreated(@Nullable Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + initAppBar(); + } + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { activity = (MainActivity) getActivity(); @@ -58,7 +68,6 @@ public class ArtistPageFragment extends Fragment { private void init() { artistPageViewModel.setArtist(getArguments().getParcelable("artist_object")); - bind.artistNameLabel.setText(artistPageViewModel.getArtist().getName()); bind.mostStreamedSongTextViewClickable.setOnClickListener(v -> { Bundle bundle = new Bundle(); @@ -68,6 +77,24 @@ public class ArtistPageFragment extends Fragment { }); } + private void initAppBar() { + activity.setSupportActionBar(bind.animToolbar); + if (activity.getSupportActionBar() != null) + activity.getSupportActionBar().setDisplayHomeAsUpEnabled(true); + + bind.collapsingToolbar.setTitle(artistPageViewModel.getArtist().getName()); + bind.animToolbar.setNavigationOnClickListener(v -> activity.navController.navigateUp()); + bind.collapsingToolbar.setCollapsedTitleTextColor(getResources().getColor(R.color.titleTextColor, null)); + + bind.appbar.addOnOffsetChangedListener((appBarLayout, verticalOffset) -> { + if ((bind.collapsingToolbar.getHeight() + verticalOffset) < (2 * ViewCompat.getMinimumHeight(bind.collapsingToolbar))) { + bind.animToolbar.getNavigationIcon().setColorFilter(getResources().getColor(R.color.titleTextColor, null), PorterDuff.Mode.SRC_ATOP); + } else { + bind.animToolbar.getNavigationIcon().setColorFilter(getResources().getColor(R.color.white, null), PorterDuff.Mode.SRC_ATOP); + } + }); + } + private void initBackdrop() { CustomGlideRequest.Builder .from(requireContext(), artistPageViewModel.getArtist().getBackdrop(), artistPageViewModel.getArtist().getBackdropBlurHash(), CustomGlideRequest.BACKDROP, CustomGlideRequest.TOP_QUALITY) @@ -77,16 +104,14 @@ public class ArtistPageFragment extends Fragment { private void initTopSongsView() { bind.mostStreamedSongRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext())); - bind.mostStreamedSongRecyclerView.setHasFixedSize(true); - songResultSearchAdapter = new SongResultSearchAdapter(requireContext(), getChildFragmentManager()); + songResultSearchAdapter = new SongResultSearchAdapter(activity, requireContext(), getChildFragmentManager()); bind.mostStreamedSongRecyclerView.setAdapter(songResultSearchAdapter); artistPageViewModel.getArtistTopSongList().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()); bind.albumsRecyclerView.setAdapter(albumArtistPageAdapter); diff --git a/app/src/main/java/com/cappielloantonio/play/ui/fragment/HomeFragment.java b/app/src/main/java/com/cappielloantonio/play/ui/fragment/HomeFragment.java index e087b476..c6e11d97 100644 --- a/app/src/main/java/com/cappielloantonio/play/ui/fragment/HomeFragment.java +++ b/app/src/main/java/com/cappielloantonio/play/ui/fragment/HomeFragment.java @@ -66,6 +66,7 @@ public class HomeFragment extends Fragment { public void onStart() { super.onStart(); activity.setBottomNavigationBarVisibility(true); + activity.setBottomSheetVisibility(true); } @Override @@ -123,14 +124,14 @@ public class HomeFragment extends Fragment { discoverSongAdapter = new DiscoverSongAdapter(requireContext(), homeViewModel.getDiscoverSongList()); bind.discoverSongViewPager.setAdapter(discoverSongAdapter); bind.discoverSongViewPager.setOffscreenPageLimit(3); - settDiscoverSongSlideViewOffset(20, 16); + setDiscoverSongSlideViewOffset(20, 16); } private void initRecentAddedSongView() { bind.recentlyAddedTracksRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false)); bind.recentlyAddedTracksRecyclerView.setHasFixedSize(true); - recentlyAddedMusicAdapter = new RecentMusicAdapter(requireContext(), getChildFragmentManager()); + recentlyAddedMusicAdapter = new RecentMusicAdapter(activity, requireContext(), getChildFragmentManager()); bind.recentlyAddedTracksRecyclerView.setAdapter(recentlyAddedMusicAdapter); homeViewModel.getRecentlyAddedSongList().observe(requireActivity(), songs -> recentlyAddedMusicAdapter.setItems(songs)); } @@ -153,7 +154,7 @@ public class HomeFragment extends Fragment { bind.favoritesTracksRecyclerView.setLayoutManager(new GridLayoutManager(requireContext(), 5, GridLayoutManager.HORIZONTAL, false)); bind.favoritesTracksRecyclerView.setHasFixedSize(true); - favoriteSongAdapter = new SongResultSearchAdapter(requireContext(), getChildFragmentManager()); + favoriteSongAdapter = new SongResultSearchAdapter(activity, requireContext(), getChildFragmentManager()); bind.favoritesTracksRecyclerView.setAdapter(favoriteSongAdapter); homeViewModel.getFavorites().observe(requireActivity(), songs -> favoriteSongAdapter.setItems(songs)); @@ -165,7 +166,7 @@ public class HomeFragment extends Fragment { bind.mostPlayedTracksRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false)); bind.mostPlayedTracksRecyclerView.setHasFixedSize(true); - mostPlayedMusicAdapter = new RecentMusicAdapter(requireContext(), getChildFragmentManager()); + mostPlayedMusicAdapter = new RecentMusicAdapter(activity, requireContext(), getChildFragmentManager()); bind.mostPlayedTracksRecyclerView.setAdapter(mostPlayedMusicAdapter); homeViewModel.getMostPlayedSongList().observe(requireActivity(), songs -> mostPlayedMusicAdapter.setItems(songs)); } @@ -174,12 +175,12 @@ public class HomeFragment extends Fragment { bind.recentlyPlayedTracksRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false)); bind.recentlyPlayedTracksRecyclerView.setHasFixedSize(true); - recentlyPlayedMusicAdapter = new RecentMusicAdapter(requireContext(), getChildFragmentManager()); + recentlyPlayedMusicAdapter = new RecentMusicAdapter(activity, requireContext(), getChildFragmentManager()); bind.recentlyPlayedTracksRecyclerView.setAdapter(recentlyPlayedMusicAdapter); homeViewModel.getRecentlyPlayedSongList().observe(requireActivity(), songs -> recentlyPlayedMusicAdapter.setItems(songs)); } - private void settDiscoverSongSlideViewOffset(float pageOffset, float pageMargin) { + private void setDiscoverSongSlideViewOffset(float pageOffset, float pageMargin) { bind.discoverSongViewPager.setPageTransformer((page, position) -> { float myOffset = position * -(2 * pageOffset + pageMargin); if (bind.discoverSongViewPager.getOrientation() == ViewPager2.ORIENTATION_HORIZONTAL) { diff --git a/app/src/main/java/com/cappielloantonio/play/ui/fragment/PlayerBottomSheetFragment.java b/app/src/main/java/com/cappielloantonio/play/ui/fragment/PlayerBottomSheetFragment.java new file mode 100644 index 00000000..da5f2c41 --- /dev/null +++ b/app/src/main/java/com/cappielloantonio/play/ui/fragment/PlayerBottomSheetFragment.java @@ -0,0 +1,113 @@ +package com.cappielloantonio.play.ui.fragment; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ScrollView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.view.ViewCompat; +import androidx.fragment.app.Fragment; +import androidx.lifecycle.ViewModelProvider; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.viewpager2.widget.ViewPager2; + +import com.cappielloantonio.play.R; +import com.cappielloantonio.play.adapter.PlayerNowPlayingSongAdapter; +import com.cappielloantonio.play.adapter.PlayerSongQueueAdapter; +import com.cappielloantonio.play.databinding.FragmentPlayerBottomSheetBinding; +import com.cappielloantonio.play.model.Song; +import com.cappielloantonio.play.ui.activities.MainActivity; +import com.cappielloantonio.play.viewmodel.PlayerBottomSheetViewModel; + +public class PlayerBottomSheetFragment extends Fragment { + private static final String TAG = "PlayerBottomSheetFragment"; + + private FragmentPlayerBottomSheetBinding bind; + private MainActivity activity; + private PlayerBottomSheetViewModel playerBottomSheetViewModel; + + private PlayerNowPlayingSongAdapter playerNowPlayingSongAdapter; + private PlayerSongQueueAdapter playerSongQueueAdapter; + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + activity = (MainActivity) getActivity(); + + bind = FragmentPlayerBottomSheetBinding.inflate(inflater, container, false); + View view = bind.getRoot(); + playerBottomSheetViewModel = new ViewModelProvider(requireActivity()).get(PlayerBottomSheetViewModel.class); + + initQueueSlideView(); + initQueueRecyclerView(); + + return view; + } + + private void initQueueSlideView() { + bind.playerSongCoverViewPager.setOrientation(ViewPager2.ORIENTATION_HORIZONTAL); + + playerNowPlayingSongAdapter = new PlayerNowPlayingSongAdapter(requireContext()); + bind.playerSongCoverViewPager.setAdapter(playerNowPlayingSongAdapter); + playerBottomSheetViewModel.getQueueSong().observe(requireActivity(), songs -> playerNowPlayingSongAdapter.setItems(songs)); + + bind.playerSongCoverViewPager.setOffscreenPageLimit(3); + setDiscoverSongSlideViewOffset(40, 4); + + bind.playerSongCoverViewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() { + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + super.onPageScrolled(position, positionOffset, positionOffsetPixels); + + playerBottomSheetViewModel.setNowPlayingSong(position); + } + }); + + playerBottomSheetViewModel.getNowPlayingSong().observe(requireActivity(), song -> { + if(song != null) + setSongInfo(song); + }); + } + + private void initQueueRecyclerView() { + bind.playerQueueRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext())); + bind.playerQueueRecyclerView.setHasFixedSize(true); + + playerSongQueueAdapter = new PlayerSongQueueAdapter(activity, requireContext(), getChildFragmentManager()); + bind.playerQueueRecyclerView.setAdapter(playerSongQueueAdapter); + playerBottomSheetViewModel.getQueueSong().observe(requireActivity(), songs -> playerSongQueueAdapter.setItems(songs)); + } + + private void setDiscoverSongSlideViewOffset(float pageOffset, float pageMargin) { + bind.playerSongCoverViewPager.setPageTransformer((page, position) -> { + float myOffset = position * -(2 * pageOffset + pageMargin); + if (bind.playerSongCoverViewPager.getOrientation() == ViewPager2.ORIENTATION_HORIZONTAL) { + if (ViewCompat.getLayoutDirection(bind.playerSongCoverViewPager) == ViewCompat.LAYOUT_DIRECTION_RTL) { + page.setTranslationX(-myOffset); + } else { + page.setTranslationX(myOffset); + } + } else { + page.setTranslationY(myOffset); + } + }); + } + + private void setSongInfo(Song song) { + if(song != null) { + bind.playerSongTitleLabel.setText(song.getTitle()); + bind.playerArtistNameLabel.setText(song.getArtistName()); + } + } + + public View getPlayerHeader() { + return getView().findViewById(R.id.player_header_layout); + } + + public void scrollOnTop() { + bind.playerNestedScrollView.fullScroll(ScrollView.FOCUS_UP); + } +} diff --git a/app/src/main/java/com/cappielloantonio/play/ui/fragment/SearchFragment.java b/app/src/main/java/com/cappielloantonio/play/ui/fragment/SearchFragment.java index 177d9949..4e0c535d 100644 --- a/app/src/main/java/com/cappielloantonio/play/ui/fragment/SearchFragment.java +++ b/app/src/main/java/com/cappielloantonio/play/ui/fragment/SearchFragment.java @@ -92,7 +92,7 @@ public class SearchFragment extends Fragment { bind.searchResultTracksRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext())); bind.searchResultTracksRecyclerView.setHasFixedSize(true); - songResultSearchAdapter = new SongResultSearchAdapter(requireContext(), getChildFragmentManager()); + songResultSearchAdapter = new SongResultSearchAdapter(activity, requireContext(), getChildFragmentManager()); bind.searchResultTracksRecyclerView.setAdapter(songResultSearchAdapter); // Albums diff --git a/app/src/main/java/com/cappielloantonio/play/ui/fragment/SongListPageFragment.java b/app/src/main/java/com/cappielloantonio/play/ui/fragment/SongListPageFragment.java index 6966cb50..c7d49ed1 100644 --- a/app/src/main/java/com/cappielloantonio/play/ui/fragment/SongListPageFragment.java +++ b/app/src/main/java/com/cappielloantonio/play/ui/fragment/SongListPageFragment.java @@ -93,7 +93,7 @@ public class SongListPageFragment extends Fragment { bind.songListRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext())); bind.songListRecyclerView.setHasFixedSize(true); - songResultSearchAdapter = new SongResultSearchAdapter(requireContext(), getChildFragmentManager()); + songResultSearchAdapter = new SongResultSearchAdapter(activity, requireContext(), getChildFragmentManager()); bind.songListRecyclerView.setAdapter(songResultSearchAdapter); songListPageViewModel.getSongList().observe(requireActivity(), songs -> songResultSearchAdapter.setItems(songs)); } diff --git a/app/src/main/java/com/cappielloantonio/play/util/PreferenceUtil.java b/app/src/main/java/com/cappielloantonio/play/util/PreferenceUtil.java index 6248469f..7a96cc08 100644 --- a/app/src/main/java/com/cappielloantonio/play/util/PreferenceUtil.java +++ b/app/src/main/java/com/cappielloantonio/play/util/PreferenceUtil.java @@ -58,7 +58,7 @@ public class PreferenceUtil { } public String getToken() { - return mPreferences.getString(TOKEN, ""); + return mPreferences.getString(TOKEN, null); } public void setToken(String token) { diff --git a/app/src/main/java/com/cappielloantonio/play/util/QueueUtil.java b/app/src/main/java/com/cappielloantonio/play/util/QueueUtil.java new file mode 100644 index 00000000..66b825de --- /dev/null +++ b/app/src/main/java/com/cappielloantonio/play/util/QueueUtil.java @@ -0,0 +1,23 @@ +package com.cappielloantonio.play.util; + +import com.cappielloantonio.play.model.Queue; +import com.cappielloantonio.play.model.Song; + +import java.util.ArrayList; +import java.util.List; + +public class QueueUtil { + public static Queue getQueueElementFromSong(Song song) { + return new Queue(song.getId()); + } + + public static List getQueueElementsFromSongs(List songs) { + List queue = new ArrayList<>(); + + for(Song song: songs) { + queue.add(new Queue(song.getId())); + } + + return queue; + } +} diff --git a/app/src/main/java/com/cappielloantonio/play/util/Util.java b/app/src/main/java/com/cappielloantonio/play/util/Util.java index 4cd32808..eccdb43e 100644 --- a/app/src/main/java/com/cappielloantonio/play/util/Util.java +++ b/app/src/main/java/com/cappielloantonio/play/util/Util.java @@ -1,5 +1,7 @@ package com.cappielloantonio.play.util; +import android.content.Context; + import java.util.Locale; public class Util { @@ -15,4 +17,12 @@ public class Util { return String.format(Locale.getDefault(), "%d:%02d:%02d", hours, minutes, seconds); } } + + public static float dpFromPx(final Context context, final float px) { + return px / context.getResources().getDisplayMetrics().density; + } + + public static float pxFromDp(final Context context, final float dp) { + return dp * context.getResources().getDisplayMetrics().density; + } } diff --git a/app/src/main/java/com/cappielloantonio/play/viewmodel/MainViewModel.java b/app/src/main/java/com/cappielloantonio/play/viewmodel/MainViewModel.java new file mode 100644 index 00000000..0c3c539d --- /dev/null +++ b/app/src/main/java/com/cappielloantonio/play/viewmodel/MainViewModel.java @@ -0,0 +1,31 @@ +package com.cappielloantonio.play.viewmodel; + +import android.app.Application; + +import androidx.annotation.NonNull; +import androidx.lifecycle.AndroidViewModel; + +import com.cappielloantonio.play.repository.QueueRepository; + +public class MainViewModel extends AndroidViewModel { + private static final String TAG = "SearchViewModel"; + + private QueueRepository queueRepository; + + public MainViewModel(@NonNull Application application) { + super(application); + + queueRepository = new QueueRepository(application); + } + + public boolean isQueueLoaded() { + if(queueRepository.count() == 0) + return false; + + return true; + } + + public void deleteQueue(){ + queueRepository.deleteAll(); + } +} diff --git a/app/src/main/java/com/cappielloantonio/play/viewmodel/PlayerBottomSheetViewModel.java b/app/src/main/java/com/cappielloantonio/play/viewmodel/PlayerBottomSheetViewModel.java new file mode 100644 index 00000000..f76099ed --- /dev/null +++ b/app/src/main/java/com/cappielloantonio/play/viewmodel/PlayerBottomSheetViewModel.java @@ -0,0 +1,48 @@ +package com.cappielloantonio.play.viewmodel; + +import android.app.Application; + +import androidx.annotation.NonNull; +import androidx.lifecycle.AndroidViewModel; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; + +import com.cappielloantonio.play.model.Song; +import com.cappielloantonio.play.repository.QueueRepository; + +import java.util.List; + +public class PlayerBottomSheetViewModel extends AndroidViewModel { + private static final String TAG = "HomeViewModel"; + private QueueRepository queueRepository; + + private LiveData> queueSong; + private LiveData nowPlayingSong = new MutableLiveData<>(); + + + public PlayerBottomSheetViewModel(@NonNull Application application) { + super(application); + + queueRepository = new QueueRepository(application); + + queueSong = queueRepository.getLiveQueue(); + } + + public LiveData> getQueueSong() { + return queueSong; + } + + public LiveData getNowPlayingSong() { + return nowPlayingSong; + } + + public LiveData setNowPlayingSong(int position) { + Song song = queueRepository.getSongs().get(position); + + if(song != null) { + nowPlayingSong = new MutableLiveData<>(song); + } + + return nowPlayingSong; + } +} diff --git a/app/src/main/res/drawable/button_favorite_selector.xml b/app/src/main/res/drawable/button_favorite_selector.xml index 02918708..540ec2ee 100644 --- a/app/src/main/res/drawable/button_favorite_selector.xml +++ b/app/src/main/res/drawable/button_favorite_selector.xml @@ -1,5 +1,5 @@ - - + + \ No newline at end of file diff --git a/app/src/main/res/drawable/button_play_pause_selector.xml b/app/src/main/res/drawable/button_play_pause_selector.xml new file mode 100644 index 00000000..b1c594a0 --- /dev/null +++ b/app/src/main/res/drawable/button_play_pause_selector.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/gradient_backdrop_fading_down_background_image.xml b/app/src/main/res/drawable/gradient_backdrop_fading_down_background_image.xml deleted file mode 100644 index b16aca3a..00000000 --- a/app/src/main/res/drawable/gradient_backdrop_fading_down_background_image.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_collapse_arrow.xml b/app/src/main/res/drawable/ic_collapse_arrow.xml new file mode 100644 index 00000000..d3f7ee1f --- /dev/null +++ b/app/src/main/res/drawable/ic_collapse_arrow.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_drag_handle.xml b/app/src/main/res/drawable/ic_drag_handle.xml new file mode 100644 index 00000000..68166d4e --- /dev/null +++ b/app/src/main/res/drawable/ic_drag_handle.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_favorite.xml b/app/src/main/res/drawable/ic_favorite.xml new file mode 100644 index 00000000..af39c7c9 --- /dev/null +++ b/app/src/main/res/drawable/ic_favorite.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_favorites.xml b/app/src/main/res/drawable/ic_favorites_outlined.xml similarity index 92% rename from app/src/main/res/drawable/ic_favorites.xml rename to app/src/main/res/drawable/ic_favorites_outlined.xml index e6646709..5ce8a48a 100644 --- a/app/src/main/res/drawable/ic_favorites.xml +++ b/app/src/main/res/drawable/ic_favorites_outlined.xml @@ -4,6 +4,6 @@ android:viewportWidth="24" android:viewportHeight="24"> diff --git a/app/src/main/res/drawable/ic_pause_circle.xml b/app/src/main/res/drawable/ic_pause_circle.xml new file mode 100644 index 00000000..9d47cdd0 --- /dev/null +++ b/app/src/main/res/drawable/ic_pause_circle.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_play.xml b/app/src/main/res/drawable/ic_play.xml index 76d03c34..847df52c 100644 --- a/app/src/main/res/drawable/ic_play.xml +++ b/app/src/main/res/drawable/ic_play.xml @@ -4,6 +4,6 @@ android:viewportWidth="24" android:viewportHeight="24"> diff --git a/app/src/main/res/drawable/ic_play_circle.xml b/app/src/main/res/drawable/ic_play_circle.xml new file mode 100644 index 00000000..a8530bec --- /dev/null +++ b/app/src/main/res/drawable/ic_play_circle.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_skip_next.xml b/app/src/main/res/drawable/ic_skip_next.xml new file mode 100644 index 00000000..e39146d2 --- /dev/null +++ b/app/src/main/res/drawable/ic_skip_next.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_skip_previous.xml b/app/src/main/res/drawable/ic_skip_previous.xml new file mode 100644 index 00000000..76f33c0f --- /dev/null +++ b/app/src/main/res/drawable/ic_skip_previous.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 4ad7d5f8..87ba8603 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,50 +1,54 @@ - + android:orientation="vertical"> - + android:layout_height="0dp" + android:layout_weight="1"> + + + + + + + app:menu="@menu/bottom_nav_menu"/> - - - - - - \ No newline at end of file + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_album_catalogue.xml b/app/src/main/res/layout/fragment_album_catalogue.xml index 0877d2a4..ea91dfa4 100644 --- a/app/src/main/res/layout/fragment_album_catalogue.xml +++ b/app/src/main/res/layout/fragment_album_catalogue.xml @@ -3,8 +3,7 @@ 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="match_parent" - android:paddingTop="20dp"> + android:layout_height="match_parent"> + android:paddingBottom="@dimen/global_padding_bottom"> - + android:layout_height="match_parent"> - + android:layout_height="@dimen/appbar_header_height" + android:fitsSystemWindows="true" + android:theme="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar"> - - - - - - - - - - - - - - - - - - - - - - + android:fitsSystemWindows="true" + app:contentScrim="?attr/colorPrimary" + app:expandedTitleMarginStart="@dimen/activity_margin_content" + app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"> + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_artist_catalogue.xml b/app/src/main/res/layout/fragment_artist_catalogue.xml index 620131c0..22f029eb 100644 --- a/app/src/main/res/layout/fragment_artist_catalogue.xml +++ b/app/src/main/res/layout/fragment_artist_catalogue.xml @@ -1,11 +1,9 @@ - + android:layout_height="match_parent"> + android:paddingBottom="@dimen/global_padding_bottom"> - + android:layout_height="match_parent"> - + + + + + + + + + + + + + + android:fillViewport="true" + android:paddingBottom="@dimen/global_padding_bottom" + app:layout_behavior="@string/appbar_scrolling_view_behavior"> - - - - - - - - - - - - - - - - + android:clipToPadding="false" + android:paddingTop="18dp" + android:orientation="vertical"> + + + + + + + + + + - - + android:paddingBottom="8dp" /> - - - - - - - - + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_filter.xml b/app/src/main/res/layout/fragment_filter.xml index fb697569..ccf60c7e 100644 --- a/app/src/main/res/layout/fragment_filter.xml +++ b/app/src/main/res/layout/fragment_filter.xml @@ -3,8 +3,7 @@ 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="match_parent" - android:paddingTop="20dp"> + android:layout_height="match_parent"> + android:visibility="gone"> + android:paddingBottom="@dimen/global_padding_bottom"> + android:layout_height="match_parent"> + android:paddingBottom="@dimen/global_padding_bottom"> + app:layout_behavior="@string/hide_bottom_view_on_scroll_behavior"> + android:paddingBottom="@dimen/global_padding_bottom"> @@ -297,7 +296,7 @@ android:paddingStart="16dp" android:paddingTop="8dp" android:paddingEnd="8dp" - android:paddingBottom="24dp" /> + android:paddingBottom="8dp" /> diff --git a/app/src/main/res/layout/fragment_library.xml b/app/src/main/res/layout/fragment_library.xml index d8b27a8f..da765d7b 100644 --- a/app/src/main/res/layout/fragment_library.xml +++ b/app/src/main/res/layout/fragment_library.xml @@ -4,13 +4,13 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" - app:layout_behavior="@string/hide_bottom_view_on_scroll_behavior" - android:paddingTop="20dp"> + app:layout_behavior="@string/hide_bottom_view_on_scroll_behavior"> + android:orientation="vertical" + android:paddingBottom="@dimen/global_padding_bottom"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_search.xml b/app/src/main/res/layout/fragment_search.xml index a961f0e5..c9ef3d98 100644 --- a/app/src/main/res/layout/fragment_search.xml +++ b/app/src/main/res/layout/fragment_search.xml @@ -2,8 +2,7 @@ + android:layout_height="match_parent"> + android:visibility="gone" + android:paddingBottom="@dimen/global_padding_bottom"> + android:paddingTop="20dp" + android:paddingBottom="@dimen/global_padding_bottom"> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_song_list_page.xml b/app/src/main/res/layout/fragment_song_list_page.xml index a0ac5fec..06137f2a 100644 --- a/app/src/main/res/layout/fragment_song_list_page.xml +++ b/app/src/main/res/layout/fragment_song_list_page.xml @@ -2,8 +2,7 @@ + android:layout_height="wrap_content"> + android:paddingBottom="@dimen/global_padding_bottom"/> diff --git a/app/src/main/res/layout/fragment_sync.xml b/app/src/main/res/layout/fragment_sync.xml index d305dd60..985dc34e 100644 --- a/app/src/main/res/layout/fragment_sync.xml +++ b/app/src/main/res/layout/fragment_sync.xml @@ -2,8 +2,7 @@ + android:layout_height="match_parent"> + android:layout_height="196dp" /> + android:layout_height="196dp"> diff --git a/app/src/main/res/layout/item_library_artist.xml b/app/src/main/res/layout/item_library_artist.xml index 26bba280..1cbd7eb5 100644 --- a/app/src/main/res/layout/item_library_artist.xml +++ b/app/src/main/res/layout/item_library_artist.xml @@ -1,7 +1,7 @@ diff --git a/app/src/main/res/layout/item_player_now_playing_song.xml b/app/src/main/res/layout/item_player_now_playing_song.xml new file mode 100644 index 00000000..54d28e6d --- /dev/null +++ b/app/src/main/res/layout/item_player_now_playing_song.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_player_queue_song.xml b/app/src/main/res/layout/item_player_queue_song.xml new file mode 100644 index 00000000..1dbe4c97 --- /dev/null +++ b/app/src/main/res/layout/item_player_queue_song.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/player_body_bottom_sheet.xml b/app/src/main/res/layout/player_body_bottom_sheet.xml new file mode 100644 index 00000000..27917085 --- /dev/null +++ b/app/src/main/res/layout/player_body_bottom_sheet.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/player_header_bottom_sheet.xml b/app/src/main/res/layout/player_header_bottom_sheet.xml new file mode 100644 index 00000000..270f1be8 --- /dev/null +++ b/app/src/main/res/layout/player_header_bottom_sheet.xml @@ -0,0 +1,45 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/nav_graph_home.xml b/app/src/main/res/navigation/nav_graph_home.xml deleted file mode 100644 index d6dae682..00000000 --- a/app/src/main/res/navigation/nav_graph_home.xml +++ /dev/null @@ -1,77 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/navigation/nav_graph_library.xml b/app/src/main/res/navigation/nav_graph_library.xml deleted file mode 100644 index b8287428..00000000 --- a/app/src/main/res/navigation/nav_graph_library.xml +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/navigation/nav_graph_search.xml b/app/src/main/res/navigation/nav_graph_search.xml deleted file mode 100644 index f068511d..00000000 --- a/app/src/main/res/navigation/nav_graph_search.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/navigation/nav_graph_settings.xml b/app/src/main/res/navigation/nav_graph_settings.xml deleted file mode 100644 index a4795e86..00000000 --- a/app/src/main/res/navigation/nav_graph_settings.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml index 37392ab2..78b5bd8e 100644 --- a/app/src/main/res/values-night/colors.xml +++ b/app/src/main/res/values-night/colors.xml @@ -11,6 +11,7 @@ #121211 #1D1D1D + #1D1D1C #DADADA #9B9B9B diff --git a/app/src/main/res/values-night/styles.xml b/app/src/main/res/values-night/styles.xml index c111c32a..112b50e8 100644 --- a/app/src/main/res/values-night/styles.xml +++ b/app/src/main/res/values-night/styles.xml @@ -22,4 +22,10 @@ 82% 82% + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index a41a14a0..d427559a 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -14,6 +14,7 @@ #FFFFFF #FFFFFF + #FFFFFE #252525 #646464 @@ -41,4 +42,6 @@ #AFAFAF #EBEBEB #FFFFFF + + #FFFFFF \ No newline at end of file diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 4ab4520f..96d326fc 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -5,4 +5,11 @@ 8dp 176dp 16dp + + 384dp + 24dp + + 56dp + + 64dp \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 484c84c3..60e57d1c 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -21,4 +21,10 @@ 82% 82% + + \ No newline at end of file diff --git a/build.gradle b/build.gradle index 02ccbfba..5e513806 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:4.1.0' + classpath 'com.android.tools.build:gradle:4.1.1' classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.10' // NOTE: Do not place your application dependencies here; they belong