add getVolume to playback interface and improve progress handler

This commit is contained in:
dkanada 2020-10-17 17:07:36 +09:00
commit 2c8229b0dc
4 changed files with 61 additions and 58 deletions

View file

@ -2,10 +2,10 @@ package com.dkanada.gramophone.service;
import android.content.Context; import android.content.Context;
import android.net.Uri; import android.net.Uri;
import android.os.Environment;
import android.util.Log; import android.util.Log;
import android.widget.Toast; import android.widget.Toast;
import com.dkanada.gramophone.App;
import com.dkanada.gramophone.R; import com.dkanada.gramophone.R;
import com.dkanada.gramophone.model.Song; import com.dkanada.gramophone.model.Song;
import com.dkanada.gramophone.service.playback.Playback; import com.dkanada.gramophone.service.playback.Playback;
@ -118,7 +118,8 @@ public class MultiPlayer implements Playback {
LeastRecentlyUsedCacheEvictor recentlyUsedCache = new LeastRecentlyUsedCacheEvictor(Long.MAX_VALUE); LeastRecentlyUsedCacheEvictor recentlyUsedCache = new LeastRecentlyUsedCacheEvictor(Long.MAX_VALUE);
ExoDatabaseProvider databaseProvider = new ExoDatabaseProvider(context); ExoDatabaseProvider databaseProvider = new ExoDatabaseProvider(context);
simpleCache = new SimpleCache(new File(Environment.getExternalStorageDirectory() + "/Gelli/cache"), recentlyUsedCache, databaseProvider); File file = new File(App.getInstance().getApplicationInfo().dataDir + "/Gelli/exoplayer");
simpleCache = new SimpleCache(file, recentlyUsedCache, databaseProvider);
dataSource = buildDataSourceFactory(); dataSource = buildDataSourceFactory();
} }
@ -184,12 +185,7 @@ public class MultiPlayer implements Playback {
.createMediaSource(uri); .createMediaSource(uri);
} }
if (mediaSource.getSize() < position) { mediaSource.addMediaSource(Math.min(mediaSource.getSize(), position), source);
mediaSource.addMediaSource(mediaSource.getSize(), source);
} else {
mediaSource.addMediaSource(position, source);
}
if (position == 0) start(); if (position == 0) start();
} }
}); });
@ -247,7 +243,7 @@ public class MultiPlayer implements Playback {
} }
@Override @Override
public int getPosition() { public int getProgress() {
if (!isReady) return -1; if (!isReady) return -1;
return (int) exoPlayer.getCurrentPosition(); return (int) exoPlayer.getCurrentPosition();
} }
@ -259,7 +255,7 @@ public class MultiPlayer implements Playback {
} }
@Override @Override
public void setPosition(int position) { public void setProgress(int position) {
exoPlayer.seekTo(position); exoPlayer.seekTo(position);
} }
@ -267,4 +263,9 @@ public class MultiPlayer implements Playback {
public void setVolume(int volume) { public void setVolume(int volume) {
exoPlayer.setVolume(volume / 100f); exoPlayer.setVolume(volume / 100f);
} }
@Override
public int getVolume() {
return (int) (exoPlayer.getVolume() * 100);
}
} }

View file

