diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index e785fea33..a4ea84e98 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -121,7 +121,7 @@
@@ -256,13 +256,25 @@
android:name="android.appwidget.provider"
android:resource="@xml/app_widget_card_info" />
+
+
+
+
+
+
+
@@ -284,6 +296,6 @@
+ android:value=".cast.CastOptionsProvider" />
diff --git a/app/src/main/java/code/name/monkey/retromusic/appwidgets/AppWidgetMD3.kt b/app/src/main/java/code/name/monkey/retromusic/appwidgets/AppWidgetMD3.kt
new file mode 100644
index 000000000..a60c7bca5
--- /dev/null
+++ b/app/src/main/java/code/name/monkey/retromusic/appwidgets/AppWidgetMD3.kt
@@ -0,0 +1,273 @@
+/*
+ * Copyright (c) 2020 Hemanth Savarla.
+ *
+ * Licensed under the GNU General Public License v3
+ *
+ * This is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ */
+package code.name.monkey.retromusic.appwidgets
+
+import android.app.PendingIntent
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.graphics.Bitmap
+import android.graphics.drawable.Drawable
+import android.text.TextUtils
+import android.view.View
+import android.widget.RemoteViews
+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.appwidgets.base.BaseAppWidget
+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.service.MusicService
+import code.name.monkey.retromusic.service.MusicService.*
+import code.name.monkey.retromusic.util.DensityUtil
+import code.name.monkey.retromusic.util.ImageUtil
+import code.name.monkey.retromusic.util.PreferenceUtil
+import code.name.monkey.retromusic.util.RetroUtil
+import com.bumptech.glide.Glide
+import com.bumptech.glide.request.target.SimpleTarget
+import com.bumptech.glide.request.target.Target
+import com.bumptech.glide.request.transition.Transition
+
+class AppWidgetMD3 : BaseAppWidget() {
+ private var target: Target? = null // for cancellation
+
+ /**
+ * Initialize given widgets to default state, where we launch Music on default click and hide
+ * actions if service not running.
+ */
+ override fun defaultAppWidget(context: Context, appWidgetIds: IntArray) {
+ val appWidgetView = RemoteViews(context.packageName, R.layout.app_widget_md3)
+
+ appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE)
+ appWidgetView.setImageViewResource(R.id.image, R.drawable.default_audio_art)
+ val secondaryColor = MaterialValueHelper.getSecondaryTextColor(context, true)
+ appWidgetView.setImageViewBitmap(
+ R.id.button_next, createBitmap(
+ RetroUtil.getTintedVectorDrawable(
+ context,
+ R.drawable.ic_skip_next,
+ secondaryColor
+ ), 1f
+ )
+ )
+ appWidgetView.setImageViewBitmap(
+ R.id.button_prev, createBitmap(
+ RetroUtil.getTintedVectorDrawable(
+ context,
+ R.drawable.ic_skip_previous,
+ secondaryColor
+ ), 1f
+ )
+ )
+ appWidgetView.setImageViewBitmap(
+ R.id.button_toggle_play_pause, createBitmap(
+ RetroUtil.getTintedVectorDrawable(
+ context,
+ R.drawable.ic_play_arrow_white_32dp,
+ secondaryColor
+ ), 1f
+ )
+ )
+
+ linkButtons(context, appWidgetView)
+ pushUpdate(context, appWidgetIds, appWidgetView)
+ }
+
+ /**
+ * Update all active widget instances by pushing changes
+ */
+ override fun performUpdate(service: MusicService, appWidgetIds: IntArray?) {
+ val appWidgetView = RemoteViews(service.packageName, R.layout.app_widget_md3)
+
+ val isPlaying = service.isPlaying
+ val song = service.currentSong
+
+ // Set the titles and artwork
+ if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName)) {
+ appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE)
+ } else {
+ appWidgetView.setViewVisibility(R.id.media_titles, View.VISIBLE)
+ appWidgetView.setTextViewText(R.id.title, song.title)
+ appWidgetView.setTextViewText(R.id.text, getSongArtistAndAlbum(song))
+ }
+
+ // Set correct drawable for pause state
+ val playPauseRes =
+ if (isPlaying) R.drawable.ic_pause else R.drawable.ic_play_arrow_white_32dp
+ appWidgetView.setImageViewBitmap(
+ R.id.button_toggle_play_pause, createBitmap(
+ RetroUtil.getTintedVectorDrawable(
+ service,
+ playPauseRes,
+ MaterialValueHelper.getSecondaryTextColor(service, true)
+ ), 1f
+ )
+ )
+
+ // Set prev/next button drawables
+ appWidgetView.setImageViewBitmap(
+ R.id.button_next, createBitmap(
+ RetroUtil.getTintedVectorDrawable(
+ service,
+ R.drawable.ic_skip_next,
+ MaterialValueHelper.getSecondaryTextColor(service, true)
+ ), 1f
+ )
+ )
+ appWidgetView.setImageViewBitmap(
+ R.id.button_prev, createBitmap(
+ RetroUtil.getTintedVectorDrawable(
+ service,
+ R.drawable.ic_skip_previous,
+ MaterialValueHelper.getSecondaryTextColor(service, true)
+ ), 1f
+ )
+ )
+
+ // Link actions buttons to intents
+ linkButtons(service, appWidgetView)
+
+ if (imageSize == 0) {
+ imageSize =
+ service.resources.getDimensionPixelSize(R.dimen.app_widget_card_image_size)
+ }
+ if (cardRadius == 0f) {
+ cardRadius =
+ DensityUtil.dip2px(service, 8F).toFloat()
+ }
+
+ // Load the album cover async and push the update on completion
+ 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(imageSize, imageSize) {
+ override fun onResourceReady(
+ resource: BitmapPaletteWrapper,
+ transition: Transition?
+ ) {
+ val palette = resource.palette
+ update(
+ resource.bitmap, palette.getVibrantColor(
+ palette.getMutedColor(
+ MaterialValueHelper.getSecondaryTextColor(
+ service, true
+ )
+ )
+ )
+ )
+ }
+
+ override fun onLoadFailed(errorDrawable: Drawable?) {
+ super.onLoadFailed(errorDrawable)
+ update(null, MaterialValueHelper.getSecondaryTextColor(service, true))
+ }
+
+ private fun update(bitmap: Bitmap?, color: Int) {
+ // Set correct drawable for pause state
+ appWidgetView.setImageViewBitmap(
+ R.id.button_toggle_play_pause, ImageUtil.createBitmap(
+ ImageUtil.getTintedVectorDrawable(
+ service, playPauseRes, color
+ )
+ )
+ )
+
+ // Set prev/next button drawables
+ appWidgetView.setImageViewBitmap(
+ R.id.button_next, ImageUtil.createBitmap(
+ ImageUtil.getTintedVectorDrawable(
+ service, R.drawable.ic_skip_next, color
+ )
+ )
+ )
+ appWidgetView.setImageViewBitmap(
+ R.id.button_prev, ImageUtil.createBitmap(
+ ImageUtil.getTintedVectorDrawable(
+ service, R.drawable.ic_skip_previous, color
+ )
+ )
+ )
+
+ val image = getAlbumArtDrawable(service.resources, bitmap)
+ val roundedBitmap = createRoundedBitmap(
+ image, imageSize, imageSize, cardRadius, cardRadius, cardRadius, cardRadius
+ )
+ appWidgetView.setImageViewBitmap(R.id.image, roundedBitmap)
+
+ pushUpdate(service, appWidgetIds, appWidgetView)
+ }
+ })
+ }
+ }
+
+ /**
+ * Link up various button actions using [PendingIntent].
+ */
+ private fun linkButtons(context: Context, views: RemoteViews) {
+ val action = Intent(context, MainActivity::class.java)
+ .putExtra(
+ MainActivity.EXPAND_PANEL,
+ PreferenceUtil.isExpandPanel
+ )
+
+ val serviceName = ComponentName(context, MusicService::class.java)
+
+ // Home
+ action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
+ var pendingIntent =
+ PendingIntent.getActivity(
+ context, 0, action, if (VersionUtils.hasMarshmallow())
+ PendingIntent.FLAG_IMMUTABLE
+ else 0
+ )
+ views.setOnClickPendingIntent(R.id.image, pendingIntent)
+ views.setOnClickPendingIntent(R.id.media_titles, pendingIntent)
+
+ // Previous track
+ pendingIntent = buildPendingIntent(context, ACTION_REWIND, serviceName)
+ views.setOnClickPendingIntent(R.id.button_prev, pendingIntent)
+
+ // Play and pause
+ pendingIntent = buildPendingIntent(context, ACTION_TOGGLE_PAUSE, serviceName)
+ views.setOnClickPendingIntent(R.id.button_toggle_play_pause, pendingIntent)
+
+ // Next track
+ pendingIntent = buildPendingIntent(context, ACTION_SKIP, serviceName)
+ views.setOnClickPendingIntent(R.id.button_next, pendingIntent)
+ }
+
+ companion object {
+
+ const val NAME = "app_widget_md3"
+
+ private var mInstance: AppWidgetMD3? = null
+ private var imageSize = 0
+ private var cardRadius = 0F
+
+ val instance: AppWidgetMD3
+ @Synchronized get() {
+ if (mInstance == null) {
+ mInstance = AppWidgetMD3()
+ }
+ return mInstance!!
+ }
+ }
+}
diff --git a/app/src/main/java/code/name/monkey/retromusic/service/MusicService.java b/app/src/main/java/code/name/monkey/retromusic/service/MusicService.java
index 0647dbc2c..99067cd4c 100644
--- a/app/src/main/java/code/name/monkey/retromusic/service/MusicService.java
+++ b/app/src/main/java/code/name/monkey/retromusic/service/MusicService.java
@@ -81,6 +81,7 @@ import code.name.monkey.retromusic.activities.LockScreenActivity;
import code.name.monkey.retromusic.appwidgets.AppWidgetBig;
import code.name.monkey.retromusic.appwidgets.AppWidgetCard;
import code.name.monkey.retromusic.appwidgets.AppWidgetClassic;
+import code.name.monkey.retromusic.appwidgets.AppWidgetMD3;
import code.name.monkey.retromusic.appwidgets.AppWidgetSmall;
import code.name.monkey.retromusic.appwidgets.AppWidgetText;
import code.name.monkey.retromusic.auto.AutoMediaIDHelper;
@@ -198,6 +199,8 @@ public class MusicService extends MediaBrowserServiceCompat
private final AppWidgetText appWidgetText = AppWidgetText.Companion.getInstance();
+ private final AppWidgetMD3 appWidgetMd3 = AppWidgetMD3.Companion.getInstance();
+
private final BroadcastReceiver widgetIntentReceiver =
new BroadcastReceiver() {
@Override
@@ -226,6 +229,10 @@ public class MusicService extends MediaBrowserServiceCompat
appWidgetText.performUpdate(MusicService.this, ids);
break;
}
+ case AppWidgetMD3.NAME: {
+ appWidgetMd3.performUpdate(MusicService.this, ids);
+ break;
+ }
}
}
}
@@ -1551,6 +1558,7 @@ public class MusicService extends MediaBrowserServiceCompat
appWidgetSmall.notifyChange(this, what);
appWidgetCard.notifyChange(this, what);
appWidgetText.notifyChange(this, what);
+ appWidgetMd3.notifyChange(this, what);
}
private void setCustomAction(PlaybackStateCompat.Builder stateBuilder) {
diff --git a/app/src/main/res/drawable/app_widget_background.xml b/app/src/main/res/drawable/app_widget_background.xml
new file mode 100644
index 000000000..e845d20cb
--- /dev/null
+++ b/app/src/main/res/drawable/app_widget_background.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/app_widget_md3.xml b/app/src/main/res/layout/app_widget_md3.xml
new file mode 100644
index 000000000..55f3fb286
--- /dev/null
+++ b/app/src/main/res/layout/app_widget_md3.xml
@@ -0,0 +1,100 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index ac705d443..a3f2064cf 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -39,6 +39,9 @@
2dp
12dp
+ 96dp
+ 80dp
+
32dp
8dp
48dp
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 44901dcdd..229596627 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -518,4 +518,5 @@
Do you want to restore backup?
New Backup
Backup and restore your settings, playlists
+ MD3
diff --git a/app/src/main/res/xml/app_widget_md3_info.xml b/app/src/main/res/xml/app_widget_md3_info.xml
new file mode 100644
index 000000000..6aff4feba
--- /dev/null
+++ b/app/src/main/res/xml/app_widget_md3_info.xml
@@ -0,0 +1,11 @@
+
+
\ No newline at end of file