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 @@