diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6f166beb..94cd758c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -139,6 +139,19 @@ android:resource="@xml/app_widget_classic_info" /> + + + + + + + + diff --git a/app/src/main/java/com/kabouzeid/gramophone/appwidgets/AppWidgetClassic.java b/app/src/main/java/com/kabouzeid/gramophone/appwidgets/AppWidgetClassic.java index 33500b9c..19fc2f46 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/appwidgets/AppWidgetClassic.java +++ b/app/src/main/java/com/kabouzeid/gramophone/appwidgets/AppWidgetClassic.java @@ -62,9 +62,9 @@ public class AppWidgetClassic extends BaseAppWidget { appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE); 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)); + appWidgetView.setImageViewBitmap(R.id.button_next, createBitmap(Util.getTintedDrawable(context, R.drawable.ic_skip_next_white_24dp, MaterialValueHelper.getSecondaryTextColor(context, false)), 1f)); + appWidgetView.setImageViewBitmap(R.id.button_prev, createBitmap(Util.getTintedDrawable(context, R.drawable.ic_skip_previous_white_24dp, MaterialValueHelper.getSecondaryTextColor(context, false)), 1f)); + appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, createBitmap(Util.getTintedDrawable(context, R.drawable.ic_play_arrow_white_24dp, MaterialValueHelper.getSecondaryTextColor(context, false)), 1f)); linkButtons(context, appWidgetView); pushUpdate(context, appWidgetIds, appWidgetView); @@ -122,11 +122,11 @@ public class AppWidgetClassic extends BaseAppWidget { // Set correct drawable for pause state int playPauseRes = isPlaying ? R.drawable.ic_pause_white_24dp : R.drawable.ic_play_arrow_white_24dp; - appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, createBitmap(Util.getTintedDrawable(service, playPauseRes, MaterialValueHelper.getPrimaryTextColor(service, true)), 1f)); + appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, createBitmap(Util.getTintedDrawable(service, playPauseRes, MaterialValueHelper.getSecondaryTextColor(service, false)), 1f)); // set prev/next button drawables - appWidgetView.setImageViewBitmap(R.id.button_next, createBitmap(Util.getTintedDrawable(service, R.drawable.ic_skip_next_white_24dp, MaterialValueHelper.getPrimaryTextColor(service, true)), 1f)); - appWidgetView.setImageViewBitmap(R.id.button_prev, createBitmap(Util.getTintedDrawable(service, R.drawable.ic_skip_previous_white_24dp, MaterialValueHelper.getPrimaryTextColor(service, true)), 1f)); + appWidgetView.setImageViewBitmap(R.id.button_next, createBitmap(Util.getTintedDrawable(service, R.drawable.ic_skip_next_white_24dp, MaterialValueHelper.getSecondaryTextColor(service, false)), 1f)); + appWidgetView.setImageViewBitmap(R.id.button_prev, createBitmap(Util.getTintedDrawable(service, R.drawable.ic_skip_previous_white_24dp, MaterialValueHelper.getSecondaryTextColor(service, false)), 1f)); // Link actions buttons to intents linkButtons(service, appWidgetView); diff --git a/app/src/main/java/com/kabouzeid/gramophone/appwidgets/AppWidgetSmall.java b/app/src/main/java/com/kabouzeid/gramophone/appwidgets/AppWidgetSmall.java new file mode 100644 index 00000000..d9f0ba3f --- /dev/null +++ b/app/src/main/java/com/kabouzeid/gramophone/appwidgets/AppWidgetSmall.java @@ -0,0 +1,208 @@ +package com.kabouzeid.gramophone.appwidgets; + +import android.app.PendingIntent; +import android.appwidget.AppWidgetManager; +import android.content.ComponentName; +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; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.request.animation.GlideAnimation; +import com.bumptech.glide.request.target.SimpleTarget; +import com.bumptech.glide.request.target.Target; +import com.kabouzeid.appthemehelper.util.MaterialValueHelper; +import com.kabouzeid.gramophone.R; +import com.kabouzeid.gramophone.glide.SongGlideRequest; +import com.kabouzeid.gramophone.model.Song; +import com.kabouzeid.gramophone.service.MusicService; +import com.kabouzeid.gramophone.ui.activities.MainActivity; +import com.kabouzeid.gramophone.util.Util; + +/** + * @author Karim Abou Zeid (kabouzeid) + */ +public class AppWidgetSmall extends BaseAppWidget { + public static final String NAME = "app_widget_small"; + + private static AppWidgetSmall mInstance; + + public static synchronized AppWidgetSmall getInstance() { + if (mInstance == null) { + mInstance = new AppWidgetSmall(); + } + return mInstance; + } + + /** + * {@inheritDoc} + */ + @Override + public void onUpdate(final Context context, final AppWidgetManager appWidgetManager, + final int[] appWidgetIds) { + defaultAppWidget(context, appWidgetIds); + final Intent updateIntent = new Intent(MusicService.APP_WIDGET_UPDATE); + updateIntent.putExtra(MusicService.EXTRA_APP_WIDGET_NAME, NAME); + updateIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds); + updateIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + context.sendBroadcast(updateIntent); + } + + /** + * Initialize given widgets to default state, where we launch Music on + * default click and hide actions if service not running. + */ + private void defaultAppWidget(final Context context, final int[] appWidgetIds) { + final RemoteViews appWidgetView = new RemoteViews(context.getPackageName(), R.layout.app_widget_small); + + appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE); + 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.getSecondaryTextColor(context, false)), 1f)); + appWidgetView.setImageViewBitmap(R.id.button_prev, createBitmap(Util.getTintedDrawable(context, R.drawable.ic_skip_previous_white_24dp, MaterialValueHelper.getSecondaryTextColor(context, false)), 1f)); + appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, createBitmap(Util.getTintedDrawable(context, R.drawable.ic_play_arrow_white_24dp, MaterialValueHelper.getSecondaryTextColor(context, false)), 1f)); + + linkButtons(context, appWidgetView); + pushUpdate(context, appWidgetIds, appWidgetView); + } + + private void pushUpdate(final Context context, final int[] appWidgetIds, final RemoteViews views) { + final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); + if (appWidgetIds != null) { + appWidgetManager.updateAppWidget(appWidgetIds, views); + } else { + appWidgetManager.updateAppWidget(new ComponentName(context, getClass()), views); + } + } + + /** + * Check against {@link AppWidgetManager} if there are any instances of this + * widget. + */ + private boolean hasInstances(final Context context) { + final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); + final int[] mAppWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, + getClass())); + return mAppWidgetIds.length > 0; + } + + /** + * Handle a change notification coming over from + * {@link MusicService} + */ + public void notifyChange(final MusicService service, final String what) { + if (hasInstances(service)) { + if (MusicService.META_CHANGED.equals(what) || MusicService.PLAY_STATE_CHANGED.equals(what)) { + performUpdate(service, null); + } + } + } + + /** + * Update all active widget instances by pushing changes + */ + public void performUpdate(final MusicService service, final int[] appWidgetIds) { + final RemoteViews appWidgetView = new RemoteViews(service.getPackageName(), R.layout.app_widget_small); + + final boolean isPlaying = service.isPlaying(); + final Song song = service.getCurrentSong(); + + // Set the titles and artwork + if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName)) { + appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE); + } else { + if (TextUtils.isEmpty(song.title) || TextUtils.isEmpty(song.artistName)) { + appWidgetView.setTextViewText(R.id.text_separator, ""); + } else { + appWidgetView.setTextViewText(R.id.text_separator, "•"); + } + + appWidgetView.setViewVisibility(R.id.media_titles, View.VISIBLE); + appWidgetView.setTextViewText(R.id.title, song.title); + appWidgetView.setTextViewText(R.id.text, song.artistName); + } + + // Set correct drawable for pause state + int playPauseRes = isPlaying ? R.drawable.ic_pause_white_24dp : R.drawable.ic_play_arrow_white_24dp; + appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, createBitmap(Util.getTintedDrawable(service, playPauseRes, MaterialValueHelper.getSecondaryTextColor(service, false)), 1f)); + + // set prev/next button drawables + appWidgetView.setImageViewBitmap(R.id.button_next, createBitmap(Util.getTintedDrawable(service, R.drawable.ic_skip_next_white_24dp, MaterialValueHelper.getSecondaryTextColor(service, false)), 1f)); + appWidgetView.setImageViewBitmap(R.id.button_prev, createBitmap(Util.getTintedDrawable(service, R.drawable.ic_skip_previous_white_24dp, MaterialValueHelper.getSecondaryTextColor(service, false)), 1f)); + + // Link actions buttons to intents + linkButtons(service, appWidgetView); + + // load the album cover async and push the update on completion + final Context appContext = service.getApplicationContext(); + final int widgetImageSize = service.getResources().getDimensionPixelSize(R.dimen.app_widget_small_image_size); + service.runOnUiThread(new Runnable() { + @Override + public void run() { + if (target != null) { + Glide.clear(target); + } + target = SongGlideRequest.Builder.from(Glide.with(appContext), song) + .checkIgnoreMediaStore(appContext) + .asBitmap().build() + .into(new SimpleTarget(widgetImageSize, widgetImageSize) { + @Override + public void onResourceReady(Bitmap resource, GlideAnimation glideAnimation) { + 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); + if (bitmap == null) { + appWidgetView.setImageViewResource(R.id.image, R.drawable.default_album_art); + } else { + appWidgetView.setImageViewBitmap(R.id.image, bitmap); + } + pushUpdate(appContext, appWidgetIds, appWidgetView); + } + }); + } + }); + } + + private Target target; // for cancellation + + /** + * Link up various button actions using {@link PendingIntent}. + */ + private void linkButtons(final Context context, final RemoteViews views) { + Intent action; + PendingIntent pendingIntent; + + final ComponentName serviceName = new ComponentName(context, MusicService.class); + + // Home + action = new Intent(context, MainActivity.class); + action.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); + pendingIntent = PendingIntent.getActivity(context, 0, action, 0); + views.setOnClickPendingIntent(R.id.image, pendingIntent); + views.setOnClickPendingIntent(R.id.media_titles, pendingIntent); + + // Previous track + pendingIntent = buildPendingIntent(context, MusicService.ACTION_REWIND, serviceName); + views.setOnClickPendingIntent(R.id.button_prev, pendingIntent); + + // Play and pause + pendingIntent = buildPendingIntent(context, MusicService.ACTION_TOGGLE_PAUSE, serviceName); + views.setOnClickPendingIntent(R.id.button_toggle_play_pause, pendingIntent); + + // Next track + pendingIntent = buildPendingIntent(context, MusicService.ACTION_SKIP, serviceName); + views.setOnClickPendingIntent(R.id.button_next, pendingIntent); + } +} 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 16afe26e..fbc80057 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/service/MusicService.java +++ b/app/src/main/java/com/kabouzeid/gramophone/service/MusicService.java @@ -38,6 +38,7 @@ import com.bumptech.glide.request.animation.GlideAnimation; import com.bumptech.glide.request.target.SimpleTarget; import com.kabouzeid.gramophone.R; import com.kabouzeid.gramophone.appwidgets.AppWidgetClassic; +import com.kabouzeid.gramophone.appwidgets.AppWidgetSmall; import com.kabouzeid.gramophone.glide.BlurTransformation; import com.kabouzeid.gramophone.glide.SongGlideRequest; import com.kabouzeid.gramophone.helper.PlayingNotificationHelper; @@ -112,6 +113,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP private final IBinder musicBind = new MusicBinder(); + private AppWidgetSmall appWidgetSmall = AppWidgetSmall.getInstance(); private AppWidgetClassic appWidgetClassic = AppWidgetClassic.getInstance(); private Playback playback; @@ -937,6 +939,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP private void sendChangeInternal(final String what) { sendBroadcast(new Intent(what)); appWidgetClassic.notifyChange(this, what); + appWidgetSmall.notifyChange(this, what); } private void handleChangeInternal(@NonNull final String what) { @@ -1161,6 +1164,9 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP if (AppWidgetClassic.NAME.equals(command)) { final int[] small = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS); appWidgetClassic.performUpdate(MusicService.this, small); + } else if (AppWidgetSmall.NAME.equals(command)) { + final int[] small = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS); + appWidgetSmall.performUpdate(MusicService.this, small); } } }; diff --git a/app/src/main/res/layout/app_widget_classic.xml b/app/src/main/res/layout/app_widget_classic.xml index c1079215..35e33fe5 100644 --- a/app/src/main/res/layout/app_widget_classic.xml +++ b/app/src/main/res/layout/app_widget_classic.xml @@ -3,7 +3,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="@dimen/app_widget_classic_height" - android:background="#AAFFFFFF" + android:background="#AA000000" android:orientation="horizontal" tools:ignore="ContentDescription"> @@ -34,7 +34,7 @@ android:layout_weight="1" android:background="@drawable/notification_selector" tools:src="@drawable/ic_skip_previous_white_24dp" - tools:tint="#000" /> + tools:tint="#fff" /> + tools:tint="#fff" /> + tools:tint="#fff" /> @@ -73,7 +73,7 @@ android:gravity="center_vertical" android:singleLine="true" android:textAppearance="@style/TextAppearance.AppCompat.Subhead" - android:textColor="@color/ate_primary_text_light" + android:textColor="@color/ate_primary_text_dark" tools:text="Title" /> diff --git a/app/src/main/res/layout/app_widget_small.xml b/app/src/main/res/layout/app_widget_small.xml new file mode 100644 index 00000000..0121481b --- /dev/null +++ b/app/src/main/res/layout/app_widget_small.xml @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index a81e7b44..9b200137 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -64,4 +64,9 @@ http://developer.android.com/guide/topics/appwidgets/index.html#CreatingLayout 250dp 40dp + 48dp + 48dp + 250dp + 40dp + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 08c2e3d7..613b69b4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -246,4 +246,5 @@ Listing files %s is the new start directory. Phonograph - Classic + Phonograph - Small diff --git a/app/src/main/res/xml/app_widget_small_info.xml b/app/src/main/res/xml/app_widget_small_info.xml new file mode 100644 index 00000000..7edb7429 --- /dev/null +++ b/app/src/main/res/xml/app_widget_small_info.xml @@ -0,0 +1,12 @@ + + + + \ No newline at end of file