From 42a00dee95cf88ccf97de86784ca27c156e22855 Mon Sep 17 00:00:00 2001 From: Prathamesh More Date: Wed, 1 Dec 2021 20:14:46 +0530 Subject: [PATCH] [Widget] Added a new MD3 widget --- app/src/main/AndroidManifest.xml | 18 +- .../retromusic/appwidgets/AppWidgetMD3.kt | 273 ++++++++++++++++++ .../retromusic/service/MusicService.java | 8 + .../res/drawable/app_widget_background.xml | 10 + app/src/main/res/layout/app_widget_md3.xml | 100 +++++++ app/src/main/res/values/dimens.xml | 3 + app/src/main/res/values/strings.xml | 1 + app/src/main/res/xml/app_widget_md3_info.xml | 11 + 8 files changed, 421 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/code/name/monkey/retromusic/appwidgets/AppWidgetMD3.kt create mode 100644 app/src/main/res/drawable/app_widget_background.xml create mode 100644 app/src/main/res/layout/app_widget_md3.xml create mode 100644 app/src/main/res/xml/app_widget_md3_info.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e785fea3..a4ea84e9 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 00000000..a60c7bca --- /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 0647dbc2..99067cd4 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 00000000..e845d20c --- /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 00000000..55f3fb28 --- /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 ac705d44..a3f2064c 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 44901dcd..22959662 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 00000000..6aff4feb --- /dev/null +++ b/app/src/main/res/xml/app_widget_md3_info.xml @@ -0,0 +1,11 @@ + + \ No newline at end of file