widgets: revert most widget changes

Revert the introduction of the thin/tiny widgets, but keep the new
cover layout I created while working on them.

There is simply no way I can cram controls and metadata within the
size bucket that the thin widget occupies. I have decided to give up
and revert the widget to it's old form.

I understand why the thin widget is not appealing. However, the sizing
at which a widget can properly accomodate a taller widget is just too
precise and not really large enough to justify it's existance.
This commit is contained in:
OxygenCobalt 2022-05-25 11:03:59 -06:00
parent 4c74879cf1
commit 444e4299d6
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
26 changed files with 201 additions and 231 deletions

View file

@ -12,7 +12,7 @@
#### What's Improved
- Re-enabled theme customization on Android 12
- The tab selector now hides itself when there is only one tab
- Added more buttons to the smallest widget form
- Made the cover on the thin widget larger
#### What's Fixed
- Fixed incorrect ellipsizing on song items

View file

@ -24,15 +24,15 @@ android {
// ExoPlayer needs Java 8 to compile.
compileOptions {
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
freeCompilerArgs += "-Xjvm-default=all"
}
compileOptions {
targetCompatibility JavaVersion.VERSION_1_8
}
buildTypes {
debug {
debuggable true
@ -94,8 +94,7 @@ dependencies {
// Exoplayer
// WARNING: THE EXOPLAYER VERSION MUST BE KEPT IN LOCK-STEP WITH THE FLAC EXTENSION.
// IF NOT, VERY UNFRIENDLY BUILD FAILURES AND CRASHES MAY ENSUE.
def exoplayerVersion = "2.17.1"
implementation "com.google.android.exoplayer:exoplayer-core:$exoplayerVersion"
implementation "com.google.android.exoplayer:exoplayer-core:2.17.1"
implementation fileTree(dir: "libs", include: ["extension-*.aar"])
// Image loading

View file

@ -145,7 +145,7 @@ class HomeViewModel : ViewModel(), SettingsManager.Callback, MusicStore.Callback
}
}
override fun onLibraryChanged() {
override fun onLibrarySettingsChanged() {
tabs = visibleTabs
_shouldRecreateTabs.value = true
}

View file

@ -66,8 +66,6 @@ class PlaybackPanelFragment :
) {
// --- UI SETUP ---
logD(binding.root.paddingBottom)
binding.root.setOnApplyWindowInsetsListener { _, insets ->
val bars = insets.systemBarInsetsCompat
val gestures = insets.systemGestureInsetsCompat

View file

@ -31,8 +31,9 @@ enum class ReplayGainMode {
DYNAMIC;
companion object {
fun fromIntCode(value: Int): ReplayGainMode? {
return when (value) {
/** Convert an int [code] into an instance, or null if it isn't valid. */
fun fromIntCode(code: Int): ReplayGainMode? {
return when (code) {
IntegerTable.REPLAY_GAIN_MODE_OFF -> OFF
IntegerTable.REPLAY_GAIN_MODE_TRACK -> TRACK
IntegerTable.REPLAY_GAIN_MODE_ALBUM -> ALBUM
@ -42,11 +43,3 @@ enum class ReplayGainMode {
}
}
}
/** Represents the ReplayGain pre-amp values. */
data class ReplayGainPreAmp(
/** The value to use when ReplayGain tags are present. */
val with: Float,
/** The value to use when ReplayGain tags are not present. */
val without: Float,
)

View file

@ -0,0 +1,26 @@
/*
* Copyright (c) 2022 Auxio Project
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.oxycblt.auxio.playback.replaygain
/** Represents the ReplayGain pre-amp values. */
data class ReplayGainPreAmp(
/** The value to use when ReplayGain tags are present. */
val with: Float,
/** The value to use when ReplayGain tags are not present. */
val without: Float,
)

View file

@ -59,9 +59,9 @@ enum class RepeatMode {
}
companion object {
/** Convert an int [constant] into a LoopMode, or null if it isn't valid. */
fun fromIntCode(constant: Int): RepeatMode? {
return when (constant) {
/** Convert an int [code] into an instance, or null if it isn't valid. */
fun fromIntCode(code: Int): RepeatMode? {
return when (code) {
IntegerTable.REPEAT_MODE_NONE -> NONE
IntegerTable.REPEAT_MODE_ALL -> ALL
IntegerTable.REPEAT_MODE_TRACK -> TRACK

View file

@ -32,6 +32,7 @@ import org.oxycblt.auxio.playback.state.PlaybackStateManager
import org.oxycblt.auxio.playback.state.RepeatMode
import org.oxycblt.auxio.settings.SettingsManager
import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.unlikelyToBeNull
/**
*/
@ -106,10 +107,21 @@ class MediaSessionComponent(private val context: Context, private val player: Pl
.putText(MediaMetadataCompat.METADATA_KEY_COMPOSER, artist)
.putText(MediaMetadataCompat.METADATA_KEY_WRITER, artist)
.putText(MediaMetadataCompat.METADATA_KEY_GENRE, song.genre.resolveName(context))
.putLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER, song.track?.toLong() ?: 0L)
.putText(MediaMetadataCompat.METADATA_KEY_DATE, song.album.year?.toString())
.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, song.durationMs)
if (song.track != null) {
metadata.putLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER, song.track.toLong())
}
if (song.disc != null) {
metadata.putLong(MediaMetadataCompat.METADATA_KEY_DISC_NUMBER, song.disc.toLong())
}
if (song.album.year != null) {
metadata.putString(
MediaMetadataCompat.METADATA_KEY_DATE, unlikelyToBeNull(song.album.year).toString())
}
// Normally, android expects one to provide a URI to the metadata instance instead of
// a full blown bitmap. In practice, this is not ideal in the slightest, as we cannot
// provide any user customization or quality of life improvements with a flat URI.

View file

@ -66,7 +66,7 @@ fun handleAccentCompat(prefs: SharedPreferences): Accent {
}
}
return Accent(prefs.getInt(SettingsManager.KEY_ACCENT, 5))
return Accent.from(prefs.getInt(SettingsManager.KEY_ACCENT, 5))
}
/** Cache of the old keys used in Auxio. */

View file

@ -65,7 +65,7 @@ class SettingsManager private constructor(context: Context) :
}
/**
* Whether to display the LoopMode or the shuffle status on the notification. False if loop,
* Whether to display the RepeatMode or the shuffle status on the notification. False if repeat,
* true if shuffle.
*/
val useAltNotifAction: Boolean
@ -256,7 +256,7 @@ class SettingsManager private constructor(context: Context) :
when (key) {
KEY_USE_ALT_NOTIFICATION_ACTION -> callbacks.forEach { it.onNotifSettingsChanged() }
KEY_SHOW_COVERS, KEY_QUALITY_COVERS -> callbacks.forEach { it.onCoverSettingsChanged() }
KEY_LIB_TABS -> callbacks.forEach { it.onLibraryChanged() }
KEY_LIB_TABS -> callbacks.forEach { it.onLibrarySettingsChanged() }
KEY_REPLAY_GAIN, KEY_PRE_AMP_WITH, KEY_PRE_AMP_WITHOUT ->
callbacks.forEach { it.onReplayGainSettingsChanged() }
}
@ -268,7 +268,7 @@ class SettingsManager private constructor(context: Context) :
* context.
*/
interface Callback {
fun onLibraryChanged() {}
fun onLibrarySettingsChanged() {}
fun onNotifSettingsChanged() {}
fun onCoverSettingsChanged() {}
fun onReplayGainSettingsChanged() {}

View file

@ -77,7 +77,7 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
// TODO: Scale this drawable based on available space after padding
// FIXME: Scale this drawable based on available space after padding
imageMatrix =
centerMatrix.apply {

View file

@ -63,6 +63,12 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
styledAttrs.recycle()
// Use clipToOutline and a background drawable to crop images. While Coil's transformation
// could theoretically be used to round corners, the corner radius is dependent on the
// dimensions of the image, which will result in inconsistent corners across different
// album covers unless we resize all covers to be the same size. clipToOutline is both
// cheaper and more elegant. As a side-note, this also allows us to re-use the same
// background for both the tonal background color and the corner rounding.
clipToOutline = true
background =
MaterialShapeDrawable().apply {
@ -79,11 +85,6 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
override fun onAttachedToWindow() {
super.onAttachedToWindow()
// Use clipToOutline and a background drawable to crop images. While Coil's transformation
// could theoretically be used to round corners, the corner radius is dependent on the
// dimensions of the image, which will result in inconsistent corners across different
// album covers unless we resize all covers to be the same size. clipToOutline is both
// cheaper and more elegant.
if (!isInEditMode) {
val settingsManager = SettingsManager.getInstance()
if (settingsManager.roundCovers) {

View file

@ -17,7 +17,6 @@
package org.oxycblt.auxio.ui
import android.app.Dialog
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@ -35,17 +34,39 @@ import org.oxycblt.auxio.util.logD
abstract class ViewBindingDialogFragment<T : ViewBinding> : DialogFragment() {
private var _binding: T? = null
/**
* Inflate the binding from the given [inflater]. This should usually be done by the binding
* implementation's inflate function.
*/
protected abstract fun onCreateBinding(inflater: LayoutInflater): T
protected open fun onBindingCreated(binding: T, savedInstanceState: Bundle?) {}
protected open fun onDestroyBinding(binding: T) {}
/** Called during [onCreateDialog]. Dialog elements should be configured here. */
protected open fun onConfigDialog(builder: AlertDialog.Builder) {}
/**
* Called during [onViewCreated] when the binding was successfully inflated and set as the view.
* This is where view setup should occur.
*/
protected open fun onBindingCreated(binding: T, savedInstanceState: Bundle?) {}
/**
* Called during [onDestroyView] when the binding should be destroyed and all callbacks or
* leaking elements be released.
*/
protected open fun onDestroyBinding(binding: T) {}
/** Maybe get the binding. This will be null outside of the fragment view lifecycle. */
protected val binding: T?
get() = _binding
/**
* Get the binding under the assumption that the fragment has a view at this state in the
* lifecycle. This will throw an exception if the fragment is not in a valid lifecycle.
*/
protected fun requireBinding(): T {
return requireNotNull(_binding) {
"ViewBinding was not available, as the fragment was not in a valid state"
"ViewBinding was available. Fragment should be a valid state " +
"right now, but instead it was ${lifecycle.currentState}"
}
}
@ -55,12 +76,11 @@ abstract class ViewBindingDialogFragment<T : ViewBinding> : DialogFragment() {
savedInstanceState: Bundle?
): View = onCreateBinding(inflater).also { _binding = it }.root
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return MaterialAlertDialogBuilder(requireActivity(), theme).run {
override fun onCreateDialog(savedInstanceState: Bundle?) =
MaterialAlertDialogBuilder(requireActivity(), theme).run {
onConfigDialog(this)
create()
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@ -73,5 +93,6 @@ abstract class ViewBindingDialogFragment<T : ViewBinding> : DialogFragment() {
super.onDestroyView()
onDestroyBinding(requireBinding())
_binding = null
logD("Fragment destroyed")
}
}

View file

@ -32,16 +32,36 @@ import org.oxycblt.auxio.util.logD
abstract class ViewBindingFragment<T : ViewBinding> : Fragment() {
private var _binding: T? = null
/**
* Inflate the binding from the given [inflater]. This should usually be done by the binding
* implementation's inflate function.
*/
protected abstract fun onCreateBinding(inflater: LayoutInflater): T
/**
* Called during [onViewCreated] when the binding was successfully inflated and set as the view.
* This is where view setup should occur.
*/
protected open fun onBindingCreated(binding: T, savedInstanceState: Bundle?) {}
/**
* Called during [onDestroyView] when the binding should be destroyed and all callbacks or
* leaking elements be released.
*/
protected open fun onDestroyBinding(binding: T) {}
/** Maybe get the binding. This will be null outside of the fragment view lifecycle. */
protected val binding: T?
get() = _binding
/**
* Get the binding under the assumption that the fragment has a view at this state in the
* lifecycle. This will throw an exception if the fragment is not in a valid lifecycle.
*/
protected fun requireBinding(): T {
return requireNotNull(_binding) {
"ViewBinding was not available, as the fragment was not in a valid state"
"ViewBinding was available. Fragment should be a valid state " +
"right now, but instead it was ${lifecycle.currentState}"
}
}
@ -61,5 +81,6 @@ abstract class ViewBindingFragment<T : ViewBinding> : Fragment() {
super.onDestroyView()
onDestroyBinding(requireBinding())
_binding = null
logD("Fragment destroyed")
}
}

View file

@ -19,9 +19,7 @@ package org.oxycblt.auxio.ui.accent
import android.os.Build
import org.oxycblt.auxio.R
val ACCENT_COUNT: Int
get() = ACCENT_NAMES.size
import org.oxycblt.auxio.util.logW
private val ACCENT_NAMES =
intArrayOf(
@ -116,7 +114,7 @@ private val ACCENT_PRIMARY_COLORS =
* @property primary The primary color resource for this accent
* @author OxygenCobalt
*/
data class Accent(val index: Int) {
class Accent private constructor(val index: Int) {
val name: Int
get() = ACCENT_NAMES[index]
val theme: Int
@ -126,7 +124,24 @@ data class Accent(val index: Int) {
val primary: Int
get() = ACCENT_PRIMARY_COLORS[index]
override fun equals(other: Any?) = other is Accent && index == other.index
override fun hashCode() = index.hashCode()
companion object {
fun from(index: Int): Accent {
if (index > (MAX - 1)) {
logW("Account outside of bounds [idx: $index, max: $MAX")
return Accent(5)
}
return Accent(index)
}
/**
* The maximum amount of accents that are valid. This excludes the dynamic accent on
* versions that do not support it.
*/
val MAX =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
ACCENT_THEMES.size

View file

@ -30,7 +30,10 @@ import org.oxycblt.auxio.util.getColorSafe
import org.oxycblt.auxio.util.inflater
import org.oxycblt.auxio.util.stateList
/** An adapter that displays the accent palette. */
/**
* An adapter that displays the accent palette.
* @author OxygenCobalt
*/
class AccentAdapter(listener: Listener) :
MonoAdapter<Accent, AccentAdapter.Listener, AccentViewHolder>(listener) {
var selectedAccent: Accent? = null
@ -64,7 +67,7 @@ class AccentAdapter(listener: Listener) :
}
class AccentData : BackingData<Accent>() {
override fun getItem(position: Int) = Accent(position)
override fun getItem(position: Int) = Accent.from(position)
override fun getItemCount() = Accent.MAX
}
}
@ -84,15 +87,15 @@ class AccentViewHolder private constructor(private val binding: ItemAccentBindin
}
fun setSelected(isSelected: Boolean) {
val context = binding.accent.context
binding.accent.isEnabled = !isSelected
binding.accent.imageTintList =
if (isSelected) {
context.getAttrColorSafe(R.attr.colorSurface).stateList
} else {
context.getColorSafe(android.R.color.transparent).stateList
}
binding.accent.apply {
isEnabled = !isSelected
imageTintList =
if (isSelected) {
context.getAttrColorSafe(R.attr.colorSurface).stateList
} else {
context.getColorSafe(android.R.color.transparent).stateList
}
}
}
companion object {

View file

@ -55,17 +55,17 @@ class AccentCustomizeDialog :
}
override fun onBindingCreated(binding: DialogAccentBinding, savedInstanceState: Bundle?) {
// --- UI SETUP ---
binding.accentRecycler.adapter = accentAdapter
accentAdapter.setSelectedAccent(
if (savedInstanceState != null) {
Accent(savedInstanceState.getInt(KEY_PENDING_ACCENT))
Accent.from(savedInstanceState.getInt(KEY_PENDING_ACCENT))
} else {
settingsManager.accent
},
binding.accentRecycler)
// --- UI SETUP ---
binding.accentRecycler.adapter = accentAdapter
}
override fun onSaveInstanceState(outState: Bundle) {

View file

@ -222,22 +222,20 @@ fun Context.showToast(@StringRes str: Int) {
}
/** Create a [PendingIntent] that leads to Auxio's [MainActivity] */
fun Context.newMainIntent(): PendingIntent {
return PendingIntent.getActivity(
fun Context.newMainIntent(): PendingIntent =
PendingIntent.getActivity(
this,
IntegerTable.REQUEST_CODE,
Intent(this, MainActivity::class.java),
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) PendingIntent.FLAG_IMMUTABLE else 0)
}
/** Create a broadcast [PendingIntent] */
fun Context.newBroadcastIntent(what: String): PendingIntent {
return PendingIntent.getBroadcast(
fun Context.newBroadcastIntent(what: String): PendingIntent =
PendingIntent.getBroadcast(
this,
IntegerTable.REQUEST_CODE,
Intent(what),
Intent(what).setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) PendingIntent.FLAG_IMMUTABLE else 0)
}
/** Hard-restarts the app. Useful for forcing the app to reload music. */
fun Context.hardRestart() {

View file

@ -33,20 +33,13 @@ import org.oxycblt.auxio.util.newMainIntent
fun createDefaultWidget(context: Context) = createViews(context, R.layout.widget_default)
/**
* The tiny widget like a small or medium widget, but for landscape or exceptionally small screens.
*/
fun createTinyWidget(context: Context, state: WidgetComponent.WidgetState) =
createViews(context, R.layout.widget_tiny)
.applyCover(context, state)
.applyBasicControls(context, state)
/**
* The thin widget is like a wide or large widget, but for landscape or exceptionally small screens.
* The thin widget is a weird outlier widget intended to work well on strange launchers or
* landscape grid launchers that allow really thin widget sizing.
*/
fun createThinWidget(context: Context, state: WidgetComponent.WidgetState) =
createViews(context, R.layout.widget_thin)
.applyCover(context, state)
.applyFullControls(context, state)
.applyMeta(context, state)
.applyBasicControls(context, state)
/**
* The small widget is for 2x2 widgets and just shows the cover art and playback controls. This is

View file

@ -58,8 +58,7 @@ class WidgetProvider : AppWidgetProvider() {
// Map each widget form to the cells where it would look at least okay.
val views =
mapOf(
SizeF(180f, 100f) to createTinyWidget(context, state),
SizeF(372f, 100f) to createThinWidget(context, state),
SizeF(180f, 100f) to createThinWidget(context, state),
SizeF(180f, 152f) to createSmallWidget(context, state),
SizeF(272f, 152f) to createWideWidget(context, state),
SizeF(180f, 270f) to createMediumWidget(context, state),

View file

@ -8,7 +8,7 @@
android:theme="@style/Theme.Widget">
<!--
See widget_small.xml for an explanation for the ImageView setup
See widget_small.xml for an explanation for the ImageView setup.
-->
<android.widget.ImageView

View file

@ -8,7 +8,7 @@
android:theme="@style/Theme.Widget">
<!--
See widget_small.xml for an explanation for the ImageView setup
See widget_small.xml for an explanation for the ImageView setup.
-->
<android.widget.ImageView

View file

@ -8,7 +8,7 @@
android:theme="@style/Theme.Widget">
<!--
For this widget form to work, we need to scale the ImageView across a 1:1 aspect ratio.
For most widget forms to work, we need to scale the ImageView across a 1:1 aspect ratio.
However, since we are working with a RemoteViews instance, we can't just use ConstraintLayout
to achieve this. We can use RelativeLayout, but there's no way to force an aspect ratio with
that layout. However, if we create an invisible ImageView that contains a massive fixed-size

View file

@ -5,22 +5,22 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorSurface"
android:theme="@style/Theme.Widget"
android:baselineAligned="false"
android:orientation="horizontal"
android:baselineAligned="false">
android:theme="@style/Theme.Widget">
<!--
Wrapping the 1:1 ImageView hack in a LinearLayout allows the view to measure greedily
without squishing the controls.
-->
<RelativeLayout
<android.widget.RelativeLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1">
<!--
See widget_small.xml for an explanation for the ImageView setup
See widget_small.xml for an explanation for the ImageView setup.
-->
<android.widget.ImageView
@ -52,63 +52,53 @@
android:src="@drawable/ic_remote_default_cover"
tools:ignore="ContentDescription" />
</RelativeLayout>
</android.widget.RelativeLayout>
<android.widget.LinearLayout
android:id="@+id/widget_panel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:layout_gravity="center"
android:orientation="horizontal"
android:layout_marginTop="@dimen/spacing_medium"
android:layout_marginEnd="@dimen/spacing_medium"
android:layout_marginBottom="@dimen/spacing_medium"
android:layout_marginEnd="@dimen/spacing_medium">
android:layout_weight="2"
android:orientation="horizontal">
<android.widget.ImageButton
android:id="@+id/widget_repeat"
style="@style/Widget.Auxio.PlaybackButton.AppWidget"
<android.widget.LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:contentDescription="@string/desc_change_repeat"
android:src="@drawable/ic_repeat" />
android:layout_marginEnd="@dimen/spacing_small"
android:orientation="vertical">
<android.widget.ImageButton
android:id="@+id/widget_skip_prev"
style="@style/Widget.Auxio.PlaybackButton.AppWidget"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:contentDescription="@string/desc_skip_prev"
android:src="@drawable/ic_skip_prev" />
<android.widget.TextView
android:id="@+id/widget_song"
style="@style/Widget.Auxio.TextView.Primary.AppWidget"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/def_widget_song" />
<android.widget.ImageButton
android:id="@+id/widget_play_pause"
style="@style/Widget.Auxio.PlaybackButton.AppWidget"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:contentDescription="@string/desc_play_pause"
android:src="@drawable/sel_playing_state" />
<android.widget.TextView
android:id="@+id/widget_artist"
style="@style/Widget.Auxio.TextView.Secondary.AppWidget"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/def_widget_artist" />
<android.widget.ImageButton
android:id="@+id/widget_skip_next"
style="@style/Widget.Auxio.PlaybackButton.AppWidget"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:contentDescription="@string/desc_skip_next"
android:src="@drawable/ic_skip_next" />
</android.widget.LinearLayout>
<android.widget.ImageButton
android:id="@+id/widget_play_pause"
style="@style/Widget.Auxio.PlaybackButton.AppWidget"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="@dimen/size_btn_small"
android:minHeight="@dimen/size_btn_small"
android:contentDescription="@string/desc_play_pause"
android:src="@drawable/ic_play" />
<android.widget.ImageButton
android:id="@+id/widget_shuffle"
style="@style/Widget.Auxio.PlaybackButton.AppWidget"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:contentDescription="@string/desc_shuffle"
android:src="@drawable/ic_shuffle_state" />
</android.widget.LinearLayout>
</LinearLayout>

View file

@ -1,99 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@android:id/background"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorSurface"
android:theme="@style/Theme.Widget"
android:orientation="horizontal"
android:baselineAligned="false">
<!--
Wrapping the 1:1 ImageView hack in a LinearLayout allows the view to measure greedily
without squishing the controls.
-->
<RelativeLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1">
<!--
See widget_small.xml for an explanation for the ImageView setup
-->
<android.widget.ImageView
android:id="@+id/widget_aspect_ratio"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true"
android:layout_marginStart="@dimen/spacing_medium"
android:layout_marginTop="@dimen/spacing_medium"
android:layout_marginEnd="@dimen/spacing_medium"
android:layout_marginBottom="@dimen/spacing_medium"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
android:src="@drawable/ui_remote_aspect_ratio"
android:visibility="invisible"
tools:ignore="ContentDescription" />
<android.widget.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_remote_default_cover"
tools:ignore="ContentDescription" />
</RelativeLayout>
<android.widget.LinearLayout
android:id="@+id/widget_panel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:layout_gravity="center"
android:orientation="horizontal"
android:layout_marginTop="@dimen/spacing_medium"
android:layout_marginBottom="@dimen/spacing_medium"
android:layout_marginEnd="@dimen/spacing_medium">
<android.widget.ImageButton
android:id="@+id/widget_skip_prev"
style="@style/Widget.Auxio.PlaybackButton.AppWidget"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:contentDescription="@string/desc_skip_prev"
android:src="@drawable/ic_skip_prev" />
<android.widget.ImageButton
android:id="@+id/widget_play_pause"
style="@style/Widget.Auxio.PlaybackButton.AppWidget"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:contentDescription="@string/desc_play_pause"
android:src="@drawable/sel_playing_state" />
<android.widget.ImageButton
android:id="@+id/widget_skip_next"
style="@style/Widget.Auxio.PlaybackButton.AppWidget"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:contentDescription="@string/desc_skip_next"
android:src="@drawable/ic_skip_next" />
</android.widget.LinearLayout>
</LinearLayout>

View file

@ -8,7 +8,7 @@
android:theme="@style/Theme.Widget">
<!--
See widget_small.xml for an explanation for the ImageView setup
See widget_small.xml for an explanation for the ImageView setup.
-->
<android.widget.ImageView