widgets: improve preview

Use a 5x5 instead of a 4x5 preview for the widget so that it lines up
better with most default launcher configurations.
This commit is contained in:
OxygenCobalt 2021-08-08 15:09:28 -06:00
parent 4cc433f7ef
commit 97459fdcca
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
7 changed files with 82 additions and 82 deletions

View file

@ -29,8 +29,7 @@ import org.oxycblt.auxio.ui.newMainIntent
private fun createViews( private fun createViews(
context: Context, context: Context,
@LayoutRes layout: Int, @LayoutRes layout: Int
state: WidgetState
): RemoteViews { ): RemoteViews {
val views = RemoteViews(context.packageName, layout) val views = RemoteViews(context.packageName, layout)
@ -39,31 +38,35 @@ private fun createViews(
context.newMainIntent() context.newMainIntent()
) )
views.setOnClickPendingIntent( return views
}
private fun RemoteViews.applyState(context: Context, state: WidgetState) {
setOnClickPendingIntent(
R.id.widget_skip_prev, R.id.widget_skip_prev,
context.newBroadcastIntent( context.newBroadcastIntent(
PlaybackService.ACTION_SKIP_PREV PlaybackService.ACTION_SKIP_PREV
) )
) )
views.setOnClickPendingIntent( setOnClickPendingIntent(
R.id.widget_play_pause, R.id.widget_play_pause,
context.newBroadcastIntent( context.newBroadcastIntent(
PlaybackService.ACTION_PLAY_PAUSE PlaybackService.ACTION_PLAY_PAUSE
) )
) )
views.setOnClickPendingIntent( setOnClickPendingIntent(
R.id.widget_skip_next, R.id.widget_skip_next,
context.newBroadcastIntent( context.newBroadcastIntent(
PlaybackService.ACTION_SKIP_NEXT PlaybackService.ACTION_SKIP_NEXT
) )
) )
views.setTextViewText(R.id.widget_song, state.song.name) setTextViewText(R.id.widget_song, state.song.name)
views.setTextViewText(R.id.widget_artist, state.song.album.artist.name) setTextViewText(R.id.widget_artist, state.song.album.artist.name)
views.setImageViewResource( setImageViewResource(
R.id.widget_play_pause, R.id.widget_play_pause,
if (state.isPlaying) { if (state.isPlaying) {
R.drawable.ic_pause R.drawable.ic_pause
@ -73,24 +76,29 @@ private fun createViews(
) )
if (state.albumArt != null) { if (state.albumArt != null) {
views.setImageViewBitmap(R.id.widget_cover, state.albumArt) setImageViewBitmap(R.id.widget_cover, state.albumArt)
views.setContentDescription( setContentDescription(
R.id.widget_cover, context.getString(R.string.desc_album_cover, state.song.album.name) R.id.widget_cover, context.getString(R.string.desc_album_cover, state.song.album.name)
) )
} else { } else {
views.setImageViewResource(R.id.widget_cover, R.drawable.ic_song) setImageViewResource(R.id.widget_cover, R.drawable.ic_song)
views.setContentDescription(R.id.widget_cover, context.getString(R.string.desc_no_cover)) setContentDescription(R.id.widget_cover, context.getString(R.string.desc_no_cover))
}
} }
return views fun createDefaultWidget(context: Context): RemoteViews {
return createViews(context, R.layout.widget_default)
} }
fun createSmallWidget(context: Context, state: WidgetState): RemoteViews { fun createSmallWidget(context: Context, state: WidgetState): RemoteViews {
return createViews(context, R.layout.widget_small, state) val views = createViews(context, R.layout.widget_small)
views.applyState(context, state)
return views
} }
fun createFullWidget(context: Context, state: WidgetState): RemoteViews { fun createFullWidget(context: Context, state: WidgetState): RemoteViews {
val views = createViews(context, R.layout.widget_full, state) val views = createViews(context, R.layout.widget_full)
views.applyState(context, state)
views.setOnClickPendingIntent( views.setOnClickPendingIntent(
R.id.widget_loop, R.id.widget_loop,
@ -107,9 +115,9 @@ fun createFullWidget(context: Context, state: WidgetState): RemoteViews {
) )
// The main way the large widget differs from the other widgets is the addition of extra // The main way the large widget differs from the other widgets is the addition of extra
// controls. However, since the context we use to load attributes is from the main process, // controls. However, since we can't retrieve the context of our views here, we cant
// attempting to dynamically color anything will result in an error. More duplicate // dynamically set the image view attributes. More duplicate resources it is. This is
// resources it is. This is getting really tiring. // getting really tiring.
val shuffleRes = when { val shuffleRes = when {
state.isShuffled -> R.drawable.ic_shuffle_tinted state.isShuffled -> R.drawable.ic_shuffle_tinted

View file

@ -18,7 +18,6 @@
package org.oxycblt.auxio.widgets package org.oxycblt.auxio.widgets
import android.annotation.SuppressLint
import android.appwidget.AppWidgetHostView import android.appwidget.AppWidgetHostView
import android.appwidget.AppWidgetManager import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider import android.appwidget.AppWidgetProvider
@ -30,12 +29,10 @@ import android.os.Bundle
import android.util.SizeF import android.util.SizeF
import android.widget.RemoteViews import android.widget.RemoteViews
import org.oxycblt.auxio.BuildConfig import org.oxycblt.auxio.BuildConfig
import org.oxycblt.auxio.R
import org.oxycblt.auxio.coil.loadBitmap import org.oxycblt.auxio.coil.loadBitmap
import org.oxycblt.auxio.logD import org.oxycblt.auxio.logD
import org.oxycblt.auxio.playback.state.PlaybackStateManager import org.oxycblt.auxio.playback.state.PlaybackStateManager
import org.oxycblt.auxio.ui.isLandscape import org.oxycblt.auxio.ui.isLandscape
import org.oxycblt.auxio.ui.newMainIntent
/** /**
* Auxio's one and only appwidget. This widget follows a more unorthodox approach, effectively * Auxio's one and only appwidget. This widget follows a more unorthodox approach, effectively
@ -44,15 +41,61 @@ import org.oxycblt.auxio.ui.newMainIntent
* - For widgets Wx2 or higher, show an expanded view with album art and basic controls * - For widgets Wx2 or higher, show an expanded view with album art and basic controls
* - For widgets 4x2 or higher, show a complete view with all playback controls * - For widgets 4x2 or higher, show a complete view with all playback controls
* *
* Other widget variants might be added if there is sufficient demand.
*
* For more specific details about these sub-widgets, see Forms.kt. * For more specific details about these sub-widgets, see Forms.kt.
*/ */
class WidgetProvider : AppWidgetProvider() { class WidgetProvider : AppWidgetProvider() {
/*
* Update the widget based on the playback state.
*/
fun update(context: Context, playbackManager: PlaybackStateManager) {
val appWidgetManager = AppWidgetManager.getInstance(context)
val song = playbackManager.song
if (song == null) {
reset(context)
return
}
loadBitmap(context, song) { bitmap ->
val state = WidgetState(
song,
bitmap,
playbackManager.isPlaying,
playbackManager.isShuffling,
playbackManager.loopMode
)
// Map each widget form to the cells where it would look at least okay.
val views = mapOf(
SizeF(180f, 110f) to createSmallWidget(context, state),
SizeF(250f, 110f) to createFullWidget(context, state)
)
appWidgetManager.applyViewsCompat(context, views)
}
}
/*
* Revert this widget to its default view
*/
fun reset(context: Context) {
logD("Resetting widget")
AppWidgetManager.getInstance(context).updateAppWidget(
ComponentName(context, this::class.java), createDefaultWidget(context)
)
}
// / --- OVERRIDES ---
override fun onUpdate( override fun onUpdate(
context: Context, context: Context,
appWidgetManager: AppWidgetManager, appWidgetManager: AppWidgetManager,
appWidgetIds: IntArray appWidgetIds: IntArray
) { ) {
applyDefaultViews(context, appWidgetManager) reset(context)
requestUpdate(context) requestUpdate(context)
} }
@ -71,44 +114,15 @@ class WidgetProvider : AppWidgetProvider() {
} }
} }
/* // / --- INTERNAL METHODS ---
* Update the widget based on the playback state.
*/
fun update(context: Context, playbackManager: PlaybackStateManager) {
val appWidgetManager = AppWidgetManager.getInstance(context)
val song = playbackManager.song
if (song == null) { private fun requestUpdate(context: Context) {
applyDefaultViews(context, appWidgetManager) logD("Sending update intent to PlaybackService")
return
}
loadBitmap(context, song) { bitmap -> val intent = Intent(ACTION_WIDGET_UPDATE)
val state = WidgetState( .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
song,
bitmap,
playbackManager.isPlaying,
playbackManager.isShuffling,
playbackManager.loopMode
)
// Map each widget form to the rough dimensions where it would look at least okay. context.sendBroadcast(intent)
val views = mapOf(
SizeF(110f, 110f) to createSmallWidget(context, state),
SizeF(250f, 110f) to createFullWidget(context, state)
)
appWidgetManager.applyViewsCompat(context, views)
}
}
/*
* Revert this widget to its default view
*/
fun reset(context: Context) {
logD("Resetting widget")
applyDefaultViews(context, AppWidgetManager.getInstance(context))
} }
private fun AppWidgetManager.applyViewsCompat( private fun AppWidgetManager.applyViewsCompat(
@ -190,27 +204,6 @@ class WidgetProvider : AppWidgetProvider() {
} }
} }
@SuppressLint("RemoteViewLayout")
private fun applyDefaultViews(context: Context, manager: AppWidgetManager) {
val views = RemoteViews(context.packageName, R.layout.widget_default)
views.setOnClickPendingIntent(
android.R.id.background,
context.newMainIntent()
)
manager.updateAppWidget(ComponentName(context, this::class.java), views)
}
private fun requestUpdate(context: Context) {
logD("Sending update intent to PlaybackService")
val intent = Intent(ACTION_WIDGET_UPDATE)
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
context.sendBroadcast(intent)
}
companion object { companion object {
const val ACTION_WIDGET_UPDATE = BuildConfig.APPLICATION_ID + ".action.WIDGET_UPDATE" const val ACTION_WIDGET_UPDATE = BuildConfig.APPLICATION_ID + ".action.WIDGET_UPDATE"
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

View file

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<style name="Widget.Component.AppWidget.Button" parent="Widget.Component.AppWidget.Button.Base"> <style name="Widget.Component.AppWidget.Button" parent="Widget.Component.AppWidget.Button.Base">
<item name="android:background">@drawable/ui_unbounded_ripple</item> <item name="android:background">@drawable/ui_unbounded_ripple</item>
</style> </style>

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<color name="surface_day">#fffafafa</color> <color name="surface_day">#fffafafa</color>
<color name="surface_night">#ff121212</color> <color name="surface_night">#ff151515</color>
<color name="surface_black">#ff000000</color> <color name="surface_black">#ff000000</color>
<color name="surface">@color/surface_day</color> <color name="surface">@color/surface_day</color>