Fix notification bitmap and progressbar

This commit is contained in:
CappielloAntonio 2021-04-12 10:35:41 +02:00
parent b1fca633a6
commit 54bc709317
10 changed files with 247 additions and 228 deletions

View file

@ -55,11 +55,27 @@ public class CustomGlideRequest {
return new Builder(context, item, placeholder, itemType, quality);
}
public BitmapBuilder bitmap() {
return new BitmapBuilder(this);
}
public RequestBuilder<Drawable> build() {
return requestManager.load(item);
}
}
public static class BitmapBuilder {
private final Builder builder;
public BitmapBuilder(Builder builder) {
this.builder = builder;
}
public RequestBuilder<Bitmap> build() {
return builder.requestManager.asBitmap().load(builder.item);
}
}
public static RequestOptions createRequestOptions(String item, Drawable placeholder) {
RequestOptions options = new RequestOptions()
.placeholder(placeholder)

View file

@ -1,23 +0,0 @@
package com.cappielloantonio.play.glide.palette;
import android.graphics.Bitmap;
import androidx.palette.graphics.Palette;
public class BitmapPaletteWrapper {
private final Bitmap bitmap;
private final Palette palette;
public BitmapPaletteWrapper(Bitmap bitmap, Palette palette) {
this.bitmap = bitmap;
this.palette = palette;
}
public Bitmap getBitmap() {
return bitmap;
}
public Palette getPalette() {
return palette;
}
}

View file

@ -138,6 +138,10 @@ public class MusicPlayerRemote {
return musicService != null && musicService.isPlaying();
}
public static boolean isLoading() {
return musicService != null && musicService.isLoading();
}
public static void resumePlaying() {
if (musicService != null) {
musicService.play();

View file

@ -6,4 +6,4 @@ public interface MediaCallback {
void onError(Exception exception);
void onLoadMedia(List<?> media);
}
}

View file

@ -5,8 +5,6 @@ import android.net.Uri;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.NonNull;
import com.cappielloantonio.play.R;
import com.cappielloantonio.play.model.Song;
import com.cappielloantonio.play.service.playback.Playback;
@ -15,14 +13,10 @@ import com.cappielloantonio.play.util.PreferenceUtil;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.database.ExoDatabaseProvider;
import com.google.android.exoplayer2.source.ConcatenatingMediaSource;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.source.hls.HlsMediaSource;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.source.MediaSourceFactory;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import com.google.android.exoplayer2.upstream.FileDataSource;
@ -32,117 +26,62 @@ import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvicto
import com.google.android.exoplayer2.upstream.cache.SimpleCache;
import java.io.File;
import java.io.IOException;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Dispatcher;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class MultiPlayer implements Playback {
public static final String TAG = MultiPlayer.class.getSimpleName();
private final Context context;
private final OkHttpClient httpClient;
private SimpleExoPlayer exoPlayer;
private ConcatenatingMediaSource mediaSource;
private final SimpleExoPlayer exoPlayer;
private final SimpleCache simpleCache;
private final DataSource.Factory dataSource;
private PlaybackCallbacks callbacks;
private boolean isReady = false;
private boolean isPlaying = false;
private boolean requestPlay = false;
private int requestProgress = 0;
private final ExoPlayer.EventListener eventListener = new ExoPlayer.EventListener() {
@Override
public void onTracksChanged(@NonNull TrackGroupArray trackGroups, @NonNull TrackSelectionArray trackSelections) {
Log.i(TAG, "onTracksChanged");
}
@Override
public void onIsLoadingChanged(boolean isLoadingChanged) {
Log.i(TAG, "onIsLoadingChanged: " + isLoadingChanged);
}
@Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
Log.i(TAG, "onPlayerStateChanged playWhenReady: " + playWhenReady);
Log.i(TAG, "onPlayerStateChanged playbackState: " + playbackState);
if (callbacks == null) return;
if (requestProgress != 0 && playbackState == Player.STATE_READY) {
exoPlayer.seekTo(requestProgress);
requestProgress = 0;
}
if (exoPlayer.isPlaying() || requestPlay && playbackState == ExoPlayer.STATE_READY) {
requestPlay = false;
isPlaying = true;
exoPlayer.setPlayWhenReady(true);
callbacks.onTrackStarted();
}
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 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, "onPositionDiscontinuity: " + reason);
int windowIndex = exoPlayer.getCurrentWindowIndex();
if (windowIndex == 1) {
mediaSource.removeMediaSource(0);
if (exoPlayer.isPlaying()) {
// there are still songs left in the queue
callbacks.onTrackWentToNext();
} else {
callbacks.onTrackEnded();
}
}
Log.i(TAG, String.format("onPositionDiscontinuity: %d", reason));
}
@Override
public void onPlayerError(ExoPlaybackException error) {
Log.i(TAG, "onPlayerError: " + error.getMessage());
if (context == null) {
return;
}
Log.i(TAG, String.format("onPlayerError: %s", error.getMessage()));
Toast.makeText(context, context.getResources().getString(R.string.unplayable_file), Toast.LENGTH_SHORT).show();
exoPlayer.release();
exoPlayer = new SimpleExoPlayer.Builder(context).build();
isReady = false;
}
};
public MultiPlayer(Context context) {
this.context = context;
Dispatcher dispatcher = new Dispatcher();
dispatcher.setMaxRequests(1);
httpClient = new OkHttpClient.Builder().dispatcher(dispatcher).build();
exoPlayer = new SimpleExoPlayer.Builder(context).build();
mediaSource = new ConcatenatingMediaSource();
MediaSourceFactory mediaSourceFactory = new UnknownMediaSourceFactory(buildDataSourceFactory());
exoPlayer = new SimpleExoPlayer.Builder(context).setMediaSourceFactory(mediaSourceFactory).build();
exoPlayer.addListener(eventListener);
exoPlayer.prepare(mediaSource);
exoPlayer.setRepeatMode(Player.REPEAT_MODE_OFF);
exoPlayer.prepare();
long cacheSize = PreferenceUtil.getInstance(context).getMediaCacheSize();
LeastRecentlyUsedCacheEvictor recentlyUsedCache = new LeastRecentlyUsedCacheEvictor(cacheSize);
@ -150,63 +89,36 @@ public class MultiPlayer implements Playback {
File cacheDirectory = new File(context.getCacheDir(), "exoplayer");
simpleCache = new SimpleCache(cacheDirectory, recentlyUsedCache, databaseProvider);
dataSource = buildDataSourceFactory();
}
@Override
public void setDataSource(Song song) {
isReady = false;
mediaSource = new ConcatenatingMediaSource();
String uri = MusicUtil.getSongFileUri(song);
MediaItem mediaItem = exoPlayer.getCurrentMediaItem();
exoPlayer.addListener(eventListener);
exoPlayer.prepare(mediaSource);
// queue and other information is currently handled outside exoplayer
exoPlayer.setRepeatMode(Player.REPEAT_MODE_OFF);
if (mediaItem != null && mediaItem.playbackProperties.uri.toString().equals(uri)) {
return;
}
exoPlayer.clearMediaItems();
appendDataSource(MusicUtil.getSongFileUri(song));
exoPlayer.seekTo(0, 0);
}
@Override
public void queueDataSource(Song song) {
String path = MusicUtil.getSongFileUri(song);
if (mediaSource.getSize() == 2 && mediaSource.getMediaSource(1).getTag() != path) {
mediaSource.removeMediaSource(1);
while (exoPlayer.getMediaItemCount() > 1) {
exoPlayer.removeMediaItem(1);
}
if (mediaSource.getSize() != 2) {
appendDataSource(path);
}
appendDataSource(MusicUtil.getSongFileUri(song));
}
private void appendDataSource(String path) {
Uri uri = Uri.parse(path);
MediaItem mediaItem = MediaItem.fromUri(uri);
httpClient.newCall(new Request.Builder().url(path).head().build()).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Toast.makeText(context, context.getResources().getString(R.string.unplayable_file), Toast.LENGTH_SHORT).show();
e.printStackTrace();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
MediaSource source;
if (response.header("Content-Type").equals("application/x-mpegURL")) {
source = new HlsMediaSource.Factory(dataSource)
.setTag(path)
.setAllowChunklessPreparation(true)
.createMediaSource(uri);
} else {
source = new ProgressiveMediaSource.Factory(dataSource)
.setTag(path)
.createMediaSource(uri);
}
mediaSource.addMediaSource(source);
isReady = true;
}
});
exoPlayer.addMediaItem(mediaItem);
}
private DataSource.Factory buildDataSourceFactory() {
@ -227,28 +139,26 @@ public class MultiPlayer implements Playback {
@Override
public boolean isReady() {
return isReady;
return exoPlayer.getPlayWhenReady();
}
@Override
public boolean isPlaying() {
return isReady && isPlaying;
return exoPlayer.isPlaying() || exoPlayer.getPlayWhenReady();
}
@Override
public boolean isLoading() {
return exoPlayer.getPlaybackState() == Player.STATE_BUFFERING;
}
@Override
public void start() {
if (!isReady) {
requestPlay = true;
return;
}
isPlaying = true;
exoPlayer.setPlayWhenReady(true);
}
@Override
public void pause() {
isPlaying = false;
exoPlayer.setPlayWhenReady(false);
}
@ -256,30 +166,20 @@ public class MultiPlayer implements Playback {
public void stop() {
simpleCache.release();
exoPlayer.release();
exoPlayer = null;
isReady = false;
}
@Override
public int getProgress() {
if (!isReady) return -1;
return (int) exoPlayer.getCurrentPosition();
}
@Override
public int getDuration() {
if (!isReady) return -1;
return (int) exoPlayer.getDuration();
}
@Override
public void setProgress(int progress) {
if (!isReady) {
requestProgress = progress;
return;
}
exoPlayer.seekTo(progress);
}

View file

@ -50,8 +50,12 @@ import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
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 static com.google.android.exoplayer2.Player.PLAY_WHEN_READY_CHANGE_REASON_END_OF_MEDIA_ITEM;
public class MusicService extends Service implements Playback.PlaybackCallbacks {
public static final String PACKAGE_NAME = "com.dkanada.gramophone";
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";
@ -104,6 +108,7 @@ public class MusicService extends Service implements Playback.PlaybackCallbacks
private PowerManager.WakeLock wakeLock;
private PlaybackHandler playerHandler;
private Handler uiThreadHandler;
private ThrottledSeekHandler throttledSeekHandler;
private QueueHandler queueHandler;
private ProgressHandler progressHandler;
@ -160,6 +165,7 @@ public class MusicService extends Service implements Playback.PlaybackCallbacks
queueHandler = new QueueHandler(this, queueHandlerThread.getLooper());
throttledSeekHandler = new ThrottledSeekHandler(playerHandler);
uiThreadHandler = new Handler();
registerReceiver(becomingNoisyReceiver, new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY));
@ -352,8 +358,8 @@ public class MusicService extends Service implements Playback.PlaybackCallbacks
if (restoredPositionInTrack > 0) seek(restoredPositionInTrack);
notHandledMetaChangedForCurrentTrack = true;
sendChangeInternal(META_CHANGED);
sendChangeInternal(QUEUE_CHANGED);
handleChangeInternal(META_CHANGED);
handleChangeInternal(QUEUE_CHANGED);
}
}
@ -386,6 +392,10 @@ public class MusicService extends Service implements Playback.PlaybackCallbacks
return playback != null && playback.isPlaying();
}
public boolean isLoading() {
return playback != null && playback.isLoading();
}
public int getPosition() {
return position;
}
@ -433,7 +443,6 @@ public class MusicService extends Service implements Playback.PlaybackCallbacks
public void initNotification() {
playingNotification = new PlayingNotification();
playingNotification.init(this);
}
@ -471,6 +480,12 @@ public class MusicService extends Service implements Playback.PlaybackCallbacks
.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() {
@ -714,23 +729,31 @@ public class MusicService extends Service implements Playback.PlaybackCallbacks
}
@Override
public void onTrackStarted() {
progressHandler.sendEmptyMessage(TRACK_STARTED);
public void onStateChanged(int state) {
notifyChange(STATE_CHANGED);
prepareNext();
}
@Override
public void onTrackWentToNext() {
playerHandler.sendEmptyMessage(TRACK_CHANGED);
progressHandler.sendEmptyMessage(TRACK_CHANGED);
public void onReadyChanged(boolean ready, int reason) {
notifyChange(STATE_CHANGED);
if (ready) {
progressHandler.sendEmptyMessage(TRACK_STARTED);
prepareNext();
} else if (reason == PLAY_WHEN_READY_CHANGE_REASON_END_OF_MEDIA_ITEM) {
progressHandler.sendEmptyMessage(TRACK_ENDED);
}
}
@Override
public void onTrackEnded() {
playerHandler.sendEmptyMessage(TRACK_ENDED);
progressHandler.sendEmptyMessage(TRACK_ENDED);
public void onTrackChanged(int reason) {
if (reason == MEDIA_ITEM_TRANSITION_REASON_AUTO) {
playerHandler.sendEmptyMessage(TRACK_CHANGED);
progressHandler.sendEmptyMessage(TRACK_CHANGED);
} else if (reason == MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED) {
progressHandler.sendEmptyMessage(TRACK_CHANGED);
prepareNext();
}
}
private static final class PlaybackHandler extends Handler {
@ -959,8 +982,8 @@ public class MusicService extends Service implements Playback.PlaybackCallbacks
info.setItemId(mService.get().getCurrentSong().getId());
info.setPositionTicks(progress * 10000);
task.cancel(true);
executorService.shutdownNow();
if (task != null) task.cancel(true);
if (executorService != null) executorService.shutdownNow();
}
}
}

View file

@ -0,0 +1,73 @@
package com.cappielloantonio.play.service
import com.google.android.exoplayer2.MediaItem
import com.google.android.exoplayer2.drm.DrmSessionManager
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory
import com.google.android.exoplayer2.source.MediaSource
import com.google.android.exoplayer2.source.MediaSourceFactory
import com.google.android.exoplayer2.source.ProgressiveMediaSource
import com.google.android.exoplayer2.source.hls.HlsMediaSource
import com.google.android.exoplayer2.upstream.*
import kotlinx.coroutines.*
import java.net.HttpURLConnection
import java.net.URL
class UnknownMediaSourceFactory(dataSourceFactory: DataSource.Factory) : MediaSourceFactory {
private val hlsMediaSource : HlsMediaSource.Factory
private val progressiveMediaSource : ProgressiveMediaSource.Factory
private var loadErrorHandlingPolicy: LoadErrorHandlingPolicy
override fun setDrmSessionManager(drmSessionManager: DrmSessionManager?): MediaSourceFactory {
return this
}
override fun setDrmHttpDataSourceFactory(drmHttpDataSourceFactory: HttpDataSource.Factory?): MediaSourceFactory {
return this
}
override fun setDrmUserAgent(drmUserAgent: String?): MediaSourceFactory {
return this
}
override fun setLoadErrorHandlingPolicy(loadErrorHandlingPolicy: LoadErrorHandlingPolicy?): MediaSourceFactory {
this.loadErrorHandlingPolicy = loadErrorHandlingPolicy!!
return this
}
override fun getSupportedTypes(): IntArray {
return intArrayOf()
}
override fun createMediaSource(mediaItem: MediaItem): MediaSource {
val type: String? = runBlocking {
httpGet(mediaItem.playbackProperties!!.uri.toString())
}
val sourceFactory: MediaSourceFactory = if (type == "application/x-mpegURL") {
hlsMediaSource
} else {
progressiveMediaSource
}
return sourceFactory.createMediaSource(mediaItem)
}
private suspend fun httpGet(url: String?): String? {
return withContext(Dispatchers.IO) {
val request = URL(url)
val conn = request.openConnection() as HttpURLConnection
return@withContext conn.getHeaderField("Content-Type")
}
}
init {
hlsMediaSource = HlsMediaSource.Factory(dataSourceFactory)
progressiveMediaSource = ProgressiveMediaSource.Factory(dataSourceFactory, DefaultExtractorsFactory())
loadErrorHandlingPolicy = DefaultLoadErrorHandlingPolicy()
}
}

View file

@ -3,21 +3,28 @@ package com.cappielloantonio.play.service.notification;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import androidx.annotation.RequiresApi;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.os.Build;
import android.graphics.drawable.Drawable;
import androidx.annotation.RequiresApi;
import androidx.annotation.NonNull;
import androidx.core.app.NotificationCompat;
import androidx.media.app.NotificationCompat.MediaStyle;
import com.cappielloantonio.play.R;
import com.cappielloantonio.play.glide.CustomGlideRequest;
import com.cappielloantonio.play.model.Song;
import com.cappielloantonio.play.service.MusicService;
import com.cappielloantonio.play.ui.activities.MainActivity;
import com.cappielloantonio.play.service.MusicService;
import com.bumptech.glide.request.target.CustomTarget;
import com.bumptech.glide.request.transition.Transition;
import static android.content.Context.NOTIFICATION_SERVICE;
import static com.cappielloantonio.play.service.MusicService.ACTION_REWIND;
@ -41,18 +48,14 @@ public class PlayingNotification {
public synchronized void init(MusicService service) {
this.service = service;
notificationManager = (NotificationManager) service.getSystemService(NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createNotificationChannel();
}
createNotificationChannel();
}
public synchronized void update() {
stopped = false;
final Song song = service.getCurrentSong();
final boolean isPlaying = service.isPlaying();
final int playButtonResId = isPlaying ? R.drawable.ic_pause_white_24dp : R.drawable.ic_play_arrow_white_24dp;
Intent action = new Intent(service, MainActivity.class);
@ -64,39 +67,59 @@ public class PlayingNotification {
intent.setComponent(serviceName);
final PendingIntent deleteIntent = PendingIntent.getService(service, 0, intent, 0);
// Bitmap 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);
final int bigNotificationImageSize = service.getResources().getDimensionPixelSize(R.dimen.notification_big_image_size);
service.runOnUiThread(() -> CustomGlideRequest.Builder
.from(service, song.getPrimary(), song.getBlurHash(), CustomGlideRequest.PRIMARY, CustomGlideRequest.TOP_QUALITY)
.bitmap()
.build()
.into(new CustomTarget<Bitmap>(bigNotificationImageSize, bigNotificationImageSize) {
@Override
public void onResourceReady(@NonNull Bitmap resource, Transition<? super Bitmap> glideAnimation) {
update(resource);
}
builder.setStyle(new androidx.media.app.NotificationCompat.MediaStyle().setMediaSession(service.getMediaSession().getSessionToken())
.setShowActionsInCompactView(0, 1, 2))
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setColor(Color.TRANSPARENT);
@Override
public void onLoadFailed(Drawable drawable) {
update(null);
}
// notification has been stopped before loading was finished
if (stopped) return;
@Override
public void onLoadCleared(Drawable drawable) {
update(null);
}
updateNotifyModeAndPostNotification(builder.build());
void update(Bitmap bitmap) {
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 MediaStyle().setMediaSession(service.getMediaSession().getSessionToken()).setShowActionsInCompactView(0, 1, 2))
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
// notification has been stopped before loading was finished
if (stopped) return;
updateNotifyModeAndPostNotification(builder.build());
}
}));
}
public synchronized void stop() {
@ -115,6 +138,7 @@ public class PlayingNotification {
void updateNotifyModeAndPostNotification(Notification notification) {
int newNotifyMode;
if (service.isPlaying()) {
newNotifyMode = NOTIFY_MODE_FOREGROUND;
} else {

View file

@ -13,6 +13,8 @@ public interface Playback {
boolean isPlaying();
boolean isLoading();
void start();
void pause();
@ -30,10 +32,10 @@ public interface Playback {
int getVolume();
interface PlaybackCallbacks {
void onTrackStarted();
void onStateChanged(int state);
void onTrackWentToNext();
void onReadyChanged(boolean ready, int reason);
void onTrackEnded();
void onTrackChanged(int reason);
}
}