Add experimental bottom sheet player

This commit is contained in:
Antonio Cappiello 2020-12-05 21:31:12 +01:00
parent 9af0afa441
commit f837bb14e2
65 changed files with 1570 additions and 547 deletions

View file

@ -0,0 +1,76 @@
package com.cappielloantonio.play.adapter;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.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<PlayerNowPlayingSongAdapter.ViewHolder> {
private static final String TAG = "DiscoverSongAdapter";
private List<Song> 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<Song> songs) {
this.songs = songs;
notifyDataSetChanged();
}
}

View file

@ -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<PlayerSongQueueAdapter.ViewHolder> {
private static final String TAG = "SongResultSearchAdapter";
private List<Song> 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<Song> songs) {
this.songs = songs;
notifyDataSetChanged();
}
public Song getItem(int id) {
return songs.get(id);
}
}

View file

@ -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<RecentMusicAdapter.
private List<Song> 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<RecentMusicAdapter.
@Override
public void onClick(View view) {
SongRepository songRepository = new SongRepository(App.getInstance());
QueueRepository queueRepository = new QueueRepository(App.getInstance());
songRepository.increasePlayCount(songs.get(getAdapterPosition()));
queueRepository.insertAllAndStartNew(songs.subList(getAdapterPosition(), songs.size()));
mainActivity.isBottomSheetInPeek(true);
}
@Override

View file

@ -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 com.cappielloantonio.play.util.Util;
import java.util.ArrayList;
@ -30,10 +32,12 @@ public class SongResultSearchAdapter extends RecyclerView.Adapter<SongResultSear
private List<Song> 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<SongResultSear
@Override
public void onClick(View view) {
SongRepository songRepository = new SongRepository(App.getInstance());
QueueRepository queueRepository = new QueueRepository(App.getInstance());
songRepository.increasePlayCount(songs.get(getAdapterPosition()));
queueRepository.insertAllAndStartNew(songs.subList(getAdapterPosition(), songs.size()));
mainActivity.isBottomSheetInPeek(true);
}
@Override
@ -106,5 +115,4 @@ public class SongResultSearchAdapter extends RecyclerView.Adapter<SongResultSear
public Song getItem(int id) {
return songs.get(id);
}
}

View file

@ -21,9 +21,9 @@ public class ConnectivityStatusBroadcastReceiver extends BroadcastReceiver {
boolean noConnectivity = intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
if (noConnectivity) {
activity.activityMainBinding.offlineModeLinearLayout.setVisibility(View.VISIBLE);
activity.bind.offlineModeTextView.setVisibility(View.VISIBLE);
} else {
activity.activityMainBinding.offlineModeLinearLayout.setVisibility(View.GONE);
activity.bind.offlineModeTextView.setVisibility(View.GONE);
}
}
}

View file

@ -10,6 +10,7 @@ import com.cappielloantonio.play.database.dao.AlbumDao;
import com.cappielloantonio.play.database.dao.ArtistDao;
import com.cappielloantonio.play.database.dao.GenreDao;
import com.cappielloantonio.play.database.dao.PlaylistDao;
import com.cappielloantonio.play.database.dao.QueueDao;
import com.cappielloantonio.play.database.dao.RecentSearchDao;
import com.cappielloantonio.play.database.dao.SongDao;
import com.cappielloantonio.play.database.dao.SongGenreCrossDao;
@ -17,11 +18,12 @@ import com.cappielloantonio.play.model.Album;
import com.cappielloantonio.play.model.Artist;
import com.cappielloantonio.play.model.Genre;
import com.cappielloantonio.play.model.Playlist;
import com.cappielloantonio.play.model.Queue;
import com.cappielloantonio.play.model.RecentSearch;
import com.cappielloantonio.play.model.Song;
import com.cappielloantonio.play.model.SongGenreCross;
@Database(entities = {Album.class, Artist.class, Genre.class, Playlist.class, Song.class, RecentSearch.class, SongGenreCross.class}, version = 5, exportSchema = false)
@Database(entities = {Album.class, Artist.class, Genre.class, Playlist.class, Song.class, RecentSearch.class, SongGenreCross.class, Queue.class}, version = 6, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
private static final String TAG = "AppDatabase";
@ -51,4 +53,6 @@ public abstract class AppDatabase extends RoomDatabase {
public abstract RecentSearchDao recentSearchDao();
public abstract SongGenreCrossDao songGenreCrossDao();
public abstract QueueDao queueDao();
}

View file

@ -0,0 +1,37 @@
package com.cappielloantonio.play.database.dao;
import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import androidx.room.Query;
import com.cappielloantonio.play.model.Queue;
import com.cappielloantonio.play.model.Song;
import java.util.List;
@Dao
public interface QueueDao {
@Query("SELECT * FROM song JOIN queue ON song.id = queue.song_id")
LiveData<List<Song>> getAll();
@Query("SELECT * FROM song JOIN queue ON song.id = queue.song_id")
List<Song> getAllSimple();
@Insert(onConflict = OnConflictStrategy.REPLACE)
void insert(Queue songQueueObject);
@Insert(onConflict = OnConflictStrategy.REPLACE)
void insertAll(List<Queue> songQueueObject);
@Delete
void delete(Queue songQueueObject);
@Query("DELETE FROM queue")
void deleteAll();
@Query("SELECT COUNT(*) FROM queue;")
int count();
}

View file

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

View file

@ -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<List<Song>> listLiveQueue;
public QueueRepository(Application application) {
AppDatabase database = AppDatabase.getInstance(application);
queueDao = database.queueDao();
}
public LiveData<List<Song>> getLiveQueue() {
listLiveQueue = queueDao.getAll();
return listLiveQueue;
}
public List<Song> getSongs() {
List<Song> 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<Song> songs) {
InsertAllThreadSafe insertAll = new InsertAllThreadSafe(queueDao, songs);
Thread thread = new Thread(insertAll);
thread.start();
}
public void insertAllAndStartNew(List<Song> 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<Song> songs;
public InsertAllThreadSafe(QueueDao queueDao, List<Song> 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<Song> songs;
public GetSongsThreadSafe(QueueDao queueDao) {
this.queueDao = queueDao;
}
@Override
public void run() {
songs = queueDao.getAllSimple();
}
public List<Song> getSongs() {
return songs;
}
}
}

View file

@ -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();
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -58,7 +58,7 @@ public class PreferenceUtil {
}
public String getToken() {
return mPreferences.getString(TOKEN, "");
return mPreferences.getString(TOKEN, null);
}
public void setToken(String token) {

View file

@ -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<Queue> getQueueElementsFromSongs(List<Song> songs) {
List<Queue> queue = new ArrayList<>();
for(Song song: songs) {
queue.add(new Queue(song.getId()));
}
return queue;
}
}

View file

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

View file

@ -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();
}
}

View file

@ -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<List<Song>> queueSong;
private LiveData<Song> nowPlayingSong = new MutableLiveData<>();
public PlayerBottomSheetViewModel(@NonNull Application application) {
super(application);
queueRepository = new QueueRepository(application);
queueSong = queueRepository.getLiveQueue();
}
public LiveData<List<Song>> getQueueSong() {
return queueSong;
}
public LiveData<Song> getNowPlayingSong() {
return nowPlayingSong;
}
public LiveData<Song> setNowPlayingSong(int position) {
Song song = queueRepository.getSongs().get(position);
if(song != null) {
nowPlayingSong = new MutableLiveData<>(song);
}
return nowPlayingSong;
}
}