diff --git a/.idea/misc.xml b/.idea/misc.xml index 8d1cf2da..33b2e19b 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -77,6 +77,8 @@ + + @@ -112,7 +114,7 @@ - + diff --git a/app/build.gradle b/app/build.gradle index 82f75f2c..4cc0ef5a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -66,6 +66,7 @@ dependencies { // Exoplayer implementation 'com.google.android.exoplayer:exoplayer:2.12.2' + implementation 'androidx.legacy:legacy-support-v4:1.0.0' annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0' annotationProcessor 'androidx.room:room-compiler:2.4.0' diff --git a/app/src/main/java/com/cappielloantonio/play/adapter/PlayerNowPlayingSongAdapter.java b/app/src/main/java/com/cappielloantonio/play/adapter/PlayerNowPlayingSongAdapter.java index f02c5c20..acedcbc3 100644 --- a/app/src/main/java/com/cappielloantonio/play/adapter/PlayerNowPlayingSongAdapter.java +++ b/app/src/main/java/com/cappielloantonio/play/adapter/PlayerNowPlayingSongAdapter.java @@ -1,91 +1,34 @@ 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.annotation.NonNull; -import androidx.recyclerview.widget.RecyclerView; +import androidx.fragment.app.Fragment; +import androidx.viewpager2.adapter.FragmentStateAdapter; -import com.bumptech.glide.load.resource.bitmap.RoundedCorners; -import com.cappielloantonio.play.R; -import com.cappielloantonio.play.glide.CustomGlideRequest; -import com.cappielloantonio.play.model.Song; -import com.cappielloantonio.play.service.MusicPlayerRemote; +import com.cappielloantonio.play.ui.fragment.PlayerCoverFragment; +import com.cappielloantonio.play.ui.fragment.PlayerLyricsFragment; -import java.util.ArrayList; -import java.util.List; +public class PlayerNowPlayingSongAdapter extends FragmentStateAdapter { + private static final String TAG = "PlayerNowPlayingSongInfoAdapter"; -public class PlayerNowPlayingSongAdapter extends RecyclerView.Adapter { - private static final String TAG = "PlayerNowPlayingSongAdapter"; - - private final LayoutInflater inflater; - private final Context context; - - private List songs; - - public PlayerNowPlayingSongAdapter(Context context) { - this.context = context; - this.inflater = LayoutInflater.from(context); - this.songs = new ArrayList<>(); + public PlayerNowPlayingSongAdapter(@NonNull Fragment fragment) { + super(fragment); } @NonNull @Override - public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - View view = inflater.inflate(R.layout.item_player_now_playing_song, parent, false); - return new ViewHolder(view); - } + public Fragment createFragment(int position) { + switch (position) { + case 0: + return new PlayerCoverFragment(); + case 1: + return new PlayerLyricsFragment(); + } - @Override - public void onBindViewHolder(ViewHolder holder, int position) { - Song song = songs.get(position); - - CustomGlideRequest.Builder - .from(context, song.getId(), CustomGlideRequest.SONG_PIC, null) - .build() - .transform(new RoundedCorners(CustomGlideRequest.CORNER_RADIUS)) - .into(holder.cover); + return new PlayerCoverFragment(); } @Override public int getItemCount() { - return songs.size(); - } - - public Song getItem(int position) { - try { - return songs.get(position); - } catch (IndexOutOfBoundsException e) { - return null; - } - } - - public void setItems(List songs) { - this.songs = songs; - notifyDataSetChanged(); - } - - public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { - ImageView cover; - - ViewHolder(View itemView) { - super(itemView); - - cover = itemView.findViewById(R.id.now_playing_song_cover_image_view); - - itemView.setOnClickListener(this); - } - - @Override - public void onClick(View view) { - if (MusicPlayerRemote.isPlaying()) { - MusicPlayerRemote.pauseSong(); - } else { - MusicPlayerRemote.resumePlaying(); - } - } + return 2; } } \ 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 index 9dc4244d..99d94a70 100644 --- a/app/src/main/java/com/cappielloantonio/play/adapter/PlayerSongQueueAdapter.java +++ b/app/src/main/java/com/cappielloantonio/play/adapter/PlayerSongQueueAdapter.java @@ -101,7 +101,7 @@ public class PlayerSongQueueAdapter extends RecyclerView.Adapter activity.collapseBottomSheet()); } - private void initLyricsView() { - /*playerBottomSheetViewModel.getLyrics().observe(requireActivity(), lyrics -> { - if (lyrics != null && !lyrics.trim().equals("")) { - bodyBind.playerSongLyricsCardview.setVisibility(View.VISIBLE); - } else { - bodyBind.playerSongLyricsCardview.setVisibility(View.GONE); - } - - bodyBind.playerSongLyricsTextView.setText(MusicUtil.getReadableString(lyrics)); - }); - - bodyBind.playerSongLyricsLabelClickable.setOnClickListener(view -> { - if (bodyBind.playerSongLyricsTextView.getVisibility() == View.INVISIBLE || bodyBind.playerSongLyricsTextView.getVisibility() == View.GONE) { - setLyricsTextViewVisibility(true); - } else { - setLyricsTextViewVisibility(false); - } - });*/ - } - - private void initQueueSlideView() { + private void initCoverLyricsSlideView() { bodyBind.playerSongCoverViewPager.setOrientation(ViewPager2.ORIENTATION_HORIZONTAL); - - playerNowPlayingSongAdapter = new PlayerNowPlayingSongAdapter(requireContext()); - bodyBind.playerSongCoverViewPager.setAdapter(playerNowPlayingSongAdapter); - playerBottomSheetViewModel.getQueueSong().observe(requireActivity(), queue -> playerNowPlayingSongAdapter.setItems(MappingUtil.mapQueue(queue))); - - bodyBind.playerSongCoverViewPager.setOffscreenPageLimit(3); - bodyBind.playerSongCoverViewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() { - // 0 = IDLE - // 1 = DRAGGING - // 2 = SETTLING - // -1 = NEW - int pageState = -1; - - @Override - public void onPageScrollStateChanged(int state) { - super.onPageScrollStateChanged(state); - pageState = state; - } - - @Override - public void onPageSelected(int position) { - super.onPageSelected(position); - - if (pageState != -1) { - MusicPlayerRemote.playSongAt(position); - pageState = -1; - } - } - }); - - setViewPageDelayed(PreferenceUtil.getInstance(requireContext()).getPosition()); + bodyBind.playerSongCoverViewPager.setAdapter(new PlayerNowPlayingSongAdapter(this)); } private void initQueueRecyclerView() { @@ -326,17 +275,8 @@ public class PlayerBottomSheetFragment extends Fragment implements MusicServiceE }); } - private void setViewPageDelayed(int position) { - final Handler handler = new Handler(); - final Runnable r = () -> { - if (bind != null) bodyBind.playerSongCoverViewPager.setCurrentItem(position, false); - }; - handler.postDelayed(r, 100); - } - - private void setSongInfo(Song song) { - // setLyricsTextViewVisibility(false); - playerBottomSheetViewModel.refreshSongLyrics(requireActivity(), song); + public void setSongInfo(Song song) { + playerBottomSheetViewModel.refreshSongInfo(requireActivity(), song); bodyBind.playerSongTitleLabel.setText(MusicUtil.getReadableString(song.getTitle())); bodyBind.playerArtistNameLabel.setText(MusicUtil.getReadableString(song.getArtistName())); @@ -354,7 +294,7 @@ public class PlayerBottomSheetFragment extends Fragment implements MusicServiceE } public void setPlayerCommandViewVisibility(boolean isVisible) { - if(isVisible) { + if (isVisible) { bodyBind.playerCommandCardview.setVisibility(View.VISIBLE); } else { bodyBind.playerCommandCardview.setVisibility(View.GONE); @@ -377,11 +317,6 @@ public class PlayerBottomSheetFragment extends Fragment implements MusicServiceE bind.playerNestedScrollView.fullScroll(ScrollView.FOCUS_UP); } - public void scrollPager(Song song, int page, boolean smoothScroll) { - bodyBind.playerSongCoverViewPager.setCurrentItem(page, smoothScroll); - setSongInfo(song); - } - @Override public void onServiceConnected() { setSongInfo(Objects.requireNonNull(MusicPlayerRemote.getCurrentSong())); @@ -400,7 +335,6 @@ public class PlayerBottomSheetFragment extends Fragment implements MusicServiceE @Override public void onPlayMetadataChanged() { - setViewPageDelayed(MusicPlayerRemote.getPosition()); setSongInfo(Objects.requireNonNull(MusicPlayerRemote.getCurrentSong())); } diff --git a/app/src/main/java/com/cappielloantonio/play/ui/fragment/PlayerCoverFragment.java b/app/src/main/java/com/cappielloantonio/play/ui/fragment/PlayerCoverFragment.java new file mode 100644 index 00000000..743ed579 --- /dev/null +++ b/app/src/main/java/com/cappielloantonio/play/ui/fragment/PlayerCoverFragment.java @@ -0,0 +1,51 @@ +package com.cappielloantonio.play.ui.fragment; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; +import androidx.lifecycle.ViewModelProvider; + +import com.bumptech.glide.load.resource.bitmap.RoundedCorners; +import com.cappielloantonio.play.databinding.FragmentPlayerCoverBinding; +import com.cappielloantonio.play.glide.CustomGlideRequest; +import com.cappielloantonio.play.viewmodel.PlayerBottomSheetViewModel; + +public class PlayerCoverFragment extends Fragment { + private static final String TAG = "PlayerCoverFragment"; + + private FragmentPlayerCoverBinding bind; + private PlayerBottomSheetViewModel playerBottomSheetViewModel; + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + bind = FragmentPlayerCoverBinding.inflate(inflater, container, false); + View view = bind.getRoot(); + playerBottomSheetViewModel = new ViewModelProvider(requireActivity()).get(PlayerBottomSheetViewModel.class); + + init(); + + return view; + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + bind = null; + } + + private void init() { + playerBottomSheetViewModel.getLiveSong().observe(requireActivity(), song -> { + if (song != null) { + CustomGlideRequest.Builder + .from(requireContext(), song.getId(), CustomGlideRequest.SONG_PIC, null) + .build() + .transform(new RoundedCorners(CustomGlideRequest.CORNER_RADIUS)) + .into(bind.nowPlayingSongCoverImageView); + } + }); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/cappielloantonio/play/ui/fragment/PlayerLyricsFragment.java b/app/src/main/java/com/cappielloantonio/play/ui/fragment/PlayerLyricsFragment.java new file mode 100644 index 00000000..819a4170 --- /dev/null +++ b/app/src/main/java/com/cappielloantonio/play/ui/fragment/PlayerLyricsFragment.java @@ -0,0 +1,52 @@ +package com.cappielloantonio.play.ui.fragment; + +import android.annotation.SuppressLint; +import android.os.Bundle; +import android.text.method.ScrollingMovementMethod; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.lifecycle.ViewModelProvider; + +import com.cappielloantonio.play.R; +import com.cappielloantonio.play.databinding.FragmentPlayerLyricsBinding; +import com.cappielloantonio.play.util.MusicUtil; +import com.cappielloantonio.play.viewmodel.PlayerBottomSheetViewModel; + +public class PlayerLyricsFragment extends Fragment { + private static final String TAG = "PlayerLyricsFragment"; + + private FragmentPlayerLyricsBinding bind; + private PlayerBottomSheetViewModel playerBottomSheetViewModel; + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + bind = FragmentPlayerLyricsBinding.inflate(inflater, container, false); + View view = bind.getRoot(); + playerBottomSheetViewModel = new ViewModelProvider(requireActivity()).get(PlayerBottomSheetViewModel.class); + + initLyrics(); + + return view; + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + bind = null; + } + + private void initLyrics() { + playerBottomSheetViewModel.getLiveLyrics().observe(requireActivity(), lyrics -> { + if (lyrics == null || lyrics.trim().equals("")) { + bind.nowPlayingSongLyricsTextView.setText(R.string.player_song_lyrics_none_available_label); + } else { + bind.nowPlayingSongLyricsTextView.setText(MusicUtil.getReadableString(lyrics)); + } + }); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/cappielloantonio/play/viewmodel/PlayerBottomSheetViewModel.java b/app/src/main/java/com/cappielloantonio/play/viewmodel/PlayerBottomSheetViewModel.java index 06ab7b4e..f43ebac0 100644 --- a/app/src/main/java/com/cappielloantonio/play/viewmodel/PlayerBottomSheetViewModel.java +++ b/app/src/main/java/com/cappielloantonio/play/viewmodel/PlayerBottomSheetViewModel.java @@ -29,7 +29,9 @@ public class PlayerBottomSheetViewModel extends AndroidViewModel { private final ArtistRepository artistRepository; private final QueueRepository queueRepository; - private final MutableLiveData songLyrics = new MutableLiveData<>(null); + private final MutableLiveData lyricsLiveData = new MutableLiveData<>(null); + private final MutableLiveData songLiveData = new MutableLiveData<>(null); + private final LiveData> queueSong; public PlayerBottomSheetViewModel(@NonNull Application application) { @@ -81,11 +83,17 @@ public class PlayerBottomSheetViewModel extends AndroidViewModel { return artistRepository.getArtist(song.getArtistId()); } - public LiveData getLyrics() { - return songLyrics; + public LiveData getLiveSong() { + return songLiveData; } - public void refreshSongLyrics(LifecycleOwner owner, Song song) { - songRepository.getSongLyrics(song).observe(owner, songLyrics::postValue); + public LiveData getLiveLyrics() { + return lyricsLiveData; } + + public void refreshSongInfo(LifecycleOwner owner, Song song) { + songLiveData.postValue(song); + songRepository.getSongLyrics(song).observe(owner, lyricsLiveData::postValue); + } + } diff --git a/app/src/main/res/layout/fragment_player_cover.xml b/app/src/main/res/layout/fragment_player_cover.xml new file mode 100644 index 00000000..e0bf3ebe --- /dev/null +++ b/app/src/main/res/layout/fragment_player_cover.xml @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_player_lyrics.xml b/app/src/main/res/layout/fragment_player_lyrics.xml new file mode 100644 index 00000000..01aa9bd2 --- /dev/null +++ b/app/src/main/res/layout/fragment_player_lyrics.xml @@ -0,0 +1,12 @@ + + + + \ 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 index b12b9143..dc7d7ba0 100644 --- a/app/src/main/res/layout/player_body_bottom_sheet.xml +++ b/app/src/main/res/layout/player_body_bottom_sheet.xml @@ -2,6 +2,7 @@ #DADADA #CFCFCF #CCCCCC - #080808 + #404040 #CFCFCF #1D1D1D diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 08e45380..91b08027 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -114,6 +114,7 @@ Hide Show Lyrics + No lyrics available The playing notification provides actions for play/pause etc. Playing Notification Playlist Catalogue diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index a0a07bb5..f0262d0d 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -129,6 +129,12 @@ @color/titleTextColor + +