diff --git a/app/src/main/java/com/kabouzeid/gramophone/appwidgets/AppWidgetSmall.java b/app/src/main/java/com/kabouzeid/gramophone/appwidgets/AppWidgetSmall.java index ca0f3be8..b7436113 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/appwidgets/AppWidgetSmall.java +++ b/app/src/main/java/com/kabouzeid/gramophone/appwidgets/AppWidgetSmall.java @@ -7,6 +7,7 @@ import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; +import android.support.annotation.Nullable; import android.text.TextUtils; import android.view.View; import android.widget.RemoteViews; @@ -63,6 +64,7 @@ public class AppWidgetSmall extends BaseAppWidget { appWidgetView.setViewVisibility(R.id.image, View.INVISIBLE); appWidgetView.setImageViewBitmap(R.id.button_next, createBitmap(Util.getTintedDrawable(context, R.drawable.ic_skip_next_white_24dp, MaterialValueHelper.getPrimaryTextColor(context, true)), 1f)); appWidgetView.setImageViewBitmap(R.id.button_prev, createBitmap(Util.getTintedDrawable(context, R.drawable.ic_skip_previous_white_24dp, MaterialValueHelper.getPrimaryTextColor(context, true)), 1f)); + appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, createBitmap(Util.getTintedDrawable(context, R.drawable.ic_play_arrow_white_24dp, MaterialValueHelper.getPrimaryTextColor(context, true)), 1f)); linkButtons(context, appWidgetView); pushUpdate(context, appWidgetIds, appWidgetView); @@ -140,16 +142,22 @@ public class AppWidgetSmall extends BaseAppWidget { .into(new SimpleTarget(widgetImageSize, widgetImageSize) { @Override public void onResourceReady(Bitmap resource, GlideAnimation glideAnimation) { - appWidgetView.setViewVisibility(R.id.image, View.VISIBLE); - appWidgetView.setImageViewBitmap(R.id.image, resource); - pushUpdate(appContext, appWidgetIds, appWidgetView); + update(resource); } @Override public void onLoadFailed(Exception e, Drawable errorDrawable) { super.onLoadFailed(e, errorDrawable); + update(null); + } + + private void update(@Nullable Bitmap bitmap) { appWidgetView.setViewVisibility(R.id.image, View.VISIBLE); - appWidgetView.setImageViewResource(R.id.image, R.drawable.default_album_art); + if (bitmap == null) { + appWidgetView.setImageViewResource(R.id.image, R.drawable.default_album_art); + } else { + appWidgetView.setImageViewBitmap(R.id.image, bitmap); + } pushUpdate(appContext, appWidgetIds, appWidgetView); } }); diff --git a/app/src/main/java/com/kabouzeid/gramophone/helper/PlayingNotificationHelper.java b/app/src/main/java/com/kabouzeid/gramophone/helper/PlayingNotificationHelper.java index ac39aaa4..ac363cf0 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/helper/PlayingNotificationHelper.java +++ b/app/src/main/java/com/kabouzeid/gramophone/helper/PlayingNotificationHelper.java @@ -5,7 +5,6 @@ package com.kabouzeid.gramophone.helper; */ import android.app.Notification; -import android.app.NotificationManager; import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; @@ -18,6 +17,8 @@ import android.os.Build; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.NotificationCompat; +import android.text.TextUtils; +import android.view.View; import android.widget.RemoteViews; import com.bumptech.glide.Glide; @@ -39,65 +40,55 @@ import com.kabouzeid.gramophone.util.Util; public class PlayingNotificationHelper { public static final String TAG = PlayingNotificationHelper.class.getSimpleName(); + private static final int NOTIFICATION_ID = 1; - private final MusicService service; - - private final NotificationManager notificationManager; - private Notification notification; - private int notificationId = 1; - - private RemoteViews notificationLayout; - private RemoteViews notificationLayoutBig; - - private Song currentSong; - private boolean isPlaying; - - private boolean isDark; - private boolean isColored; + private MusicService service; private Target target; + private boolean stopped; + public PlayingNotificationHelper(@NonNull final MusicService service) { this.service = service; - notificationManager = (NotificationManager) service - .getSystemService(Context.NOTIFICATION_SERVICE); - - int bigNotificationImageSize = service.getResources().getDimensionPixelSize(R.dimen.notification_big_image_size); - target = new SimpleTarget(bigNotificationImageSize, bigNotificationImageSize) { - @Override - public void onLoadFailed(Exception e, Drawable errorDrawable) { - super.onLoadFailed(e, errorDrawable); - setAlbumCover(null, Color.TRANSPARENT); - } - - @Override - public void onResourceReady(BitmapPaletteWrapper resource, GlideAnimation glideAnimation) { - setAlbumCover(resource.getBitmap(), PhonographColorUtil.getColor(resource.getPalette(), Color.TRANSPARENT)); - } - }; } - public void updateNotification() { - updateNotification(PreferenceUtil.getInstance(service).coloredNotification()); - } + public synchronized void updateNotification() { + stopped = false; - public void updateNotification(final boolean isColored) { - Song song = service.getCurrentSong(); - if (song.id == -1) { - service.stopForeground(true); - return; + final Song song = service.getCurrentSong(); + + final boolean isPlaying = service.isPlaying(); + + final RemoteViews notificationLayout = new RemoteViews(service.getPackageName(), R.layout.notification); + final RemoteViews notificationLayoutBig = new RemoteViews(service.getPackageName(), R.layout.notification_big); + + if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName)) { + notificationLayout.setViewVisibility(R.id.media_titles, View.INVISIBLE); + } else { + notificationLayout.setViewVisibility(R.id.media_titles, View.VISIBLE); + notificationLayout.setTextViewText(R.id.title, song.title); + notificationLayout.setTextViewText(R.id.text, song.artistName); } - currentSong = song; - this.isColored = isColored; - this.isPlaying = service.isPlaying(); - notificationLayout = new RemoteViews(service.getPackageName(), R.layout.notification); - notificationLayoutBig = new RemoteViews(service.getPackageName(), R.layout.notification_big); + if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName) && TextUtils.isEmpty(song.albumName)) { + notificationLayoutBig.setViewVisibility(R.id.media_titles, View.INVISIBLE); + } else { + notificationLayoutBig.setViewVisibility(R.id.media_titles, View.VISIBLE); + notificationLayoutBig.setTextViewText(R.id.title, song.title); + notificationLayoutBig.setTextViewText(R.id.text, song.artistName); + notificationLayoutBig.setTextViewText(R.id.text2, song.albumName); + } - notification = new NotificationCompat.Builder(service) + linkButtons(notificationLayout, notificationLayoutBig); + + Intent action = new Intent(service, MainActivity.class); + action.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); + PendingIntent openAppPendingIntent = PendingIntent.getActivity(service, 0, action, 0); + + final Notification notification = new NotificationCompat.Builder(service) .setSmallIcon(R.drawable.ic_notification) - .setContentIntent(getOpenMusicControllerPendingIntent()) - .setCategory(NotificationCompat.CATEGORY_PROGRESS) + .setContentIntent(openAppPendingIntent) + .setCategory(NotificationCompat.CATEGORY_SERVICE) .setPriority(NotificationCompat.PRIORITY_MAX) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) .setContent(notificationLayout) @@ -105,177 +96,115 @@ public class PlayingNotificationHelper { notification.bigContentView = notificationLayoutBig; - setUpCollapsedLayout(); - setUpExpandedLayout(); - loadAlbumArt(); - setUpPlaybackActions(); - setUpExpandedPlaybackActions(); - - service.startForeground(notificationId, notification); - } - - private PendingIntent getOpenMusicControllerPendingIntent() { - Intent intent = new Intent(service, MainActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); - return PendingIntent.getActivity(service, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); - } - - private void setUpExpandedPlaybackActions() { - notificationLayoutBig.setOnClickPendingIntent(R.id.action_play_pause, - retrievePlaybackActions(1)); - - notificationLayoutBig.setOnClickPendingIntent(R.id.action_next, - retrievePlaybackActions(2)); - - notificationLayoutBig.setOnClickPendingIntent(R.id.action_prev, - retrievePlaybackActions(3)); - - notificationLayoutBig.setOnClickPendingIntent(R.id.action_quit, - retrievePlaybackActions(4)); - } - - private void setUpPlaybackActions() { - notificationLayout.setOnClickPendingIntent(R.id.action_play_pause, - retrievePlaybackActions(1)); - - notificationLayout.setOnClickPendingIntent(R.id.action_next, - retrievePlaybackActions(2)); - - notificationLayout.setOnClickPendingIntent(R.id.action_prev, - retrievePlaybackActions(3)); - } - - private PendingIntent retrievePlaybackActions(final int which) { - Intent action; - PendingIntent pendingIntent; - final ComponentName serviceName = new ComponentName(service, MusicService.class); - switch (which) { - case 1: - action = new Intent(MusicService.ACTION_TOGGLE_PAUSE); - action.setComponent(serviceName); - pendingIntent = PendingIntent.getService(service, 1, action, 0); - return pendingIntent; - case 2: - action = new Intent(MusicService.ACTION_SKIP); - action.setComponent(serviceName); - pendingIntent = PendingIntent.getService(service, 2, action, 0); - return pendingIntent; - case 3: - action = new Intent(MusicService.ACTION_REWIND); - action.setComponent(serviceName); - pendingIntent = PendingIntent.getService(service, 3, action, 0); - return pendingIntent; - case 4: - action = new Intent(MusicService.ACTION_QUIT); - action.setComponent(serviceName); - pendingIntent = PendingIntent.getService(service, 4, action, 0); - return pendingIntent; - default: - break; - } - return null; - } - - private void setUpCollapsedLayout() { - notificationLayout.setTextViewText(R.id.title, currentSong.title); - notificationLayout.setTextViewText(R.id.text, currentSong.artistName); - notificationLayout.setTextViewText(R.id.text2, currentSong.albumName); - } - - private void setUpExpandedLayout() { - notificationLayoutBig.setTextViewText(R.id.title, currentSong.title); - notificationLayoutBig.setTextViewText(R.id.text, currentSong.artistName); - notificationLayoutBig.setTextViewText(R.id.text2, currentSong.albumName); - } - - private void loadAlbumArt() { + final int bigNotificationImageSize = service.getResources().getDimensionPixelSize(R.dimen.notification_big_image_size); service.runOnUiThread(new Runnable() { @Override public void run() { - SongGlideRequest.Builder.from(Glide.with(service), currentSong) + if (target != null) { + Glide.clear(target); + } + target = SongGlideRequest.Builder.from(Glide.with(service), song) .checkIgnoreMediaStore(service) .generatePalette(service).build() - .into(target); + .into(new SimpleTarget(bigNotificationImageSize, bigNotificationImageSize) { + @Override + public void onResourceReady(BitmapPaletteWrapper resource, GlideAnimation glideAnimation) { + update(resource.getBitmap(), PhonographColorUtil.getColor(resource.getPalette(), Color.TRANSPARENT)); + } + + @Override + public void onLoadFailed(Exception e, Drawable errorDrawable) { + super.onLoadFailed(e, errorDrawable); + update(null, Color.TRANSPARENT); + } + + private void update(@Nullable Bitmap bitmap, int bgColor) { + if (bitmap != null) { + notificationLayout.setImageViewBitmap(R.id.image, bitmap); + notificationLayoutBig.setImageViewBitmap(R.id.image, bitmap); + } else { + notificationLayout.setImageViewResource(R.id.image, R.drawable.default_album_art); + notificationLayoutBig.setImageViewResource(R.id.image, R.drawable.default_album_art); + } + + if (!PreferenceUtil.getInstance(service).coloredNotification()) { + bgColor = Color.TRANSPARENT; + } + setBackgroundColor(bgColor); + setNotificationContent(bgColor == Color.TRANSPARENT ? Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP : ColorUtil.isColorLight(bgColor)); + + if (stopped) return; + service.startForeground(NOTIFICATION_ID, notification); + } + + private void setBackgroundColor(int color) { + notificationLayout.setInt(R.id.root, "setBackgroundColor", color); + notificationLayoutBig.setInt(R.id.root, "setBackgroundColor", color); + } + + private void setNotificationContent(boolean dark) { + int primary = MaterialValueHelper.getPrimaryTextColor(service, dark); + int secondary = MaterialValueHelper.getSecondaryTextColor(service, dark); + + Bitmap prev = createBitmap(Util.getTintedDrawable(service, R.drawable.ic_skip_previous_white_24dp, primary), 1.5f); + Bitmap next = createBitmap(Util.getTintedDrawable(service, R.drawable.ic_skip_next_white_24dp, primary), 1.5f); + Bitmap playPause = createBitmap(Util.getTintedDrawable(service, isPlaying ? R.drawable.ic_pause_white_24dp : R.drawable.ic_play_arrow_white_24dp, primary), 1.5f); + Bitmap close = createBitmap(Util.getTintedDrawable(service, R.drawable.ic_close_white_24dp, secondary), 1f); + + notificationLayout.setTextColor(R.id.title, primary); + notificationLayout.setTextColor(R.id.text, 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.text, secondary); + notificationLayoutBig.setTextColor(R.id.text2, secondary); + notificationLayoutBig.setImageViewBitmap(R.id.action_prev, prev); + notificationLayoutBig.setImageViewBitmap(R.id.action_next, next); + notificationLayoutBig.setImageViewBitmap(R.id.action_play_pause, playPause); + notificationLayoutBig.setImageViewBitmap(R.id.action_quit, close); + } + }); } }); + } - private void setAlbumCover(@Nullable Bitmap cover, int bgColor) { - if (cover != null) { - notificationLayout.setImageViewBitmap(R.id.icon, cover); - notificationLayoutBig.setImageViewBitmap(R.id.icon, cover); - } else { - notificationLayout.setImageViewResource(R.id.icon, R.drawable.default_album_art); - notificationLayoutBig.setImageViewResource(R.id.icon, R.drawable.default_album_art); - } - - if (!isColored) { - bgColor = Color.TRANSPARENT; - } - setBackgroundColor(bgColor); - setDarkNotificationContent(bgColor == Color.TRANSPARENT ? Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP : ColorUtil.isColorLight(bgColor)); - - if (notification != null) { - notificationManager.notify(notificationId, notification); - } - } - - private void setBackgroundColor(int color) { - notificationLayout.setInt(R.id.root, "setBackgroundColor", color); - notificationLayoutBig.setInt(R.id.root, "setBackgroundColor", color); - } - - public void killNotification() { + public synchronized void killNotification() { + stopped = true; service.stopForeground(true); - notification = null; } - public void updatePlayState(final boolean setPlaying) { - isPlaying = setPlaying; + private void linkButtons(final RemoteViews notificationLayout, final RemoteViews notificationLayoutBig) { + PendingIntent pendingIntent; - if (notification == null) { - updateNotification(); - } - setPlayPauseDrawable(); - if (notification != null) { - notificationManager.notify(notificationId, notification); - } + final ComponentName serviceName = new ComponentName(service, MusicService.class); + + // Previous track + pendingIntent = buildPendingIntent(service, MusicService.ACTION_REWIND, serviceName); + notificationLayout.setOnClickPendingIntent(R.id.action_prev, pendingIntent); + notificationLayoutBig.setOnClickPendingIntent(R.id.action_prev, pendingIntent); + + // Play and pause + pendingIntent = buildPendingIntent(service, MusicService.ACTION_TOGGLE_PAUSE, serviceName); + notificationLayout.setOnClickPendingIntent(R.id.action_play_pause, pendingIntent); + notificationLayoutBig.setOnClickPendingIntent(R.id.action_play_pause, pendingIntent); + + // Next track + pendingIntent = buildPendingIntent(service, MusicService.ACTION_SKIP, serviceName); + notificationLayout.setOnClickPendingIntent(R.id.action_next, pendingIntent); + notificationLayoutBig.setOnClickPendingIntent(R.id.action_next, pendingIntent); + + // Quit + pendingIntent = buildPendingIntent(service, MusicService.ACTION_QUIT, serviceName); + notificationLayoutBig.setOnClickPendingIntent(R.id.action_quit, pendingIntent); } - private void setDarkNotificationContent(boolean dark) { - isDark = dark; - setPlayPauseDrawable(); - - if (notificationLayout != null && notificationLayoutBig != null) { - int primary = MaterialValueHelper.getPrimaryTextColor(service, dark); - int secondary = MaterialValueHelper.getSecondaryTextColor(service, dark); - - Bitmap prev = createBitmap(Util.getTintedDrawable(service, R.drawable.ic_skip_previous_white_24dp, primary), 1.5f); - Bitmap next = createBitmap(Util.getTintedDrawable(service, R.drawable.ic_skip_next_white_24dp, primary), 1.5f); - Bitmap close = createBitmap(Util.getTintedDrawable(service, R.drawable.ic_close_white_24dp, secondary), 1f); - - notificationLayout.setTextColor(R.id.title, primary); - notificationLayout.setTextColor(R.id.text, secondary); - notificationLayout.setImageViewBitmap(R.id.action_prev, prev); - notificationLayout.setImageViewBitmap(R.id.action_next, next); - - notificationLayoutBig.setTextColor(R.id.title, primary); - notificationLayoutBig.setTextColor(R.id.text, secondary); - notificationLayoutBig.setTextColor(R.id.text2, secondary); - notificationLayoutBig.setImageViewBitmap(R.id.action_prev, prev); - notificationLayoutBig.setImageViewBitmap(R.id.action_next, next); - notificationLayoutBig.setImageViewBitmap(R.id.action_quit, close); - } - } - - private void setPlayPauseDrawable() { - Bitmap playPause = createBitmap(Util.getTintedDrawable(service, isPlaying ? R.drawable.ic_pause_white_24dp : R.drawable.ic_play_arrow_white_24dp, MaterialValueHelper.getPrimaryTextColor(service, isDark)), 1.5f); - if (notificationLayout != null) { - notificationLayout.setImageViewBitmap(R.id.action_play_pause, playPause); - } - if (notificationLayoutBig != null) { - notificationLayoutBig.setImageViewBitmap(R.id.action_play_pause, playPause); - } + protected PendingIntent buildPendingIntent(Context context, final String action, final ComponentName serviceName) { + Intent intent = new Intent(action); + intent.setComponent(serviceName); + return PendingIntent.getService(context, 0, intent, 0); } private static Bitmap createBitmap(Drawable drawable, float sizeMultiplier) { diff --git a/app/src/main/java/com/kabouzeid/gramophone/service/MusicService.java b/app/src/main/java/com/kabouzeid/gramophone/service/MusicService.java index eb5284d6..5bdcc145 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/service/MusicService.java +++ b/app/src/main/java/com/kabouzeid/gramophone/service/MusicService.java @@ -122,7 +122,6 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP private int shuffleMode; private int repeatMode; private boolean queuesRestored; - private boolean audioDucking; private boolean pausedByTransientLossOfFocus; private boolean receiversAndRemoteControlClientRegistered; private PlayingNotificationHelper playingNotificationHelper; @@ -170,8 +169,6 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, getClass().getName()); wakeLock.setReferenceCounted(false); - audioDucking = PreferenceUtil.getInstance(this).audioDucking(); - musicPlayerHandlerThread = new HandlerThread("PlaybackHandler"); musicPlayerHandlerThread.start(); playerHandler = new PlaybackHandler(this, musicPlayerHandlerThread.getLooper()); @@ -573,10 +570,6 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP } } - private void updateNotification() { - playingNotificationHelper.updateNotification(); - } - public int getNextPosition(boolean force) { int position = getPosition() + 1; switch (getRepeatMode()) { @@ -950,7 +943,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP switch (what) { case PLAY_STATE_CHANGED: final boolean isPlaying = isPlaying(); - playingNotificationHelper.updatePlayState(isPlaying); + playingNotificationHelper.updateNotification(); //noinspection deprecation remoteControlClient.setPlaybackState(isPlaying ? RemoteControlClient.PLAYSTATE_PLAYING : RemoteControlClient.PLAYSTATE_PAUSED); if (!isPlaying && getSongProgressMillis() > 0) { @@ -959,7 +952,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP songPlayCountHelper.notifyPlayStateChanged(isPlaying); break; case META_CHANGED: - updateNotification(); + playingNotificationHelper.updateNotification(); updateRemoteControlClient(); savePosition(); savePositionInTrack(); @@ -998,9 +991,6 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { switch (key) { - case PreferenceUtil.AUDIO_DUCKING: - audioDucking = sharedPreferences.getBoolean(key, true); - break; case PreferenceUtil.GAPLESS_PLAYBACK: if (sharedPreferences.getBoolean(key, false)) { prepareNext(); @@ -1013,7 +1003,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP updateRemoteControlClient(); break; case PreferenceUtil.COLORED_NOTIFICATION: - playingNotificationHelper.updateNotification(sharedPreferences.getBoolean(key, false)); + playingNotificationHelper.updateNotification(); break; } } @@ -1048,7 +1038,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP switch (msg.what) { case DUCK: - if (service.audioDucking) { + if (PreferenceUtil.getInstance(service).audioDucking()) { currentDuckVolume -= .05f; if (currentDuckVolume > .2f) { sendEmptyMessageDelayed(DUCK, 10); @@ -1062,7 +1052,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP break; case UNDUCK: - if (service.audioDucking) { + if (PreferenceUtil.getInstance(service).audioDucking()) { currentDuckVolume += .03f; if (currentDuckVolume < 1f) { sendEmptyMessageDelayed(UNDUCK, 10); diff --git a/app/src/main/res/layout/notification.xml b/app/src/main/res/layout/notification.xml index 2f329614..fb325e9d 100644 --- a/app/src/main/res/layout/notification.xml +++ b/app/src/main/res/layout/notification.xml @@ -37,7 +37,7 @@ android:layout_weight="0"> - @@ -101,18 +101,18 @@ android:id="@+id/media_actions" android:layout_width="match_parent" android:layout_height="48dp" - android:layout_below="@+id/text_container" + android:layout_below="@+id/media_titles" android:layout_marginEnd="12dp" android:layout_marginStart="12dp" - android:layout_toEndOf="@id/icon" - android:layout_toRightOf="@id/icon" + android:layout_toEndOf="@id/image" + android:layout_toRightOf="@id/image" android:layoutDirection="ltr" android:orientation="horizontal" tools:ignore="UnusedAttribute"> - - -