diff --git a/app/src/main/java/org/oxycblt/auxio/recycler/SortMode.kt b/app/src/main/java/org/oxycblt/auxio/recycler/SortMode.kt index 24549b7e4..a3645debc 100644 --- a/app/src/main/java/org/oxycblt/auxio/recycler/SortMode.kt +++ b/app/src/main/java/org/oxycblt/auxio/recycler/SortMode.kt @@ -173,8 +173,8 @@ enum class SortMode(@DrawableRes val iconRes: Int) { val list = mutableListOf() 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 } diff --git a/app/src/main/java/org/oxycblt/auxio/widgets/Forms.kt b/app/src/main/java/org/oxycblt/auxio/widgets/Forms.kt index 6618f5c97..8bf4036fc 100644 --- a/app/src/main/java/org/oxycblt/auxio/widgets/Forms.kt +++ b/app/src/main/java/org/oxycblt/auxio/widgets/Forms.kt @@ -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 diff --git a/app/src/main/java/org/oxycblt/auxio/widgets/WidgetProvider.kt b/app/src/main/java/org/oxycblt/auxio/widgets/WidgetProvider.kt index 7c4ac2de7..da3933872 100644 --- a/app/src/main/java/org/oxycblt/auxio/widgets/WidgetProvider.kt +++ b/app/src/main/java/org/oxycblt/auxio/widgets/WidgetProvider.kt @@ -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" } diff --git a/app/src/main/res/drawable-nodpi/ui_widget_preview.png b/app/src/main/res/drawable-nodpi/ui_widget_preview.png new file mode 100644 index 000000000..c79c6b3d3 Binary files /dev/null and b/app/src/main/res/drawable-nodpi/ui_widget_preview.png differ diff --git a/app/src/main/res/drawable/ui_widget_preview.png b/app/src/main/res/drawable/ui_widget_preview.png deleted file mode 100755 index 800ee2741..000000000 Binary files a/app/src/main/res/drawable/ui_widget_preview.png and /dev/null differ diff --git a/app/src/main/res/values-v31/styles_component.xml b/app/src/main/res/values-v31/styles_component.xml index 87f67a2e5..efb0ce40a 100644 --- a/app/src/main/res/values-v31/styles_component.xml +++ b/app/src/main/res/values-v31/styles_component.xml @@ -1,6 +1,5 @@ - diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 474403059..c2a0f09b1 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -1,7 +1,7 @@ #fffafafa - #ff121212 + #ff151515 #ff000000 @color/surface_day