From eb293022e8034216be3d32e73c7feed7df6eae70 Mon Sep 17 00:00:00 2001 From: OxygenCobalt Date: Tue, 24 May 2022 10:08:12 -0600 Subject: [PATCH] widgets: rework tiny widget Rework the tiny widget to cram even more information into it. The tiny widget is nominally meant for edge cases like exceptionally small screens or landscape mode, but apparently it's triggered on some devices in normal use because of platform fragmentation and OEM insanity. Update the tiny widget layout with some new buttons in order to make it more usable in mnormal use. This is still nowehre near ideal. For example, when triggering the layout on my device, it ends up squishing the buttons. But it should probably work better outside of those edge cases. --- .../java/org/oxycblt/auxio/MainActivity.kt | 3 +- .../java/org/oxycblt/auxio/MainFragment.kt | 2 - .../org/oxycblt/auxio/image/BitmapProvider.kt | 12 +++- .../org/oxycblt/auxio/image/Components.kt | 17 ++---- .../main/java/org/oxycblt/auxio/ui/Sort.kt | 32 +++-------- .../org/oxycblt/auxio/ui/StyledImageView.kt | 2 - .../java/org/oxycblt/auxio/widgets/Forms.kt | 6 +- .../oxycblt/auxio/widgets/WidgetComponent.kt | 6 +- .../oxycblt/auxio/widgets/WidgetProvider.kt | 7 +++ app/src/main/res/drawable/ic_down.xml | 2 +- .../res/drawable/ic_remote_default_cover.xml | 2 +- app/src/main/res/layout/widget_large.xml | 9 +-- app/src/main/res/layout/widget_medium.xml | 9 +-- app/src/main/res/layout/widget_tiny.xml | 57 ++++++++++++------- app/src/main/res/layout/widget_wide.xml | 9 +-- app/src/main/res/values/strings.xml | 1 + 16 files changed, 85 insertions(+), 91 deletions(-) diff --git a/app/src/main/java/org/oxycblt/auxio/MainActivity.kt b/app/src/main/java/org/oxycblt/auxio/MainActivity.kt index 8ec8cef82..df2ccc292 100644 --- a/app/src/main/java/org/oxycblt/auxio/MainActivity.kt +++ b/app/src/main/java/org/oxycblt/auxio/MainActivity.kt @@ -39,7 +39,8 @@ import org.oxycblt.auxio.util.systemBarInsetsCompat /** * The single [AppCompatActivity] for Auxio. * - * TODO: Add a new view for crashes with a stack trace + * TODO: Add crash reporting and error screens. This likely has to be an external activity, so it is + * blocked by eliminating exitProcess from the app. * * TODO: Custom language support * diff --git a/app/src/main/java/org/oxycblt/auxio/MainFragment.kt b/app/src/main/java/org/oxycblt/auxio/MainFragment.kt index 29e76ce7f..bb1b2da37 100644 --- a/app/src/main/java/org/oxycblt/auxio/MainFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/MainFragment.kt @@ -44,8 +44,6 @@ import org.oxycblt.auxio.util.logD * A wrapper around the home fragment that shows the playback fragment and controls the more * high-level navigation features. * @author OxygenCobalt - * - * TODO: Add a new view with a stack trace whenever the music loading process fails. */ class MainFragment : ViewBindingFragment() { private val playbackModel: PlaybackViewModel by activityViewModels() diff --git a/app/src/main/java/org/oxycblt/auxio/image/BitmapProvider.kt b/app/src/main/java/org/oxycblt/auxio/image/BitmapProvider.kt index 52b794a74..ad8be8224 100644 --- a/app/src/main/java/org/oxycblt/auxio/image/BitmapProvider.kt +++ b/app/src/main/java/org/oxycblt/auxio/image/BitmapProvider.kt @@ -41,6 +41,7 @@ import org.oxycblt.auxio.music.Song class BitmapProvider(private val context: Context) { private var currentRequest: Request? = null + /* If this provider is currently attempting to load something. */ val isBusy: Boolean get() = currentRequest?.run { !disposable.isDisposed } ?: false @@ -53,7 +54,7 @@ class BitmapProvider(private val context: Context) { currentRequest = null val request = - target.setupRequest( + target.onConfigRequest( ImageRequest.Builder(context) .data(song) .size(Size.ORIGINAL) @@ -76,8 +77,15 @@ class BitmapProvider(private val context: Context) { private data class Request(val disposable: Disposable, val callback: Target) + /** Represents the target for a request. */ interface Target { - fun setupRequest(builder: ImageRequest.Builder): ImageRequest.Builder = builder + /** Modify the default request with custom attributes. */ + fun onConfigRequest(builder: ImageRequest.Builder): ImageRequest.Builder = builder + + /** + * Called when the loading process is completed. [bitmap] will be null if there was an + * error. + */ fun onCompleted(bitmap: Bitmap?) } } diff --git a/app/src/main/java/org/oxycblt/auxio/image/Components.kt b/app/src/main/java/org/oxycblt/auxio/image/Components.kt index 8d5e5613a..0184a203e 100644 --- a/app/src/main/java/org/oxycblt/auxio/image/Components.kt +++ b/app/src/main/java/org/oxycblt/auxio/image/Components.kt @@ -71,9 +71,8 @@ class AlbumArtFetcher private constructor(private val context: Context, private } class AlbumFactory : Fetcher.Factory { - override fun create(data: Album, options: Options, imageLoader: ImageLoader): Fetcher { - return AlbumArtFetcher(options.context, data) - } + override fun create(data: Album, options: Options, imageLoader: ImageLoader) = + AlbumArtFetcher(options.context, data) } } @@ -90,14 +89,12 @@ private constructor( override suspend fun fetch(): FetchResult? { val albums = Sort.ByName(true).albums(artist.albums) val results = albums.mapAtMost(4) { album -> fetchArt(context, album) } - return createMosaic(context, results, size) } class Factory : Fetcher.Factory { - override fun create(data: Artist, options: Options, imageLoader: ImageLoader): Fetcher { - return ArtistImageFetcher(options.context, options.size, data) - } + override fun create(data: Artist, options: Options, imageLoader: ImageLoader) = + ArtistImageFetcher(options.context, options.size, data) } } @@ -115,14 +112,12 @@ private constructor( // Don't sort here to preserve compatibility with previous versions of this image. val albums = genre.songs.groupBy { it.album }.keys val results = albums.mapAtMost(4) { album -> fetchArt(context, album) } - return createMosaic(context, results, size) } class Factory : Fetcher.Factory { - override fun create(data: Genre, options: Options, imageLoader: ImageLoader): Fetcher { - return GenreImageFetcher(options.context, options.size, data) - } + override fun create(data: Genre, options: Options, imageLoader: ImageLoader) = + GenreImageFetcher(options.context, options.size, data) } } diff --git a/app/src/main/java/org/oxycblt/auxio/ui/Sort.kt b/app/src/main/java/org/oxycblt/auxio/ui/Sort.kt index cbcbd1f4b..6fee2dcd1 100644 --- a/app/src/main/java/org/oxycblt/auxio/ui/Sort.kt +++ b/app/src/main/java/org/oxycblt/auxio/ui/Sort.kt @@ -119,9 +119,7 @@ sealed class Sort(open val isAscending: Boolean) { genres.sortWith(compareByDynamic(NameComparator()) { it }) } - override fun ascending(newIsAscending: Boolean): Sort { - return ByName(newIsAscending) - } + override fun ascending(newIsAscending: Boolean) = ByName(newIsAscending) } /** Sort by the album of an item, only supported by [Song] */ @@ -140,9 +138,7 @@ sealed class Sort(open val isAscending: Boolean) { compareBy(NameComparator()) { it })) } - override fun ascending(newIsAscending: Boolean): Sort { - return ByAlbum(newIsAscending) - } + override fun ascending(newIsAscending: Boolean) = ByAlbum(newIsAscending) } /** Sort by the artist of an item, only supported by [Album] and [Song] */ @@ -171,9 +167,7 @@ sealed class Sort(open val isAscending: Boolean) { compareBy(NameComparator()) { it })) } - override fun ascending(newIsAscending: Boolean): Sort { - return ByArtist(newIsAscending) - } + override fun ascending(newIsAscending: Boolean) = ByArtist(newIsAscending) } /** Sort by the year of an item, only supported by [Album] and [Song] */ @@ -200,9 +194,7 @@ sealed class Sort(open val isAscending: Boolean) { compareBy(NameComparator()) { it })) } - override fun ascending(newIsAscending: Boolean): Sort { - return ByYear(newIsAscending) - } + override fun ascending(newIsAscending: Boolean) = ByYear(newIsAscending) } /** Sort by the duration of the item. Supports all items. */ @@ -237,9 +229,7 @@ sealed class Sort(open val isAscending: Boolean) { compareByDynamic { it.durationSecs }, compareBy(NameComparator()) { it })) } - override fun ascending(newIsAscending: Boolean): Sort { - return ByDuration(newIsAscending) - } + override fun ascending(newIsAscending: Boolean) = ByDuration(newIsAscending) } /** Sort by the amount of songs. Only applicable to music parents. */ @@ -268,9 +258,7 @@ sealed class Sort(open val isAscending: Boolean) { compareByDynamic { it.songs.size }, compareBy(NameComparator()) { it })) } - override fun ascending(newIsAscending: Boolean): Sort { - return ByCount(newIsAscending) - } + override fun ascending(newIsAscending: Boolean) = ByCount(newIsAscending) } /** @@ -293,9 +281,7 @@ sealed class Sort(open val isAscending: Boolean) { compareBy(NameComparator()) { it })) } - override fun ascending(newIsAscending: Boolean): Sort { - return ByDisc(newIsAscending) - } + override fun ascending(newIsAscending: Boolean) = ByDisc(newIsAscending) } /** @@ -317,9 +303,7 @@ sealed class Sort(open val isAscending: Boolean) { compareBy(NameComparator()) { it })) } - override fun ascending(newIsAscending: Boolean): Sort { - return ByTrack(newIsAscending) - } + override fun ascending(newIsAscending: Boolean) = ByTrack(newIsAscending) } val intCode: Int diff --git a/app/src/main/java/org/oxycblt/auxio/ui/StyledImageView.kt b/app/src/main/java/org/oxycblt/auxio/ui/StyledImageView.kt index d9f7234f0..2349271bd 100644 --- a/app/src/main/java/org/oxycblt/auxio/ui/StyledImageView.kt +++ b/app/src/main/java/org/oxycblt/auxio/ui/StyledImageView.kt @@ -41,7 +41,6 @@ import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.settings.SettingsManager import org.oxycblt.auxio.util.getColorStateListSafe import org.oxycblt.auxio.util.getDrawableSafe -import org.oxycblt.auxio.util.logD /** * An [AppCompatImageView] that applies many of the stylistic choices that Auxio uses regarding @@ -130,7 +129,6 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr } override fun draw(canvas: Canvas) { - logD(src.alpha) src.bounds.set(canvas.clipBounds) val adjustWidth = src.bounds.width() / 4 val adjustHeight = src.bounds.height() / 4 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 bf18449fc..9f11ad3ca 100644 --- a/app/src/main/java/org/oxycblt/auxio/widgets/Forms.kt +++ b/app/src/main/java/org/oxycblt/auxio/widgets/Forms.kt @@ -35,13 +35,13 @@ fun createDefaultWidget(context: Context): RemoteViews { } /** - * The tiny widget is for an edge-case situation where a 2xN widget happens to be smaller than - * 100dp. It just shows the cover, titles, and a button. + * The tiny widget is for an edge-case situation where a widget falls under the size class of the + * small widget, either via landscape mode or exceptionally small screens. */ fun createTinyWidget(context: Context, state: WidgetComponent.WidgetState): RemoteViews { return createViews(context, R.layout.widget_tiny) .applyMeta(context, state) - .applyPlayControls(context, state) + .applyBasicControls(context, state) } /** diff --git a/app/src/main/java/org/oxycblt/auxio/widgets/WidgetComponent.kt b/app/src/main/java/org/oxycblt/auxio/widgets/WidgetComponent.kt index 8641c581d..8119943b9 100644 --- a/app/src/main/java/org/oxycblt/auxio/widgets/WidgetComponent.kt +++ b/app/src/main/java/org/oxycblt/auxio/widgets/WidgetComponent.kt @@ -50,6 +50,10 @@ class WidgetComponent(private val context: Context) : init { playbackManager.addCallback(this) settingsManager.addCallback(this) + + if (playbackManager.isInitialized) { + update() + } } /* @@ -76,7 +80,7 @@ class WidgetComponent(private val context: Context) : provider.load( song, object : BitmapProvider.Target { - override fun setupRequest(builder: ImageRequest.Builder): ImageRequest.Builder { + override fun onConfigRequest(builder: ImageRequest.Builder): ImageRequest.Builder { // The widget has two distinct styles that we must transform the album art to // accommodate: // - Before Android 12, the widget has hard edges, so we don't need to round 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 16978b8dc..8658d5a42 100644 --- a/app/src/main/java/org/oxycblt/auxio/widgets/WidgetProvider.kt +++ b/app/src/main/java/org/oxycblt/auxio/widgets/WidgetProvider.kt @@ -122,6 +122,13 @@ class WidgetProvider : AppWidgetProvider() { val name = ComponentName(context, WidgetProvider::class.java) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + for (id in getAppWidgetIds(name)) { + val options = getAppWidgetOptions(id) + logD( + options.getParcelableArrayList(AppWidgetManager.OPTION_APPWIDGET_SIZES) + ?: "no sizes") + } + // Widgets are automatically responsive on Android 12, no need to do anything. updateAppWidget(name, RemoteViews(views)) } else { diff --git a/app/src/main/res/drawable/ic_down.xml b/app/src/main/res/drawable/ic_down.xml index fed95d407..8b9b17aeb 100644 --- a/app/src/main/res/drawable/ic_down.xml +++ b/app/src/main/res/drawable/ic_down.xml @@ -7,5 +7,5 @@ android:viewportHeight="24"> + android:pathData="M 18.119999,7.0600006 12,13.166666 5.8800004,7.0600006 4.0000006,8.9400004 12,16.939999 19.999999,8.9400004 Z" /> diff --git a/app/src/main/res/drawable/ic_remote_default_cover.xml b/app/src/main/res/drawable/ic_remote_default_cover.xml index a97938c73..02ad5acc2 100644 --- a/app/src/main/res/drawable/ic_remote_default_cover.xml +++ b/app/src/main/res/drawable/ic_remote_default_cover.xml @@ -13,7 +13,7 @@ a terrible API. --> - - + + + + + + + + + - - - diff --git a/app/src/main/res/layout/widget_wide.xml b/app/src/main/res/layout/widget_wide.xml index 4a2242ca3..8f64ab103 100644 --- a/app/src/main/res/layout/widget_wide.xml +++ b/app/src/main/res/layout/widget_wide.xml @@ -8,14 +8,7 @@ android:theme="@style/Theme.Widget"> Orange Brown Grey + Dynamic Disc %d