@ -63,8 +63,10 @@ import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.Random;
import java.util.Timer; import java.util.concurrent.Executors;
import java.util.TimerTask; import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class MusicService extends Service implements SharedPreferences.OnSharedPreferenceChangeListener, Playback.PlaybackCallbacks { public class MusicService extends Service implements SharedPreferences.OnSharedPreferenceChangeListener, Playback.PlaybackCallbacks {
public static final String PHONOGRAPH_PACKAGE_NAME = "com.dkanada.gramophone"; public static final String PHONOGRAPH_PACKAGE_NAME = "com.dkanada.gramophone";
@ -78,7 +80,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
public static final String ACTION_REWIND = PHONOGRAPH_PACKAGE_NAME + ".rewind"; public static final String ACTION_REWIND = PHONOGRAPH_PACKAGE_NAME + ".rewind";
public static final String ACTION_QUIT = PHONOGRAPH_PACKAGE_NAME + ".quitservice"; public static final String ACTION_QUIT = PHONOGRAPH_PACKAGE_NAME + ".quitservice";
public static final String ACTION_PENDING_QUIT = PHONOGRAPH_PACKAGE_NAME + ".pendingquitservice"; public static final String ACTION_PENDING_QUIT = PHONOGRAPH_PACKAGE_NAME + ".pendingquitservice";
public static final String INTENT_EXTRA_PLAYLIST = PHONOGRAPH_PACKAGE_NAME + "intentextra.playlist"; public static final String INTENT_EXTRA_PLAYLIST = PHONOGRAPH_PACKAGE_NAME + ".intentextra.playlist";
public static final String INTENT_EXTRA_SHUFFLE_MODE = PHONOGRAPH_PACKAGE_NAME + ".intentextra.shufflemode"; public static final String INTENT_EXTRA_SHUFFLE_MODE = PHONOGRAPH_PACKAGE_NAME + ".intentextra.shufflemode";
public static final String APP_WIDGET_UPDATE = PHONOGRAPH_PACKAGE_NAME + ".appwidgetupdate"; public static final String APP_WIDGET_UPDATE = PHONOGRAPH_PACKAGE_NAME + ".appwidgetupdate";
@ -101,7 +103,6 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
public static final int FOCUS_CHANGE = 6; public static final int FOCUS_CHANGE = 6;
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 RESTORE_QUEUES = 9;
public static final int SHUFFLE_MODE_NONE = 0; public static final int SHUFFLE_MODE_NONE = 0;
public static final int SHUFFLE_MODE_SHUFFLE = 1; public static final int SHUFFLE_MODE_SHUFFLE = 1;
@ -111,6 +112,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
public static final int REPEAT_MODE_THIS = 2; public static final int REPEAT_MODE_THIS = 2;
public static final int SAVE_QUEUE = 0; public static final int SAVE_QUEUE = 0;
public static final int LOAD_QUEUE = 9;
private final IBinder musicBinder = new MusicBinder(); private final IBinder musicBinder = new MusicBinder();
@ -121,10 +123,13 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
private AppWidgetClassic appWidgetClassic = AppWidgetClassic.getInstance(); private AppWidgetClassic appWidgetClassic = AppWidgetClassic.getInstance();
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 List<Song> originalPlayingQueue = new ArrayList<>();
private int position = -1; private int position = -1;
private int nextPosition = -1; private int nextPosition = -1;
private int shuffleMode; private int shuffleMode;
private int repeatMode; private int repeatMode;
private boolean queuesRestored; private boolean queuesRestored;
@ -146,6 +151,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
private ThrottledSeekHandler throttledSeekHandler; private ThrottledSeekHandler throttledSeekHandler;
private QueueSaveHandler queueSaveHandler; private QueueSaveHandler queueSaveHandler;
private ProgressHandler progressHandler; private ProgressHandler progressHandler;
private HandlerThread playerHandlerThread; private HandlerThread playerHandlerThread;
private HandlerThread queueSaveHandlerThread; private HandlerThread queueSaveHandlerThread;
@ -176,16 +182,13 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, getClass().getName()); wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, getClass().getName());
wakeLock.setReferenceCounted(false); wakeLock.setReferenceCounted(false);
playback = new MultiPlayer(this);
playback.setCallbacks(this);
playerHandlerThread = new HandlerThread(PlaybackHandler.class.getName()); playerHandlerThread = new HandlerThread(PlaybackHandler.class.getName());
playerHandlerThread.start(); playerHandlerThread.start();
playerHandler = new PlaybackHandler(this, playerHandlerThread.getLooper()); playerHandler = new PlaybackHandler(this, playerHandlerThread.getLooper());
playback = new MultiPlayer(this);
playback.setCallbacks(this);
initMediaSession();
// queue saving needs to run on a separate thread so that it doesn't block the playback handler events
queueSaveHandlerThread = new HandlerThread(QueueSaveHandler.class.getName(), Process.THREAD_PRIORITY_BACKGROUND); queueSaveHandlerThread = new HandlerThread(QueueSaveHandler.class.getName(), Process.THREAD_PRIORITY_BACKGROUND);
queueSaveHandlerThread.start(); queueSaveHandlerThread.start();
queueSaveHandler = new QueueSaveHandler(this, queueSaveHandlerThread.getLooper()); queueSaveHandler = new QueueSaveHandler(this, queueSaveHandlerThread.getLooper());
@ -200,6 +203,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
PreferenceUtil.getInstance(this).registerOnSharedPreferenceChangedListener(this); PreferenceUtil.getInstance(this).registerOnSharedPreferenceChangedListener(this);
initNotification(); initNotification();
initMediaSession();
restoreState(); restoreState();
mediaSession.setActive(true); mediaSession.setActive(true);
@ -388,8 +392,8 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
notifyChange(SHUFFLE_MODE_CHANGED); notifyChange(SHUFFLE_MODE_CHANGED);
notifyChange(REPEAT_MODE_CHANGED); notifyChange(REPEAT_MODE_CHANGED);
playerHandler.removeMessages(RESTORE_QUEUES); playerHandler.removeMessages(LOAD_QUEUE);
playerHandler.sendEmptyMessage(RESTORE_QUEUES); playerHandler.sendEmptyMessage(LOAD_QUEUE);
} }
private synchronized void restoreQueuesAndPositionIfNecessary() { private synchronized void restoreQueuesAndPositionIfNecessary() {
@ -843,7 +847,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
} }
public int getSongProgressMillis() { public int getSongProgressMillis() {
return playback.getPosition(); return playback.getProgress();
} }
public int getSongDurationMillis() { public int getSongDurationMillis() {
@ -861,7 +865,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
public int seek(int millis) { public int seek(int millis) {
synchronized (this) { synchronized (this) {
playback.setPosition(millis); playback.setProgress(millis);
throttledSeekHandler.notifySeek(); throttledSeekHandler.notifySeek();
return millis; return millis;
} }
@ -1112,7 +1116,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
service.prepareNextImpl(); service.prepareNextImpl();
break; break;
case RESTORE_QUEUES: case LOAD_QUEUE:
service.restoreQueuesAndPositionIfNecessary(); service.restoreQueuesAndPositionIfNecessary();
break; break;
@ -1196,33 +1200,29 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
@Override @Override
public void run() { public void run() {
saveProgress();
notifyChange(PLAY_STATE_CHANGED); notifyChange(PLAY_STATE_CHANGED);
} }
} }
private static final class ProgressHandler extends Handler { private static final class ProgressHandler extends Handler {
private WeakReference<MusicService> mService; private WeakReference<MusicService> mService;
private Timer mTimer;
private ScheduledExecutorService executorService;
private Future<?> task;
public ProgressHandler(MusicService service, Looper looper) { public ProgressHandler(MusicService service, Looper looper) {
super(looper); super(looper);
mService = new WeakReference<>(service); mService = new WeakReference<>(service);
mTimer = new Timer();
} }
@Override @Override
public void handleMessage(@NonNull final Message msg) { public void handleMessage(@NonNull final Message msg) {
final MusicService service = mService.get();
if (service == null) {
return;
}
switch (msg.what) { switch (msg.what) {
case PLAY_SONG: case PLAY_SONG:
case TRACK_WENT_TO_NEXT:
onStart(); onStart();
case TRACK_WENT_TO_NEXT:
onNext();
break; break;
case TRACK_ENDED: case TRACK_ENDED:
onStop(); onStop();
@ -1230,20 +1230,21 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
} }
public void onStart() { public void onStart() {
if (executorService != null) executorService.shutdownNow();
executorService = Executors.newScheduledThreadPool(1);
task = executorService.scheduleAtFixedRate(this::onProgress, 10, 10, TimeUnit.SECONDS);
}
public void onNext() {
PlaybackStartInfo startInfo = new PlaybackStartInfo(); PlaybackStartInfo startInfo = new PlaybackStartInfo();
TimerTask mTask = new TimerTask() {
@Override
public void run() {
onProgress();
}
};
startInfo.setItemId(mService.get().getCurrentSong().id); startInfo.setItemId(mService.get().getCurrentSong().id);
startInfo.setVolumeLevel(mService.get().playback.getVolume());
startInfo.setCanSeek(true); startInfo.setCanSeek(true);
startInfo.setIsPaused(false); startInfo.setIsPaused(false);
App.getApiClient().ReportPlaybackStartAsync(startInfo, new EmptyResponse()); App.getApiClient().ReportPlaybackStartAsync(startInfo, new EmptyResponse());
mTimer.schedule(mTask, 10000, 10000);
} }
public void onProgress() { public void onProgress() {
@ -1252,26 +1253,26 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
// TODO these cause a wrong thread error // TODO these cause a wrong thread error
long progress = mService.get().getSongProgressMillis(); long progress = mService.get().getSongProgressMillis();
double duration = mService.get().getSongDurationMillis(); double duration = mService.get().getSongDurationMillis();
if (progress / duration > 0.9) onStop(); if (progress / duration > 0.9) {
Song current = mService.get().getCurrentSong();
String user = App.getApiClient().getCurrentUserId();
Date time = new Date(System.currentTimeMillis());
App.getApiClient().MarkPlayedAsync(current.id, user, time, new Response<>());
}
progressInfo.setItemId(mService.get().getCurrentSong().id); progressInfo.setItemId(mService.get().getCurrentSong().id);
progressInfo.setPositionTicks(progress * 10000); progressInfo.setPositionTicks(progress * 10000);
progressInfo.setCanSeek(true); progressInfo.setVolumeLevel(mService.get().playback.getVolume());
progressInfo.setIsPaused(!mService.get().playback.isPlaying()); progressInfo.setIsPaused(!mService.get().playback.isPlaying());
progressInfo.setCanSeek(true);
App.getApiClient().ensureWebSocket();
App.getApiClient().ReportPlaybackProgressAsync(progressInfo, new EmptyResponse()); App.getApiClient().ReportPlaybackProgressAsync(progressInfo, new EmptyResponse());
} }
public void onStop() { public void onStop() {
mTimer.purge(); task.cancel(true);
executorService.shutdownNow();
Song current = mService.get().getCurrentSong();
String user = App.getApiClient().getCurrentUserId();
Date time = new Date(System.currentTimeMillis());
if (current == Song.EMPTY_SONG) return;
App.getApiClient().MarkPlayedAsync(current.id, user, time, new Response<>());
} }
} }
} }

View file

@ -19,14 +19,16 @@ public interface Playback {
boolean isPlaying(); boolean isPlaying();
int getPosition(); int getProgress();
int getDuration(); int getDuration();
void setPosition(int position); void setProgress(int position);
void setVolume(int volume); void setVolume(int volume);
int getVolume();
interface PlaybackCallbacks { interface PlaybackCallbacks {
void onTrackStarted(); void onTrackStarted();

View file

@ -55,16 +55,16 @@ public class SplashActivity extends AbsBaseActivity {
builder.setMessage(R.string.battery_optimizations_message) builder.setMessage(R.string.battery_optimizations_message)
.setTitle(R.string.battery_optimizations_title) .setTitle(R.string.battery_optimizations_title)
.setNegativeButton(R.string.ignore, (dialog, id) -> login()) .setNegativeButton(R.string.ignore, (dialog, id) -> login())
.setPositiveButton(R.string.disable, (dialog, id) -> openPowerSettings(SplashActivity.this)) .setPositiveButton(R.string.disable, (dialog, id) -> openPowerSettings())
.show(); .show();
} }
@RequiresApi(api = Build.VERSION_CODES.M) @RequiresApi(api = Build.VERSION_CODES.M)
private void openPowerSettings(Context context) { private void openPowerSettings() {
Intent intent = new Intent(); Intent intent = new Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setAction(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS); intent.setAction(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);
context.startActivity(intent); startActivity(intent);
} }
public void login() { public void login() {
@ -102,6 +102,5 @@ public class SplashActivity extends AbsBaseActivity {
Intent intent = new Intent(this, LoginActivity.class); Intent intent = new Intent(this, LoginActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent); startActivity(intent);
finish();
} }
} }