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:
parent
4cc433f7ef
commit
97459fdcca
7 changed files with 82 additions and 82 deletions
|
@ -173,8 +173,8 @@ enum class SortMode(@DrawableRes val iconRes: Int) {
|
|||
val list = mutableListOf<Song>()
|
||||
|
||||
songs.groupBy { it.album }.entries.sortedWith(compareByDescending { it.key.year }).forEach { entry ->
|
||||
list.addAll(entry.value.sortedWith(compareBy { it.track }))
|
||||
}
|
||||
list.addAll(entry.value.sortedWith(compareBy { it.track }))
|
||||
}
|
||||
|
||||
list
|
||||
}
|
||||
|
|
|
@ -29,8 +29,7 @@ import org.oxycblt.auxio.ui.newMainIntent
|
|||
|
||||
private fun createViews(
|
||||
context: Context,
|
||||
@LayoutRes layout: Int,
|
||||
state: WidgetState
|
||||
@LayoutRes layout: Int
|
||||
): RemoteViews {
|
||||
val views = RemoteViews(context.packageName, layout)
|
||||
|
||||
|
@ -39,31 +38,35 @@ private fun createViews(
|
|||
context.newMainIntent()
|
||||
)
|
||||
|
||||
views.setOnClickPendingIntent(
|
||||
return views
|
||||
}
|
||||
|
||||
private fun RemoteViews.applyState(context: Context, state: WidgetState) {
|
||||
setOnClickPendingIntent(
|
||||
R.id.widget_skip_prev,
|
||||
context.newBroadcastIntent(
|
||||
PlaybackService.ACTION_SKIP_PREV
|
||||
)
|
||||
)
|
||||
|
||||
views.setOnClickPendingIntent(
|
||||
setOnClickPendingIntent(
|
||||
R.id.widget_play_pause,
|
||||
context.newBroadcastIntent(
|
||||
PlaybackService.ACTION_PLAY_PAUSE
|
||||
)
|
||||
)
|
||||
|
||||
views.setOnClickPendingIntent(
|
||||
setOnClickPendingIntent(
|
||||
R.id.widget_skip_next,
|
||||
context.newBroadcastIntent(
|
||||
PlaybackService.ACTION_SKIP_NEXT
|
||||
)
|
||||
)
|
||||
|
||||
views.setTextViewText(R.id.widget_song, state.song.name)
|
||||
views.setTextViewText(R.id.widget_artist, state.song.album.artist.name)
|
||||
setTextViewText(R.id.widget_song, state.song.name)
|
||||
setTextViewText(R.id.widget_artist, state.song.album.artist.name)
|
||||
|
||||
views.setImageViewResource(
|
||||
setImageViewResource(
|
||||
R.id.widget_play_pause,
|
||||
if (state.isPlaying) {
|
||||
R.drawable.ic_pause
|
||||
|
@ -73,24 +76,29 @@ private fun createViews(
|
|||
)
|
||||
|
||||
if (state.albumArt != null) {
|
||||
views.setImageViewBitmap(R.id.widget_cover, state.albumArt)
|
||||
views.setContentDescription(
|
||||
setImageViewBitmap(R.id.widget_cover, state.albumArt)
|
||||
setContentDescription(
|
||||
R.id.widget_cover, context.getString(R.string.desc_album_cover, state.song.album.name)
|
||||
)
|
||||
} else {
|
||||
views.setImageViewResource(R.id.widget_cover, R.drawable.ic_song)
|
||||
views.setContentDescription(R.id.widget_cover, context.getString(R.string.desc_no_cover))
|
||||
setImageViewResource(R.id.widget_cover, R.drawable.ic_song)
|
||||
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 {
|
||||
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 {
|
||||
val views = createViews(context, R.layout.widget_full, state)
|
||||
val views = createViews(context, R.layout.widget_full)
|
||||
views.applyState(context, state)
|
||||
|
||||
views.setOnClickPendingIntent(
|
||||
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
|
||||
// controls. However, since the context we use to load attributes is from the main process,
|
||||
// attempting to dynamically color anything will result in an error. More duplicate
|
||||
// resources it is. This is getting really tiring.
|
||||
// controls. However, since we can't retrieve the context of our views here, we cant
|
||||
// dynamically set the image view attributes. More duplicate resources it is. This is
|
||||
// getting really tiring.
|
||||
|
||||
val shuffleRes = when {
|
||||
state.isShuffled -> R.drawable.ic_shuffle_tinted
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
|
||||
package org.oxycblt.auxio.widgets
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.appwidget.AppWidgetHostView
|
||||
import android.appwidget.AppWidgetManager
|
||||
import android.appwidget.AppWidgetProvider
|
||||
|
@ -30,12 +29,10 @@ import android.os.Bundle
|
|||
import android.util.SizeF
|
||||
import android.widget.RemoteViews
|
||||
import org.oxycblt.auxio.BuildConfig
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.coil.loadBitmap
|
||||
import org.oxycblt.auxio.logD
|
||||
import org.oxycblt.auxio.playback.state.PlaybackStateManager
|
||||
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
|
||||
|
@ -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 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.
|
||||
*/
|
||||
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(
|
||||
context: Context,
|
||||
appWidgetManager: AppWidgetManager,
|
||||
appWidgetIds: IntArray
|
||||
) {
|
||||
applyDefaultViews(context, appWidgetManager)
|
||||
reset(context)
|
||||
requestUpdate(context)
|
||||
}
|
||||
|
||||
|
@ -71,44 +114,15 @@ 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
|
||||
// / --- INTERNAL METHODS ---
|
||||
|
||||
if (song == null) {
|
||||
applyDefaultViews(context, appWidgetManager)
|
||||
return
|
||||
}
|
||||
private fun requestUpdate(context: Context) {
|
||||
logD("Sending update intent to PlaybackService")
|
||||
|
||||
loadBitmap(context, song) { bitmap ->
|
||||
val state = WidgetState(
|
||||
song,
|
||||
bitmap,
|
||||
playbackManager.isPlaying,
|
||||
playbackManager.isShuffling,
|
||||
playbackManager.loopMode
|
||||
)
|
||||
val intent = Intent(ACTION_WIDGET_UPDATE)
|
||||
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
|
||||
|
||||
// Map each widget form to the rough dimensions where it would look at least okay.
|
||||
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))
|
||||
context.sendBroadcast(intent)
|
||||
}
|
||||
|
||||
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 {
|
||||
const val ACTION_WIDGET_UPDATE = BuildConfig.APPLICATION_ID + ".action.WIDGET_UPDATE"
|
||||
}
|
||||
|
|
BIN
app/src/main/res/drawable-nodpi/ui_widget_preview.png
Normal file
BIN
app/src/main/res/drawable-nodpi/ui_widget_preview.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
Binary file not shown.
Before Width: | Height: | Size: 17 KiB |
|
@ -1,6 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<style name="Widget.Component.AppWidget.Button" parent="Widget.Component.AppWidget.Button.Base">
|
||||
<item name="android:background">@drawable/ui_unbounded_ripple</item>
|
||||
</style>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<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">@color/surface_day</color>
|
||||
|
|
Loading…
Reference in a new issue