diff --git a/CHANGELOG.md b/CHANGELOG.md
index adecf82f2..4688896cd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,7 @@
- Gapless playback is now used whenever possible
- Added "Remember pause" setting that makes remain paused when skipping
or editing queue
+- Added 1x4 and 1x3 widget forms
#### What's Improved
- The playback state is now saved more often, improving persistence
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 d67892300..9280d2ea2 100644
--- a/app/src/main/java/org/oxycblt/auxio/widgets/WidgetComponent.kt
+++ b/app/src/main/java/org/oxycblt/auxio/widgets/WidgetComponent.kt
@@ -167,6 +167,7 @@ constructor(
*
* @param song [Queue.currentSong]
* @param cover A pre-loaded album cover [Bitmap] for [song].
+ * @param cover A pre-loaded album cover [Bitmap] for [song], with rounded corners.
* @param isPlaying [PlaybackStateManager.playerState]
* @param repeatMode [PlaybackStateManager.repeatMode]
* @param isShuffled [Queue.isShuffled]
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 153b7bccf..7a3bc6c40 100644
--- a/app/src/main/java/org/oxycblt/auxio/widgets/WidgetProvider.kt
+++ b/app/src/main/java/org/oxycblt/auxio/widgets/WidgetProvider.kt
@@ -91,11 +91,14 @@ class WidgetProvider : AppWidgetProvider() {
// the widget elements, plus some leeway for text sizing.
val views =
mapOf(
- SizeF(180f, 100f) to newThinLayout(context, uiSettings, state),
- SizeF(180f, 152f) to newSmallLayout(context, uiSettings, state),
- SizeF(272f, 152f) to newWideLayout(context, uiSettings, state),
- SizeF(180f, 272f) to newMediumLayout(context, uiSettings, state),
- SizeF(272f, 272f) to newLargeLayout(context, uiSettings, state))
+ SizeF(180f, 48f) to newThinStickLayout(context, state),
+ SizeF(304f, 48f) to newWideStickLayout(context, state),
+ SizeF(180f, 100f) to newThinWaferLayout(context, uiSettings, state),
+ SizeF(304f, 100f) to newWideWaferLayout(context, uiSettings, state),
+ SizeF(180f, 152f) to newThinDockedLayout(context, uiSettings, state),
+ SizeF(304f, 152f) to newWideDockedLayout(context, uiSettings, state),
+ SizeF(180f, 272f) to newThinPaneLayout(context, uiSettings, state),
+ SizeF(304f, 272f) to newWidePaneLayout(context, uiSettings, state))
// Manually update AppWidgetManager with the new views.
val awm = AppWidgetManager.getInstance(context)
@@ -139,60 +142,78 @@ class WidgetProvider : AppWidgetProvider() {
private fun newDefaultLayout(context: Context) =
newRemoteViews(context, R.layout.widget_default)
- private fun newThinLayout(
+ private fun newThinStickLayout(context: Context, state: WidgetComponent.PlaybackState) =
+ newRemoteViews(context, R.layout.widget_stick_thin).setupTimelineControls(context, state)
+
+ private fun newWideStickLayout(context: Context, state: WidgetComponent.PlaybackState) =
+ newRemoteViews(context, R.layout.widget_stick_wide).setupFullControls(context, state)
+
+ private fun newThinWaferLayout(
context: Context,
uiSettings: UISettings,
state: WidgetComponent.PlaybackState
) =
- newRemoteViews(context, R.layout.widget_thin)
+ newRemoteViews(context, R.layout.widget_wafer_thin)
.setupBackground(
uiSettings,
)
- .setupPlaybackState(context, state)
+ .setupCover(context, state.takeIf { canDisplayWaferCover(uiSettings) })
.setupTimelineControls(context, state)
- private fun newSmallLayout(
+ private fun newWideWaferLayout(
context: Context,
uiSettings: UISettings,
state: WidgetComponent.PlaybackState
) =
- newRemoteViews(context, R.layout.widget_small)
+ newRemoteViews(context, R.layout.widget_wafer_wide)
+ .setupBackground(
+ uiSettings,
+ )
+ .setupCover(context, state.takeIf { canDisplayWaferCover(uiSettings) })
+ .setupFullControls(context, state)
+
+ private fun newThinDockedLayout(
+ context: Context,
+ uiSettings: UISettings,
+ state: WidgetComponent.PlaybackState
+ ) =
+ newRemoteViews(context, R.layout.widget_docked_thin)
.setupBar(
uiSettings,
)
.setupCover(context, state)
.setupTimelineControls(context, state)
- private fun newMediumLayout(
+ private fun newWideDockedLayout(
context: Context,
uiSettings: UISettings,
state: WidgetComponent.PlaybackState
) =
- newRemoteViews(context, R.layout.widget_medium)
- .setupBackground(
- uiSettings,
- )
- .setupPlaybackState(context, state)
- .setupTimelineControls(context, state)
-
- private fun newWideLayout(
- context: Context,
- uiSettings: UISettings,
- state: WidgetComponent.PlaybackState
- ) =
- newRemoteViews(context, R.layout.widget_wide)
+ newRemoteViews(context, R.layout.widget_docked_wide)
.setupBar(
uiSettings,
)
.setupCover(context, state)
.setupFullControls(context, state)
- private fun newLargeLayout(
+ private fun newThinPaneLayout(
context: Context,
uiSettings: UISettings,
state: WidgetComponent.PlaybackState
) =
- newRemoteViews(context, R.layout.widget_large)
+ newRemoteViews(context, R.layout.widget_pane_thin)
+ .setupBackground(
+ uiSettings,
+ )
+ .setupPlaybackState(context, state)
+ .setupTimelineControls(context, state)
+
+ private fun newWidePaneLayout(
+ context: Context,
+ uiSettings: UISettings,
+ state: WidgetComponent.PlaybackState
+ ) =
+ newRemoteViews(context, R.layout.widget_pane_wide)
.setupBackground(
uiSettings,
)
@@ -246,8 +267,14 @@ class WidgetProvider : AppWidgetProvider() {
*/
private fun RemoteViews.setupCover(
context: Context,
- state: WidgetComponent.PlaybackState
+ state: WidgetComponent.PlaybackState?
): RemoteViews {
+ if (state == null) {
+ setImageViewBitmap(R.id.widget_cover, null)
+ setContentDescription(R.id.widget_cover, null)
+ return this
+ }
+
if (state.cover != null) {
setImageViewBitmap(R.id.widget_cover, state.cover)
setContentDescription(
@@ -388,6 +415,18 @@ class WidgetProvider : AppWidgetProvider() {
return this
}
+ private fun useRoundedRemoteViews(uiSettings: UISettings) =
+ uiSettings.roundMode || Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
+
+ private fun canDisplayWaferCover(uiSettings: UISettings) =
+ // We cannot display album covers in the wafer-style widget when round mode is enabled
+ // below Android 12, as:
+ // - We cannot rely on system widget corner clipping, like on Android 12+
+ // - We cannot manually clip the widget ourselves due to broken clipToOutline support
+ // - We cannot determine the exact widget height that would allow us to clip the loaded
+ // image itself
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.S || !uiSettings.roundMode
+
companion object {
/**
* Broadcast when [WidgetProvider] desires to update it's widget with new information.
diff --git a/app/src/main/java/org/oxycblt/auxio/widgets/WidgetUtil.kt b/app/src/main/java/org/oxycblt/auxio/widgets/WidgetUtil.kt
index cd7151b13..799aa8a67 100644
--- a/app/src/main/java/org/oxycblt/auxio/widgets/WidgetUtil.kt
+++ b/app/src/main/java/org/oxycblt/auxio/widgets/WidgetUtil.kt
@@ -28,7 +28,6 @@ import androidx.annotation.DrawableRes
import androidx.annotation.IdRes
import androidx.annotation.LayoutRes
import kotlin.math.sqrt
-import org.oxycblt.auxio.ui.UISettings
import org.oxycblt.auxio.util.isLandscape
import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.newMainPendingIntent
@@ -139,13 +138,3 @@ fun AppWidgetManager.updateAppWidgetCompat(
}
}
}
-
-/**
- * Returns whether rounded UI elements are appropriate for the widget, either based on the current
- * settings or if the widget has to fit in aesthetically with other widgets.
- *
- * @param [uiSettings] [UISettings] required to obtain round mode configuration.
- * @return true if to use round mode, false otherwise.
- */
-fun useRoundedRemoteViews(uiSettings: UISettings) =
- uiSettings.roundMode || Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
diff --git a/app/src/main/res/drawable-v31/ui_widget_rectangle_button_bg.xml b/app/src/main/res/drawable-v31/ui_widget_rectangle_button_bg.xml
new file mode 100644
index 000000000..feba7d550
--- /dev/null
+++ b/app/src/main/res/drawable-v31/ui_widget_rectangle_button_bg.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_remote_default_cover_24.xml b/app/src/main/res/drawable/ic_remote_default_cover_24.xml
index fa860ad09..d849d7106 100644
--- a/app/src/main/res/drawable/ic_remote_default_cover_24.xml
+++ b/app/src/main/res/drawable/ic_remote_default_cover_24.xml
@@ -6,8 +6,15 @@
android:viewportHeight="24">
-
+ android:pathData="M 2.6000008,9.3143836e-7 H 21.399999 c 1.4404,0 2.6,1.15959996856164 2.6,2.59999986856164 V 21.399999 c 0,1.4404 -1.1596,2.6 -2.6,2.6 H 2.6000008 c -1.4403999,0 -2.59999986856164,-1.1596 -2.59999986856164,-2.6 V 2.6000008 C 9.3143836e-7,1.1596009 1.1596009,9.3143836e-7 2.6000008,9.3143836e-7 Z" />
+
+
+
+
diff --git a/app/src/main/res/drawable/ui_widget_circle_button_bg.xml b/app/src/main/res/drawable/ui_widget_circle_button_bg.xml
new file mode 100644
index 000000000..75c293f4b
--- /dev/null
+++ b/app/src/main/res/drawable/ui_widget_circle_button_bg.xml
@@ -0,0 +1,5 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ui_widget_rectangle_button_bg.xml b/app/src/main/res/drawable/ui_widget_rectangle_button_bg.xml
new file mode 100644
index 000000000..b03eb6f1c
--- /dev/null
+++ b/app/src/main/res/drawable/ui_widget_rectangle_button_bg.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/widget_small.xml b/app/src/main/res/layout/widget_docked_thin.xml
similarity index 100%
rename from app/src/main/res/layout/widget_small.xml
rename to app/src/main/res/layout/widget_docked_thin.xml
diff --git a/app/src/main/res/layout/widget_wide.xml b/app/src/main/res/layout/widget_docked_wide.xml
similarity index 100%
rename from app/src/main/res/layout/widget_wide.xml
rename to app/src/main/res/layout/widget_docked_wide.xml
diff --git a/app/src/main/res/layout/widget_medium.xml b/app/src/main/res/layout/widget_pane_thin.xml
similarity index 100%
rename from app/src/main/res/layout/widget_medium.xml
rename to app/src/main/res/layout/widget_pane_thin.xml
diff --git a/app/src/main/res/layout/widget_large.xml b/app/src/main/res/layout/widget_pane_wide.xml
similarity index 100%
rename from app/src/main/res/layout/widget_large.xml
rename to app/src/main/res/layout/widget_pane_wide.xml
diff --git a/app/src/main/res/layout/widget_stick_thin.xml b/app/src/main/res/layout/widget_stick_thin.xml
new file mode 100644
index 000000000..776d26525
--- /dev/null
+++ b/app/src/main/res/layout/widget_stick_thin.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/widget_stick_wide.xml b/app/src/main/res/layout/widget_stick_wide.xml
new file mode 100644
index 000000000..ff5a97d6b
--- /dev/null
+++ b/app/src/main/res/layout/widget_stick_wide.xml
@@ -0,0 +1,84 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/widget_thin.xml b/app/src/main/res/layout/widget_thin.xml
deleted file mode 100644
index a3201fc54..000000000
--- a/app/src/main/res/layout/widget_thin.xml
+++ /dev/null
@@ -1,106 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/widget_wafer_thin.xml b/app/src/main/res/layout/widget_wafer_thin.xml
new file mode 100644
index 000000000..fe7ec01dc
--- /dev/null
+++ b/app/src/main/res/layout/widget_wafer_thin.xml
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/widget_wafer_wide.xml b/app/src/main/res/layout/widget_wafer_wide.xml
new file mode 100644
index 000000000..f32d97b4b
--- /dev/null
+++ b/app/src/main/res/layout/widget_wafer_wide.xml
@@ -0,0 +1,128 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/xml-v31/widget_info.xml b/app/src/main/res/xml-v31/widget_info.xml
index 11fcafb44..1d12aaff8 100644
--- a/app/src/main/res/xml-v31/widget_info.xml
+++ b/app/src/main/res/xml-v31/widget_info.xml
@@ -4,10 +4,10 @@
android:initialLayout="@layout/widget_default"
android:minWidth="@dimen/widget_def_width"
android:minHeight="@dimen/widget_def_height"
+ android:minResizeHeight="0dp"
android:minResizeWidth="@dimen/widget_def_width"
- android:minResizeHeight="@dimen/widget_def_height"
android:previewImage="@drawable/ui_widget_preview"
- android:previewLayout="@layout/widget_small"
+ android:previewLayout="@layout/widget_docked_thin"
android:resizeMode="horizontal|vertical"
android:targetCellWidth="3"
android:targetCellHeight="2"
diff --git a/app/src/main/res/xml/widget_info.xml b/app/src/main/res/xml/widget_info.xml
index 84ac71fc7..35608e938 100644
--- a/app/src/main/res/xml/widget_info.xml
+++ b/app/src/main/res/xml/widget_info.xml
@@ -3,8 +3,8 @@
android:initialLayout="@layout/widget_default"
android:minWidth="@dimen/widget_def_width"
android:minHeight="@dimen/widget_def_height"
+ android:minResizeHeight="0dp"
android:minResizeWidth="@dimen/widget_def_width"
- android:minResizeHeight="@dimen/widget_def_height"
android:previewImage="@drawable/ui_widget_preview"
android:resizeMode="horizontal|vertical"
android:updatePeriodMillis="0"