mirror of
https://github.com/antebudimir/tempus.git
synced 2025-12-31 17:43:32 +00:00
Deleted all references to the old version of exoplayer from services and helpers
This commit is contained in:
parent
f9ac2f2646
commit
7a3cdb8806
6 changed files with 3 additions and 1544 deletions
|
|
@ -1,218 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project Licensed under the Apache
|
||||
* License, Version 2.0 (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
|
||||
* or agreed to in writing, software distributed under the License is
|
||||
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the specific language
|
||||
* governing permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
// Modified for Phonograph by Karim Abou Zeid (kabouzeid).
|
||||
|
||||
package com.cappielloantonio.play.broadcast.receiver;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.PowerManager;
|
||||
import android.os.PowerManager.WakeLock;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.cappielloantonio.play.BuildConfig;
|
||||
import com.cappielloantonio.play.service.MusicService;
|
||||
|
||||
/**
|
||||
* Used to control headset playback.
|
||||
* Single press: pause/resume
|
||||
* Double press: next track
|
||||
* Triple press: previous track
|
||||
*/
|
||||
public class MediaButtonIntentReceiver extends BroadcastReceiver {
|
||||
public static final String TAG = MediaButtonIntentReceiver.class.getSimpleName();
|
||||
private static final boolean DEBUG = BuildConfig.DEBUG;
|
||||
private static final int MSG_HEADSET_DOUBLE_CLICK_TIMEOUT = 2;
|
||||
|
||||
private static final int DOUBLE_CLICK = 400;
|
||||
|
||||
private static WakeLock mWakeLock = null;
|
||||
private static int mClickCounter = 0;
|
||||
private static long mLastClickTime = 0;
|
||||
|
||||
@SuppressLint("HandlerLeak")
|
||||
private static final Handler mHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(final Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_HEADSET_DOUBLE_CLICK_TIMEOUT:
|
||||
final int clickCount = msg.arg1;
|
||||
final String command;
|
||||
|
||||
if (DEBUG) Log.v(TAG, "Handling headset click, count = " + clickCount);
|
||||
switch (clickCount) {
|
||||
case 1:
|
||||
command = MusicService.ACTION_TOGGLE;
|
||||
break;
|
||||
case 2:
|
||||
command = MusicService.ACTION_SKIP;
|
||||
break;
|
||||
case 3:
|
||||
command = MusicService.ACTION_REWIND;
|
||||
break;
|
||||
default:
|
||||
command = null;
|
||||
break;
|
||||
}
|
||||
|
||||
if (command != null) {
|
||||
final Context context = (Context) msg.obj;
|
||||
startService(context, command);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
releaseWakeLockIfHandlerIdle();
|
||||
}
|
||||
};
|
||||
|
||||
public static boolean handleIntent(final Context context, final Intent intent) {
|
||||
final String intentAction = intent.getAction();
|
||||
if (Intent.ACTION_MEDIA_BUTTON.equals(intentAction)) {
|
||||
final KeyEvent event = intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
|
||||
if (event == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final int keycode = event.getKeyCode();
|
||||
final int action = event.getAction();
|
||||
|
||||
// fallback to system time if event time is not available
|
||||
final long eventTime = event.getEventTime() != 0
|
||||
? event.getEventTime()
|
||||
: System.currentTimeMillis();
|
||||
|
||||
String command = null;
|
||||
switch (keycode) {
|
||||
case KeyEvent.KEYCODE_MEDIA_STOP:
|
||||
command = MusicService.ACTION_STOP;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_HEADSETHOOK:
|
||||
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
|
||||
command = MusicService.ACTION_TOGGLE;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_MEDIA_NEXT:
|
||||
command = MusicService.ACTION_SKIP;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
|
||||
command = MusicService.ACTION_REWIND;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_MEDIA_PAUSE:
|
||||
command = MusicService.ACTION_PAUSE;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_MEDIA_PLAY:
|
||||
command = MusicService.ACTION_PLAY;
|
||||
break;
|
||||
}
|
||||
|
||||
if (command != null) {
|
||||
if (action == KeyEvent.ACTION_DOWN) {
|
||||
if (event.getRepeatCount() == 0) {
|
||||
// Only consider the first event in a sequence, not the repeat events,
|
||||
// so that we don't trigger in cases where the first event went to
|
||||
// a different app (e.g. when the user ends a phone call by
|
||||
// long pressing the headset button)
|
||||
|
||||
// The service may or may not be running, but we need to send it
|
||||
// a command.
|
||||
if (keycode == KeyEvent.KEYCODE_HEADSETHOOK || keycode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) {
|
||||
if (eventTime - mLastClickTime >= DOUBLE_CLICK) {
|
||||
mClickCounter = 0;
|
||||
}
|
||||
|
||||
mClickCounter++;
|
||||
if (DEBUG) Log.v(TAG, "Got headset click, count = " + mClickCounter);
|
||||
mHandler.removeMessages(MSG_HEADSET_DOUBLE_CLICK_TIMEOUT);
|
||||
|
||||
Message msg = mHandler.obtainMessage(
|
||||
MSG_HEADSET_DOUBLE_CLICK_TIMEOUT, mClickCounter, 0, context);
|
||||
|
||||
long delay = mClickCounter < 3 ? DOUBLE_CLICK : 0;
|
||||
if (mClickCounter >= 3) {
|
||||
mClickCounter = 0;
|
||||
}
|
||||
|
||||
mLastClickTime = eventTime;
|
||||
acquireWakeLockAndSendMessage(context, msg, delay);
|
||||
} else {
|
||||
startService(context, command);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void startService(Context context, String command) {
|
||||
final Intent intent = new Intent(context, MusicService.class);
|
||||
intent.setAction(command);
|
||||
try {
|
||||
// IMPORTANT NOTE: (kind of a hack)
|
||||
// on Android O and above the following crashes when the app is not running
|
||||
// there is no good way to check whether the app is running so we catch the exception
|
||||
// we do not always want to use startForegroundService() because then one gets an ANR
|
||||
// if no notification is displayed via startForeground()
|
||||
// according to Play analytics this happens a lot, I suppose for example if command = PAUSE
|
||||
context.startService(intent);
|
||||
} catch (IllegalStateException ignored) {
|
||||
ContextCompat.startForegroundService(context, intent);
|
||||
}
|
||||
}
|
||||
|
||||
private static void acquireWakeLockAndSendMessage(Context context, Message msg, long delay) {
|
||||
if (mWakeLock == null) {
|
||||
Context appContext = context.getApplicationContext();
|
||||
PowerManager pm = (PowerManager) appContext.getSystemService(Context.POWER_SERVICE);
|
||||
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, context.getClass().getName());
|
||||
mWakeLock.setReferenceCounted(false);
|
||||
}
|
||||
|
||||
if (DEBUG) Log.v(TAG, "Acquiring wake lock and sending " + msg.what);
|
||||
|
||||
mWakeLock.acquire(10000);
|
||||
mHandler.sendMessageDelayed(msg, delay);
|
||||
}
|
||||
|
||||
private static void releaseWakeLockIfHandlerIdle() {
|
||||
if (mHandler.hasMessages(MSG_HEADSET_DOUBLE_CLICK_TIMEOUT)) {
|
||||
if (DEBUG) Log.v(TAG, "Handler still has messages pending, not releasing wake lock");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mWakeLock != null) {
|
||||
if (DEBUG) Log.v(TAG, "Releasing wake lock");
|
||||
|
||||
mWakeLock.release();
|
||||
mWakeLock = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(final Context context, final Intent intent) {
|
||||
if (DEBUG) Log.v(TAG, "Received intent: " + intent);
|
||||
if (handleIntent(context, intent) && isOrderedBroadcast()) {
|
||||
abortBroadcast();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
package com.cappielloantonio.play.helper;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.cappielloantonio.play.service.MusicPlayerRemote;
|
||||
|
||||
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 final Callback callback;
|
||||
private final int intervalPlaying;
|
||||
private final int intervalPaused;
|
||||
|
||||
public MusicProgressViewUpdateHelper(Callback callback) {
|
||||
this.callback = callback;
|
||||
this.intervalPlaying = UPDATE_INTERVAL_PLAYING;
|
||||
this.intervalPaused = UPDATE_INTERVAL_PAUSED;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
queueNextRefresh(1);
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
removeMessages(CMD_REFRESH_PROGRESS_VIEWS);
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,213 +0,0 @@
|
|||
package com.cappielloantonio.play.service;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.cappielloantonio.play.R;
|
||||
import com.cappielloantonio.play.interfaces.Playback;
|
||||
import com.cappielloantonio.play.model.Song;
|
||||
import com.cappielloantonio.play.util.CacheUtil;
|
||||
import com.cappielloantonio.play.util.DownloadUtil;
|
||||
import com.cappielloantonio.play.util.MusicUtil;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||
import com.google.android.exoplayer2.ExoPlayer;
|
||||
import com.google.android.exoplayer2.MediaItem;
|
||||
import com.google.android.exoplayer2.Player;
|
||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||
import com.google.android.exoplayer2.audio.AudioAttributes;
|
||||
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
import com.google.android.exoplayer2.upstream.cache.CacheDataSource;
|
||||
import com.google.android.exoplayer2.upstream.cache.SimpleCache;
|
||||
|
||||
public class MultiPlayer implements Playback {
|
||||
public static final String TAG = MultiPlayer.class.getSimpleName();
|
||||
|
||||
private final Context context;
|
||||
private final SimpleExoPlayer exoPlayer;
|
||||
private final SimpleCache simpleCache;
|
||||
|
||||
private PlaybackCallbacks callbacks;
|
||||
|
||||
private final ExoPlayer.EventListener eventListener = new ExoPlayer.EventListener() {
|
||||
@Override
|
||||
public void onPlayWhenReadyChanged(boolean playWhenReady, int reason) {
|
||||
Log.i(TAG, String.format("onPlayWhenReadyChanged: %b %d", playWhenReady, reason));
|
||||
|
||||
if (callbacks != null) {
|
||||
callbacks.onReadyChanged(playWhenReady, reason);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaybackStateChanged(int state) {
|
||||
Log.i(TAG, String.format("onPlaybackStateChanged: %d", state));
|
||||
|
||||
if (callbacks != null) {
|
||||
callbacks.onStateChanged(state);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaybackSuppressionReasonChanged(@Player.PlaybackSuppressionReason int playbackSuppressionReason) {
|
||||
Log.i(TAG, String.format("onPlaybackSuppressionReasonChanged: %d", playbackSuppressionReason));
|
||||
|
||||
if (callbacks != null) {
|
||||
callbacks.onStateChanged(Player.STATE_READY);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMediaItemTransition(MediaItem mediaItem, int reason) {
|
||||
Log.i(TAG, String.format("onMediaItemTransition: %s %d", mediaItem, reason));
|
||||
|
||||
if (exoPlayer.getMediaItemCount() > 1) {
|
||||
exoPlayer.removeMediaItem(0);
|
||||
}
|
||||
|
||||
if (callbacks != null) {
|
||||
callbacks.onTrackChanged(reason);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPositionDiscontinuity(int reason) {
|
||||
Log.i(TAG, String.format("onPositionDiscontinuity: %d", reason));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerError(ExoPlaybackException error) {
|
||||
Log.i(TAG, String.format("onPlayerError: %s", error.getMessage()));
|
||||
Toast.makeText(context, context.getResources().getString(R.string.exo_info_unplayable_file), Toast.LENGTH_SHORT).show();
|
||||
|
||||
exoPlayer.clearMediaItems();
|
||||
exoPlayer.prepare();
|
||||
}
|
||||
};
|
||||
|
||||
public MultiPlayer(Context context) {
|
||||
this.context = context;
|
||||
this.simpleCache = CacheUtil.getCache(context);
|
||||
|
||||
DataSource.Factory downloadDataSourceFactory = new CacheDataSource.Factory()
|
||||
.setCache(DownloadUtil.getDownloadCache(context))
|
||||
.setUpstreamDataSourceFactory(DownloadUtil.getHttpDataSourceFactory())
|
||||
.setCacheWriteDataSinkFactory(null); // Disable writing.
|
||||
|
||||
DataSource.Factory cacheDataSourceFactory = new CacheDataSource.Factory()
|
||||
.setCache(simpleCache)
|
||||
.setUpstreamDataSourceFactory(CacheUtil.getHttpDataSourceFactory());
|
||||
|
||||
AudioAttributes audioAttributes = new AudioAttributes.Builder()
|
||||
.setUsage(C.USAGE_MEDIA)
|
||||
.setContentType(C.CONTENT_TYPE_MUSIC)
|
||||
.build();
|
||||
|
||||
/* TODO: Capire come affiancare due media source factory */
|
||||
exoPlayer = new SimpleExoPlayer.Builder(context)
|
||||
.setMediaSourceFactory(new DefaultMediaSourceFactory(cacheDataSourceFactory))
|
||||
.setMediaSourceFactory(new DefaultMediaSourceFactory(downloadDataSourceFactory))
|
||||
.setAudioAttributes(audioAttributes, true)
|
||||
.setHandleAudioBecomingNoisy(true)
|
||||
.setWakeMode(C.WAKE_MODE_NETWORK)
|
||||
.build();
|
||||
|
||||
exoPlayer.addListener(eventListener);
|
||||
exoPlayer.prepare();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDataSource(Song song) {
|
||||
String uri = MusicUtil.getSongStreamUri(context, song);
|
||||
MediaItem mediaItem = exoPlayer.getCurrentMediaItem();
|
||||
|
||||
if (mediaItem != null && mediaItem.playbackProperties != null && mediaItem.playbackProperties.uri.toString().equals(uri)) {
|
||||
return;
|
||||
}
|
||||
|
||||
exoPlayer.clearMediaItems();
|
||||
appendDataSource(MusicUtil.getSongStreamUri(context, song));
|
||||
exoPlayer.seekTo(0, 0);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void queueDataSource(Song song) {
|
||||
while (exoPlayer.getMediaItemCount() > 1) {
|
||||
exoPlayer.removeMediaItem(1);
|
||||
}
|
||||
|
||||
appendDataSource(MusicUtil.getSongStreamUri(context, song));
|
||||
}
|
||||
|
||||
private void appendDataSource(String path) {
|
||||
Uri uri = Uri.parse(path);
|
||||
MediaItem mediaItem = MediaItem.fromUri(uri);
|
||||
|
||||
exoPlayer.addMediaItem(mediaItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCallbacks(Playback.PlaybackCallbacks callbacks) {
|
||||
this.callbacks = callbacks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReady() {
|
||||
return exoPlayer.getPlayWhenReady();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlaying() {
|
||||
return exoPlayer.getPlayWhenReady() && exoPlayer.getPlaybackSuppressionReason() == Player.PLAYBACK_SUPPRESSION_REASON_NONE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoading() {
|
||||
return exoPlayer.getPlaybackState() == Player.STATE_BUFFERING;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
exoPlayer.setPlayWhenReady(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pause() {
|
||||
exoPlayer.setPlayWhenReady(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
simpleCache.release();
|
||||
exoPlayer.release();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getProgress() {
|
||||
return (int) exoPlayer.getCurrentPosition();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProgress(int progress) {
|
||||
exoPlayer.seekTo(progress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDuration() {
|
||||
return (int) exoPlayer.getDuration();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVolume() {
|
||||
return (int) (exoPlayer.getVolume() * 100);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVolume(int volume) {
|
||||
exoPlayer.setVolume(volume / 100f);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,325 +0,0 @@
|
|||
package com.cappielloantonio.play.service;
|
||||
|
||||
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 androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.cappielloantonio.play.App;
|
||||
import com.cappielloantonio.play.model.Song;
|
||||
import com.cappielloantonio.play.repository.QueueRepository;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
public class MusicPlayerRemote {
|
||||
private static final String TAG = "MusicPlayerRemote";
|
||||
private static final WeakHashMap<Context, ServiceBinder> mConnectionMap = new WeakHashMap<>();
|
||||
public static MusicService musicService;
|
||||
|
||||
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 void playSongAt(final int position) {
|
||||
if (musicService != null) {
|
||||
musicService.playSongAt(position);
|
||||
}
|
||||
}
|
||||
|
||||
public static void pauseSong() {
|
||||
if (musicService != null) {
|
||||
musicService.pause();
|
||||
}
|
||||
}
|
||||
|
||||
public static void playNextSong() {
|
||||
if (musicService != null) {
|
||||
musicService.playNextSong();
|
||||
}
|
||||
}
|
||||
|
||||
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 boolean isLoading() {
|
||||
return musicService != null && musicService.isLoading();
|
||||
}
|
||||
|
||||
public static void quitPlaying() {
|
||||
if (musicService != null) {
|
||||
musicService.quitPlaying();
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
if (musicService != null) {
|
||||
return musicService.getCurrentSong();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static int getPosition() {
|
||||
if (musicService != null) {
|
||||
return musicService.getPosition();
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static void setPosition(final int position) {
|
||||
if (musicService != null) {
|
||||
musicService.setPosition(position);
|
||||
}
|
||||
}
|
||||
|
||||
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 int seekTo(int millis) {
|
||||
if (musicService != null) {
|
||||
return musicService.seek(millis);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static boolean playNext(Song song) {
|
||||
if (musicService != null) {
|
||||
QueueRepository queueRepository = new QueueRepository(App.getInstance());
|
||||
|
||||
if (getPlayingQueue().size() > 0) {
|
||||
musicService.addSong(getPosition() + 1, song);
|
||||
queueRepository.insertAllAndStartNew(getPlayingQueue());
|
||||
} else {
|
||||
List<Song> songToEnqueue = new ArrayList<>();
|
||||
songToEnqueue.add(song);
|
||||
queueRepository.insertAllAndStartNew(songToEnqueue);
|
||||
openQueue(songToEnqueue, 0, true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean playNext(@NonNull List<Song> songs) {
|
||||
if (musicService != null) {
|
||||
QueueRepository queueRepository = new QueueRepository(App.getInstance());
|
||||
|
||||
if (getPlayingQueue().size() > 0) {
|
||||
musicService.addSongs(getPosition() + 1, songs);
|
||||
queueRepository.insertAllAndStartNew(getPlayingQueue());
|
||||
} else {
|
||||
List<Song> songToEnqueue = new ArrayList<>();
|
||||
songToEnqueue.addAll(songs);
|
||||
queueRepository.insertAllAndStartNew(songToEnqueue);
|
||||
openQueue(songToEnqueue, 0, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean enqueue(Song song) {
|
||||
if (musicService != null) {
|
||||
QueueRepository queueRepository = new QueueRepository(App.getInstance());
|
||||
|
||||
if (getPlayingQueue().size() > 0) {
|
||||
musicService.addSong(song);
|
||||
queueRepository.insertAllAndStartNew(getPlayingQueue());
|
||||
} else {
|
||||
List<Song> songToEnqueue = new ArrayList<>();
|
||||
songToEnqueue.add(song);
|
||||
queueRepository.insertAllAndStartNew(songToEnqueue);
|
||||
openQueue(songToEnqueue, 0, true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean enqueue(@NonNull List<Song> songs) {
|
||||
if (musicService != null) {
|
||||
QueueRepository queueRepository = new QueueRepository(App.getInstance());
|
||||
|
||||
if (getPlayingQueue().size() > 0) {
|
||||
musicService.addSongs(songs);
|
||||
queueRepository.insertAllAndStartNew(getPlayingQueue());
|
||||
} else {
|
||||
List<Song> songToEnqueue = new ArrayList<>();
|
||||
songToEnqueue.addAll(songs);
|
||||
queueRepository.insertAllAndStartNew(songToEnqueue);
|
||||
openQueue(songToEnqueue, 0, true);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,718 +0,0 @@
|
|||
package com.cappielloantonio.play.service;
|
||||
|
||||
import static com.google.android.exoplayer2.Player.MEDIA_ITEM_TRANSITION_REASON_AUTO;
|
||||
import static com.google.android.exoplayer2.Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.os.Binder;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.support.v4.media.MediaMetadataCompat;
|
||||
import android.support.v4.media.session.MediaSessionCompat;
|
||||
import android.support.v4.media.session.PlaybackStateCompat;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.cappielloantonio.play.App;
|
||||
import com.cappielloantonio.play.R;
|
||||
import com.cappielloantonio.play.broadcast.receiver.MediaButtonIntentReceiver;
|
||||
import com.cappielloantonio.play.interfaces.Playback;
|
||||
import com.cappielloantonio.play.model.Playlist;
|
||||
import com.cappielloantonio.play.model.Song;
|
||||
import com.cappielloantonio.play.repository.QueueRepository;
|
||||
import com.cappielloantonio.play.repository.SongRepository;
|
||||
import com.cappielloantonio.play.ui.notification.PlayingNotification;
|
||||
import com.cappielloantonio.play.util.MusicUtil;
|
||||
import com.cappielloantonio.play.util.PreferenceUtil;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class MusicService extends Service implements Playback.PlaybackCallbacks {
|
||||
private static final String TAG = "MusicService";
|
||||
|
||||
public static final String PACKAGE_NAME = "com.antoniocappiello.play";
|
||||
public static final String ACTION_TOGGLE = PACKAGE_NAME + ".toggle";
|
||||
public static final String ACTION_PLAY = PACKAGE_NAME + ".play";
|
||||
public static final String ACTION_PLAY_PLAYLIST = PACKAGE_NAME + ".play.playlist";
|
||||
public static final String ACTION_PAUSE = PACKAGE_NAME + ".pause";
|
||||
public static final String ACTION_STOP = PACKAGE_NAME + ".stop";
|
||||
public static final String ACTION_SKIP = PACKAGE_NAME + ".skip";
|
||||
public static final String ACTION_REWIND = PACKAGE_NAME + ".rewind";
|
||||
public static final String ACTION_QUIT = PACKAGE_NAME + ".quit";
|
||||
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 STATE_CHANGED = PACKAGE_NAME + ".state.changed";
|
||||
public static final String META_CHANGED = PACKAGE_NAME + ".meta.changed";
|
||||
public static final String QUEUE_CHANGED = PACKAGE_NAME + ".queue.changed";
|
||||
|
||||
public static final int TRACK_CHANGED = 1;
|
||||
public static final int TRACK_ENDED = 2;
|
||||
public static final int PLAY_SONG = 3;
|
||||
public static final int PREPARE_NEXT = 4;
|
||||
public static final int SET_POSITION = 5;
|
||||
|
||||
private static final long MEDIA_SESSION_ACTIONS = PlaybackStateCompat.ACTION_PLAY
|
||||
| PlaybackStateCompat.ACTION_PAUSE
|
||||
| PlaybackStateCompat.ACTION_PLAY_PAUSE
|
||||
| PlaybackStateCompat.ACTION_SKIP_TO_NEXT
|
||||
| PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS
|
||||
| PlaybackStateCompat.ACTION_STOP
|
||||
| PlaybackStateCompat.ACTION_SEEK_TO;
|
||||
|
||||
private final IBinder musicBinder = new MusicBinder();
|
||||
|
||||
public boolean pendingQuit = false;
|
||||
private Playback playback;
|
||||
private List<Song> playingQueue = new ArrayList<>();
|
||||
private int position = -1;
|
||||
private int nextPosition = -1;
|
||||
private boolean notHandledMetaChangedForCurrentTrack;
|
||||
|
||||
private PlayingNotification playingNotification;
|
||||
private MediaSessionCompat mediaSession;
|
||||
private PlaybackHandler playerHandler;
|
||||
private Handler uiThreadHandler;
|
||||
private ThrottledSeekHandler throttledSeekHandler;
|
||||
private HandlerThread playerHandlerThread;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
playback = new MultiPlayer(this);
|
||||
playback.setCallbacks(this);
|
||||
|
||||
playerHandlerThread = new HandlerThread(PlaybackHandler.class.getName());
|
||||
playerHandlerThread.start();
|
||||
playerHandler = new PlaybackHandler(this, playerHandlerThread.getLooper());
|
||||
|
||||
throttledSeekHandler = new ThrottledSeekHandler(playerHandler);
|
||||
uiThreadHandler = new Handler();
|
||||
|
||||
initNotification();
|
||||
initMediaSession();
|
||||
restoreState();
|
||||
|
||||
mediaSession.setActive(true);
|
||||
}
|
||||
|
||||
private void initMediaSession() {
|
||||
ComponentName mediaButtonReceiverComponentName = new ComponentName(getApplicationContext(), MediaButtonIntentReceiver.class);
|
||||
|
||||
Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
|
||||
mediaButtonIntent.setComponent(mediaButtonReceiverComponentName);
|
||||
|
||||
PendingIntent mediaButtonReceiverPendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, mediaButtonIntent, PendingIntent.FLAG_IMMUTABLE);
|
||||
|
||||
mediaSession = new MediaSessionCompat(this, getResources().getString(R.string.app_name), mediaButtonReceiverComponentName, mediaButtonReceiverPendingIntent);
|
||||
mediaSession.setCallback(new MediaSessionCompat.Callback() {
|
||||
@Override
|
||||
public void onPlay() {
|
||||
play();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
pause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSkipToNext() {
|
||||
playNextSong();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSkipToPrevious() {
|
||||
back(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
quit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSeekTo(long pos) {
|
||||
seek((int) pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMediaButtonEvent(Intent mediaButtonEvent) {
|
||||
return MediaButtonIntentReceiver.handleIntent(MusicService.this, mediaButtonEvent);
|
||||
}
|
||||
});
|
||||
|
||||
mediaSession.setMediaButtonReceiver(mediaButtonReceiverPendingIntent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
|
||||
if (intent != null) {
|
||||
if (intent.getAction() != null) {
|
||||
String action = intent.getAction();
|
||||
switch (action) {
|
||||
case ACTION_TOGGLE:
|
||||
if (isPlaying()) {
|
||||
pause();
|
||||
} else {
|
||||
play();
|
||||
}
|
||||
break;
|
||||
case ACTION_PAUSE:
|
||||
pause();
|
||||
break;
|
||||
case ACTION_PLAY:
|
||||
play();
|
||||
break;
|
||||
case ACTION_PLAY_PLAYLIST:
|
||||
Playlist playlist = intent.getParcelableExtra(INTENT_EXTRA_PLAYLIST);
|
||||
if (playlist != null) {
|
||||
List<Song> playlistSongs = new ArrayList<>();
|
||||
if (!playlistSongs.isEmpty()) {
|
||||
openQueue(playlistSongs, 0, true);
|
||||
} else {
|
||||
Toast.makeText(getApplicationContext(), R.string.exo_info_empty_playlist, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(getApplicationContext(), R.string.exo_info_empty_playlist, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
break;
|
||||
case ACTION_REWIND:
|
||||
back(true);
|
||||
break;
|
||||
case ACTION_SKIP:
|
||||
playNextSong();
|
||||
break;
|
||||
case ACTION_STOP:
|
||||
case ACTION_QUIT:
|
||||
pendingQuit = false;
|
||||
quit();
|
||||
break;
|
||||
case ACTION_PENDING_QUIT:
|
||||
pendingQuit = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
mediaSession.setActive(false);
|
||||
quit();
|
||||
releaseResources();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return musicBinder;
|
||||
}
|
||||
|
||||
public void saveState() {
|
||||
savePosition();
|
||||
saveProgress();
|
||||
}
|
||||
|
||||
private void savePosition() {
|
||||
PreferenceUtil.getInstance(getApplicationContext()).setPosition(getPosition());
|
||||
}
|
||||
|
||||
private void saveProgress() {
|
||||
Log.d(TAG, "saveProgress(): " + getSongProgressMillis());
|
||||
PreferenceUtil.getInstance(getApplicationContext()).setProgress(getSongProgressMillis());
|
||||
}
|
||||
|
||||
private void restoreState() {
|
||||
try {
|
||||
QueueRepository queueRepository = new QueueRepository(App.getInstance());
|
||||
List<Song> restoredQueue = queueRepository.getSongs();
|
||||
|
||||
int restoredPosition = PreferenceUtil.getInstance(getApplicationContext()).getPosition();
|
||||
int restoredPositionInTrack = PreferenceUtil.getInstance(getApplicationContext()).getProgress();
|
||||
|
||||
if (restoredQueue.size() > 0 && restoredPosition != -1) {
|
||||
this.playingQueue = restoredQueue;
|
||||
|
||||
position = restoredPosition;
|
||||
openCurrent();
|
||||
|
||||
if (restoredPositionInTrack > 0) seek(restoredPositionInTrack);
|
||||
|
||||
notHandledMetaChangedForCurrentTrack = true;
|
||||
handleChangeInternal(META_CHANGED);
|
||||
handleChangeInternal(QUEUE_CHANGED);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void quit() {
|
||||
pause();
|
||||
playingNotification.stop();
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
private void releaseResources() {
|
||||
playerHandler.removeCallbacksAndMessages(null);
|
||||
playerHandlerThread.quitSafely();
|
||||
|
||||
playback.stop();
|
||||
mediaSession.release();
|
||||
}
|
||||
|
||||
public boolean isPlaying() {
|
||||
return playback != null && playback.isPlaying();
|
||||
}
|
||||
|
||||
public boolean isLoading() {
|
||||
return playback != null && playback.isLoading();
|
||||
}
|
||||
|
||||
public void quitPlaying() {
|
||||
quit();
|
||||
}
|
||||
|
||||
public int getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
public void setPosition(final int position) {
|
||||
// handle this on the handlers thread to avoid blocking the ui thread
|
||||
playerHandler.removeMessages(SET_POSITION);
|
||||
playerHandler.obtainMessage(SET_POSITION, position, 0).sendToTarget();
|
||||
}
|
||||
|
||||
public void playNextSong() {
|
||||
playSongAt(getNextPosition());
|
||||
}
|
||||
|
||||
private void openTrackAndPrepareNextAt(int position) {
|
||||
synchronized (this) {
|
||||
this.position = position;
|
||||
|
||||
openCurrent();
|
||||
playback.start();
|
||||
|
||||
notifyChange(META_CHANGED);
|
||||
notHandledMetaChangedForCurrentTrack = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void openCurrent() {
|
||||
synchronized (this) {
|
||||
// current song will be null when queue is cleared
|
||||
if (getCurrentSong() == null) return;
|
||||
|
||||
playback.setDataSource(getCurrentSong());
|
||||
}
|
||||
}
|
||||
|
||||
private void prepareNext() {
|
||||
playerHandler.removeMessages(PREPARE_NEXT);
|
||||
playerHandler.obtainMessage(PREPARE_NEXT).sendToTarget();
|
||||
}
|
||||
|
||||
private void prepareNextImpl() {
|
||||
synchronized (this) {
|
||||
nextPosition = getNextPosition();
|
||||
playback.queueDataSource(getSongAt(nextPosition));
|
||||
}
|
||||
|
||||
increaseSongCount();
|
||||
}
|
||||
|
||||
public void initNotification() {
|
||||
playingNotification = new PlayingNotification();
|
||||
playingNotification.init(this);
|
||||
}
|
||||
|
||||
public void updateNotification() {
|
||||
if (playingNotification != null && getCurrentSong() != null) {
|
||||
playingNotification.update();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateMediaSessionState() {
|
||||
mediaSession.setPlaybackState(
|
||||
new PlaybackStateCompat.Builder()
|
||||
.setActions(MEDIA_SESSION_ACTIONS)
|
||||
.setState(isPlaying() ? PlaybackStateCompat.STATE_PLAYING : PlaybackStateCompat.STATE_PAUSED, getSongProgressMillis(), 1)
|
||||
.build());
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
private void updateMediaSessionMetadata() {
|
||||
final Song song = getCurrentSong();
|
||||
|
||||
if (song == null) {
|
||||
mediaSession.setMetadata(null);
|
||||
return;
|
||||
}
|
||||
|
||||
final MediaMetadataCompat.Builder metaData = new MediaMetadataCompat.Builder()
|
||||
.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, MusicUtil.getReadableString(song.getArtistName()))
|
||||
.putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ARTIST, MusicUtil.getReadableString(song.getArtistName()))
|
||||
.putString(MediaMetadataCompat.METADATA_KEY_ALBUM, MusicUtil.getReadableString(song.getAlbumName()))
|
||||
.putString(MediaMetadataCompat.METADATA_KEY_TITLE, MusicUtil.getReadableString(song.getTitle()))
|
||||
.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, song.getDuration() * 1000)
|
||||
.putLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER, getPosition() + 1)
|
||||
.putLong(MediaMetadataCompat.METADATA_KEY_YEAR, song.getYear())
|
||||
.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, null);
|
||||
|
||||
metaData.putLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS, getPlayingQueue().size());
|
||||
|
||||
mediaSession.setMetadata(metaData.build());
|
||||
}
|
||||
|
||||
public void runOnUiThread(Runnable runnable) {
|
||||
uiThreadHandler.post(runnable);
|
||||
}
|
||||
|
||||
public Song getCurrentSong() {
|
||||
return getSongAt(getPosition());
|
||||
}
|
||||
|
||||
public Song getSongAt(int position) {
|
||||
if (position >= 0 && position < getPlayingQueue().size()) {
|
||||
return getPlayingQueue().get(position);
|
||||
} else {
|
||||
return new Song();
|
||||
}
|
||||
}
|
||||
|
||||
public int getNextPosition() {
|
||||
int position = getPosition() + 1;
|
||||
|
||||
if (isLastTrack()) {
|
||||
position -= 1;
|
||||
}
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
private boolean isLastTrack() {
|
||||
return getPosition() == getPlayingQueue().size() - 1;
|
||||
}
|
||||
|
||||
public List<Song> getPlayingQueue() {
|
||||
return playingQueue;
|
||||
}
|
||||
|
||||
public void openQueue(@Nullable final List<Song> playingQueue, final int startPosition, final boolean startPlaying) {
|
||||
if (playingQueue != null && !playingQueue.isEmpty() && startPosition >= 0 && startPosition < playingQueue.size()) {
|
||||
this.playingQueue = playingQueue;
|
||||
this.position = startPosition;
|
||||
|
||||
if (startPlaying) {
|
||||
playSongAt(position);
|
||||
} else {
|
||||
setPosition(position);
|
||||
}
|
||||
|
||||
notifyChange(QUEUE_CHANGED);
|
||||
}
|
||||
}
|
||||
|
||||
public void addSong(int position, Song song) {
|
||||
playingQueue.add(position, song);
|
||||
notifyChange(QUEUE_CHANGED);
|
||||
}
|
||||
|
||||
public void addSong(Song song) {
|
||||
playingQueue.add(song);
|
||||
notifyChange(QUEUE_CHANGED);
|
||||
}
|
||||
|
||||
public void addSongs(int position, List<Song> songs) {
|
||||
playingQueue.addAll(position, songs);
|
||||
notifyChange(QUEUE_CHANGED);
|
||||
}
|
||||
|
||||
public void addSongs(List<Song> songs) {
|
||||
playingQueue.addAll(songs);
|
||||
notifyChange(QUEUE_CHANGED);
|
||||
}
|
||||
|
||||
public void removeSong(int position) {
|
||||
playingQueue.remove(position);
|
||||
reposition(position);
|
||||
notifyChange(QUEUE_CHANGED);
|
||||
}
|
||||
|
||||
private void reposition(int deletedPosition) {
|
||||
int currentPosition = getPosition();
|
||||
if (deletedPosition < currentPosition) {
|
||||
position = currentPosition - 1;
|
||||
} else if (deletedPosition == currentPosition) {
|
||||
if (playingQueue.size() > deletedPosition) {
|
||||
setPosition(position);
|
||||
} else {
|
||||
setPosition(position - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void moveSong(int from, int to) {
|
||||
if (from == to) return;
|
||||
final int currentPosition = getPosition();
|
||||
Song songToMove = playingQueue.remove(from);
|
||||
playingQueue.add(to, songToMove);
|
||||
|
||||
if (from > currentPosition && to <= currentPosition) {
|
||||
position = currentPosition + 1;
|
||||
} else if (from < currentPosition && to >= currentPosition) {
|
||||
position = currentPosition - 1;
|
||||
} else if (from == currentPosition) {
|
||||
position = to;
|
||||
}
|
||||
|
||||
notifyChange(QUEUE_CHANGED);
|
||||
}
|
||||
|
||||
public void clearQueue() {
|
||||
playingQueue.clear();
|
||||
|
||||
setPosition(-1);
|
||||
notifyChange(QUEUE_CHANGED);
|
||||
}
|
||||
|
||||
public void playSongAt(final int position) {
|
||||
// handle this on the handlers thread to avoid blocking the ui thread
|
||||
playerHandler.removeMessages(PLAY_SONG);
|
||||
playerHandler.obtainMessage(PLAY_SONG, position, 0).sendToTarget();
|
||||
}
|
||||
|
||||
private void playSongAtImpl(int position) {
|
||||
openTrackAndPrepareNextAt(position);
|
||||
}
|
||||
|
||||
public void pause() {
|
||||
if (playback.isPlaying()) {
|
||||
playback.pause();
|
||||
notifyChange(STATE_CHANGED);
|
||||
}
|
||||
}
|
||||
|
||||
public void play() {
|
||||
synchronized (this) {
|
||||
if (!playback.isPlaying()) {
|
||||
if (!playback.isReady()) {
|
||||
playSongAt(getPosition());
|
||||
} else {
|
||||
playback.start();
|
||||
if (notHandledMetaChangedForCurrentTrack) {
|
||||
handleChangeInternal(META_CHANGED);
|
||||
notHandledMetaChangedForCurrentTrack = false;
|
||||
}
|
||||
notifyChange(STATE_CHANGED);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void playPreviousSong(boolean force) {
|
||||
playSongAt(getPreviousPosition(force));
|
||||
}
|
||||
|
||||
public void back(boolean force) {
|
||||
if (getSongProgressMillis() > 5000) {
|
||||
seek(0);
|
||||
} else {
|
||||
playPreviousSong(force);
|
||||
}
|
||||
}
|
||||
|
||||
public int getPreviousPosition(boolean force) {
|
||||
return getPosition() - 1;
|
||||
}
|
||||
|
||||
public int getSongProgressMillis() {
|
||||
return playback.getProgress();
|
||||
}
|
||||
|
||||
public int getSongDurationMillis() {
|
||||
return playback.getDuration();
|
||||
}
|
||||
|
||||
public int seek(int millis) {
|
||||
synchronized (this) {
|
||||
playback.setProgress(millis);
|
||||
throttledSeekHandler.notifySeek();
|
||||
return millis;
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyChange(@NonNull final String what) {
|
||||
handleChangeInternal(what);
|
||||
sendChangeInternal(what);
|
||||
}
|
||||
|
||||
private void sendChangeInternal(final String what) {
|
||||
sendBroadcast(new Intent(what));
|
||||
}
|
||||
|
||||
private void handleChangeInternal(@NonNull final String what) {
|
||||
switch (what) {
|
||||
case STATE_CHANGED:
|
||||
updateNotification();
|
||||
updateMediaSessionState();
|
||||
if (!isPlaying()) saveProgress();
|
||||
break;
|
||||
case META_CHANGED:
|
||||
updateNotification();
|
||||
updateMediaSessionMetadata();
|
||||
updateMediaSessionState();
|
||||
savePosition();
|
||||
saveProgress();
|
||||
break;
|
||||
case QUEUE_CHANGED:
|
||||
updateMediaSessionMetadata();
|
||||
saveState();
|
||||
if (playingQueue.size() > 0) {
|
||||
prepareNext();
|
||||
} else {
|
||||
playingNotification.stop();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public MediaSessionCompat getMediaSession() {
|
||||
return mediaSession;
|
||||
}
|
||||
|
||||
private void increaseSongCount() {
|
||||
SongRepository songRepository = new SongRepository(App.getInstance());
|
||||
QueueRepository queueRepository = new QueueRepository(App.getInstance());
|
||||
|
||||
songRepository.scrobble(getCurrentSong().getId());
|
||||
queueRepository.setTimestamp(getCurrentSong());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStateChanged(int state) {
|
||||
notifyChange(STATE_CHANGED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReadyChanged(boolean ready, int reason) {
|
||||
notifyChange(STATE_CHANGED);
|
||||
|
||||
if (ready) {
|
||||
prepareNext();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrackChanged(int reason) {
|
||||
if (reason == MEDIA_ITEM_TRANSITION_REASON_AUTO) {
|
||||
playerHandler.sendEmptyMessage(TRACK_CHANGED);
|
||||
} else if (reason == MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED) {
|
||||
prepareNext();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class PlaybackHandler extends Handler {
|
||||
private final WeakReference<MusicService> mService;
|
||||
|
||||
public PlaybackHandler(final MusicService service, @NonNull final Looper looper) {
|
||||
super(looper);
|
||||
mService = new WeakReference<>(service);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(@NonNull final Message msg) {
|
||||
final MusicService service = mService.get();
|
||||
if (service == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (msg.what) {
|
||||
case TRACK_CHANGED:
|
||||
if (service.isLastTrack()) {
|
||||
service.pause();
|
||||
service.seek(0);
|
||||
service.notifyChange(STATE_CHANGED);
|
||||
} else {
|
||||
service.position = service.nextPosition;
|
||||
service.prepareNextImpl();
|
||||
service.notifyChange(META_CHANGED);
|
||||
service.notifyChange(QUEUE_CHANGED);
|
||||
}
|
||||
break;
|
||||
|
||||
case TRACK_ENDED:
|
||||
// if there is a timer finished, don't continue
|
||||
if (service.pendingQuit && service.isLastTrack()) {
|
||||
service.notifyChange(STATE_CHANGED);
|
||||
service.seek(0);
|
||||
|
||||
if (service.pendingQuit) {
|
||||
service.pendingQuit = false;
|
||||
service.quit();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
service.playNextSong();
|
||||
}
|
||||
break;
|
||||
|
||||
case PLAY_SONG:
|
||||
service.playSongAtImpl(msg.arg1);
|
||||
service.notifyChange(STATE_CHANGED);
|
||||
break;
|
||||
|
||||
case SET_POSITION:
|
||||
service.openTrackAndPrepareNextAt(msg.arg1);
|
||||
service.notifyChange(STATE_CHANGED);
|
||||
break;
|
||||
|
||||
case PREPARE_NEXT:
|
||||
service.prepareNextImpl();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class MusicBinder extends Binder {
|
||||
@NonNull
|
||||
public MusicService getService() {
|
||||
return MusicService.this;
|
||||
}
|
||||
}
|
||||
|
||||
private class ThrottledSeekHandler implements Runnable {
|
||||
// milliseconds to throttle before calling run to aggregate events
|
||||
private static final long THROTTLE = 500;
|
||||
private final Handler mHandler;
|
||||
|
||||
public ThrottledSeekHandler(Handler handler) {
|
||||
mHandler = handler;
|
||||
}
|
||||
|
||||
public void notifySeek() {
|
||||
mHandler.removeCallbacks(this);
|
||||
mHandler.postDelayed(this, THROTTLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
notifyChange(STATE_CHANGED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -15,7 +15,6 @@ import com.cappielloantonio.play.model.Song;
|
|||
import com.cappielloantonio.play.repository.ArtistRepository;
|
||||
import com.cappielloantonio.play.repository.QueueRepository;
|
||||
import com.cappielloantonio.play.repository.SongRepository;
|
||||
import com.cappielloantonio.play.service.MusicPlayerRemote;
|
||||
import com.cappielloantonio.play.util.DownloadUtil;
|
||||
import com.cappielloantonio.play.util.PreferenceUtil;
|
||||
|
||||
|
|
@ -49,7 +48,8 @@ public class PlayerBottomSheetViewModel extends AndroidViewModel {
|
|||
}
|
||||
|
||||
public Song getCurrentSong() {
|
||||
return MusicPlayerRemote.getCurrentSong();
|
||||
// return MusicPlayerRemote.getCurrentSong();
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setFavorite(Context context) {
|
||||
|
|
@ -64,7 +64,7 @@ public class PlayerBottomSheetViewModel extends AndroidViewModel {
|
|||
song.setFavorite(true);
|
||||
|
||||
if(PreferenceUtil.getInstance(context).isStarredSyncEnabled()) {
|
||||
DownloadUtil.getDownloadTracker(context).download(Collections.singletonList(song), null, null);
|
||||
// DownloadUtil.getDownloadTracker(context).download(Collections.singletonList(song), null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue