mirror of
https://github.com/antebudimir/tempus.git
synced 2025-12-31 17:43:32 +00:00
First song played: What she said - The Smiths
This commit is contained in:
parent
820f783d01
commit
c1c02b3c37
14 changed files with 787 additions and 109 deletions
|
|
@ -23,7 +23,6 @@
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<service android:name=".service.AudioDownloadService"/>
|
<service android:name=".service.MusicService" android:enabled="true"/>
|
||||||
<service android:name=".service.AudioPlayerService"/>
|
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|
@ -57,7 +57,7 @@ public class PlayerNowPlayingSongAdapter extends RecyclerView.Adapter<PlayerNowP
|
||||||
ViewHolder(View itemView) {
|
ViewHolder(View itemView) {
|
||||||
super(itemView);
|
super(itemView);
|
||||||
|
|
||||||
cover = itemView.findViewById(R.id.discover_song_cover_image_view);
|
cover = itemView.findViewById(R.id.now_playing_song_cover_image_view);
|
||||||
|
|
||||||
itemView.setOnClickListener(this);
|
itemView.setOnClickListener(this);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import androidx.recyclerview.widget.RecyclerView;
|
||||||
import com.cappielloantonio.play.App;
|
import com.cappielloantonio.play.App;
|
||||||
import com.cappielloantonio.play.R;
|
import com.cappielloantonio.play.R;
|
||||||
import com.cappielloantonio.play.glide.CustomGlideRequest;
|
import com.cappielloantonio.play.glide.CustomGlideRequest;
|
||||||
|
import com.cappielloantonio.play.helper.MusicPlayerRemote;
|
||||||
import com.cappielloantonio.play.model.Song;
|
import com.cappielloantonio.play.model.Song;
|
||||||
import com.cappielloantonio.play.repository.QueueRepository;
|
import com.cappielloantonio.play.repository.QueueRepository;
|
||||||
import com.cappielloantonio.play.repository.SongRepository;
|
import com.cappielloantonio.play.repository.SongRepository;
|
||||||
|
|
@ -98,6 +99,8 @@ public class RecentMusicAdapter extends RecyclerView.Adapter<RecentMusicAdapter.
|
||||||
|
|
||||||
PlayerBottomSheetViewModel playerBottomSheetViewModel = new ViewModelProvider(mainActivity).get(PlayerBottomSheetViewModel.class);
|
PlayerBottomSheetViewModel playerBottomSheetViewModel = new ViewModelProvider(mainActivity).get(PlayerBottomSheetViewModel.class);
|
||||||
playerBottomSheetViewModel.setNowPlayingSong(songs.get(getAdapterPosition()));
|
playerBottomSheetViewModel.setNowPlayingSong(songs.get(getAdapterPosition()));
|
||||||
|
|
||||||
|
MusicPlayerRemote.openQueue(songs.subList(getAdapterPosition(), songs.size()), getAdapterPosition(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,337 @@
|
||||||
|
package com.cappielloantonio.play.helper;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.ContextWrapper;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.ServiceConnection;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.cappielloantonio.play.R;
|
||||||
|
import com.cappielloantonio.play.model.Song;
|
||||||
|
import com.cappielloantonio.play.service.MusicService;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.WeakHashMap;
|
||||||
|
|
||||||
|
public class MusicPlayerRemote {
|
||||||
|
public static MusicService musicService;
|
||||||
|
|
||||||
|
private static final WeakHashMap<Context, ServiceBinder> mConnectionMap = new WeakHashMap<>();
|
||||||
|
|
||||||
|
public static ServiceToken bindToService(@NonNull final Context context, final ServiceConnection callback) {
|
||||||
|
Activity realActivity = ((Activity) context).getParent();
|
||||||
|
if (realActivity == null) {
|
||||||
|
realActivity = (Activity) context;
|
||||||
|
}
|
||||||
|
|
||||||
|
final ContextWrapper contextWrapper = new ContextWrapper(realActivity);
|
||||||
|
contextWrapper.startService(new Intent(contextWrapper, MusicService.class));
|
||||||
|
|
||||||
|
final ServiceBinder binder = new ServiceBinder(callback);
|
||||||
|
|
||||||
|
if (contextWrapper.bindService(new Intent().setClass(contextWrapper, MusicService.class), binder, Context.BIND_AUTO_CREATE)) {
|
||||||
|
mConnectionMap.put(contextWrapper, binder);
|
||||||
|
return new ServiceToken(contextWrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void unbindFromService(@Nullable final ServiceToken token) {
|
||||||
|
if (token == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final ContextWrapper mContextWrapper = token.mWrappedContext;
|
||||||
|
final ServiceBinder mBinder = mConnectionMap.remove(mContextWrapper);
|
||||||
|
if (mBinder == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mContextWrapper.unbindService(mBinder);
|
||||||
|
if (mConnectionMap.isEmpty()) {
|
||||||
|
musicService = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class ServiceBinder implements ServiceConnection {
|
||||||
|
private final ServiceConnection mCallback;
|
||||||
|
|
||||||
|
public ServiceBinder(final ServiceConnection callback) {
|
||||||
|
mCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onServiceConnected(final ComponentName className, final IBinder service) {
|
||||||
|
MusicService.MusicBinder binder = (MusicService.MusicBinder) service;
|
||||||
|
musicService = binder.getService();
|
||||||
|
if (mCallback != null) {
|
||||||
|
mCallback.onServiceConnected(className, service);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onServiceDisconnected(final ComponentName className) {
|
||||||
|
if (mCallback != null) {
|
||||||
|
mCallback.onServiceDisconnected(className);
|
||||||
|
}
|
||||||
|
|
||||||
|
musicService = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class ServiceToken {
|
||||||
|
public ContextWrapper mWrappedContext;
|
||||||
|
|
||||||
|
public ServiceToken(final ContextWrapper context) {
|
||||||
|
mWrappedContext = context;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void playSongAt(final int position) {
|
||||||
|
if (musicService != null) {
|
||||||
|
musicService.playSongAt(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setPosition(final int position) {
|
||||||
|
if (musicService != null) {
|
||||||
|
musicService.setPosition(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void pauseSong() {
|
||||||
|
if (musicService != null) {
|
||||||
|
musicService.pause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void playNextSong() {
|
||||||
|
if (musicService != null) {
|
||||||
|
musicService.playNextSong(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void playPreviousSong() {
|
||||||
|
if (musicService != null) {
|
||||||
|
musicService.playPreviousSong(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void back() {
|
||||||
|
if (musicService != null) {
|
||||||
|
musicService.back(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isPlaying() {
|
||||||
|
return musicService != null && musicService.isPlaying();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void resumePlaying() {
|
||||||
|
if (musicService != null) {
|
||||||
|
musicService.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void openQueue(final List<Song> queue, final int startPosition, final boolean startPlaying) {
|
||||||
|
if (!tryToHandleOpenPlayingQueue(queue, startPosition, startPlaying) && musicService != null) {
|
||||||
|
musicService.openQueue(queue, startPosition, startPlaying);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void openAndShuffleQueue(final List<Song> queue, boolean startPlaying) {
|
||||||
|
int startPosition = 0;
|
||||||
|
if (!queue.isEmpty()) {
|
||||||
|
startPosition = new Random().nextInt(queue.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tryToHandleOpenPlayingQueue(queue, startPosition, startPlaying) && musicService != null) {
|
||||||
|
openQueue(queue, startPosition, startPlaying);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean tryToHandleOpenPlayingQueue(final List<Song> queue, final int startPosition, final boolean startPlaying) {
|
||||||
|
if (getPlayingQueue() == queue) {
|
||||||
|
if (startPlaying) {
|
||||||
|
playSongAt(startPosition);
|
||||||
|
} else {
|
||||||
|
setPosition(startPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Song getCurrentSong() {
|
||||||
|
return musicService.getCurrentSong();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getPosition() {
|
||||||
|
if (musicService != null) {
|
||||||
|
return musicService.getPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Song> getPlayingQueue() {
|
||||||
|
if (musicService != null) {
|
||||||
|
return musicService.getPlayingQueue();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getSongProgressMillis() {
|
||||||
|
if (musicService != null) {
|
||||||
|
return musicService.getSongProgressMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getSongDurationMillis() {
|
||||||
|
if (musicService != null) {
|
||||||
|
return musicService.getSongDurationMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long getQueueDurationMillis(int position) {
|
||||||
|
if (musicService != null) {
|
||||||
|
return musicService.getQueueDurationMillis(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int seekTo(int millis) {
|
||||||
|
if (musicService != null) {
|
||||||
|
return musicService.seek(millis);
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getRepeatMode() {
|
||||||
|
if (musicService != null) {
|
||||||
|
return musicService.getRepeatMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
return MusicService.REPEAT_MODE_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean cycleRepeatMode() {
|
||||||
|
if (musicService != null) {
|
||||||
|
musicService.cycleRepeatMode();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean playNext(Song song) {
|
||||||
|
if (musicService != null) {
|
||||||
|
if (getPlayingQueue().size() > 0) {
|
||||||
|
musicService.addSong(getPosition() + 1, song);
|
||||||
|
} else {
|
||||||
|
List<Song> queue = new ArrayList<>();
|
||||||
|
queue.add(song);
|
||||||
|
openQueue(queue, 0, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Toast.makeText(musicService, musicService.getResources().getString(R.string.added_title_to_queue), Toast.LENGTH_SHORT).show();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean playNext(@NonNull List<Song> songs) {
|
||||||
|
if (musicService != null) {
|
||||||
|
if (getPlayingQueue().size() > 0) {
|
||||||
|
musicService.addSongs(getPosition() + 1, songs);
|
||||||
|
} else {
|
||||||
|
openQueue(songs, 0, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
final String toast = songs.size() == 1 ? musicService.getResources().getString(R.string.added_title_to_queue) : musicService.getResources().getString(R.string.added_x_titles_to_queue, songs.size());
|
||||||
|
Toast.makeText(musicService, toast, Toast.LENGTH_SHORT).show();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean enqueue(Song song) {
|
||||||
|
if (musicService != null) {
|
||||||
|
if (getPlayingQueue().size() > 0) {
|
||||||
|
musicService.addSong(song);
|
||||||
|
} else {
|
||||||
|
List<Song> queue = new ArrayList<>();
|
||||||
|
queue.add(song);
|
||||||
|
openQueue(queue, 0, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Toast.makeText(musicService, musicService.getResources().getString(R.string.added_title_to_queue), Toast.LENGTH_SHORT).show();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean enqueue(@NonNull List<Song> songs) {
|
||||||
|
if (musicService != null) {
|
||||||
|
if (getPlayingQueue().size() > 0) {
|
||||||
|
musicService.addSongs(songs);
|
||||||
|
} else {
|
||||||
|
openQueue(songs, 0, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
final String toast = songs.size() == 1 ? musicService.getResources().getString(R.string.added_title_to_queue) : musicService.getResources().getString(R.string.added_x_titles_to_queue, songs.size());
|
||||||
|
Toast.makeText(musicService, toast, Toast.LENGTH_SHORT).show();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean removeFromQueue(int position) {
|
||||||
|
if (musicService != null && position >= 0 && position < getPlayingQueue().size()) {
|
||||||
|
musicService.removeSong(position);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean moveSong(int from, int to) {
|
||||||
|
if (musicService != null && from >= 0 && to >= 0 && from < getPlayingQueue().size() && to < getPlayingQueue().size()) {
|
||||||
|
musicService.moveSong(from, to);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean clearQueue() {
|
||||||
|
if (musicService != null) {
|
||||||
|
musicService.clearQueue();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
package com.cappielloantonio.play.helper;
|
||||||
|
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Message;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
public class MusicProgressViewUpdateHelper extends Handler {
|
||||||
|
private static final int CMD_REFRESH_PROGRESS_VIEWS = 1;
|
||||||
|
|
||||||
|
private static final int MIN_INTERVAL = 20;
|
||||||
|
private static final int UPDATE_INTERVAL_PLAYING = 1000;
|
||||||
|
private static final int UPDATE_INTERVAL_PAUSED = 500;
|
||||||
|
|
||||||
|
private Callback callback;
|
||||||
|
private int intervalPlaying;
|
||||||
|
private int intervalPaused;
|
||||||
|
|
||||||
|
public void start() {
|
||||||
|
queueNextRefresh(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
removeMessages(CMD_REFRESH_PROGRESS_VIEWS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MusicProgressViewUpdateHelper(Callback callback) {
|
||||||
|
this.callback = callback;
|
||||||
|
this.intervalPlaying = UPDATE_INTERVAL_PLAYING;
|
||||||
|
this.intervalPaused = UPDATE_INTERVAL_PAUSED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleMessage(@NonNull Message msg) {
|
||||||
|
super.handleMessage(msg);
|
||||||
|
if (msg.what == CMD_REFRESH_PROGRESS_VIEWS) {
|
||||||
|
queueNextRefresh(refreshProgressViews());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int refreshProgressViews() {
|
||||||
|
final int progressMillis = MusicPlayerRemote.getSongProgressMillis();
|
||||||
|
final int totalMillis = MusicPlayerRemote.getSongDurationMillis();
|
||||||
|
|
||||||
|
callback.onUpdateProgressViews(progressMillis, totalMillis);
|
||||||
|
|
||||||
|
if (!MusicPlayerRemote.isPlaying()) {
|
||||||
|
return intervalPaused;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int remainingMillis = intervalPlaying - progressMillis % intervalPlaying;
|
||||||
|
|
||||||
|
return Math.max(MIN_INTERVAL, remainingMillis);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void queueNextRefresh(final long delay) {
|
||||||
|
final Message message = obtainMessage(CMD_REFRESH_PROGRESS_VIEWS);
|
||||||
|
removeMessages(CMD_REFRESH_PROGRESS_VIEWS);
|
||||||
|
sendMessageDelayed(message, delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Callback {
|
||||||
|
void onUpdateProgressViews(int progress, int total);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
package com.cappielloantonio.play.interfaces;
|
||||||
|
|
||||||
|
public interface MusicServiceEventListener {
|
||||||
|
void onServiceConnected();
|
||||||
|
|
||||||
|
void onServiceDisconnected();
|
||||||
|
|
||||||
|
void onQueueChanged();
|
||||||
|
|
||||||
|
void onPlayMetadataChanged();
|
||||||
|
|
||||||
|
void onPlayStateChanged();
|
||||||
|
|
||||||
|
void onRepeatModeChanged();
|
||||||
|
}
|
||||||
|
|
@ -144,6 +144,11 @@ public class Song implements Parcelable {
|
||||||
this.lastPlay = lastPlay;
|
this.lastPlay = lastPlay;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Ignore
|
||||||
|
public Song() {
|
||||||
|
this.id = UUID.randomUUID().toString();
|
||||||
|
}
|
||||||
|
|
||||||
@Ignore
|
@Ignore
|
||||||
public Song(BaseItemDto itemDto) {
|
public Song(BaseItemDto itemDto) {
|
||||||
this.id = itemDto.getId();
|
this.id = itemDto.getId();
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ import com.cappielloantonio.play.App;
|
||||||
import com.cappielloantonio.play.R;
|
import com.cappielloantonio.play.R;
|
||||||
import com.cappielloantonio.play.model.Playlist;
|
import com.cappielloantonio.play.model.Playlist;
|
||||||
import com.cappielloantonio.play.model.Song;
|
import com.cappielloantonio.play.model.Song;
|
||||||
|
import com.cappielloantonio.play.repository.QueueRepository;
|
||||||
import com.cappielloantonio.play.service.notification.PlayingNotification;
|
import com.cappielloantonio.play.service.notification.PlayingNotification;
|
||||||
import com.cappielloantonio.play.service.playback.Playback;
|
import com.cappielloantonio.play.service.playback.Playback;
|
||||||
import com.cappielloantonio.play.util.PreferenceUtil;
|
import com.cappielloantonio.play.util.PreferenceUtil;
|
||||||
|
|
@ -63,14 +64,12 @@ public class MusicService extends Service implements Playback.PlaybackCallbacks
|
||||||
public static final String ACTION_PENDING_QUIT = PACKAGE_NAME + ".quit.pending";
|
public static final String ACTION_PENDING_QUIT = PACKAGE_NAME + ".quit.pending";
|
||||||
|
|
||||||
public static final String INTENT_EXTRA_PLAYLIST = PACKAGE_NAME + ".extra.playlist";
|
public static final String INTENT_EXTRA_PLAYLIST = PACKAGE_NAME + ".extra.playlist";
|
||||||
public static final String INTENT_EXTRA_SHUFFLE = PACKAGE_NAME + ".extra.shuffle";
|
|
||||||
|
|
||||||
public static final String STATE_CHANGED = PACKAGE_NAME + ".state.changed";
|
public static final String STATE_CHANGED = PACKAGE_NAME + ".state.changed";
|
||||||
public static final String META_CHANGED = PACKAGE_NAME + ".meta.changed";
|
public static final String META_CHANGED = PACKAGE_NAME + ".meta.changed";
|
||||||
public static final String QUEUE_CHANGED = PACKAGE_NAME + ".queue.changed";
|
public static final String QUEUE_CHANGED = PACKAGE_NAME + ".queue.changed";
|
||||||
|
|
||||||
public static final String REPEAT_MODE_CHANGED = PACKAGE_NAME + ".repeat.changed";
|
public static final String REPEAT_MODE_CHANGED = PACKAGE_NAME + ".repeat.changed";
|
||||||
public static final String SHUFFLE_MODE_CHANGED = PACKAGE_NAME + ".shuffle.changed";
|
|
||||||
|
|
||||||
public static final int TRACK_STARTED = 9;
|
public static final int TRACK_STARTED = 9;
|
||||||
public static final int TRACK_CHANGED = 1;
|
public static final int TRACK_CHANGED = 1;
|
||||||
|
|
@ -84,9 +83,6 @@ public class MusicService extends Service implements Playback.PlaybackCallbacks
|
||||||
public static final int DUCK = 7;
|
public static final int DUCK = 7;
|
||||||
public static final int UNDUCK = 8;
|
public static final int UNDUCK = 8;
|
||||||
|
|
||||||
public static final int SHUFFLE_MODE_NONE = 0;
|
|
||||||
public static final int SHUFFLE_MODE_SHUFFLE = 1;
|
|
||||||
|
|
||||||
public static final int REPEAT_MODE_NONE = 0;
|
public static final int REPEAT_MODE_NONE = 0;
|
||||||
public static final int REPEAT_MODE_ALL = 1;
|
public static final int REPEAT_MODE_ALL = 1;
|
||||||
public static final int REPEAT_MODE_THIS = 2;
|
public static final int REPEAT_MODE_THIS = 2;
|
||||||
|
|
@ -101,7 +97,6 @@ public class MusicService extends Service implements Playback.PlaybackCallbacks
|
||||||
private Playback playback;
|
private Playback playback;
|
||||||
|
|
||||||
private List<Song> playingQueue = new ArrayList<>();
|
private List<Song> playingQueue = new ArrayList<>();
|
||||||
private List<Song> originalPlayingQueue = new ArrayList<>();
|
|
||||||
|
|
||||||
private int position = -1;
|
private int position = -1;
|
||||||
private int nextPosition = -1;
|
private int nextPosition = -1;
|
||||||
|
|
@ -336,12 +331,8 @@ public class MusicService extends Service implements Playback.PlaybackCallbacks
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveQueue() {
|
private void saveQueue() {
|
||||||
App.getDatabase().songDao().deleteSongs();
|
QueueRepository queueRepository = new QueueRepository(App.getInstance());
|
||||||
App.getDatabase().songDao().insertSongs(playingQueue);
|
queueRepository.insertAllAndStartNew(playingQueue);
|
||||||
|
|
||||||
App.getDatabase().queueSongDao().deleteQueueSongs();
|
|
||||||
App.getDatabase().queueSongDao().setQueue(playingQueue, 0);
|
|
||||||
App.getDatabase().queueSongDao().setQueue(originalPlayingQueue, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void savePosition() {
|
private void savePosition() {
|
||||||
|
|
@ -371,14 +362,14 @@ public class MusicService extends Service implements Playback.PlaybackCallbacks
|
||||||
|
|
||||||
private synchronized void restoreQueuesAndPositionIfNecessary() {
|
private synchronized void restoreQueuesAndPositionIfNecessary() {
|
||||||
if (!queuesRestored && playingQueue.isEmpty()) {
|
if (!queuesRestored && playingQueue.isEmpty()) {
|
||||||
List<Song> restoredQueue = App.getDatabase().queueSongDao().getQueue(0);
|
QueueRepository queueRepository = new QueueRepository(App.getInstance());
|
||||||
List<Song> restoredOriginalQueue = App.getDatabase().queueSongDao().getQueue(1);
|
|
||||||
|
List<Song> restoredQueue = queueRepository.getSongs();
|
||||||
|
|
||||||
int restoredPosition = PreferenceManager.getDefaultSharedPreferences(this).getInt(PreferenceUtil.POSITION, -1);
|
int restoredPosition = PreferenceManager.getDefaultSharedPreferences(this).getInt(PreferenceUtil.POSITION, -1);
|
||||||
int restoredPositionInTrack = PreferenceManager.getDefaultSharedPreferences(this).getInt(PreferenceUtil.PROGRESS, -1);
|
int restoredPositionInTrack = PreferenceManager.getDefaultSharedPreferences(this).getInt(PreferenceUtil.PROGRESS, -1);
|
||||||
|
|
||||||
if (restoredQueue.size() > 0 && restoredQueue.size() == restoredOriginalQueue.size() && restoredPosition != -1) {
|
if (restoredQueue.size() > 0 && restoredPosition != -1) {
|
||||||
this.originalPlayingQueue = restoredOriginalQueue;
|
|
||||||
this.playingQueue = restoredQueue;
|
this.playingQueue = restoredQueue;
|
||||||
|
|
||||||
position = restoredPosition;
|
position = restoredPosition;
|
||||||
|
|
@ -517,7 +508,11 @@ public class MusicService extends Service implements Playback.PlaybackCallbacks
|
||||||
}
|
}
|
||||||
|
|
||||||
public Song getSongAt(int position) {
|
public Song getSongAt(int position) {
|
||||||
return getPlayingQueue().get(position);
|
if (position >= 0 && position < getPlayingQueue().size()) {
|
||||||
|
return getPlayingQueue().get(position);
|
||||||
|
} else {
|
||||||
|
return new Song();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getNextPosition(boolean force) {
|
public int getNextPosition(boolean force) {
|
||||||
|
|
@ -577,10 +572,6 @@ public class MusicService extends Service implements Playback.PlaybackCallbacks
|
||||||
|
|
||||||
public void openQueue(@Nullable final List<Song> playingQueue, final int startPosition, final boolean startPlaying) {
|
public void openQueue(@Nullable final List<Song> playingQueue, final int startPosition, final boolean startPlaying) {
|
||||||
if (playingQueue != null && !playingQueue.isEmpty() && startPosition >= 0 && startPosition < playingQueue.size()) {
|
if (playingQueue != null && !playingQueue.isEmpty() && startPosition >= 0 && startPosition < playingQueue.size()) {
|
||||||
// it is important to copy the playing queue here first as we might add or remove songs later
|
|
||||||
originalPlayingQueue = new ArrayList<>(playingQueue);
|
|
||||||
this.playingQueue = new ArrayList<>(originalPlayingQueue);
|
|
||||||
|
|
||||||
if (startPlaying) {
|
if (startPlaying) {
|
||||||
playSongAt(startPosition);
|
playSongAt(startPosition);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -593,30 +584,26 @@ public class MusicService extends Service implements Playback.PlaybackCallbacks
|
||||||
|
|
||||||
public void addSong(int position, Song song) {
|
public void addSong(int position, Song song) {
|
||||||
playingQueue.add(position, song);
|
playingQueue.add(position, song);
|
||||||
originalPlayingQueue.add(position, song);
|
|
||||||
notifyChange(QUEUE_CHANGED);
|
notifyChange(QUEUE_CHANGED);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addSong(Song song) {
|
public void addSong(Song song) {
|
||||||
playingQueue.add(song);
|
playingQueue.add(song);
|
||||||
originalPlayingQueue.add(song);
|
|
||||||
notifyChange(QUEUE_CHANGED);
|
notifyChange(QUEUE_CHANGED);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addSongs(int position, List<Song> songs) {
|
public void addSongs(int position, List<Song> songs) {
|
||||||
playingQueue.addAll(position, songs);
|
playingQueue.addAll(position, songs);
|
||||||
originalPlayingQueue.addAll(position, songs);
|
|
||||||
notifyChange(QUEUE_CHANGED);
|
notifyChange(QUEUE_CHANGED);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addSongs(List<Song> songs) {
|
public void addSongs(List<Song> songs) {
|
||||||
playingQueue.addAll(songs);
|
playingQueue.addAll(songs);
|
||||||
originalPlayingQueue.addAll(songs);
|
|
||||||
notifyChange(QUEUE_CHANGED);
|
notifyChange(QUEUE_CHANGED);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeSong(int position) {
|
public void removeSong(int position) {
|
||||||
originalPlayingQueue.remove(playingQueue.remove(position));
|
playingQueue.remove(position);
|
||||||
reposition(position);
|
reposition(position);
|
||||||
notifyChange(QUEUE_CHANGED);
|
notifyChange(QUEUE_CHANGED);
|
||||||
}
|
}
|
||||||
|
|
@ -653,7 +640,6 @@ public class MusicService extends Service implements Playback.PlaybackCallbacks
|
||||||
|
|
||||||
public void clearQueue() {
|
public void clearQueue() {
|
||||||
playingQueue.clear();
|
playingQueue.clear();
|
||||||
originalPlayingQueue.clear();
|
|
||||||
|
|
||||||
setPosition(-1);
|
setPosition(-1);
|
||||||
notifyChange(QUEUE_CHANGED);
|
notifyChange(QUEUE_CHANGED);
|
||||||
|
|
@ -1059,8 +1045,8 @@ public class MusicService extends Service implements Playback.PlaybackCallbacks
|
||||||
startInfo.setCanSeek(true);
|
startInfo.setCanSeek(true);
|
||||||
startInfo.setIsPaused(false);
|
startInfo.setIsPaused(false);
|
||||||
|
|
||||||
App.getInstance().getApiClientInstance(App.getInstance()).ensureWebSocket();
|
App.getApiClientInstance(App.getInstance()).ensureWebSocket();
|
||||||
App.getInstance().getApiClientInstance(App.getInstance()).ReportPlaybackStartAsync(startInfo, new EmptyResponse());
|
App.getApiClientInstance(App.getInstance()).ReportPlaybackStartAsync(startInfo, new EmptyResponse());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onProgress() {
|
public void onProgress() {
|
||||||
|
|
@ -1071,10 +1057,10 @@ public class MusicService extends Service implements Playback.PlaybackCallbacks
|
||||||
double duration = mService.get().getSongDurationMillis();
|
double duration = mService.get().getSongDurationMillis();
|
||||||
if (progress / duration > 0.9) {
|
if (progress / duration > 0.9) {
|
||||||
Song current = mService.get().getCurrentSong();
|
Song current = mService.get().getCurrentSong();
|
||||||
String user = App.getInstance().getApiClientInstance(App.getInstance()).getCurrentUserId();
|
String user = App.getApiClientInstance(App.getInstance()).getCurrentUserId();
|
||||||
Date time = new Date(System.currentTimeMillis());
|
Date time = new Date(System.currentTimeMillis());
|
||||||
|
|
||||||
App.getInstance().getApiClientInstance(App.getInstance()).MarkPlayedAsync(current.getId(), user, time, new Response<>());
|
App.getApiClientInstance(App.getInstance()).MarkPlayedAsync(current.getId(), user, time, new Response<>());
|
||||||
}
|
}
|
||||||
|
|
||||||
progressInfo.setItemId(mService.get().getCurrentSong().getId());
|
progressInfo.setItemId(mService.get().getCurrentSong().getId());
|
||||||
|
|
@ -1083,7 +1069,7 @@ public class MusicService extends Service implements Playback.PlaybackCallbacks
|
||||||
progressInfo.setIsPaused(!mService.get().playback.isPlaying());
|
progressInfo.setIsPaused(!mService.get().playback.isPlaying());
|
||||||
progressInfo.setCanSeek(true);
|
progressInfo.setCanSeek(true);
|
||||||
|
|
||||||
App.getInstance().getApiClientInstance(App.getInstance()).ReportPlaybackProgressAsync(progressInfo, new EmptyResponse());
|
App.getApiClientInstance(App.getInstance()).ReportPlaybackProgressAsync(progressInfo, new EmptyResponse());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onStop() {
|
public void onStop() {
|
||||||
|
|
|
||||||
|
|
@ -9,19 +9,12 @@ import android.content.Intent;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.RequiresApi;
|
import androidx.annotation.RequiresApi;
|
||||||
import androidx.core.app.NotificationCompat;
|
import androidx.core.app.NotificationCompat;
|
||||||
import androidx.palette.graphics.Palette;
|
|
||||||
|
|
||||||
import com.bumptech.glide.request.target.CustomTarget;
|
|
||||||
import com.bumptech.glide.request.transition.Transition;
|
|
||||||
import com.cappielloantonio.play.R;
|
import com.cappielloantonio.play.R;
|
||||||
import com.cappielloantonio.play.glide.CustomGlideRequest;
|
|
||||||
import com.cappielloantonio.play.glide.palette.BitmapPaletteWrapper;
|
|
||||||
import com.cappielloantonio.play.model.Song;
|
import com.cappielloantonio.play.model.Song;
|
||||||
import com.cappielloantonio.play.service.MusicService;
|
import com.cappielloantonio.play.service.MusicService;
|
||||||
import com.cappielloantonio.play.ui.activities.MainActivity;
|
import com.cappielloantonio.play.ui.activities.MainActivity;
|
||||||
|
|
@ -71,64 +64,39 @@ public class PlayingNotification {
|
||||||
intent.setComponent(serviceName);
|
intent.setComponent(serviceName);
|
||||||
final PendingIntent deleteIntent = PendingIntent.getService(service, 0, intent, 0);
|
final PendingIntent deleteIntent = PendingIntent.getService(service, 0, intent, 0);
|
||||||
|
|
||||||
final int bigNotificationImageSize = service.getResources().getDimensionPixelSize(R.dimen.notification_big_image_size);
|
Bitmap bitmap = BitmapFactory.decodeResource(service.getResources(), R.drawable.default_album_art);
|
||||||
service.runOnUiThread(() -> CustomGlideRequest.Builder
|
NotificationCompat.Action playPauseAction = new NotificationCompat.Action(playButtonResId,
|
||||||
.from(service, song.getPrimary(), song.getBlurHash(), CustomGlideRequest.PRIMARY, CustomGlideRequest.TOP_QUALITY)
|
service.getString(R.string.action_play_pause),
|
||||||
.build()
|
retrievePlaybackAction(ACTION_TOGGLE));
|
||||||
.into(new CustomTarget<BitmapPaletteWrapper>(bigNotificationImageSize, bigNotificationImageSize) {
|
NotificationCompat.Action previousAction = new NotificationCompat.Action(R.drawable.ic_skip_previous_white_24dp,
|
||||||
@Override
|
service.getString(R.string.action_previous),
|
||||||
public void onResourceReady(@NonNull BitmapPaletteWrapper resource, Transition<? super BitmapPaletteWrapper> glideAnimation) {
|
retrievePlaybackAction(ACTION_REWIND));
|
||||||
Palette palette = resource.getPalette();
|
NotificationCompat.Action nextAction = new NotificationCompat.Action(R.drawable.ic_skip_next_white_24dp,
|
||||||
update(resource.getBitmap(), palette.getVibrantColor(palette.getMutedColor(Color.TRANSPARENT)));
|
service.getString(R.string.action_next),
|
||||||
}
|
retrievePlaybackAction(ACTION_SKIP));
|
||||||
|
NotificationCompat.Builder builder = new NotificationCompat.Builder(service, NOTIFICATION_CHANNEL_ID)
|
||||||
|
.setSmallIcon(R.drawable.ic_notification)
|
||||||
|
.setSubText(song.getAlbumName())
|
||||||
|
.setLargeIcon(bitmap)
|
||||||
|
.setContentIntent(clickIntent)
|
||||||
|
.setDeleteIntent(deleteIntent)
|
||||||
|
.setContentTitle(song.getTitle())
|
||||||
|
.setContentText(song.getArtistName())
|
||||||
|
.setOngoing(isPlaying)
|
||||||
|
.setShowWhen(false)
|
||||||
|
.addAction(previousAction)
|
||||||
|
.addAction(playPauseAction)
|
||||||
|
.addAction(nextAction);
|
||||||
|
|
||||||
@Override
|
builder.setStyle(new androidx.media.app.NotificationCompat.MediaStyle().setMediaSession(service.getMediaSession().getSessionToken())
|
||||||
public void onLoadFailed(Drawable drawable) {
|
.setShowActionsInCompactView(0, 1, 2))
|
||||||
update(null, Color.TRANSPARENT);
|
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||||
}
|
.setColor(Color.TRANSPARENT);
|
||||||
|
|
||||||
@Override
|
// notification has been stopped before loading was finished
|
||||||
public void onLoadCleared(Drawable drawable) {
|
if (stopped) return;
|
||||||
update(null, Color.TRANSPARENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
void update(Bitmap bitmap, int color) {
|
updateNotifyModeAndPostNotification(builder.build());
|
||||||
if (bitmap == null)
|
|
||||||
bitmap = BitmapFactory.decodeResource(service.getResources(), R.drawable.default_album_art);
|
|
||||||
NotificationCompat.Action playPauseAction = new NotificationCompat.Action(playButtonResId,
|
|
||||||
service.getString(R.string.action_play_pause),
|
|
||||||
retrievePlaybackAction(ACTION_TOGGLE));
|
|
||||||
NotificationCompat.Action previousAction = new NotificationCompat.Action(R.drawable.ic_skip_previous_white_24dp,
|
|
||||||
service.getString(R.string.action_previous),
|
|
||||||
retrievePlaybackAction(ACTION_REWIND));
|
|
||||||
NotificationCompat.Action nextAction = new NotificationCompat.Action(R.drawable.ic_skip_next_white_24dp,
|
|
||||||
service.getString(R.string.action_next),
|
|
||||||
retrievePlaybackAction(ACTION_SKIP));
|
|
||||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(service, NOTIFICATION_CHANNEL_ID)
|
|
||||||
.setSmallIcon(R.drawable.ic_notification)
|
|
||||||
.setSubText(song.getAlbumName())
|
|
||||||
.setLargeIcon(bitmap)
|
|
||||||
.setContentIntent(clickIntent)
|
|
||||||
.setDeleteIntent(deleteIntent)
|
|
||||||
.setContentTitle(song.getTitle())
|
|
||||||
.setContentText(song.getArtistName())
|
|
||||||
.setOngoing(isPlaying)
|
|
||||||
.setShowWhen(false)
|
|
||||||
.addAction(previousAction)
|
|
||||||
.addAction(playPauseAction)
|
|
||||||
.addAction(nextAction);
|
|
||||||
|
|
||||||
builder.setStyle(new androidx.media.app.NotificationCompat.MediaStyle().setMediaSession(service.getMediaSession().getSessionToken())
|
|
||||||
.setShowActionsInCompactView(0, 1, 2))
|
|
||||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
|
||||||
.setColor(color);
|
|
||||||
|
|
||||||
// notification has been stopped before loading was finished
|
|
||||||
if (stopped) return;
|
|
||||||
|
|
||||||
updateNotifyModeAndPostNotification(builder.build());
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void stop() {
|
public synchronized void stop() {
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,80 @@
|
||||||
package com.cappielloantonio.play.ui.activities.base;
|
package com.cappielloantonio.play.ui.activities.base;
|
||||||
|
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
|
import android.content.ServiceConnection;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.IBinder;
|
||||||
import android.os.PowerManager;
|
import android.os.PowerManager;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
import com.cappielloantonio.play.R;
|
import com.cappielloantonio.play.R;
|
||||||
|
import com.cappielloantonio.play.helper.MusicPlayerRemote;
|
||||||
|
import com.cappielloantonio.play.interfaces.MusicServiceEventListener;
|
||||||
|
import com.cappielloantonio.play.service.MusicService;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import pub.devrel.easypermissions.AppSettingsDialog;
|
import pub.devrel.easypermissions.AppSettingsDialog;
|
||||||
import pub.devrel.easypermissions.EasyPermissions;
|
import pub.devrel.easypermissions.EasyPermissions;
|
||||||
|
|
||||||
public class BaseActivity extends AppCompatActivity implements EasyPermissions.PermissionCallbacks {
|
public class BaseActivity extends AppCompatActivity implements EasyPermissions.PermissionCallbacks, MusicServiceEventListener {
|
||||||
|
private static final String TAG = "BaseActivity";
|
||||||
public static final int REQUEST_PERM_ACCESS = 1;
|
public static final int REQUEST_PERM_ACCESS = 1;
|
||||||
|
|
||||||
|
private final List<MusicServiceEventListener> mMusicServiceEventListeners = new ArrayList<>();
|
||||||
|
|
||||||
|
private MusicPlayerRemote.ServiceToken serviceToken;
|
||||||
|
private MusicStateReceiver musicStateReceiver;
|
||||||
|
|
||||||
|
private boolean receiverRegistered;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
Log.d(TAG, "onCreate");
|
||||||
|
|
||||||
|
serviceToken = MusicPlayerRemote.bindToService(this, new ServiceConnection() {
|
||||||
|
@Override
|
||||||
|
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||||
|
Log.d(TAG, "onServiceConnected");
|
||||||
|
BaseActivity.this.onServiceConnected();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onServiceDisconnected(ComponentName name) {
|
||||||
|
Log.d(TAG, "onServiceDisconnected");
|
||||||
|
BaseActivity.this.onServiceDisconnected();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onResume() {
|
protected void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
checkPermissions();
|
checkPermissions();
|
||||||
// checkBatteryOptimization();
|
checkBatteryOptimization();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
MusicPlayerRemote.unbindFromService(serviceToken);
|
||||||
|
if (receiverRegistered) {
|
||||||
|
unregisterReceiver(musicStateReceiver);
|
||||||
|
receiverRegistered = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkBatteryOptimization() {
|
private void checkBatteryOptimization() {
|
||||||
|
|
@ -80,4 +131,120 @@ public class BaseActivity extends AppCompatActivity implements EasyPermissions.P
|
||||||
|
|
||||||
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
|
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addMusicServiceEventListener(final MusicServiceEventListener listener) {
|
||||||
|
if (listener != null) {
|
||||||
|
mMusicServiceEventListeners.add(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeMusicServiceEventListener(final MusicServiceEventListener listener) {
|
||||||
|
if (listener != null) {
|
||||||
|
mMusicServiceEventListeners.remove(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onServiceConnected() {
|
||||||
|
if (!receiverRegistered) {
|
||||||
|
musicStateReceiver = new MusicStateReceiver(this);
|
||||||
|
|
||||||
|
final IntentFilter filter = new IntentFilter();
|
||||||
|
filter.addAction(MusicService.STATE_CHANGED);
|
||||||
|
filter.addAction(MusicService.REPEAT_MODE_CHANGED);
|
||||||
|
filter.addAction(MusicService.META_CHANGED);
|
||||||
|
filter.addAction(MusicService.QUEUE_CHANGED);
|
||||||
|
|
||||||
|
registerReceiver(musicStateReceiver, filter);
|
||||||
|
|
||||||
|
receiverRegistered = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (MusicServiceEventListener listener : mMusicServiceEventListeners) {
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onServiceConnected();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onServiceDisconnected() {
|
||||||
|
if (receiverRegistered) {
|
||||||
|
unregisterReceiver(musicStateReceiver);
|
||||||
|
receiverRegistered = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (MusicServiceEventListener listener : mMusicServiceEventListeners) {
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onServiceDisconnected();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onQueueChanged() {
|
||||||
|
for (MusicServiceEventListener listener : mMusicServiceEventListeners) {
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onQueueChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPlayMetadataChanged() {
|
||||||
|
for (MusicServiceEventListener listener : mMusicServiceEventListeners) {
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onPlayMetadataChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPlayStateChanged() {
|
||||||
|
for (MusicServiceEventListener listener : mMusicServiceEventListeners) {
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onPlayStateChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRepeatModeChanged() {
|
||||||
|
for (MusicServiceEventListener listener : mMusicServiceEventListeners) {
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onRepeatModeChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class MusicStateReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
|
private final WeakReference<BaseActivity> reference;
|
||||||
|
|
||||||
|
public MusicStateReceiver(final BaseActivity activity) {
|
||||||
|
reference = new WeakReference<>(activity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReceive(final Context context, @NonNull final Intent intent) {
|
||||||
|
final String action = intent.getAction();
|
||||||
|
BaseActivity activity = reference.get();
|
||||||
|
if (activity != null && action != null) {
|
||||||
|
switch (action) {
|
||||||
|
case MusicService.META_CHANGED:
|
||||||
|
activity.onPlayMetadataChanged();
|
||||||
|
break;
|
||||||
|
case MusicService.QUEUE_CHANGED:
|
||||||
|
activity.onQueueChanged();
|
||||||
|
break;
|
||||||
|
case MusicService.STATE_CHANGED:
|
||||||
|
activity.onPlayStateChanged();
|
||||||
|
break;
|
||||||
|
case MusicService.REPEAT_MODE_CHANGED:
|
||||||
|
activity.onRepeatModeChanged();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.ScrollView;
|
import android.widget.ScrollView;
|
||||||
|
import android.widget.SeekBar;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
@ -17,11 +19,14 @@ import com.cappielloantonio.play.R;
|
||||||
import com.cappielloantonio.play.adapter.PlayerNowPlayingSongAdapter;
|
import com.cappielloantonio.play.adapter.PlayerNowPlayingSongAdapter;
|
||||||
import com.cappielloantonio.play.adapter.PlayerSongQueueAdapter;
|
import com.cappielloantonio.play.adapter.PlayerSongQueueAdapter;
|
||||||
import com.cappielloantonio.play.databinding.FragmentPlayerBottomSheetBinding;
|
import com.cappielloantonio.play.databinding.FragmentPlayerBottomSheetBinding;
|
||||||
|
import com.cappielloantonio.play.helper.MusicPlayerRemote;
|
||||||
|
import com.cappielloantonio.play.helper.MusicProgressViewUpdateHelper;
|
||||||
|
import com.cappielloantonio.play.interfaces.MusicServiceEventListener;
|
||||||
import com.cappielloantonio.play.model.Song;
|
import com.cappielloantonio.play.model.Song;
|
||||||
import com.cappielloantonio.play.ui.activities.MainActivity;
|
import com.cappielloantonio.play.ui.activities.MainActivity;
|
||||||
import com.cappielloantonio.play.viewmodel.PlayerBottomSheetViewModel;
|
import com.cappielloantonio.play.viewmodel.PlayerBottomSheetViewModel;
|
||||||
|
|
||||||
public class PlayerBottomSheetFragment extends Fragment {
|
public class PlayerBottomSheetFragment extends Fragment implements MusicServiceEventListener, MusicProgressViewUpdateHelper.Callback {
|
||||||
private static final String TAG = "PlayerBottomSheetFragment";
|
private static final String TAG = "PlayerBottomSheetFragment";
|
||||||
|
|
||||||
private FragmentPlayerBottomSheetBinding bind;
|
private FragmentPlayerBottomSheetBinding bind;
|
||||||
|
|
@ -31,7 +36,14 @@ public class PlayerBottomSheetFragment extends Fragment {
|
||||||
private PlayerNowPlayingSongAdapter playerNowPlayingSongAdapter;
|
private PlayerNowPlayingSongAdapter playerNowPlayingSongAdapter;
|
||||||
private PlayerSongQueueAdapter playerSongQueueAdapter;
|
private PlayerSongQueueAdapter playerSongQueueAdapter;
|
||||||
|
|
||||||
private boolean isNowPlaying = false;
|
private MusicProgressViewUpdateHelper progressViewUpdateHelper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
progressViewUpdateHelper = new MusicProgressViewUpdateHelper(this);
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -49,6 +61,35 @@ public class PlayerBottomSheetFragment extends Fragment {
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
|
||||||
|
activity.addMusicServiceEventListener(this);
|
||||||
|
setUpMusicControllers();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
|
||||||
|
progressViewUpdateHelper.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
|
||||||
|
progressViewUpdateHelper.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
|
||||||
|
activity.removeMusicServiceEventListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
private void initQueueSlideView() {
|
private void initQueueSlideView() {
|
||||||
bind.playerBodyLayout.playerSongCoverViewPager.setOrientation(ViewPager2.ORIENTATION_HORIZONTAL);
|
bind.playerBodyLayout.playerSongCoverViewPager.setOrientation(ViewPager2.ORIENTATION_HORIZONTAL);
|
||||||
|
|
||||||
|
|
@ -81,6 +122,28 @@ public class PlayerBottomSheetFragment extends Fragment {
|
||||||
bind.playerBodyLayout.buttonFavorite.setOnClickListener(v -> playerBottomSheetViewModel.setFavorite());
|
bind.playerBodyLayout.buttonFavorite.setOnClickListener(v -> playerBottomSheetViewModel.setFavorite());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void initSeekBar() {
|
||||||
|
bind.playerBodyLayout.playerBigSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||||
|
if (fromUser) {
|
||||||
|
MusicPlayerRemote.seekTo(progress);
|
||||||
|
onUpdateProgressViews(MusicPlayerRemote.getSongProgressMillis(), MusicPlayerRemote.getSongDurationMillis());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void setSongInfo(Song song) {
|
private void setSongInfo(Song song) {
|
||||||
playerBottomSheetViewModel.setNowPlayingSong(song);
|
playerBottomSheetViewModel.setNowPlayingSong(song);
|
||||||
|
|
||||||
|
|
@ -95,6 +158,26 @@ public class PlayerBottomSheetFragment extends Fragment {
|
||||||
playSong(song);
|
playSong(song);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setUpMusicControllers() {
|
||||||
|
setUpPlayPauseButton();
|
||||||
|
// setUpPrevNext();
|
||||||
|
// setUpRepeatButton();
|
||||||
|
// setUpShuffleButton();
|
||||||
|
initSeekBar();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setUpPlayPauseButton() {
|
||||||
|
bind.playerBodyLayout.playPauseButton.setOnClickListener(v -> {
|
||||||
|
if (MusicPlayerRemote.isPlaying()) {
|
||||||
|
MusicPlayerRemote.pauseSong();
|
||||||
|
Toast.makeText(requireContext(), "PAUSING", Toast.LENGTH_SHORT).show();
|
||||||
|
} else {
|
||||||
|
MusicPlayerRemote.resumePlaying();
|
||||||
|
Toast.makeText(requireContext(), "PLAYING", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void playSong(Song song) {
|
private void playSong(Song song) {
|
||||||
// Toast.makeText(activity, MusicUtil.getSongFileUri(song), Toast.LENGTH_SHORT).show();
|
// Toast.makeText(activity, MusicUtil.getSongFileUri(song), Toast.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
|
|
@ -111,4 +194,40 @@ public class PlayerBottomSheetFragment extends Fragment {
|
||||||
bind.playerBodyLayout.playerSongCoverViewPager.setCurrentItem(page, smoothScroll);
|
bind.playerBodyLayout.playerSongCoverViewPager.setCurrentItem(page, smoothScroll);
|
||||||
setSongInfo(song);
|
setSongInfo(song);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onServiceConnected() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onServiceDisconnected() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onQueueChanged() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPlayMetadataChanged() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPlayStateChanged() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRepeatModeChanged() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUpdateProgressViews(int progress, int total) {
|
||||||
|
bind.playerBodyLayout.playerBigSeekBar.setMax(total);
|
||||||
|
bind.playerBodyLayout.playerBigSeekBar.setProgress(progress);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,8 @@
|
||||||
app:cardCornerRadius="4dp">
|
app:cardCornerRadius="4dp">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/discover_song_cover_image_view"
|
android:id="@+id/now_playing_song_cover_image_view"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"/>
|
||||||
android:foreground="?attr/selectableItemBackground"/>
|
|
||||||
|
|
||||||
</androidx.cardview.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
@ -22,6 +22,21 @@
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<ToggleButton
|
||||||
|
android:id="@+id/play_pause_button"
|
||||||
|
android:layout_width="26dp"
|
||||||
|
android:layout_height="26dp"
|
||||||
|
android:background="@drawable/button_play_pause_selector"
|
||||||
|
android:checked="true"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
android:text=""
|
||||||
|
android:textOff=""
|
||||||
|
android:textOn=""
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<androidx.viewpager2.widget.ViewPager2
|
<androidx.viewpager2.widget.ViewPager2
|
||||||
android:id="@+id/player_song_cover_view_pager"
|
android:id="@+id/player_song_cover_view_pager"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
|
|
@ -32,16 +47,14 @@
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/collapse_bottom_sheet_button" />
|
app:layout_constraintTop_toBottomOf="@+id/collapse_bottom_sheet_button" />
|
||||||
|
|
||||||
<ProgressBar
|
<SeekBar
|
||||||
android:id="@+id/player_big_progress_bar"
|
android:id="@+id/player_big_seek_bar"
|
||||||
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:layout_marginStart="28dp"
|
android:layout_marginStart="28dp"
|
||||||
android:layout_marginTop="18dp"
|
android:layout_marginTop="18dp"
|
||||||
android:layout_marginEnd="28dp"
|
android:layout_marginEnd="28dp"
|
||||||
android:progress="50"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/player_song_cover_view_pager" />
|
app:layout_constraintTop_toBottomOf="@+id/player_song_cover_view_pager" />
|
||||||
|
|
@ -51,7 +64,7 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/player_big_progress_bar"
|
app:layout_constraintTop_toBottomOf="@+id/player_big_seek_bar"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,8 @@
|
||||||
<string name="unplayable_file">Couldn\'t play this song.</string>
|
<string name="unplayable_file">Couldn\'t play this song.</string>
|
||||||
<string name="playlist_is_empty">Playlist is empty</string>
|
<string name="playlist_is_empty">Playlist is empty</string>
|
||||||
<string name="audio_focus_denied">Audio focus denied.</string>
|
<string name="audio_focus_denied">Audio focus denied.</string>
|
||||||
|
<string name="added_title_to_queue">"Added 1 title to the queue."</string>
|
||||||
|
<string name="added_x_titles_to_queue">Added %1$d titles to the queue.</string>
|
||||||
|
|
||||||
<string name="action_play_next">Play next</string>
|
<string name="action_play_next">Play next</string>
|
||||||
<string name="action_play">Play</string>
|
<string name="action_play">Play</string>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue