From 36228d053636601453fd59fe6517aee6f5ceb0db Mon Sep 17 00:00:00 2001 From: OxygenCobalt Date: Thu, 21 Oct 2021 19:17:55 -0600 Subject: [PATCH] widgets: improve widgets Enhance widgets in a couple of ways: 1. Make the cover art expand to fit it's aspect ratio with padding instead of the entire widget. This does the cover art justice and is more visually appealing in general. 2. Add two new widget forms: Terminal, which applies to only the smallest widgets, and Minimal, which applies to short and wide widgets --- .../java/org/oxycblt/auxio/coil/CoilUtils.kt | 11 +- .../org/oxycblt/auxio/home/HomeFragment.kt | 3 - .../auxio/playback/queue/QueueDragCallback.kt | 2 - .../java/org/oxycblt/auxio/util/ViewUtil.kt | 1 + .../java/org/oxycblt/auxio/widgets/Forms.kt | 16 ++- .../oxycblt/auxio/widgets/WidgetProvider.kt | 53 +++++++-- .../res/drawable/ui_widget_aspect_ratio.xml | 11 ++ app/src/main/res/layout/widget_full.xml | 60 +++++++--- app/src/main/res/layout/widget_minimal.xml | 104 ++++++++++++++++++ app/src/main/res/layout/widget_small.xml | 67 ++++++++--- app/src/main/res/layout/widget_terminal.xml | 63 +++++++++++ app/src/main/res/xml-v31/widget_info.xml | 2 +- app/src/main/res/xml/widget_info.xml | 2 +- 13 files changed, 346 insertions(+), 49 deletions(-) create mode 100644 app/src/main/res/drawable/ui_widget_aspect_ratio.xml create mode 100644 app/src/main/res/layout/widget_minimal.xml create mode 100644 app/src/main/res/layout/widget_terminal.xml diff --git a/app/src/main/java/org/oxycblt/auxio/coil/CoilUtils.kt b/app/src/main/java/org/oxycblt/auxio/coil/CoilUtils.kt index cda029250..127f477ba 100644 --- a/app/src/main/java/org/oxycblt/auxio/coil/CoilUtils.kt +++ b/app/src/main/java/org/oxycblt/auxio/coil/CoilUtils.kt @@ -27,6 +27,8 @@ import androidx.databinding.BindingAdapter import coil.Coil import coil.fetch.Fetcher import coil.request.ImageRequest +import coil.size.OriginalSize +import coil.transform.RoundedCornersTransformation import org.oxycblt.auxio.R import org.oxycblt.auxio.music.Album import org.oxycblt.auxio.music.Artist @@ -110,7 +112,12 @@ inline fun ImageView.load( * failed/shouldn't occur. * **This not meant for UIs, instead use the Binding Adapters.** */ -fun loadBitmap(context: Context, song: Song, onDone: (Bitmap?) -> Unit) { +fun loadBitmap( + context: Context, + song: Song, + cornerRadius: Float = 0f, + onDone: (Bitmap?) -> Unit +) { val settingsManager = SettingsManager.getInstance() if (!settingsManager.showCovers) { @@ -122,6 +129,8 @@ fun loadBitmap(context: Context, song: Song, onDone: (Bitmap?) -> Unit) { ImageRequest.Builder(context) .data(song.album) .fetcher(AlbumArtFetcher(context)) + .size(OriginalSize) + .transformations(RoundedCornersTransformation(cornerRadius)) .target( onError = { onDone(null) }, onSuccess = { onDone(it.toBitmap()) } diff --git a/app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt b/app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt index 294cc0fef..ca91f635c 100644 --- a/app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt @@ -56,9 +56,6 @@ import org.oxycblt.auxio.util.logE /** * The main "Launching Point" fragment of Auxio, allowing navigation to the detail * views for each respective fragment. - * FIXME: Edge-to-edge is borked still, unsure how to really fix this aside from making some - * magic layout like Material Files, but even then it might not work since the scrolling - * views are not laid side-by-side to the layout itself. * @author OxygenCobalt */ class HomeFragment : Fragment() { diff --git a/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueDragCallback.kt b/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueDragCallback.kt index ceee98a1a..0c4560cec 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueDragCallback.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueDragCallback.kt @@ -90,8 +90,6 @@ class QueueDragCallback(private val playbackModel: PlaybackViewModel) : ItemTouc // themselves when being dragged. Too bad google's implementation of this doesn't even // work! To emulate it on my own, I check if this child is in a drag state and then animate // an elevation change. - // TODO: Some other enhancements I could make maybe - // - Maybe stopping dragged items from extending beyond their specific part of the queue? val holder = viewHolder as QueueAdapter.QueueSongViewHolder diff --git a/app/src/main/java/org/oxycblt/auxio/util/ViewUtil.kt b/app/src/main/java/org/oxycblt/auxio/util/ViewUtil.kt index 159d591a0..298db1efd 100644 --- a/app/src/main/java/org/oxycblt/auxio/util/ViewUtil.kt +++ b/app/src/main/java/org/oxycblt/auxio/util/ViewUtil.kt @@ -194,6 +194,7 @@ fun View.applyEdge(onApply: (Rect) -> Unit) { * and removing the padding if it isnt available, which works okayish. I think Material Files has * a better implementation of the same fix however, so once I'm able to hack that layout into * Auxio things should be better. + * TODO: Get rid of this get rid of this get rid of this */ fun RecyclerView.applyEdgeRespectingBar( playbackModel: PlaybackViewModel, 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 b2be578d4..4edd64498 100644 --- a/app/src/main/java/org/oxycblt/auxio/widgets/Forms.kt +++ b/app/src/main/java/org/oxycblt/auxio/widgets/Forms.kt @@ -56,7 +56,7 @@ private fun RemoteViews.applyMeta(context: Context, state: WidgetState) { } } -fun RemoteViews.applyControls(context: Context, state: WidgetState) { +private fun RemoteViews.applyControls(context: Context, state: WidgetState) { setOnClickPendingIntent( R.id.widget_skip_prev, context.newBroadcastIntent( @@ -92,6 +92,20 @@ fun createDefaultWidget(context: Context): RemoteViews { return createViews(context, R.layout.widget_default) } +fun createTerminalWidget(context: Context, state: WidgetState): RemoteViews { + val views = createViews(context, R.layout.widget_terminal) + views.applyMeta(context, state) + views.applyControls(context, state) + return views +} + +fun createMinimalWidget(context: Context, state: WidgetState): RemoteViews { + val views = createViews(context, R.layout.widget_minimal) + views.applyMeta(context, state) + views.applyControls(context, state) + return views +} + fun createSmallWidget(context: Context, state: WidgetState): RemoteViews { val views = createViews(context, R.layout.widget_small) views.applyMeta(context, state) 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 afe49cd5e..e5b8392a0 100644 --- a/app/src/main/java/org/oxycblt/auxio/widgets/WidgetProvider.kt +++ b/app/src/main/java/org/oxycblt/auxio/widgets/WidgetProvider.kt @@ -24,23 +24,32 @@ import android.appwidget.AppWidgetProvider import android.content.ComponentName import android.content.Context import android.content.Intent +import android.graphics.Bitmap import android.os.Build import android.os.Bundle import android.util.SizeF import android.widget.RemoteViews +import androidx.core.graphics.drawable.toBitmap +import coil.Coil +import coil.request.ImageRequest +import coil.size.OriginalSize +import coil.transform.RoundedCornersTransformation import org.oxycblt.auxio.BuildConfig -import org.oxycblt.auxio.coil.loadBitmap +import org.oxycblt.auxio.coil.AlbumArtFetcher +import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.playback.state.PlaybackStateManager import org.oxycblt.auxio.util.isLandscape import org.oxycblt.auxio.util.logD /** * Auxio's one and only appwidget. This widget follows a more unorthodox approach, effectively - * packing what could be considered multiple widgets into a single responsive widget. More - * specifically: + * packing what could be considered multiple widgets into a single responsive widget. All types + * are listed below: * - * - 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 + * - Full: Large widgets will show cover art and all controls + * - Small: Tall and thin widgets will show cover art and three controls + * - Minimal: Wide and short widgets will show cover art and all controls in a compact manner + * - Text: Small widgets will only show text and three controls * * There are some minor problems with this implementation [notably UI jittering when the widget * picks a new layout below Android 12], but this is tolerable. It may be improved in the future. @@ -60,7 +69,7 @@ class WidgetProvider : AppWidgetProvider() { return } - loadBitmap(context, song) { bitmap -> + loadWidgetBitmap(context, song) { bitmap -> val state = WidgetState( song, bitmap, @@ -71,14 +80,39 @@ class WidgetProvider : AppWidgetProvider() { // 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) + SizeF(180f, 110f) to createTerminalWidget(context, state), + SizeF(250f, 110f) to createMinimalWidget(context, state), + SizeF(180f, 270f) to createSmallWidget(context, state), + SizeF(250f, 270f) to createFullWidget(context, state) ) appWidgetManager.applyViewsCompat(context, views) } } + private fun loadWidgetBitmap(context: Context, song: Song, onDone: (Bitmap?) -> Unit) { + val cornerRadius = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + context.resources.getDimensionPixelSize( + android.R.dimen.system_app_widget_inner_radius + ).toFloat() + } else { + 0f + } + + Coil.imageLoader(context).enqueue( + ImageRequest.Builder(context) + .data(song.album) + .fetcher(AlbumArtFetcher(context)) + .size(OriginalSize) + .transformations(RoundedCornersTransformation(cornerRadius)) + .target( + onError = { onDone(null) }, + onSuccess = { onDone(it.toBitmap()) } + ) + .build() + ) + } + /* * Revert this widget to its default view */ @@ -110,9 +144,12 @@ class WidgetProvider : AppWidgetProvider() { super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions) if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { + // We can't resize the widget until we can generate the views, so request an update // from PlaybackService. requestUpdate(context) + } else { + logD(newOptions?.getParcelableArrayList(AppWidgetManager.OPTION_APPWIDGET_SIZES) ?: "nothing") } } diff --git a/app/src/main/res/drawable/ui_widget_aspect_ratio.xml b/app/src/main/res/drawable/ui_widget_aspect_ratio.xml new file mode 100644 index 000000000..acd86eeec --- /dev/null +++ b/app/src/main/res/drawable/ui_widget_aspect_ratio.xml @@ -0,0 +1,11 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/widget_full.xml b/app/src/main/res/layout/widget_full.xml index b20c48468..fc85508a1 100644 --- a/app/src/main/res/layout/widget_full.xml +++ b/app/src/main/res/layout/widget_full.xml @@ -1,22 +1,53 @@ - + android:theme="@style/Theme.Widget" + android:layout_width="match_parent" + android:layout_height="match_parent"> - + + + + + android:layout_alignStart="@id/widget_aspect_ratio" + android:layout_alignTop="@id/widget_aspect_ratio" + android:layout_alignEnd="@id/widget_aspect_ratio" + android:layout_alignBottom="@id/widget_aspect_ratio" + android:src="@drawable/ic_song" + tools:ignore="ContentDescription" /> - + + android:layout_marginTop="@dimen/spacing_medium"> - + diff --git a/app/src/main/res/layout/widget_minimal.xml b/app/src/main/res/layout/widget_minimal.xml new file mode 100644 index 000000000..5c07104fe --- /dev/null +++ b/app/src/main/res/layout/widget_minimal.xml @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/widget_small.xml b/app/src/main/res/layout/widget_small.xml index 1a33476eb..5bafa713c 100644 --- a/app/src/main/res/layout/widget_small.xml +++ b/app/src/main/res/layout/widget_small.xml @@ -1,22 +1,53 @@ - + android:theme="@style/Theme.Widget" + android:layout_width="match_parent" + android:layout_height="match_parent"> - + + + + + android:layout_alignStart="@id/widget_aspect_ratio" + android:layout_alignTop="@id/widget_aspect_ratio" + android:layout_alignEnd="@id/widget_aspect_ratio" + android:layout_alignBottom="@id/widget_aspect_ratio" + android:src="@drawable/ic_song" + tools:ignore="ContentDescription" /> - + + android:layout_marginTop="@dimen/spacing_medium" + android:orientation="horizontal"> + + - diff --git a/app/src/main/res/layout/widget_terminal.xml b/app/src/main/res/layout/widget_terminal.xml new file mode 100644 index 000000000..c70814fce --- /dev/null +++ b/app/src/main/res/layout/widget_terminal.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/xml-v31/widget_info.xml b/app/src/main/res/xml-v31/widget_info.xml index f4a4379d8..ce568e78a 100644 --- a/app/src/main/res/xml-v31/widget_info.xml +++ b/app/src/main/res/xml-v31/widget_info.xml @@ -7,6 +7,6 @@ android:previewLayout="@layout/widget_small" android:resizeMode="horizontal|vertical" android:minWidth="180dp" - android:minHeight="110dp" + android:minHeight="180dp" android:updatePeriodMillis="0" android:widgetCategory="home_screen" /> \ No newline at end of file diff --git a/app/src/main/res/xml/widget_info.xml b/app/src/main/res/xml/widget_info.xml index f74fde98b..5cea913df 100644 --- a/app/src/main/res/xml/widget_info.xml +++ b/app/src/main/res/xml/widget_info.xml @@ -2,7 +2,7 @@