[Notification] Fixed and improved playing notification
This commit is contained in:
parent
ba9c928588
commit
1df50491fc
4 changed files with 482 additions and 443 deletions
|
@ -25,6 +25,7 @@ import static code.name.monkey.retromusic.ConstantsKt.CROSS_FADE_DURATION;
|
|||
import static code.name.monkey.retromusic.ConstantsKt.TOGGLE_HEADSET;
|
||||
import static code.name.monkey.retromusic.service.AudioFader.startFadeAnimator;
|
||||
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
|
@ -34,6 +35,7 @@ import android.content.Context;
|
|||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.database.ContentObserver;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Point;
|
||||
|
@ -106,6 +108,7 @@ import code.name.monkey.retromusic.util.PreferenceUtil;
|
|||
import code.name.monkey.retromusic.util.RetroUtil;
|
||||
import code.name.monkey.retromusic.volume.AudioVolumeObserver;
|
||||
import code.name.monkey.retromusic.volume.OnAudioVolumeChangedListener;
|
||||
import kotlin.Unit;
|
||||
|
||||
/**
|
||||
* @author Karim Abou Zeid (kabouzeid), Andrew Neal
|
||||
|
@ -280,7 +283,8 @@ public class MusicService extends MediaBrowserServiceCompat
|
|||
new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(final Context context, final Intent intent) {
|
||||
updateNotification();
|
||||
playingNotification.updateFavorite(getCurrentSong(), MusicService.this::startForegroundOrNotify);
|
||||
startForegroundOrNotify();
|
||||
}
|
||||
};
|
||||
private final BroadcastReceiver lockScreenReceiver =
|
||||
|
@ -363,6 +367,8 @@ public class MusicService extends MediaBrowserServiceCompat
|
|||
private ThrottledSeekHandler throttledSeekHandler;
|
||||
private Handler uiThreadHandler;
|
||||
private PowerManager.WakeLock wakeLock;
|
||||
private NotificationManager notificationManager;
|
||||
private boolean isForeground = false;
|
||||
|
||||
private static Bitmap copy(Bitmap bitmap) {
|
||||
Bitmap.Config config = bitmap.getConfig();
|
||||
|
@ -424,6 +430,10 @@ public class MusicService extends MediaBrowserServiceCompat
|
|||
registerReceiver(updateFavoriteReceiver, new IntentFilter(FAVORITE_STATE_CHANGED));
|
||||
registerReceiver(lockScreenReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF));
|
||||
|
||||
setSessionToken(mediaSession.getSessionToken());
|
||||
if (VersionUtils.INSTANCE.hasMarshmallow()) {
|
||||
notificationManager = getSystemService(NotificationManager.class);
|
||||
}
|
||||
initNotification();
|
||||
|
||||
mediaStoreObserver = new MediaStoreObserver(this, playerHandler);
|
||||
|
@ -474,7 +484,6 @@ public class MusicService extends MediaBrowserServiceCompat
|
|||
|
||||
mPackageValidator = new PackageValidator(this, R.xml.allowed_media_browser_callers);
|
||||
mMusicProvider.setMusicService(this);
|
||||
setSessionToken(mediaSession.getSessionToken());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -772,11 +781,10 @@ public class MusicService extends MediaBrowserServiceCompat
|
|||
public void initNotification() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
|
||||
&& !PreferenceUtil.INSTANCE.isClassicNotification()) {
|
||||
playingNotification = new PlayingNotificationImpl();
|
||||
playingNotification = PlayingNotificationImpl.Companion.from(this, notificationManager, mediaSession);
|
||||
} else {
|
||||
playingNotification = new PlayingNotificationOreo();
|
||||
playingNotification = PlayingNotificationOreo.Companion.from(this, notificationManager);
|
||||
}
|
||||
playingNotification.init(this);
|
||||
}
|
||||
|
||||
public boolean isLastTrack() {
|
||||
|
@ -844,7 +852,8 @@ public class MusicService extends MediaBrowserServiceCompat
|
|||
// Request from an untrusted package: return an empty browser root
|
||||
return new BrowserRoot(AutoMediaIDHelper.MEDIA_ID_EMPTY_ROOT, null);
|
||||
} else {
|
||||
/** By default return the browsable root. Treat the EXTRA_RECENT flag as a special case
|
||||
/**
|
||||
* By default return the browsable root. Treat the EXTRA_RECENT flag as a special case
|
||||
* and return the recent root instead.
|
||||
*/
|
||||
boolean isRecentRequest = false;
|
||||
|
@ -889,7 +898,7 @@ public class MusicService extends MediaBrowserServiceCompat
|
|||
/* Switch to MultiPlayer if Crossfade duration is 0 and
|
||||
Playback is not an instance of MultiPlayer */
|
||||
if (playback != null)
|
||||
playback.setCrossFadeDuration(PreferenceUtil.INSTANCE.getCrossFadeDuration());
|
||||
playback.setCrossFadeDuration(PreferenceUtil.INSTANCE.getCrossFadeDuration());
|
||||
if (!(playback instanceof MultiPlayer) && PreferenceUtil.INSTANCE.getCrossFadeDuration() == 0) {
|
||||
if (playback != null) {
|
||||
playback.release();
|
||||
|
@ -1049,6 +1058,7 @@ public class MusicService extends MediaBrowserServiceCompat
|
|||
}
|
||||
|
||||
public void pause() {
|
||||
Log.i(TAG, "Paused");
|
||||
pausedByTransientLossOfFocus = false;
|
||||
if (playback != null && playback.isPlaying()) {
|
||||
startFadeAnimator(playback, false, () -> {
|
||||
|
@ -1170,7 +1180,8 @@ public class MusicService extends MediaBrowserServiceCompat
|
|||
|
||||
public void quit() {
|
||||
pause();
|
||||
playingNotification.stop();
|
||||
stopForeground(true);
|
||||
notificationManager.cancel(PlayingNotification.NOTIFICATION_ID);
|
||||
|
||||
closeAudioEffectSession();
|
||||
getAudioManager().abandonAudioFocus(audioFocusListener);
|
||||
|
@ -1333,7 +1344,8 @@ public class MusicService extends MediaBrowserServiceCompat
|
|||
|
||||
public void updateNotification() {
|
||||
if (playingNotification != null && getCurrentSong().getId() != -1) {
|
||||
playingNotification.update();
|
||||
stopForegroundAndNotification();
|
||||
initNotification();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1407,17 +1419,19 @@ public class MusicService extends MediaBrowserServiceCompat
|
|||
private void handleChangeInternal(@NonNull final String what) {
|
||||
switch (what) {
|
||||
case PLAY_STATE_CHANGED:
|
||||
updateNotification();
|
||||
updateMediaSessionPlaybackState();
|
||||
final boolean isPlaying = isPlaying();
|
||||
if (!isPlaying && getSongProgressMillis() > 0) {
|
||||
savePositionInTrack();
|
||||
}
|
||||
songPlayCountHelper.notifyPlayStateChanged(isPlaying);
|
||||
playingNotification.setPlaying(isPlaying);
|
||||
startForegroundOrNotify();
|
||||
break;
|
||||
case FAVORITE_STATE_CHANGED:
|
||||
playingNotification.updateFavorite(getCurrentSong(), this::startForegroundOrNotify);
|
||||
case META_CHANGED:
|
||||
updateNotification();
|
||||
playingNotification.updateMetadata(getCurrentSong(), this::startForegroundOrNotify);
|
||||
updateMediaSessionMetaData();
|
||||
updateMediaSessionPlaybackState();
|
||||
savePosition();
|
||||
|
@ -1435,12 +1449,41 @@ public class MusicService extends MediaBrowserServiceCompat
|
|||
if (playingQueue.size() > 0) {
|
||||
prepareNext();
|
||||
} else {
|
||||
playingNotification.stop();
|
||||
stopForegroundAndNotification();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private Unit startForegroundOrNotify() {
|
||||
if (!isForeground) {
|
||||
// Specify that this is a media service, if supported.
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
startForeground(
|
||||
PlayingNotification.NOTIFICATION_ID, playingNotification.build(),
|
||||
ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK
|
||||
);
|
||||
} else {
|
||||
startForeground(PlayingNotification.NOTIFICATION_ID, playingNotification.build());
|
||||
}
|
||||
|
||||
isForeground = true;
|
||||
} else {
|
||||
// If we are already in foreground just update the notification
|
||||
notificationManager.notify(
|
||||
PlayingNotification.NOTIFICATION_ID, playingNotification.build()
|
||||
);
|
||||
}
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
|
||||
private void stopForegroundAndNotification() {
|
||||
stopForeground(true);
|
||||
notificationManager.cancel(PlayingNotification.NOTIFICATION_ID);
|
||||
|
||||
isForeground = false;
|
||||
}
|
||||
|
||||
private boolean openCurrent() {
|
||||
synchronized (this) {
|
||||
try {
|
||||
|
@ -1600,8 +1643,8 @@ public class MusicService extends MediaBrowserServiceCompat
|
|||
mediaButtonIntent.setComponent(mediaButtonReceiverComponentName);
|
||||
|
||||
PendingIntent mediaButtonReceiverPendingIntent;
|
||||
mediaButtonReceiverPendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, mediaButtonIntent,
|
||||
VersionUtils.INSTANCE.hasMarshmallow() ? PendingIntent.FLAG_IMMUTABLE : 0);
|
||||
mediaButtonReceiverPendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, mediaButtonIntent,
|
||||
VersionUtils.INSTANCE.hasMarshmallow() ? PendingIntent.FLAG_IMMUTABLE : 0);
|
||||
|
||||
mediaSession = new MediaSessionCompat(
|
||||
this,
|
||||
|
|
|
@ -15,95 +15,51 @@
|
|||
package code.name.monkey.retromusic.service.notification
|
||||
|
||||
|
||||
import android.app.Notification
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.content.Context.NOTIFICATION_SERVICE
|
||||
import android.content.pm.ServiceInfo
|
||||
import android.os.Build
|
||||
import android.content.Context
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.core.app.NotificationCompat
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.service.MusicService
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
|
||||
|
||||
abstract class PlayingNotification {
|
||||
protected lateinit var service: MusicService
|
||||
protected var stopped: Boolean = false
|
||||
private var notifyMode = NOTIFY_MODE_BACKGROUND
|
||||
private var notificationManager: NotificationManager? = null
|
||||
abstract class PlayingNotification(context: Context) :
|
||||
NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID) {
|
||||
|
||||
abstract fun updateMetadata(song: Song, onUpdate: () -> Unit)
|
||||
|
||||
@Synchronized
|
||||
fun init(service: MusicService) {
|
||||
this.service = service
|
||||
notificationManager = service.getSystemService(NOTIFICATION_SERVICE) as NotificationManager
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
createNotificationChannel()
|
||||
}
|
||||
}
|
||||
abstract fun setPlaying(isPlaying: Boolean)
|
||||
|
||||
abstract fun update()
|
||||
|
||||
@Synchronized
|
||||
fun stop() {
|
||||
stopped = true
|
||||
service.stopForeground(true)
|
||||
notificationManager!!.cancel(NOTIFICATION_ID)
|
||||
}
|
||||
|
||||
internal fun updateNotifyModeAndPostNotification(notification: Notification) {
|
||||
val newNotifyMode: Int = if (service.isPlaying) {
|
||||
NOTIFY_MODE_FOREGROUND
|
||||
} else {
|
||||
NOTIFY_MODE_BACKGROUND
|
||||
}
|
||||
|
||||
if (notifyMode != newNotifyMode && newNotifyMode == NOTIFY_MODE_BACKGROUND) {
|
||||
service.stopForeground(false)
|
||||
}
|
||||
|
||||
if (newNotifyMode == NOTIFY_MODE_FOREGROUND) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
service.startForeground(
|
||||
NOTIFICATION_ID,
|
||||
notification,
|
||||
ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK
|
||||
)
|
||||
} else {
|
||||
service.startForeground(NOTIFICATION_ID,notification)
|
||||
}
|
||||
} else if (newNotifyMode == NOTIFY_MODE_BACKGROUND) {
|
||||
notificationManager!!.notify(NOTIFICATION_ID, notification)
|
||||
}
|
||||
|
||||
notifyMode = newNotifyMode
|
||||
}
|
||||
|
||||
@RequiresApi(26)
|
||||
private fun createNotificationChannel() {
|
||||
var notificationChannel: NotificationChannel? = notificationManager!!
|
||||
.getNotificationChannel(NOTIFICATION_CHANNEL_ID)
|
||||
if (notificationChannel == null) {
|
||||
notificationChannel = NotificationChannel(
|
||||
NOTIFICATION_CHANNEL_ID,
|
||||
service.getString(R.string.playing_notification_name),
|
||||
NotificationManager.IMPORTANCE_LOW
|
||||
)
|
||||
notificationChannel.description =
|
||||
service.getString(R.string.playing_notification_description)
|
||||
notificationChannel.enableLights(false)
|
||||
notificationChannel.enableVibration(false)
|
||||
notificationChannel.setShowBadge(false)
|
||||
|
||||
notificationManager!!.createNotificationChannel(notificationChannel)
|
||||
}
|
||||
}
|
||||
abstract fun updateFavorite(song: Song, onUpdate: () -> Unit)
|
||||
|
||||
companion object {
|
||||
const val NOTIFICATION_CONTROLS_SIZE_MULTIPLIER = 1.0f
|
||||
internal const val NOTIFICATION_CHANNEL_ID = "playing_notification"
|
||||
private const val NOTIFICATION_ID = 1
|
||||
private const val NOTIFY_MODE_FOREGROUND = 1
|
||||
private const val NOTIFY_MODE_BACKGROUND = 0
|
||||
const val NOTIFICATION_ID = 1
|
||||
|
||||
|
||||
@RequiresApi(26)
|
||||
fun createNotificationChannel(
|
||||
context: Context,
|
||||
notificationManager: NotificationManager
|
||||
) {
|
||||
var notificationChannel: NotificationChannel? = notificationManager
|
||||
.getNotificationChannel(NOTIFICATION_CHANNEL_ID)
|
||||
if (notificationChannel == null) {
|
||||
notificationChannel = NotificationChannel(
|
||||
NOTIFICATION_CHANNEL_ID,
|
||||
context.getString(R.string.playing_notification_name),
|
||||
NotificationManager.IMPORTANCE_LOW
|
||||
)
|
||||
notificationChannel.description =
|
||||
context.getString(R.string.playing_notification_description)
|
||||
notificationChannel.enableLights(false)
|
||||
notificationChannel.enableVibration(false)
|
||||
notificationChannel.setShowBadge(false)
|
||||
|
||||
notificationManager.createNotificationChannel(notificationChannel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,14 +14,16 @@
|
|||
|
||||
package code.name.monkey.retromusic.service.notification
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Build
|
||||
import android.support.v4.media.session.MediaSessionCompat
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.text.HtmlCompat
|
||||
import androidx.media.app.NotificationCompat.MediaStyle
|
||||
|
@ -33,180 +35,196 @@ import code.name.monkey.retromusic.db.toSongEntity
|
|||
import code.name.monkey.retromusic.glide.GlideApp
|
||||
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.service.MusicService
|
||||
import code.name.monkey.retromusic.service.MusicService.*
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import code.name.monkey.retromusic.util.RetroColorUtil
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.request.target.SimpleTarget
|
||||
import com.bumptech.glide.request.target.Target
|
||||
import com.bumptech.glide.request.target.CustomTarget
|
||||
import com.bumptech.glide.request.transition.Transition
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.core.component.KoinComponent
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class PlayingNotificationImpl : PlayingNotification(), KoinComponent {
|
||||
private var target: Target<BitmapPaletteWrapper>? = null
|
||||
@SuppressLint("RestrictedApi")
|
||||
class PlayingNotificationImpl(
|
||||
val context: Context,
|
||||
mediaSessionToken: MediaSessionCompat.Token
|
||||
) : PlayingNotification(context) {
|
||||
|
||||
@Synchronized
|
||||
override fun update() {
|
||||
stopped = false
|
||||
GlobalScope.launch {
|
||||
val song = service.currentSong
|
||||
init {
|
||||
val action = Intent(context, MainActivity::class.java)
|
||||
action.putExtra(MainActivity.EXPAND_PANEL, PreferenceUtil.isExpandPanel)
|
||||
action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||
val clickIntent =
|
||||
PendingIntent.getActivity(
|
||||
context,
|
||||
0,
|
||||
action,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or if (VersionUtils.hasMarshmallow())
|
||||
PendingIntent.FLAG_IMMUTABLE
|
||||
else 0
|
||||
)
|
||||
|
||||
val serviceName = ComponentName(context, MusicService::class.java)
|
||||
val intent = Intent(ACTION_QUIT)
|
||||
intent.component = serviceName
|
||||
val deleteIntent = PendingIntent.getService(
|
||||
context,
|
||||
0,
|
||||
intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or (if (VersionUtils.hasMarshmallow())
|
||||
PendingIntent.FLAG_IMMUTABLE
|
||||
else 0)
|
||||
)
|
||||
val toggleFavorite = buildFavoriteAction(false)
|
||||
val playPauseAction = buildPlayAction(true)
|
||||
val previousAction = NotificationCompat.Action(
|
||||
R.drawable.ic_skip_previous_round_white_32dp,
|
||||
context.getString(R.string.action_previous),
|
||||
retrievePlaybackAction(ACTION_REWIND)
|
||||
)
|
||||
val nextAction = NotificationCompat.Action(
|
||||
R.drawable.ic_skip_next_round_white_32dp,
|
||||
context.getString(R.string.action_next),
|
||||
retrievePlaybackAction(ACTION_SKIP)
|
||||
)
|
||||
val dismissAction = NotificationCompat.Action(
|
||||
R.drawable.ic_close,
|
||||
context.getString(R.string.customactivityoncrash_error_activity_error_details_close),
|
||||
retrievePlaybackAction(ACTION_QUIT)
|
||||
)
|
||||
setSmallIcon(R.drawable.ic_notification)
|
||||
setContentIntent(clickIntent)
|
||||
setDeleteIntent(deleteIntent)
|
||||
setShowWhen(false)
|
||||
addAction(toggleFavorite)
|
||||
addAction(previousAction)
|
||||
addAction(playPauseAction)
|
||||
addAction(nextAction)
|
||||
addAction(dismissAction)
|
||||
|
||||
setStyle(
|
||||
MediaStyle()
|
||||
.setMediaSession(mediaSessionToken)
|
||||
.setShowActionsInCompactView(1, 2, 3)
|
||||
)
|
||||
setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
if (Build.VERSION.SDK_INT <=
|
||||
Build.VERSION_CODES.O && PreferenceUtil.isColoredNotification
|
||||
) {
|
||||
this.color = color
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateMetadata(song: Song, onUpdate: () -> Unit) {
|
||||
setContentTitle(song.title)
|
||||
setContentText(
|
||||
HtmlCompat.fromHtml(
|
||||
"<b>" + song.albumName + "</b>",
|
||||
HtmlCompat.FROM_HTML_MODE_LEGACY
|
||||
)
|
||||
)
|
||||
val bigNotificationImageSize = context.resources
|
||||
.getDimensionPixelSize(R.dimen.notification_big_image_size)
|
||||
GlideApp.with(context).asBitmapPalette().songCoverOptions(song)
|
||||
.load(RetroGlideExtension.getSongModel(song))
|
||||
//.checkIgnoreMediaStore()
|
||||
.centerCrop()
|
||||
.into(object : CustomTarget<BitmapPaletteWrapper>(
|
||||
bigNotificationImageSize,
|
||||
bigNotificationImageSize
|
||||
) {
|
||||
override fun onResourceReady(
|
||||
resource: BitmapPaletteWrapper,
|
||||
transition: Transition<in BitmapPaletteWrapper>?
|
||||
) {
|
||||
setLargeIcon(
|
||||
resource.bitmap
|
||||
)
|
||||
if (Build.VERSION.SDK_INT <=
|
||||
Build.VERSION_CODES.O && PreferenceUtil.isColoredNotification
|
||||
) {
|
||||
color = RetroColorUtil.getColor(resource.palette, Color.TRANSPARENT)
|
||||
}
|
||||
onUpdate()
|
||||
}
|
||||
|
||||
override fun onLoadFailed(errorDrawable: Drawable?) {
|
||||
super.onLoadFailed(errorDrawable)
|
||||
setLargeIcon(null)
|
||||
onUpdate()
|
||||
}
|
||||
|
||||
override fun onLoadCleared(placeholder: Drawable?) {
|
||||
setLargeIcon(null)
|
||||
onUpdate()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun buildPlayAction(isPlaying: Boolean): NotificationCompat.Action {
|
||||
val playButtonResId =
|
||||
if (isPlaying) R.drawable.ic_pause_white_48dp else R.drawable.ic_play_arrow_white_48dp
|
||||
return NotificationCompat.Action.Builder(
|
||||
playButtonResId,
|
||||
context.getString(R.string.action_play_pause),
|
||||
retrievePlaybackAction(ACTION_TOGGLE_PAUSE)
|
||||
).build()
|
||||
}
|
||||
|
||||
private fun buildFavoriteAction(isFavorite: Boolean): NotificationCompat.Action {
|
||||
val favoriteResId =
|
||||
if (isFavorite) R.drawable.ic_favorite else R.drawable.ic_favorite_border
|
||||
return NotificationCompat.Action.Builder(
|
||||
favoriteResId,
|
||||
context.getString(R.string.action_toggle_favorite),
|
||||
retrievePlaybackAction(TOGGLE_FAVORITE)
|
||||
).build()
|
||||
}
|
||||
|
||||
override fun setPlaying(isPlaying: Boolean) {
|
||||
mActions[2] = buildPlayAction(isPlaying)
|
||||
}
|
||||
|
||||
override fun updateFavorite(song: Song, onUpdate: () -> Unit) {
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
val playlist: PlaylistEntity = MusicUtil.repository.favoritePlaylist()
|
||||
val isPlaying = service.isPlaying
|
||||
val isFavorite = if (playlist != null) {
|
||||
val songEntity = song.toSongEntity(playlist.playListId)
|
||||
MusicUtil.repository.isFavoriteSong(songEntity).isNotEmpty()
|
||||
} else false
|
||||
|
||||
val playButtonResId =
|
||||
if (isPlaying) R.drawable.ic_pause_white_48dp else R.drawable.ic_play_arrow_white_48dp
|
||||
val favoriteResId =
|
||||
if (isFavorite) R.drawable.ic_favorite else R.drawable.ic_favorite_border
|
||||
|
||||
val action = Intent(service, MainActivity::class.java)
|
||||
action.putExtra(MainActivity.EXPAND_PANEL, PreferenceUtil.isExpandPanel)
|
||||
action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||
val clickIntent =
|
||||
PendingIntent.getActivity(
|
||||
service,
|
||||
0,
|
||||
action,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or if (VersionUtils.hasMarshmallow())
|
||||
PendingIntent.FLAG_IMMUTABLE
|
||||
else 0
|
||||
)
|
||||
|
||||
val serviceName = ComponentName(service, MusicService::class.java)
|
||||
val intent = Intent(ACTION_QUIT)
|
||||
intent.component = serviceName
|
||||
val deleteIntent = PendingIntent.getService(
|
||||
service,
|
||||
0,
|
||||
intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or (if (VersionUtils.hasMarshmallow())
|
||||
PendingIntent.FLAG_IMMUTABLE
|
||||
else 0)
|
||||
)
|
||||
val bigNotificationImageSize = service.resources
|
||||
.getDimensionPixelSize(R.dimen.notification_big_image_size)
|
||||
service.runOnUiThread {
|
||||
if (target != null) {
|
||||
Glide.with(service).clear(target)
|
||||
}
|
||||
target = GlideApp.with(service).asBitmapPalette().songCoverOptions(song)
|
||||
.load(RetroGlideExtension.getSongModel(song))
|
||||
//.checkIgnoreMediaStore()
|
||||
.centerCrop()
|
||||
.into(object : SimpleTarget<BitmapPaletteWrapper>(
|
||||
bigNotificationImageSize,
|
||||
bigNotificationImageSize
|
||||
) {
|
||||
override fun onResourceReady(
|
||||
resource: BitmapPaletteWrapper,
|
||||
transition: Transition<in BitmapPaletteWrapper>?
|
||||
) {
|
||||
update(
|
||||
resource.bitmap,
|
||||
RetroColorUtil.getColor(resource.palette, Color.TRANSPARENT)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onLoadFailed(errorDrawable: Drawable?) {
|
||||
super.onLoadFailed(errorDrawable)
|
||||
update(null, Color.TRANSPARENT)
|
||||
}
|
||||
|
||||
fun update(bitmap: Bitmap?, color: Int) {
|
||||
var bitmapFinal = bitmap
|
||||
if (bitmapFinal == null) {
|
||||
bitmapFinal = BitmapFactory.decodeResource(
|
||||
service.resources,
|
||||
R.drawable.default_audio_art
|
||||
)
|
||||
}
|
||||
|
||||
val toggleFavorite = NotificationCompat.Action(
|
||||
favoriteResId,
|
||||
service.getString(R.string.action_toggle_favorite),
|
||||
retrievePlaybackAction(TOGGLE_FAVORITE)
|
||||
)
|
||||
val playPauseAction = NotificationCompat.Action(
|
||||
playButtonResId,
|
||||
service.getString(R.string.action_play_pause),
|
||||
retrievePlaybackAction(ACTION_TOGGLE_PAUSE)
|
||||
)
|
||||
val previousAction = NotificationCompat.Action(
|
||||
R.drawable.ic_skip_previous_round_white_32dp,
|
||||
service.getString(R.string.action_previous),
|
||||
retrievePlaybackAction(ACTION_REWIND)
|
||||
)
|
||||
val nextAction = NotificationCompat.Action(
|
||||
R.drawable.ic_skip_next_round_white_32dp,
|
||||
service.getString(R.string.action_next),
|
||||
retrievePlaybackAction(ACTION_SKIP)
|
||||
)
|
||||
|
||||
val builder = NotificationCompat.Builder(
|
||||
service,
|
||||
NOTIFICATION_CHANNEL_ID
|
||||
)
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setLargeIcon(bitmapFinal)
|
||||
.setContentIntent(clickIntent)
|
||||
.setDeleteIntent(deleteIntent)
|
||||
.setContentTitle(
|
||||
HtmlCompat.fromHtml(
|
||||
"<b>" + song.title + "</b>",
|
||||
HtmlCompat.FROM_HTML_MODE_LEGACY
|
||||
)
|
||||
)
|
||||
.setContentText(song.artistName)
|
||||
.setSubText(
|
||||
HtmlCompat.fromHtml(
|
||||
"<b>" + song.albumName + "</b>",
|
||||
HtmlCompat.FROM_HTML_MODE_LEGACY
|
||||
)
|
||||
)
|
||||
.setOngoing(isPlaying)
|
||||
.setShowWhen(false)
|
||||
.addAction(toggleFavorite)
|
||||
.addAction(previousAction)
|
||||
.addAction(playPauseAction)
|
||||
.addAction(nextAction)
|
||||
|
||||
builder.setStyle(
|
||||
MediaStyle()
|
||||
.setMediaSession(service.mediaSession.sessionToken)
|
||||
.setShowActionsInCompactView(1, 2, 3)
|
||||
)
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
if (Build.VERSION.SDK_INT <=
|
||||
Build.VERSION_CODES.O && PreferenceUtil.isColoredNotification
|
||||
) {
|
||||
builder.color = color
|
||||
}
|
||||
|
||||
if (stopped) {
|
||||
return // notification has been stopped before loading was finished
|
||||
}
|
||||
updateNotifyModeAndPostNotification(builder.build())
|
||||
}
|
||||
})
|
||||
withContext(Dispatchers.Main) {
|
||||
mActions[0] = buildFavoriteAction(isFavorite)
|
||||
onUpdate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun retrievePlaybackAction(action: String): PendingIntent {
|
||||
val serviceName = ComponentName(service, MusicService::class.java)
|
||||
val serviceName = ComponentName(context, MusicService::class.java)
|
||||
val intent = Intent(action)
|
||||
intent.component = serviceName
|
||||
return PendingIntent.getService(
|
||||
service, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or
|
||||
context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or
|
||||
if (VersionUtils.hasMarshmallow()) PendingIntent.FLAG_IMMUTABLE
|
||||
else 0
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun from(
|
||||
context: Context,
|
||||
notificationManager: NotificationManager,
|
||||
mediaSession: MediaSessionCompat
|
||||
): PlayingNotification {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
createNotificationChannel(context, notificationManager)
|
||||
}
|
||||
return PlayingNotificationImpl(context, mediaSession.sessionToken)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,6 +14,8 @@
|
|||
|
||||
package code.name.monkey.retromusic.service.notification
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
|
@ -21,6 +23,7 @@ import android.content.Intent
|
|||
import android.graphics.Bitmap
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Build
|
||||
import android.widget.RemoteViews
|
||||
import androidx.core.app.NotificationCompat
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil.resolveColor
|
||||
|
@ -29,6 +32,7 @@ import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
|||
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.MainActivity
|
||||
import code.name.monkey.retromusic.extensions.surfaceColor
|
||||
import code.name.monkey.retromusic.glide.GlideApp
|
||||
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
|
||||
|
@ -39,224 +43,231 @@ import code.name.monkey.retromusic.util.PreferenceUtil
|
|||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import code.name.monkey.retromusic.util.RetroUtil.createBitmap
|
||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.request.target.SimpleTarget
|
||||
import com.bumptech.glide.request.target.Target
|
||||
import com.bumptech.glide.request.target.CustomTarget
|
||||
import com.bumptech.glide.request.transition.Transition
|
||||
|
||||
/**
|
||||
* @author Hemanth S (h4h13).
|
||||
*/
|
||||
class PlayingNotificationOreo : PlayingNotification() {
|
||||
@SuppressLint("RestrictedApi")
|
||||
class PlayingNotificationOreo(
|
||||
val context: Context
|
||||
) : PlayingNotification(context) {
|
||||
|
||||
private var target: Target<BitmapPaletteWrapper>? = null
|
||||
private var primaryColor: Int = 0
|
||||
|
||||
private fun getCombinedRemoteViews(collapsed: Boolean, song: Song): RemoteViews {
|
||||
val remoteViews = RemoteViews(
|
||||
service.packageName,
|
||||
if (collapsed) R.layout.layout_notification_collapsed else R.layout.layout_notification_expanded
|
||||
)
|
||||
remoteViews.setTextViewText(
|
||||
R.id.appName,
|
||||
service.getString(R.string.app_name) + " • " + song.albumName
|
||||
)
|
||||
remoteViews.setTextViewText(R.id.title, song.title)
|
||||
remoteViews.setTextViewText(R.id.subtitle, song.artistName)
|
||||
linkButtons(remoteViews)
|
||||
return remoteViews
|
||||
}
|
||||
init {
|
||||
val notificationLayout = getCombinedRemoteViews(true)
|
||||
val notificationLayoutBig = getCombinedRemoteViews(false)
|
||||
|
||||
override fun update() {
|
||||
stopped = false
|
||||
val song = service.currentSong
|
||||
val isPlaying = service.isPlaying
|
||||
|
||||
val notificationLayout = getCombinedRemoteViews(true, song)
|
||||
val notificationLayoutBig = getCombinedRemoteViews(false, song)
|
||||
|
||||
val action = Intent(service, MainActivity::class.java)
|
||||
val action = Intent(context, MainActivity::class.java)
|
||||
action.putExtra(MainActivity.EXPAND_PANEL, PreferenceUtil.isExpandPanel)
|
||||
action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||
|
||||
val clickIntent = PendingIntent
|
||||
.getActivity(
|
||||
service,
|
||||
context,
|
||||
0,
|
||||
action,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or if (VersionUtils.hasMarshmallow())
|
||||
PendingIntent.FLAG_IMMUTABLE
|
||||
else 0
|
||||
)
|
||||
val deleteIntent = buildPendingIntent(service, ACTION_QUIT, null)
|
||||
val deleteIntent = buildPendingIntent(context, ACTION_QUIT, null)
|
||||
|
||||
val builder = NotificationCompat.Builder(service, NOTIFICATION_CHANNEL_ID)
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setContentIntent(clickIntent)
|
||||
.setDeleteIntent(deleteIntent)
|
||||
.setCategory(NotificationCompat.CATEGORY_SERVICE)
|
||||
.setPriority(NotificationCompat.PRIORITY_MAX)
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
.setCustomContentView(notificationLayout)
|
||||
.setCustomBigContentView(notificationLayoutBig)
|
||||
.setOngoing(isPlaying)
|
||||
|
||||
val bigNotificationImageSize = service.resources
|
||||
.getDimensionPixelSize(R.dimen.notification_big_image_size)
|
||||
service.runOnUiThread {
|
||||
if (target != null) {
|
||||
Glide.with(service).clear(target)
|
||||
}
|
||||
target = GlideApp.with(service).asBitmapPalette().songCoverOptions(song)
|
||||
.load(RetroGlideExtension.getSongModel(song))
|
||||
.centerCrop()
|
||||
.into(object : SimpleTarget<BitmapPaletteWrapper>(
|
||||
bigNotificationImageSize,
|
||||
bigNotificationImageSize
|
||||
) {
|
||||
override fun onResourceReady(
|
||||
resource: BitmapPaletteWrapper,
|
||||
transition: Transition<in BitmapPaletteWrapper>?
|
||||
) {
|
||||
/* val mediaNotificationProcessor = MediaNotificationProcessor(
|
||||
service,
|
||||
service
|
||||
) { i, _ -> update(resource.bitmap, i) }
|
||||
mediaNotificationProcessor.processNotification(resource.bitmap)*/
|
||||
|
||||
val colors = MediaNotificationProcessor(service, resource.bitmap)
|
||||
update(resource.bitmap, colors.backgroundColor)
|
||||
}
|
||||
|
||||
override fun onLoadFailed(errorDrawable: Drawable?) {
|
||||
super.onLoadFailed(errorDrawable)
|
||||
update(
|
||||
null,
|
||||
resolveColor(service, R.attr.colorSurface, Color.WHITE)
|
||||
)
|
||||
}
|
||||
|
||||
private fun update(bitmap: Bitmap?, bgColor: Int) {
|
||||
var bgColorFinal = bgColor
|
||||
if (bitmap != null) {
|
||||
notificationLayout.setImageViewBitmap(R.id.largeIcon, bitmap)
|
||||
notificationLayoutBig.setImageViewBitmap(R.id.largeIcon, bitmap)
|
||||
} else {
|
||||
notificationLayout.setImageViewResource(
|
||||
R.id.largeIcon,
|
||||
R.drawable.default_audio_art
|
||||
)
|
||||
notificationLayoutBig.setImageViewResource(
|
||||
R.id.largeIcon,
|
||||
R.drawable.default_audio_art
|
||||
)
|
||||
}
|
||||
|
||||
// Android 12 applies a standard Notification template to every notification
|
||||
// which will in turn have a default background so setting a different background
|
||||
// than that, looks weird
|
||||
if (!VersionUtils.hasS()) {
|
||||
if (!PreferenceUtil.isColoredNotification) {
|
||||
bgColorFinal =
|
||||
resolveColor(service, R.attr.colorPrimary, Color.WHITE)
|
||||
}
|
||||
setBackgroundColor(bgColorFinal)
|
||||
}
|
||||
setNotificationContent(ColorUtil.isColorLight(bgColorFinal))
|
||||
|
||||
if (stopped) {
|
||||
return // notification has been stopped before loading was finished
|
||||
}
|
||||
updateNotifyModeAndPostNotification(builder.build())
|
||||
}
|
||||
|
||||
private fun setBackgroundColor(color: Int) {
|
||||
notificationLayout.setInt(R.id.image, "setBackgroundColor", color)
|
||||
notificationLayoutBig.setInt(R.id.image, "setBackgroundColor", color)
|
||||
}
|
||||
|
||||
private fun setNotificationContent(dark: Boolean) {
|
||||
val primary = MaterialValueHelper.getPrimaryTextColor(service, dark)
|
||||
val secondary = MaterialValueHelper.getSecondaryTextColor(service, dark)
|
||||
|
||||
val close = createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
service,
|
||||
R.drawable.ic_close,
|
||||
primary
|
||||
), NOTIFICATION_CONTROLS_SIZE_MULTIPLIER
|
||||
)
|
||||
val prev = createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
service,
|
||||
R.drawable.ic_skip_previous_round_white_32dp,
|
||||
primary
|
||||
), NOTIFICATION_CONTROLS_SIZE_MULTIPLIER
|
||||
)
|
||||
val next = createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
service,
|
||||
R.drawable.ic_skip_next_round_white_32dp,
|
||||
primary
|
||||
), NOTIFICATION_CONTROLS_SIZE_MULTIPLIER
|
||||
)
|
||||
val playPause = createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
service,
|
||||
if (isPlaying)
|
||||
R.drawable.ic_pause_white_48dp
|
||||
else
|
||||
R.drawable.ic_play_arrow_white_48dp, primary
|
||||
), NOTIFICATION_CONTROLS_SIZE_MULTIPLIER
|
||||
)
|
||||
|
||||
notificationLayout.setTextColor(R.id.title, primary)
|
||||
notificationLayout.setTextColor(R.id.subtitle, secondary)
|
||||
notificationLayout.setTextColor(R.id.appName, secondary)
|
||||
|
||||
notificationLayout.setImageViewBitmap(R.id.action_prev, prev)
|
||||
notificationLayout.setImageViewBitmap(R.id.action_next, next)
|
||||
notificationLayout.setImageViewBitmap(R.id.action_play_pause, playPause)
|
||||
|
||||
notificationLayoutBig.setTextColor(R.id.title, primary)
|
||||
notificationLayoutBig.setTextColor(R.id.subtitle, secondary)
|
||||
notificationLayoutBig.setTextColor(R.id.appName, secondary)
|
||||
|
||||
notificationLayoutBig.setImageViewBitmap(R.id.action_quit, close)
|
||||
notificationLayoutBig.setImageViewBitmap(R.id.action_prev, prev)
|
||||
notificationLayoutBig.setImageViewBitmap(R.id.action_next, next)
|
||||
notificationLayoutBig.setImageViewBitmap(R.id.action_play_pause, playPause)
|
||||
|
||||
notificationLayout.setImageViewBitmap(
|
||||
R.id.smallIcon,
|
||||
createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
service,
|
||||
R.drawable.ic_notification,
|
||||
secondary
|
||||
), 0.6f
|
||||
)
|
||||
)
|
||||
notificationLayoutBig.setImageViewBitmap(
|
||||
R.id.smallIcon,
|
||||
createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
service,
|
||||
R.drawable.ic_notification,
|
||||
secondary
|
||||
), 0.6f
|
||||
)
|
||||
)
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (stopped) {
|
||||
return // notification has been stopped before loading was finished
|
||||
}
|
||||
updateNotifyModeAndPostNotification(builder.build())
|
||||
setSmallIcon(R.drawable.ic_notification)
|
||||
setContentIntent(clickIntent)
|
||||
setDeleteIntent(deleteIntent)
|
||||
setCategory(NotificationCompat.CATEGORY_SERVICE)
|
||||
priority = NotificationCompat.PRIORITY_MAX
|
||||
setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
setCustomContentView(notificationLayout)
|
||||
setCustomBigContentView(notificationLayoutBig)
|
||||
setOngoing(true)
|
||||
}
|
||||
|
||||
private fun getCombinedRemoteViews(collapsed: Boolean): RemoteViews {
|
||||
val remoteViews = RemoteViews(
|
||||
context.packageName,
|
||||
if (collapsed) R.layout.layout_notification_collapsed else R.layout.layout_notification_expanded
|
||||
)
|
||||
linkButtons(remoteViews)
|
||||
return remoteViews
|
||||
}
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
override fun updateMetadata(song: Song, onUpdate: () -> Unit) {
|
||||
val bigNotificationImageSize = context.resources
|
||||
.getDimensionPixelSize(R.dimen.notification_big_image_size)
|
||||
GlideApp.with(context).asBitmapPalette().songCoverOptions(song)
|
||||
.load(RetroGlideExtension.getSongModel(song))
|
||||
.centerCrop()
|
||||
.into(object : CustomTarget<BitmapPaletteWrapper>(
|
||||
bigNotificationImageSize,
|
||||
bigNotificationImageSize
|
||||
) {
|
||||
override fun onResourceReady(
|
||||
resource: BitmapPaletteWrapper,
|
||||
transition: Transition<in BitmapPaletteWrapper>?
|
||||
) {
|
||||
val colors = MediaNotificationProcessor(context, resource.bitmap)
|
||||
update(resource.bitmap, colors.backgroundColor)
|
||||
}
|
||||
|
||||
override fun onLoadFailed(errorDrawable: Drawable?) {
|
||||
super.onLoadFailed(errorDrawable)
|
||||
update(
|
||||
null,
|
||||
resolveColor(context, R.attr.colorSurface, Color.WHITE)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onLoadCleared(placeholder: Drawable?) {
|
||||
}
|
||||
|
||||
private fun update(bitmap: Bitmap?, bgColor: Int) {
|
||||
var bgColorFinal = bgColor
|
||||
if (bitmap != null) {
|
||||
contentView.setImageViewBitmap(R.id.largeIcon, bitmap)
|
||||
bigContentView.setImageViewBitmap(R.id.largeIcon, bitmap)
|
||||
} else {
|
||||
contentView.setImageViewResource(
|
||||
R.id.largeIcon,
|
||||
R.drawable.default_audio_art
|
||||
)
|
||||
bigContentView.setImageViewResource(
|
||||
R.id.largeIcon,
|
||||
R.drawable.default_audio_art
|
||||
)
|
||||
}
|
||||
|
||||
// Android 12 applies a standard Notification template to every notification
|
||||
// which will in turn have a default background so setting a different background
|
||||
// than that, looks weird
|
||||
if (!VersionUtils.hasS()) {
|
||||
if (!PreferenceUtil.isColoredNotification) {
|
||||
bgColorFinal =
|
||||
resolveColor(context, R.attr.colorSurface, Color.WHITE)
|
||||
}
|
||||
setBackgroundColor(bgColorFinal)
|
||||
setNotificationContent(ColorUtil.isColorLight(bgColorFinal))
|
||||
}else {
|
||||
setNotificationContent(!ColorUtil.isColorLight(context.surfaceColor()))
|
||||
}
|
||||
onUpdate()
|
||||
}
|
||||
|
||||
private fun setBackgroundColor(color: Int) {
|
||||
contentView.setInt(R.id.image, "setBackgroundColor", color)
|
||||
bigContentView.setInt(R.id.image, "setBackgroundColor", color)
|
||||
}
|
||||
|
||||
private fun setNotificationContent(dark: Boolean) {
|
||||
val primary = MaterialValueHelper.getPrimaryTextColor(context, dark)
|
||||
val secondary = MaterialValueHelper.getSecondaryTextColor(context, dark)
|
||||
primaryColor = primary
|
||||
|
||||
val close = createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
context,
|
||||
R.drawable.ic_close,
|
||||
primary
|
||||
), NOTIFICATION_CONTROLS_SIZE_MULTIPLIER
|
||||
)
|
||||
val prev = createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
context,
|
||||
R.drawable.ic_skip_previous_round_white_32dp,
|
||||
primary
|
||||
), NOTIFICATION_CONTROLS_SIZE_MULTIPLIER
|
||||
)
|
||||
val next = createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
context,
|
||||
R.drawable.ic_skip_next_round_white_32dp,
|
||||
primary
|
||||
), NOTIFICATION_CONTROLS_SIZE_MULTIPLIER
|
||||
)
|
||||
val playPause = getPlayPauseBitmap(true)
|
||||
|
||||
contentView.setTextColor(R.id.title, primary)
|
||||
contentView.setTextColor(R.id.subtitle, secondary)
|
||||
contentView.setTextColor(R.id.appName, secondary)
|
||||
|
||||
contentView.setImageViewBitmap(R.id.action_prev, prev)
|
||||
contentView.setImageViewBitmap(R.id.action_next, next)
|
||||
contentView.setImageViewBitmap(R.id.action_play_pause, playPause)
|
||||
|
||||
contentView.setTextViewText(
|
||||
R.id.appName,
|
||||
context.getString(R.string.app_name) + " • " + song.albumName
|
||||
)
|
||||
contentView.setTextViewText(R.id.title, song.title)
|
||||
contentView.setTextViewText(R.id.subtitle, song.artistName)
|
||||
|
||||
|
||||
bigContentView.setTextColor(R.id.title, primary)
|
||||
bigContentView.setTextColor(R.id.subtitle, secondary)
|
||||
bigContentView.setTextColor(R.id.appName, secondary)
|
||||
|
||||
bigContentView.setImageViewBitmap(R.id.action_quit, close)
|
||||
bigContentView.setImageViewBitmap(R.id.action_prev, prev)
|
||||
bigContentView.setImageViewBitmap(R.id.action_next, next)
|
||||
bigContentView.setImageViewBitmap(R.id.action_play_pause, playPause)
|
||||
|
||||
bigContentView.setTextViewText(
|
||||
R.id.appName,
|
||||
context.getString(R.string.app_name) + " • " + song.albumName
|
||||
)
|
||||
bigContentView.setTextViewText(R.id.title, song.title)
|
||||
bigContentView.setTextViewText(R.id.subtitle, song.artistName)
|
||||
|
||||
|
||||
contentView.setImageViewBitmap(
|
||||
R.id.smallIcon,
|
||||
createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
context,
|
||||
R.drawable.ic_notification,
|
||||
secondary
|
||||
), 0.6f
|
||||
)
|
||||
)
|
||||
bigContentView.setImageViewBitmap(
|
||||
R.id.smallIcon,
|
||||
createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
context,
|
||||
R.drawable.ic_notification,
|
||||
secondary
|
||||
), 0.6f
|
||||
)
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun getPlayPauseBitmap(isPlaying: Boolean): Bitmap {
|
||||
return createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
context,
|
||||
if (isPlaying)
|
||||
R.drawable.ic_pause_white_48dp
|
||||
else
|
||||
R.drawable.ic_play_arrow_white_48dp, primaryColor
|
||||
), NOTIFICATION_CONTROLS_SIZE_MULTIPLIER)
|
||||
}
|
||||
|
||||
override fun setPlaying(isPlaying: Boolean) {
|
||||
getPlayPauseBitmap(isPlaying).also {
|
||||
contentView.setImageViewBitmap(R.id.action_play_pause, it)
|
||||
bigContentView.setImageViewBitmap(R.id.action_play_pause, it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateFavorite(song: Song, onUpdate: () -> Unit) {
|
||||
|
||||
}
|
||||
|
||||
private fun buildPendingIntent(
|
||||
context: Context, action: String,
|
||||
|
@ -275,23 +286,34 @@ class PlayingNotificationOreo : PlayingNotification() {
|
|||
private fun linkButtons(notificationLayout: RemoteViews) {
|
||||
var pendingIntent: PendingIntent
|
||||
|
||||
val serviceName = ComponentName(service, MusicService::class.java)
|
||||
val serviceName = ComponentName(context, MusicService::class.java)
|
||||
|
||||
// Previous track
|
||||
pendingIntent = buildPendingIntent(service, ACTION_REWIND, serviceName)
|
||||
pendingIntent = buildPendingIntent(context, ACTION_REWIND, serviceName)
|
||||
notificationLayout.setOnClickPendingIntent(R.id.action_prev, pendingIntent)
|
||||
|
||||
// Play and pause
|
||||
pendingIntent = buildPendingIntent(service, ACTION_TOGGLE_PAUSE, serviceName)
|
||||
pendingIntent = buildPendingIntent(context, ACTION_TOGGLE_PAUSE, serviceName)
|
||||
notificationLayout.setOnClickPendingIntent(R.id.action_play_pause, pendingIntent)
|
||||
|
||||
// Next track
|
||||
pendingIntent = buildPendingIntent(service, ACTION_SKIP, serviceName)
|
||||
pendingIntent = buildPendingIntent(context, ACTION_SKIP, serviceName)
|
||||
notificationLayout.setOnClickPendingIntent(R.id.action_next, pendingIntent)
|
||||
|
||||
// Close
|
||||
pendingIntent = buildPendingIntent(service, ACTION_QUIT, serviceName)
|
||||
pendingIntent = buildPendingIntent(context, ACTION_QUIT, serviceName)
|
||||
notificationLayout.setOnClickPendingIntent(R.id.action_quit, pendingIntent)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun from(
|
||||
context: Context,
|
||||
notificationManager: NotificationManager
|
||||
): PlayingNotification {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
createNotificationChannel(context, notificationManager)
|
||||
}
|
||||
return PlayingNotificationOreo(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue