widgets: add compact/mini forms

Add two new widget forms. The compact widget form shows the cover art
[this time with a proper aspect ratio] but no controls. The mini form
only shows the metadata. This seems to work well enough and provides
more choices with how one wants to decorate their homescreen with
Auxio's widgets.
This commit is contained in:
OxygenCobalt 2021-08-15 11:43:22 -06:00
parent fed6902c21
commit c5b1293a90
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
8 changed files with 147 additions and 17 deletions

View file

@ -99,6 +99,8 @@ dependencies {
// Material // Material
implementation "com.google.android.material:material:1.4.0" implementation "com.google.android.material:material:1.4.0"
implementation 'me.zhanghai.android.fastscroll:library:1.1.7'
// --- DEBUG --- // --- DEBUG ---
// Lint // Lint

View file

@ -41,7 +41,12 @@ private fun createViews(
return views return views
} }
private fun RemoteViews.applyState(context: Context, state: WidgetState) { private fun RemoteViews.applyMeta(context: Context, state: WidgetState) {
setTextViewText(R.id.widget_song, state.song.name)
setTextViewText(R.id.widget_artist, state.song.album.artist.name)
}
fun RemoteViews.applyControls(context: Context, state: WidgetState) {
setOnClickPendingIntent( setOnClickPendingIntent(
R.id.widget_skip_prev, R.id.widget_skip_prev,
context.newBroadcastIntent( context.newBroadcastIntent(
@ -63,9 +68,6 @@ private fun RemoteViews.applyState(context: Context, state: WidgetState) {
) )
) )
setTextViewText(R.id.widget_song, state.song.name)
setTextViewText(R.id.widget_artist, state.song.album.artist.name)
setImageViewResource( setImageViewResource(
R.id.widget_play_pause, R.id.widget_play_pause,
if (state.isPlaying) { if (state.isPlaying) {
@ -74,7 +76,9 @@ private fun RemoteViews.applyState(context: Context, state: WidgetState) {
R.drawable.ic_play R.drawable.ic_play
} }
) )
}
fun RemoteViews.applyCover(context: Context, state: WidgetState) {
if (state.albumArt != null) { if (state.albumArt != null) {
setImageViewBitmap(R.id.widget_cover, state.albumArt) setImageViewBitmap(R.id.widget_cover, state.albumArt)
setContentDescription( setContentDescription(
@ -90,15 +94,32 @@ fun createDefaultWidget(context: Context): RemoteViews {
return createViews(context, R.layout.widget_default) return createViews(context, R.layout.widget_default)
} }
fun createMiniWidget(context: Context, state: WidgetState): RemoteViews {
val views = createViews(context, R.layout.widget_mini)
views.applyMeta(context, state)
return views
}
fun createCompactWidget(context: Context, state: WidgetState): RemoteViews {
val views = createViews(context, R.layout.widget_compact)
views.applyMeta(context, state)
views.applyCover(context, state)
return views
}
fun createSmallWidget(context: Context, state: WidgetState): RemoteViews { fun createSmallWidget(context: Context, state: WidgetState): RemoteViews {
val views = createViews(context, R.layout.widget_small) val views = createViews(context, R.layout.widget_small)
views.applyState(context, state) views.applyMeta(context, state)
views.applyCover(context, state)
views.applyControls(context, state)
return views return views
} }
fun createFullWidget(context: Context, state: WidgetState): RemoteViews { fun createFullWidget(context: Context, state: WidgetState): RemoteViews {
val views = createViews(context, R.layout.widget_full) val views = createViews(context, R.layout.widget_full)
views.applyState(context, state) views.applyMeta(context, state)
views.applyCover(context, state)
views.applyControls(context, state)
views.setOnClickPendingIntent( views.setOnClickPendingIntent(
R.id.widget_loop, R.id.widget_loop,

View file

@ -38,10 +38,13 @@ import org.oxycblt.auxio.ui.isLandscape
* Auxio's one and only appwidget. This widget follows a more unorthodox approach, effectively * Auxio's one and only appwidget. This widget follows a more unorthodox approach, effectively
* packing what could be considered 3 or 4 widgets into a single responsive widget. More specifically: * packing what could be considered 3 or 4 widgets into a single responsive widget. More specifically:
* *
* - For widgets 2x1 or lower, show a text-only view with no controls
* - For widgets Wx1 or lower, show a compact view with no controls.
* - For widgets Wx2 or higher, show an expanded view with album art and basic controls * - 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 * - For widgets 4x2 or higher, show a complete view with all playback controls
* *
* Other widget variants might be added if there is sufficient demand. * 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.
* *
* For more specific details about these sub-widgets, see Forms.kt. * For more specific details about these sub-widgets, see Forms.kt.
*/ */
@ -68,9 +71,13 @@ class WidgetProvider : AppWidgetProvider() {
) )
// Map each widget form to the cells where it would look at least okay. // Map each widget form to the cells where it would look at least okay.
// The large widgets are 140 instead of 110 so that they're backwards compatible
// with the old widget size reporting
val views = mapOf( val views = mapOf(
SizeF(180f, 110f) to createSmallWidget(context, state), SizeF(180f, 40f) to createMiniWidget(context, state),
SizeF(250f, 110f) to createFullWidget(context, state) SizeF(250f, 40f) to createCompactWidget(context, state),
SizeF(180f, 140f) to createSmallWidget(context, state),
SizeF(250f, 140f) to createFullWidget(context, state)
) )
appWidgetManager.applyViewsCompat(context, views) appWidgetManager.applyViewsCompat(context, views)
@ -138,8 +145,8 @@ class WidgetProvider : AppWidgetProvider() {
updateAppWidget(name, RemoteViews(views)) updateAppWidget(name, RemoteViews(views))
} else { } else {
// Otherwise, we try our best to backport the responsive behavior to older versions. // Otherwise, we try our best to backport the responsive behavior to older versions.
// This is mostly a guess based on RemoteView's documentation. It may be improved when // This is mostly a guess based on RemoteView's documentation. It seems to work well
// Android 12's source is released. // on most launchers. It may be improved when Android 12's source is released.
// Each widget has independent dimensions, so we iterate through them all // Each widget has independent dimensions, so we iterate through them all
// and do this for each. // and do this for each.
@ -168,8 +175,8 @@ class WidgetProvider : AppWidgetProvider() {
height = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT) height = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)
} }
height += padW height += padH
width += padH width += padW
logD("Assuming true widget dimens are ${width}x$height") logD("Assuming true widget dimens are ${width}x$height")

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid
android:color="#00000000" />
<size
android:width="1000px"
android:height="1000px"/>
</shape>

View file

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@android:id/background"
android:background="?attr/colorSurface"
android:theme="@style/Theme.Widget"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<!--
We can't use ConstraintLayout on widgets due to RemoteView limitations, but
for this widget form to work, we need to get the cover to preserve it's aspect
ratio. So, we use a fixed-size 1000x1000 drawable and then align the cover view
to that so that the bounds will scale properly.
This is easily one of the worst layout hacks I've done, but it seems to work.
-->
<ImageView
android:id="@+id/widget_aspect_ratio"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
android:src="@drawable/ui_widget_aspect_ratio"
android:visibility="invisible"
tools:ignore="ContentDescription" />
<ImageView
android:id="@+id/widget_cover"
android:layout_width="0dp"
android:layout_height="0dp"
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" />
<LinearLayout
android:id="@+id/widget_panel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true"
android:layout_toEndOf="@+id/widget_cover"
android:gravity="center"
android:orientation="vertical"
android:padding="@dimen/spacing_medium">
<TextView
android:id="@+id/widget_song"
style="@style/Widget.Component.AppWidget.TextView.Primary"
android:text="@string/def_widget_song" />
<TextView
android:id="@+id/widget_artist"
style="@style/Widget.Component.AppWidget.TextView.Secondary"
android:text="@string/def_widget_artist" />
</LinearLayout>
</RelativeLayout>

View file

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/background"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true"
android:background="?attr/colorSurface"
android:gravity="center"
android:orientation="vertical"
android:padding="@dimen/spacing_medium"
android:theme="@style/Theme.Widget">
<TextView
android:id="@+id/widget_song"
style="@style/Widget.Component.AppWidget.TextView.Primary"
android:text="@string/def_widget_song" />
<TextView
android:id="@+id/widget_artist"
style="@style/Widget.Component.AppWidget.TextView.Secondary"
android:text="@string/def_widget_artist" />
</LinearLayout>

View file

@ -3,10 +3,10 @@
android:description="@string/info_widget_desc" android:description="@string/info_widget_desc"
android:initialLayout="@layout/widget_small" android:initialLayout="@layout/widget_small"
android:minResizeWidth="180dp" android:minResizeWidth="180dp"
android:minResizeHeight="110dp" android:minResizeHeight="40dp"
android:previewLayout="@layout/widget_small" android:previewLayout="@layout/widget_small"
android:resizeMode="horizontal|vertical" android:resizeMode="horizontal|vertical"
android:targetCellWidth="3" android:minWidth="180dp"
android:targetCellHeight="2" android:minHeight="110dp"
android:updatePeriodMillis="0" android:updatePeriodMillis="0"
android:widgetCategory="home_screen" /> android:widgetCategory="home_screen" />

View file

@ -4,7 +4,7 @@
android:minWidth="180dp" android:minWidth="180dp"
android:minHeight="110dp" android:minHeight="110dp"
android:minResizeWidth="180dp" android:minResizeWidth="180dp"
android:minResizeHeight="110dp" android:minResizeHeight="40dp"
android:previewImage="@drawable/ui_widget_preview" android:previewImage="@drawable/ui_widget_preview"
android:resizeMode="horizontal|vertical" android:resizeMode="horizontal|vertical"
android:updatePeriodMillis="0" android:updatePeriodMillis="0"