Merge branch 'refactor_widget' into 'develop'

Updated widget handling and layouts, add Day&Night theme

See merge request ultrasonic/ultrasonic!820
This commit is contained in:
birdbird 2022-10-03 13:39:10 +00:00
commit fd096212ee
89 changed files with 1023 additions and 1632 deletions

View File

@ -77,6 +77,8 @@ android {
ignore 'MissingTranslation', 'UnusedQuantity', 'MissingQuantity' ignore 'MissingTranslation', 'UnusedQuantity', 'MissingQuantity'
warning 'ImpliedQuantity' warning 'ImpliedQuantity'
disable 'ObsoleteLintCustomCheck' disable 'ObsoleteLintCustomCheck'
textReport true
checkDependencies true
} }
namespace 'org.moire.ultrasonic' namespace 'org.moire.ultrasonic'

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<issues format="6" by="lint 7.3.0" type="baseline" client="gradle" dependencies="false" name="AGP (7.3.0)" variant="all" version="7.3.0"> <issues format="6" by="lint 7.3.0" type="baseline" client="gradle" dependencies="true" name="AGP (7.3.0)" variant="all" version="7.3.0">
<issue <issue
id="PluralsCandidate" id="PluralsCandidate"
@ -44,7 +44,7 @@
errorLine2=" ~~~~~~~~"> errorLine2=" ~~~~~~~~">
<location <location
file="src/main/AndroidManifest.xml" file="src/main/AndroidManifest.xml"
line="164" line="128"
column="10"/> column="10"/>
</issue> </issue>
@ -55,7 +55,7 @@
errorLine2=" ~~~~~~~~"> errorLine2=" ~~~~~~~~">
<location <location
file="src/main/AndroidManifest.xml" file="src/main/AndroidManifest.xml"
line="169" line="133"
column="10"/> column="10"/>
</issue> </issue>
@ -103,50 +103,6 @@
column="9"/> column="9"/>
</issue> </issue>
<issue
id="Overdraw"
message="Possible overdraw: Root element paints background `@drawable/appwidget_dark_bg_trans` with a theme that also paints a background (inferred theme is `@style/Theme.Material3.DynamicColors.Dark`)"
errorLine1=" a:background=&quot;@drawable/appwidget_dark_bg_trans&quot;"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/layout/appwidget4x1.xml"
line="7"
column="5"/>
</issue>
<issue
id="Overdraw"
message="Possible overdraw: Root element paints background `@drawable/appwidget_dark_bg_trans` with a theme that also paints a background (inferred theme is `@style/Theme.Material3.DynamicColors.Dark`)"
errorLine1=" a:background=&quot;@drawable/appwidget_dark_bg_trans&quot;"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/layout/appwidget4x2.xml"
line="7"
column="5"/>
</issue>
<issue
id="Overdraw"
message="Possible overdraw: Root element paints background `@drawable/appwidget_dark_bg_trans` with a theme that also paints a background (inferred theme is `@style/Theme.Material3.DynamicColors.Dark`)"
errorLine1=" a:background=&quot;@drawable/appwidget_dark_bg_trans&quot;"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/layout/appwidget4x3.xml"
line="7"
column="5"/>
</issue>
<issue
id="Overdraw"
message="Possible overdraw: Root element paints background `@drawable/appwidget_dark_bg_trans` with a theme that also paints a background (inferred theme is `@style/Theme.Material3.DynamicColors.Dark`)"
errorLine1=" a:background=&quot;@drawable/appwidget_dark_bg_trans&quot; >"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/layout/appwidget4x4.xml"
line="8"
column="5"/>
</issue>
<issue <issue
id="UnusedResources" id="UnusedResources"
message="The resource `R.drawable.ic_baseline_close` appears to be unused" message="The resource `R.drawable.ic_baseline_close` appears to be unused"
@ -378,182 +334,6 @@
column="4"/> column="4"/>
</issue> </issue>
<issue
id="ContentDescription"
message="Missing `contentDescription` attribute on image"
errorLine1=" &lt;ImageView"
errorLine2=" ~~~~~~~~~">
<location
file="src/main/res/layout/appwidget4x1.xml"
line="10"
column="6"/>
</issue>
<issue
id="ContentDescription"
message="Missing `contentDescription` attribute on image"
errorLine1=" &lt;ImageButton"
errorLine2=" ~~~~~~~~~~~">
<location
file="src/main/res/layout/appwidget4x1.xml"
line="76"
column="14"/>
</issue>
<issue
id="ContentDescription"
message="Missing `contentDescription` attribute on image"
errorLine1=" &lt;ImageButton"
errorLine2=" ~~~~~~~~~~~">
<location
file="src/main/res/layout/appwidget4x1.xml"
line="85"
column="14"/>
</issue>
<issue
id="ContentDescription"
message="Missing `contentDescription` attribute on image"
errorLine1=" &lt;ImageButton"
errorLine2=" ~~~~~~~~~~~">
<location
file="src/main/res/layout/appwidget4x1.xml"
line="94"
column="14"/>
</issue>
<issue
id="ContentDescription"
message="Missing `contentDescription` attribute on image"
errorLine1=" &lt;ImageView"
errorLine2=" ~~~~~~~~~">
<location
file="src/main/res/layout/appwidget4x2.xml"
line="10"
column="6"/>
</issue>
<issue
id="ContentDescription"
message="Missing `contentDescription` attribute on image"
errorLine1=" &lt;ImageButton"
errorLine2=" ~~~~~~~~~~~">
<location
file="src/main/res/layout/appwidget4x2.xml"
line="97"
column="14"/>
</issue>
<issue
id="ContentDescription"
message="Missing `contentDescription` attribute on image"
errorLine1=" &lt;ImageButton"
errorLine2=" ~~~~~~~~~~~">
<location
file="src/main/res/layout/appwidget4x2.xml"
line="106"
column="14"/>
</issue>
<issue
id="ContentDescription"
message="Missing `contentDescription` attribute on image"
errorLine1=" &lt;ImageButton"
errorLine2=" ~~~~~~~~~~~">
<location
file="src/main/res/layout/appwidget4x2.xml"
line="115"
column="14"/>
</issue>
<issue
id="ContentDescription"
message="Missing `contentDescription` attribute on image"
errorLine1=" &lt;ImageView"
errorLine2=" ~~~~~~~~~">
<location
file="src/main/res/layout/appwidget4x3.xml"
line="10"
column="6"/>
</issue>
<issue
id="ContentDescription"
message="Missing `contentDescription` attribute on image"
errorLine1=" &lt;ImageButton"
errorLine2=" ~~~~~~~~~~~">
<location
file="src/main/res/layout/appwidget4x3.xml"
line="99"
column="14"/>
</issue>
<issue
id="ContentDescription"
message="Missing `contentDescription` attribute on image"
errorLine1=" &lt;ImageButton"
errorLine2=" ~~~~~~~~~~~">
<location
file="src/main/res/layout/appwidget4x3.xml"
line="108"
column="14"/>
</issue>
<issue
id="ContentDescription"
message="Missing `contentDescription` attribute on image"
errorLine1=" &lt;ImageButton"
errorLine2=" ~~~~~~~~~~~">
<location
file="src/main/res/layout/appwidget4x3.xml"
line="117"
column="14"/>
</issue>
<issue
id="ContentDescription"
message="Missing `contentDescription` attribute on image"
errorLine1=" &lt;ImageView"
errorLine2=" ~~~~~~~~~">
<location
file="src/main/res/layout/appwidget4x4.xml"
line="10"
column="6"/>
</issue>
<issue
id="ContentDescription"
message="Missing `contentDescription` attribute on image"
errorLine1=" &lt;ImageButton"
errorLine2=" ~~~~~~~~~~~">
<location
file="src/main/res/layout/appwidget4x4.xml"
line="100"
column="14"/>
</issue>
<issue
id="ContentDescription"
message="Missing `contentDescription` attribute on image"
errorLine1=" &lt;ImageButton"
errorLine2=" ~~~~~~~~~~~">
<location
file="src/main/res/layout/appwidget4x4.xml"
line="109"
column="14"/>
</issue>
<issue
id="ContentDescription"
message="Missing `contentDescription` attribute on image"
errorLine1=" &lt;ImageButton"
errorLine2=" ~~~~~~~~~~~">
<location
file="src/main/res/layout/appwidget4x4.xml"
line="118"
column="14"/>
</issue>
<issue <issue
id="ContentDescription" id="ContentDescription"
message="Missing `contentDescription` attribute on image" message="Missing `contentDescription` attribute on image"
@ -609,127 +389,6 @@
column="10"/> column="10"/>
</issue> </issue>
<issue
id="HardcodedText"
message="Hardcoded string &quot;Title&quot;, should use `@string` resource"
errorLine1=" a:text=&quot;Title&quot;"
errorLine2=" ~~~~~~~~~~~~~~">
<location
file="src/main/res/layout/appwidget4x1.xml"
line="46"
column="17"/>
</issue>
<issue
id="HardcodedText"
message="Hardcoded string &quot;Artist&quot;, should use `@string` resource"
errorLine1=" a:text=&quot;Artist&quot;"
errorLine2=" ~~~~~~~~~~~~~~~">
<location
file="src/main/res/layout/appwidget4x1.xml"
line="64"
column="17"/>
</issue>
<issue
id="HardcodedText"
message="Hardcoded string &quot;Title&quot;, should use `@string` resource"
errorLine1=" a:text=&quot;Title&quot;"
errorLine2=" ~~~~~~~~~~~~~~">
<location
file="src/main/res/layout/appwidget4x2.xml"
line="48"
column="17"/>
</issue>
<issue
id="HardcodedText"
message="Hardcoded string &quot;Artist&quot;, should use `@string` resource"
errorLine1=" a:text=&quot;Artist&quot;"
errorLine2=" ~~~~~~~~~~~~~~~">
<location
file="src/main/res/layout/appwidget4x2.xml"
line="65"
column="17"/>
</issue>
<issue
id="HardcodedText"
message="Hardcoded string &quot;Album&quot;, should use `@string` resource"
errorLine1=" a:text=&quot;Album&quot;"
errorLine2=" ~~~~~~~~~~~~~~">
<location
file="src/main/res/layout/appwidget4x2.xml"
line="82"
column="17"/>
</issue>
<issue
id="HardcodedText"
message="Hardcoded string &quot;Title&quot;, should use `@string` resource"
errorLine1=" a:text=&quot;Title&quot;"
errorLine2=" ~~~~~~~~~~~~~~">
<location
file="src/main/res/layout/appwidget4x3.xml"
line="50"
column="17"/>
</issue>
<issue
id="HardcodedText"
message="Hardcoded string &quot;Artist&quot;, should use `@string` resource"
errorLine1=" a:text=&quot;Artist&quot;"
errorLine2=" ~~~~~~~~~~~~~~~">
<location
file="src/main/res/layout/appwidget4x3.xml"
line="66"
column="17"/>
</issue>
<issue
id="HardcodedText"
message="Hardcoded string &quot;Album&quot;, should use `@string` resource"
errorLine1=" a:text=&quot;Album&quot;"
errorLine2=" ~~~~~~~~~~~~~~">
<location
file="src/main/res/layout/appwidget4x3.xml"
line="83"
column="17"/>
</issue>
<issue
id="HardcodedText"
message="Hardcoded string &quot;Title&quot;, should use `@string` resource"
errorLine1=" a:text=&quot;Title&quot;"
errorLine2=" ~~~~~~~~~~~~~~">
<location
file="src/main/res/layout/appwidget4x4.xml"
line="51"
column="17"/>
</issue>
<issue
id="HardcodedText"
message="Hardcoded string &quot;Artist&quot;, should use `@string` resource"
errorLine1=" a:text=&quot;Artist&quot;"
errorLine2=" ~~~~~~~~~~~~~~~">
<location
file="src/main/res/layout/appwidget4x4.xml"
line="67"
column="17"/>
</issue>
<issue
id="HardcodedText"
message="Hardcoded string &quot;Album&quot;, should use `@string` resource"
errorLine1=" a:text=&quot;Album&quot;"
errorLine2=" ~~~~~~~~~~~~~~">
<location
file="src/main/res/layout/appwidget4x4.xml"
line="84"
column="16"/>
</issue>
<issue <issue
id="HardcodedText" id="HardcodedText"
message="Hardcoded string &quot;0 dB&quot;, should use `@string` resource" message="Hardcoded string &quot;0 dB&quot;, should use `@string` resource"

View File

@ -108,8 +108,8 @@
</intent-filter> </intent-filter>
</receiver> </receiver>
<receiver <receiver
android:name=".provider.UltrasonicAppWidgetProvider4X1" android:name=".provider.UltrasonicAppWidgetProvider"
android:label="Ultrasonic (4x1)" android:label="Ultrasonic"
android:exported="false"> android:exported="false">
<intent-filter> <intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/> <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
@ -117,43 +117,7 @@
<meta-data <meta-data
android:name="android.appwidget.provider" android:name="android.appwidget.provider"
android:resource="@xml/appwidget_info_4x1"/> android:resource="@xml/appwidget_info"/>
</receiver>
<receiver
android:name=".provider.UltrasonicAppWidgetProvider4X2"
android:label="Ultrasonic (4x2)"
android:exported="false">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/appwidget_info_4x2"/>
</receiver>
<receiver
android:name=".provider.UltrasonicAppWidgetProvider4X3"
android:label="Ultrasonic (4x3)"
android:exported="false">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/appwidget_info_4x3"/>
</receiver>
<receiver
android:name=".provider.UltrasonicAppWidgetProvider4X4"
android:label="Ultrasonic (4x4)"
android:exported="false">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/appwidget_info_4x4"/>
</receiver> </receiver>
<receiver android:name=".receiver.MediaButtonIntentReceiver" <receiver android:name=".receiver.MediaButtonIntentReceiver"
android:exported="true"> android:exported="true">

View File

@ -13,6 +13,7 @@ import android.content.Intent
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.content.res.Resources import android.content.res.Resources
import android.media.AudioManager import android.media.AudioManager
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.provider.MediaStore import android.provider.MediaStore
import android.provider.SearchRecentSuggestions import android.provider.SearchRecentSuggestions
@ -246,14 +247,19 @@ class NavigationActivity : AppCompatActivity() {
} }
private fun updateNavigationHeaderForServer() { private fun updateNavigationHeaderForServer() {
// Only show the vector graphic on Android 11 or earlier
val showVectorBackground = (Build.VERSION.SDK_INT < Build.VERSION_CODES.S)
val activeServer = activeServerProvider.getActiveServer() val activeServer = activeServerProvider.getActiveServer()
if (cachedServerCount == 0) if (cachedServerCount == 0)
selectServerButton?.text = getString(R.string.main_setup_server, activeServer.name) selectServerButton?.text = getString(R.string.main_setup_server, activeServer.name)
else selectServerButton?.text = activeServer.name else selectServerButton?.text = activeServer.name
val foregroundColor = ServerColor.getForegroundColor(this, activeServer.color) val foregroundColor =
val backgroundColor = ServerColor.getBackgroundColor(this, activeServer.color) ServerColor.getForegroundColor(this, activeServer.color, showVectorBackground)
val backgroundColor =
ServerColor.getBackgroundColor(this, activeServer.color)
if (activeServer.index == 0) if (activeServer.index == 0)
selectServerButton?.icon = selectServerButton?.icon =
@ -265,6 +271,11 @@ class NavigationActivity : AppCompatActivity() {
selectServerButton?.iconTint = ColorStateList.valueOf(foregroundColor) selectServerButton?.iconTint = ColorStateList.valueOf(foregroundColor)
selectServerButton?.setTextColor(foregroundColor) selectServerButton?.setTextColor(foregroundColor)
headerBackgroundImage?.setBackgroundColor(backgroundColor) headerBackgroundImage?.setBackgroundColor(backgroundColor)
// Hide the vector graphic on Android 12 or later
if (!showVectorBackground) {
headerBackgroundImage?.setImageDrawable(null)
}
} }
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {

View File

@ -1,7 +1,6 @@
package org.moire.ultrasonic.adapters package org.moire.ultrasonic.adapters
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.MotionEvent import android.view.MotionEvent
@ -21,7 +20,6 @@ class TrackViewBinder(
val onContextMenuClick: ((MenuItem, Track) -> Boolean)? = null, val onContextMenuClick: ((MenuItem, Track) -> Boolean)? = null,
val checkable: Boolean, val checkable: Boolean,
val draggable: Boolean, val draggable: Boolean,
context: Context,
val lifecycleOwner: LifecycleOwner, val lifecycleOwner: LifecycleOwner,
val createContextMenu: (View, Track) -> PopupMenu = { view, _ -> val createContextMenu: (View, Track) -> PopupMenu = { view, _ ->
Utils.createPopupMenu( Utils.createPopupMenu(
@ -36,8 +34,6 @@ class TrackViewBinder(
// Set our layout files // Set our layout files
val layout = R.layout.list_item_track val layout = R.layout.list_item_track
private val imageHelper: Utils.ImageHelper = Utils.ImageHelper(context)
override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup): TrackViewHolder { override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup): TrackViewHolder {
return TrackViewHolder(inflater.inflate(layout, parent, false)) return TrackViewHolder(inflater.inflate(layout, parent, false))
} }
@ -56,8 +52,6 @@ class TrackViewBinder(
} }
} }
holder.imageHelper = imageHelper
// Remove observer before binding // Remove observer before binding
holder.observableChecked.removeObservers(lifecycleOwner) holder.observableChecked.removeObservers(lifecycleOwner)

View File

@ -1,12 +1,12 @@
package org.moire.ultrasonic.adapters package org.moire.ultrasonic.adapters
import android.graphics.drawable.Drawable
import android.view.View import android.view.View
import android.widget.Checkable import android.widget.Checkable
import android.widget.CheckedTextView import android.widget.CheckedTextView
import android.widget.ImageView import android.widget.ImageView
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
@ -38,7 +38,6 @@ class TrackViewHolder(val view: View) : RecyclerView.ViewHolder(view), Checkable
var check: CheckedTextView = view.findViewById(R.id.song_check) var check: CheckedTextView = view.findViewById(R.id.song_check)
var drag: ImageView = view.findViewById(R.id.song_drag) var drag: ImageView = view.findViewById(R.id.song_drag)
var observableChecked = MutableLiveData(false) var observableChecked = MutableLiveData(false)
lateinit var imageHelper: Utils.ImageHelper
private var rating: LinearLayout = view.findViewById(R.id.song_five_star) private var rating: LinearLayout = view.findViewById(R.id.song_five_star)
private var fiveStar1: ImageView = view.findViewById(R.id.song_five_star_1) private var fiveStar1: ImageView = view.findViewById(R.id.song_five_star_1)
@ -125,11 +124,15 @@ class TrackViewHolder(val view: View) : RecyclerView.ViewHolder(view), Checkable
rxBusSubscription?.dispose() rxBusSubscription?.dispose()
} }
private val playingIcon by lazy {
ContextCompat.getDrawable(view.context, R.drawable.ic_stat_play)!!
}
private fun setPlayIcon(isPlaying: Boolean) { private fun setPlayIcon(isPlaying: Boolean) {
if (isPlaying && !isPlayingCached) { if (isPlaying && !isPlayingCached) {
isPlayingCached = true isPlayingCached = true
title.setCompoundDrawablesWithIntrinsicBounds( title.setCompoundDrawablesWithIntrinsicBounds(
imageHelper.playingImage, null, null, null playingIcon, null, null, null
) )
} else if (!isPlaying && isPlayingCached) { } else if (!isPlaying && isPlayingCached) {
isPlayingCached = false isPlayingCached = false
@ -214,13 +217,13 @@ class TrackViewHolder(val view: View) : RecyclerView.ViewHolder(view), Checkable
when (status) { when (status) {
DownloadState.DONE -> { DownloadState.DONE -> {
showStatusImage(imageHelper.downloadedImage) showStatusImage(R.drawable.ic_downloaded)
} }
DownloadState.PINNED -> { DownloadState.PINNED -> {
showStatusImage(imageHelper.pinImage) showStatusImage(R.drawable.ic_menu_pin)
} }
DownloadState.FAILED -> { DownloadState.FAILED -> {
showStatusImage(imageHelper.errorImage) showStatusImage(R.drawable.ic_baseline_error)
} }
DownloadState.DOWNLOADING -> { DownloadState.DOWNLOADING -> {
showProgress() showProgress()
@ -237,10 +240,14 @@ class TrackViewHolder(val view: View) : RecyclerView.ViewHolder(view), Checkable
} }
} }
private fun showStatusImage(image: Drawable?) { private fun showStatusImage(image: Int?) {
progressIndicator.isVisible = false progressIndicator.isVisible = false
statusImage.isVisible = true statusImage.isVisible = true
statusImage.setImageDrawable(image) if (image != null) {
statusImage.setImageResource(image)
} else {
statusImage.setImageDrawable(null)
}
} }
private fun showIndefiniteProgress() { private fun showIndefiniteProgress() {

View File

@ -1,15 +1,11 @@
package org.moire.ultrasonic.adapters package org.moire.ultrasonic.adapters
import android.content.Context
import android.graphics.drawable.Drawable
import android.view.MenuInflater import android.view.MenuInflater
import android.view.View import android.view.View
import android.widget.PopupMenu import android.widget.PopupMenu
import androidx.core.content.ContextCompat
import org.moire.ultrasonic.R import org.moire.ultrasonic.R
import org.moire.ultrasonic.data.ActiveServerProvider import org.moire.ultrasonic.data.ActiveServerProvider
import org.moire.ultrasonic.domain.Identifiable import org.moire.ultrasonic.domain.Identifiable
import org.moire.ultrasonic.util.Settings
object Utils { object Utils {
@JvmStatic @JvmStatic
@ -31,41 +27,6 @@ object Utils {
return popup return popup
} }
/**
* Provides cached drawables for the UI
*/
class ImageHelper(context: Context) {
lateinit var errorImage: Drawable
lateinit var pinImage: Drawable
lateinit var downloadedImage: Drawable
lateinit var downloadingImage: List<Drawable>
lateinit var playingImage: Drawable
var theme: String
init {
theme = Settings.theme
getDrawables(context)
}
private fun getDrawables(context: Context) {
pinImage = ContextCompat.getDrawable(context, R.drawable.ic_menu_pin)!!
downloadedImage =
ContextCompat.getDrawable(context, R.drawable.stat_sys_download_anim_0)!!
errorImage = ContextCompat.getDrawable(context, R.drawable.ic_baseline_error)!!
downloadingImage = listOf(
ContextCompat.getDrawable(context, R.drawable.stat_sys_download_anim_1)!!,
ContextCompat.getDrawable(context, R.drawable.stat_sys_download_anim_2)!!,
ContextCompat.getDrawable(context, R.drawable.stat_sys_download_anim_3)!!,
ContextCompat.getDrawable(context, R.drawable.stat_sys_download_anim_4)!!,
ContextCompat.getDrawable(context, R.drawable.stat_sys_download_anim_5)!!,
ContextCompat.getDrawable(context, R.drawable.stat_sys_download_anim_6)!!,
ContextCompat.getDrawable(context, R.drawable.stat_sys_download_anim_7)!!,
)
playingImage = ContextCompat.getDrawable(context, R.drawable.ic_stat_play)!!
}
}
interface SectionedBinder { interface SectionedBinder {
fun getSectionName(item: Identifiable): String fun getSectionName(item: Identifiable): String
} }

View File

@ -57,7 +57,6 @@ class DownloadsFragment : MultiListFragment<Track>() {
{ _, _ -> true }, { _, _ -> true },
checkable = false, checkable = false,
draggable = false, draggable = false,
context = requireContext(),
lifecycleOwner = viewLifecycleOwner lifecycleOwner = viewLifecycleOwner
) )
) )

View File

@ -69,7 +69,7 @@ class EditServerFragment : Fragment(), OnBackPressedHandler {
private var userNameEditText: TextInputLayout? = null private var userNameEditText: TextInputLayout? = null
private var passwordEditText: TextInputLayout? = null private var passwordEditText: TextInputLayout? = null
private var selfSignedSwitch: SwitchMaterial? = null private var selfSignedSwitch: SwitchMaterial? = null
private var ldapSwitch: SwitchMaterial? = null private var plaintextSwitch: SwitchMaterial? = null
private var jukeboxSwitch: SwitchMaterial? = null private var jukeboxSwitch: SwitchMaterial? = null
private var saveButton: Button? = null private var saveButton: Button? = null
private var testButton: Button? = null private var testButton: Button? = null
@ -102,7 +102,7 @@ class EditServerFragment : Fragment(), OnBackPressedHandler {
userNameEditText = view.findViewById(R.id.edit_server_username) userNameEditText = view.findViewById(R.id.edit_server_username)
passwordEditText = view.findViewById(R.id.edit_server_password) passwordEditText = view.findViewById(R.id.edit_server_password)
selfSignedSwitch = view.findViewById(R.id.edit_self_signed) selfSignedSwitch = view.findViewById(R.id.edit_self_signed)
ldapSwitch = view.findViewById(R.id.edit_ldap) plaintextSwitch = view.findViewById(R.id.edit_plaintext)
jukeboxSwitch = view.findViewById(R.id.edit_jukebox) jukeboxSwitch = view.findViewById(R.id.edit_jukebox)
saveButton = view.findViewById(R.id.edit_save) saveButton = view.findViewById(R.id.edit_save)
testButton = view.findViewById(R.id.edit_test) testButton = view.findViewById(R.id.edit_test)
@ -195,7 +195,7 @@ class EditServerFragment : Fragment(), OnBackPressedHandler {
private fun updateColor(color: Int?) { private fun updateColor(color: Int?) {
val image = ContextCompat.getDrawable(requireContext(), R.drawable.thumb_drawable) val image = ContextCompat.getDrawable(requireContext(), R.drawable.thumb_drawable)
currentColor = ServerColor.getBackgroundColor(requireContext(), color) currentColor = color ?: ServerColor.getBackgroundColor(requireContext(), null)
image?.setTint(currentColor) image?.setTint(currentColor)
serverColorImageView?.background = image serverColorImageView?.background = image
} }
@ -221,7 +221,7 @@ class EditServerFragment : Fragment(), OnBackPressedHandler {
::selfSignedSwitch.name, selfSignedSwitch!!.isChecked ::selfSignedSwitch.name, selfSignedSwitch!!.isChecked
) )
savedInstanceState.putBoolean( savedInstanceState.putBoolean(
::ldapSwitch.name, ldapSwitch!!.isChecked ::plaintextSwitch.name, plaintextSwitch!!.isChecked
) )
savedInstanceState.putBoolean( savedInstanceState.putBoolean(
::jukeboxSwitch.name, jukeboxSwitch!!.isChecked ::jukeboxSwitch.name, jukeboxSwitch!!.isChecked
@ -258,7 +258,7 @@ class EditServerFragment : Fragment(), OnBackPressedHandler {
savedInstanceState.getString(::passwordEditText.name) savedInstanceState.getString(::passwordEditText.name)
) )
selfSignedSwitch!!.isChecked = savedInstanceState.getBoolean(::selfSignedSwitch.name) selfSignedSwitch!!.isChecked = savedInstanceState.getBoolean(::selfSignedSwitch.name)
ldapSwitch!!.isChecked = savedInstanceState.getBoolean(::ldapSwitch.name) plaintextSwitch!!.isChecked = savedInstanceState.getBoolean(::plaintextSwitch.name)
jukeboxSwitch!!.isChecked = savedInstanceState.getBoolean(::jukeboxSwitch.name) jukeboxSwitch!!.isChecked = savedInstanceState.getBoolean(::jukeboxSwitch.name)
updateColor(savedInstanceState.getInt(::serverColorImageView.name)) updateColor(savedInstanceState.getInt(::serverColorImageView.name))
if (savedInstanceState.containsKey(::selectedColor.name)) if (savedInstanceState.containsKey(::selectedColor.name))
@ -277,7 +277,7 @@ class EditServerFragment : Fragment(), OnBackPressedHandler {
userNameEditText!!.editText?.setText(currentServerSetting!!.userName) userNameEditText!!.editText?.setText(currentServerSetting!!.userName)
passwordEditText!!.editText?.setText(currentServerSetting!!.password) passwordEditText!!.editText?.setText(currentServerSetting!!.password)
selfSignedSwitch!!.isChecked = currentServerSetting!!.allowSelfSignedCertificate selfSignedSwitch!!.isChecked = currentServerSetting!!.allowSelfSignedCertificate
ldapSwitch!!.isChecked = currentServerSetting!!.forcePlainTextPassword plaintextSwitch!!.isChecked = currentServerSetting!!.forcePlainTextPassword
jukeboxSwitch!!.isChecked = currentServerSetting!!.jukeboxByDefault jukeboxSwitch!!.isChecked = currentServerSetting!!.jukeboxByDefault
updateColor(currentServerSetting!!.color) updateColor(currentServerSetting!!.color)
} }
@ -331,7 +331,7 @@ class EditServerFragment : Fragment(), OnBackPressedHandler {
currentServerSetting!!.userName = userNameEditText!!.editText?.text.toString() currentServerSetting!!.userName = userNameEditText!!.editText?.text.toString()
currentServerSetting!!.password = passwordEditText!!.editText?.text.toString() currentServerSetting!!.password = passwordEditText!!.editText?.text.toString()
currentServerSetting!!.allowSelfSignedCertificate = selfSignedSwitch!!.isChecked currentServerSetting!!.allowSelfSignedCertificate = selfSignedSwitch!!.isChecked
currentServerSetting!!.forcePlainTextPassword = ldapSwitch!!.isChecked currentServerSetting!!.forcePlainTextPassword = plaintextSwitch!!.isChecked
currentServerSetting!!.jukeboxByDefault = jukeboxSwitch!!.isChecked currentServerSetting!!.jukeboxByDefault = jukeboxSwitch!!.isChecked
} }
@ -354,7 +354,7 @@ class EditServerFragment : Fragment(), OnBackPressedHandler {
currentServerSetting!!.userName != userNameEditText!!.editText?.text.toString() || currentServerSetting!!.userName != userNameEditText!!.editText?.text.toString() ||
currentServerSetting!!.password != passwordEditText!!.editText?.text.toString() || currentServerSetting!!.password != passwordEditText!!.editText?.text.toString() ||
currentServerSetting!!.allowSelfSignedCertificate != selfSignedSwitch!!.isChecked || currentServerSetting!!.allowSelfSignedCertificate != selfSignedSwitch!!.isChecked ||
currentServerSetting!!.forcePlainTextPassword != ldapSwitch!!.isChecked || currentServerSetting!!.forcePlainTextPassword != plaintextSwitch!!.isChecked ||
currentServerSetting!!.jukeboxByDefault != jukeboxSwitch!!.isChecked currentServerSetting!!.jukeboxByDefault != jukeboxSwitch!!.isChecked
} }

View File

@ -86,9 +86,9 @@ class NowPlayingFragment : Fragment() {
private fun update() { private fun update() {
try { try {
if (mediaPlayerController.isPlaying) { if (mediaPlayerController.isPlaying) {
playButton!!.setIconResource(R.drawable.media_pause_normal) playButton!!.setIconResource(R.drawable.media_pause)
} else { } else {
playButton!!.setIconResource(R.drawable.media_start_normal) playButton!!.setIconResource(R.drawable.media_start)
} }
val file = mediaPlayerController.currentMediaItem?.toTrack() val file = mediaPlayerController.currentMediaItem?.toTrack()

View File

@ -838,13 +838,11 @@ class PlayerFragment :
viewAdapter.register( viewAdapter.register(
TrackViewBinder( TrackViewBinder(
onItemClick = clickHandler, onItemClick = clickHandler,
onContextMenuClick = { menu, id -> onContextMenuItemSelected(menu, id) },
checkable = false, checkable = false,
draggable = true, draggable = true,
context = requireContext(),
lifecycleOwner = viewLifecycleOwner, lifecycleOwner = viewLifecycleOwner,
createContextMenu = { view, track -> onCreateContextMenu(view, track) }, ) { view, track -> onCreateContextMenu(view, track) }.apply {
onContextMenuClick = { menu, id -> onContextMenuItemSelected(menu, id) },
).apply {
this.startDrag = { holder -> this.startDrag = { holder ->
dragTouchHelper.startDrag(holder) dragTouchHelper.startDrag(holder)
} }

View File

@ -120,7 +120,6 @@ class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
onContextMenuClick = ::onContextMenuItemSelected, onContextMenuClick = ::onContextMenuItemSelected,
checkable = false, checkable = false,
draggable = false, draggable = false,
context = requireContext(),
lifecycleOwner = viewLifecycleOwner lifecycleOwner = viewLifecycleOwner
) )
) )

View File

@ -63,7 +63,6 @@ class ServerSelectorFragment : Fragment() {
listView?.adapter = serverRowAdapter listView?.adapter = serverRowAdapter
listView?.onItemClickListener = AdapterView.OnItemClickListener { parent, _, position, _ -> listView?.onItemClickListener = AdapterView.OnItemClickListener { parent, _, position, _ ->
val server = parent.getItemAtPosition(position) as ServerSetting val server = parent.getItemAtPosition(position) as ServerSetting
ActiveServerProvider.setActiveServerById(server.id) ActiveServerProvider.setActiveServerById(server.id)
findNavController().popBackStack(R.id.mainFragment, false) findNavController().popBackStack(R.id.mainFragment, false)

View File

@ -133,7 +133,6 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
onContextMenuClick = { menu, id -> onContextMenuItemSelected(menu, id) }, onContextMenuClick = { menu, id -> onContextMenuItemSelected(menu, id) },
checkable = true, checkable = true,
draggable = false, draggable = false,
context = requireContext(),
lifecycleOwner = viewLifecycleOwner lifecycleOwner = viewLifecycleOwner
) )
) )

View File

@ -16,7 +16,6 @@ import java.io.InputStream
import java.io.OutputStream import java.io.OutputStream
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.CountDownLatch import java.util.concurrent.CountDownLatch
import org.moire.ultrasonic.BuildConfig
import org.moire.ultrasonic.R import org.moire.ultrasonic.R
import org.moire.ultrasonic.api.subsonic.SubsonicAPIClient import org.moire.ultrasonic.api.subsonic.SubsonicAPIClient
import org.moire.ultrasonic.api.subsonic.throwOnFailure import org.moire.ultrasonic.api.subsonic.throwOnFailure
@ -46,7 +45,7 @@ class ImageLoader(
.addRequestHandler(AvatarRequestHandler(apiClient)) .addRequestHandler(AvatarRequestHandler(apiClient))
.memoryCache(LruCache(calculateMemoryCacheSize(context))) .memoryCache(LruCache(calculateMemoryCacheSize(context)))
.build().apply { .build().apply {
setIndicatorsEnabled(BuildConfig.DEBUG) // setIndicatorsEnabled(BuildConfig.DEBUG)
} }
private fun load(request: ImageRequest) = when (request) { private fun load(request: ImageRequest) = when (request) {

View File

@ -1203,7 +1203,7 @@ class AutoMediaBrowserCallback(var player: Player, val libraryService: MediaLibr
mediaId, mediaId,
null, null,
false, false,
icon = R.drawable.media_start_normal icon = R.drawable.media_start
) )
} }

View File

@ -30,6 +30,8 @@ import org.moire.ultrasonic.activity.NavigationActivity
import org.moire.ultrasonic.app.UApp import org.moire.ultrasonic.app.UApp
import org.moire.ultrasonic.audiofx.EqualizerController import org.moire.ultrasonic.audiofx.EqualizerController
import org.moire.ultrasonic.data.ActiveServerProvider import org.moire.ultrasonic.data.ActiveServerProvider
import org.moire.ultrasonic.domain.Track
import org.moire.ultrasonic.provider.UltrasonicAppWidgetProvider
import org.moire.ultrasonic.service.DownloadService import org.moire.ultrasonic.service.DownloadService
import org.moire.ultrasonic.service.MusicServiceFactory.getMusicService import org.moire.ultrasonic.service.MusicServiceFactory.getMusicService
import org.moire.ultrasonic.service.RxBus import org.moire.ultrasonic.service.RxBus
@ -172,6 +174,12 @@ class PlaybackService : MediaLibraryService(), KoinComponent {
} }
override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) { override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
updateWidgetTrack(mediaItem?.toTrack())
cacheNextSongs()
}
override fun onIsPlayingChanged(isPlaying: Boolean) {
updateWidgetPlayerState(isPlaying)
cacheNextSongs() cacheNextSongs()
} }
} }
@ -205,4 +213,14 @@ class PlaybackService : MediaLibraryService(), KoinComponent {
.setContentType(C.AUDIO_CONTENT_TYPE_MUSIC) .setContentType(C.AUDIO_CONTENT_TYPE_MUSIC)
.build() .build()
} }
private fun updateWidgetTrack(song: Track?) {
val context = UApp.applicationContext()
UltrasonicAppWidgetProvider.notifyTrackChange(context, song)
}
private fun updateWidgetPlayerState(isPlaying: Boolean) {
val context = UApp.applicationContext()
UltrasonicAppWidgetProvider.notifyPlayerStateChange(context, isPlaying)
}
} }

View File

@ -14,10 +14,12 @@ import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Build import android.os.Build
import android.os.Bundle
import android.os.Environment import android.os.Environment
import android.view.KeyEvent import android.view.KeyEvent
import android.view.View.GONE
import android.view.View.VISIBLE
import android.widget.RemoteViews import android.widget.RemoteViews
import java.lang.Exception
import org.moire.ultrasonic.R import org.moire.ultrasonic.R
import org.moire.ultrasonic.activity.NavigationActivity import org.moire.ultrasonic.activity.NavigationActivity
import org.moire.ultrasonic.domain.Track import org.moire.ultrasonic.domain.Track
@ -27,141 +29,197 @@ import org.moire.ultrasonic.util.Constants
import timber.log.Timber import timber.log.Timber
/** /**
* Widget Provider for the Ultrasonic Widgets * Widget Provider for the Ultrasonic Widget
*/ */
@Suppress("MagicNumber") @Suppress("MagicNumber")
open class UltrasonicAppWidgetProvider : AppWidgetProvider() { open class UltrasonicAppWidgetProvider : AppWidgetProvider() {
@JvmField
protected var layoutId = 0
override fun onUpdate( override fun onUpdate(
context: Context, context: Context,
appWidgetManager: AppWidgetManager, appWidgetManager: AppWidgetManager,
appWidgetIds: IntArray appWidgetIds: IntArray
) { ) {
defaultAppWidget(context, appWidgetIds) updateTrackAndState(context, appWidgetIds)
} }
/** override fun onAppWidgetOptionsChanged(
* Initialize given widgets to default state, where we launch Ultrasonic on default click context: Context?,
* and hide actions if service not running. appWidgetManager: AppWidgetManager?,
*/ appWidgetId: Int,
private fun defaultAppWidget(context: Context, appWidgetIds: IntArray) { newOptions: Bundle?
val res = context.resources
val views = RemoteViews(context.packageName, layoutId)
views.setTextViewText(R.id.title, null)
views.setTextViewText(R.id.album, null)
views.setTextViewText(R.id.artist, res.getText(R.string.widget_initial_text))
linkButtons(context, views, false)
pushUpdate(context, appWidgetIds, views)
}
private fun pushUpdate(context: Context, appWidgetIds: IntArray?, views: RemoteViews) {
// Update specific list of appWidgetIds if given, otherwise default to all
val manager = AppWidgetManager.getInstance(context)
if (manager != null) {
if (appWidgetIds != null) {
manager.updateAppWidget(appWidgetIds, views)
} else {
manager.updateAppWidget(ComponentName(context, this.javaClass), views)
}
}
}
/**
* Handle a change notification coming over from [MediaPlayerController]
*/
fun notifyChange(context: Context, currentSong: Track?, playing: Boolean, setAlbum: Boolean) {
if (hasInstances(context)) {
performUpdate(context, currentSong, playing, setAlbum)
}
}
/**
* Check against [AppWidgetManager] if there are any instances of this widget.
*/
private fun hasInstances(context: Context): Boolean {
val manager = AppWidgetManager.getInstance(context)
if (manager != null) {
val appWidgetIds = manager.getAppWidgetIds(ComponentName(context, javaClass))
return appWidgetIds.isNotEmpty()
}
return false
}
/**
* Update all active widget instances by pushing changes
*/
private fun performUpdate(
context: Context,
currentSong: Track?,
playing: Boolean,
setAlbum: Boolean
) { ) {
Timber.d("Updating Widget") updateTrackAndState(context!!, intArrayOf(appWidgetId))
val res = context.resources
val views = RemoteViews(context.packageName, layoutId)
val title = currentSong?.title
val artist = currentSong?.artist
val album = currentSong?.album
var errorState: CharSequence? = null
// Show error message?
val status = Environment.getExternalStorageState()
if (status == Environment.MEDIA_SHARED || status == Environment.MEDIA_UNMOUNTED) {
errorState = res.getText(R.string.widget_sdcard_busy)
} else if (status == Environment.MEDIA_REMOVED) {
errorState = res.getText(R.string.widget_sdcard_missing)
} else if (currentSong == null) {
errorState = res.getText(R.string.widget_initial_text)
}
if (errorState != null) {
// Show error state to user
views.setTextViewText(R.id.title, null)
views.setTextViewText(R.id.artist, errorState)
if (setAlbum) {
views.setTextViewText(R.id.album, null)
}
views.setImageViewResource(R.id.appwidget_coverart, R.drawable.unknown_album)
} else {
// No error, so show normal titles
views.setTextViewText(R.id.title, title)
views.setTextViewText(R.id.artist, artist)
if (setAlbum) {
views.setTextViewText(R.id.album, album)
}
}
// Set correct drawable for pause state
if (playing) {
views.setImageViewResource(R.id.control_play, R.drawable.media_pause_normal)
} else {
views.setImageViewResource(R.id.control_play, R.drawable.media_start_normal)
}
// Set the cover art
try {
val bitmap =
if (currentSong == null) null else BitmapUtils.getAlbumArtBitmapFromDisk(
currentSong,
240
)
if (bitmap == null) {
// Set default cover art
views.setImageViewResource(R.id.appwidget_coverart, R.drawable.unknown_album)
} else {
views.setImageViewBitmap(R.id.appwidget_coverart, bitmap)
}
} catch (all: Exception) {
Timber.e(all, "Failed to load cover art")
views.setImageViewResource(R.id.appwidget_coverart, R.drawable.unknown_album)
}
// Link actions buttons to intents
linkButtons(context, views, currentSong != null)
pushUpdate(context, null, views)
} }
companion object { companion object {
private var isPlaying: Boolean = false
private var track: Track? = null
/**
* Pushes the current track details to the widgets
*/
fun notifyTrackChange(context: Context, currentSong: Track?) {
this.track = currentSong
if (hasInstances(context)) {
// The widget won't update correctly if only the track or the state is updated
updateTrackAndState(context, null)
}
}
/**
* Pushes the current player state to the widgets
*/
fun notifyPlayerStateChange(context: Context, isPlaying: Boolean) {
this.isPlaying = isPlaying
if (hasInstances(context)) {
// The widget won't update correctly if only the track or the state is updated
updateTrackAndState(context, null)
}
}
/**
* Send the track and the player state to the widgets
*/
private fun updateTrackAndState(context: Context, appWidgetIds: IntArray? = null) {
pushUpdate(context, appWidgetIds) {
updateTrack(context, it, track)
updatePlayerState(it, isPlaying)
}
}
/**
* Iterates through the instances of the widget, and pushes the update to them
*/
private fun pushUpdate(
context: Context,
appWidgetIds: IntArray? = null,
update: (RemoteViews) -> Unit
) {
val manager = AppWidgetManager.getInstance(context)
if (manager != null) {
val widgetIds =
appWidgetIds ?: manager.getAppWidgetIds(
ComponentName(
context,
UltrasonicAppWidgetProvider::class.java
)
)
widgetIds.forEach {
val widgetOptions = manager.getAppWidgetOptions(it)
val minHeight =
widgetOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)
val minWidth =
widgetOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)
val layoutId = getLayout(minHeight, minWidth)
val views = RemoteViews(context.packageName, layoutId)
update(views)
manager.updateAppWidget(it, views)
}
}
}
/**
* Computes the layout to be displayed for the widget height
*/
private fun getLayout(height: Int, width: Int): Int {
val portrait = (width / height.toFloat()) < 1.8F
if (portrait) return R.layout.appwidget_portrait
if (height > 100) return R.layout.appwidget_landscape
return R.layout.appwidget_landscape_small
}
/**
* Check against [AppWidgetManager] if there are any instances of this widget.
*/
private fun hasInstances(context: Context): Boolean {
val manager = AppWidgetManager.getInstance(context)
if (manager != null) {
val appWidgetIds = manager.getAppWidgetIds(
ComponentName(
context,
UltrasonicAppWidgetProvider::class.java
)
)
return appWidgetIds.isNotEmpty()
}
return false
}
/**
* Update Player state in widgets
*/
private fun updatePlayerState(views: RemoteViews, isPlaying: Boolean) {
if (isPlaying) {
views.setImageViewResource(R.id.control_play, R.drawable.media_pause)
} else {
views.setImageViewResource(R.id.control_play, R.drawable.media_start)
}
}
/**
* Update Track details in widgets
*/
private fun updateTrack(
context: Context,
views: RemoteViews,
currentSong: Track?
) {
Timber.d("Updating Widget")
val res = context.resources
val title = currentSong?.title
val artist = currentSong?.artist
val album = currentSong?.album
var errorState: CharSequence? = null
// Show error message?
val status = Environment.getExternalStorageState()
if (status == Environment.MEDIA_SHARED || status == Environment.MEDIA_UNMOUNTED) {
errorState = res.getText(R.string.widget_sdcard_busy)
} else if (status == Environment.MEDIA_REMOVED) {
errorState = res.getText(R.string.widget_sdcard_missing)
} else if (currentSong == null) {
errorState = res.getText(R.string.widget_initial_text)
}
if (errorState != null) {
// Show error state to user
views.setViewVisibility(R.id.title, GONE)
views.setViewVisibility(R.id.album, GONE)
views.setTextViewText(R.id.artist, res.getText(R.string.widget_initial_text))
views.setBoolean(R.id.artist, "setSingleLine", false)
views.setImageViewResource(R.id.appwidget_coverart, R.drawable.unknown_album)
} else {
// No error, so show normal titles
views.setTextViewText(R.id.title, title)
views.setTextViewText(R.id.artist, artist)
views.setTextViewText(R.id.album, album)
views.setBoolean(R.id.artist, "setSingleLine", true)
views.setViewVisibility(R.id.title, VISIBLE)
views.setViewVisibility(R.id.album, VISIBLE)
}
// Set the cover art
try {
val bitmap =
if (currentSong == null) null else BitmapUtils.getAlbumArtBitmapFromDisk(
currentSong,
240
)
if (bitmap == null) {
// Set default cover art
views.setImageViewResource(R.id.appwidget_coverart, R.drawable.unknown_album)
} else {
views.setImageViewBitmap(R.id.appwidget_coverart, bitmap)
}
} catch (all: Exception) {
Timber.e(all, "Failed to load cover art")
views.setImageViewResource(R.id.appwidget_coverart, R.drawable.unknown_album)
}
// Link actions buttons to intents
linkButtons(context, views, currentSong != null)
}
/** /**
* Link up various button actions using [PendingIntent]. * Link up various button actions using [PendingIntent].
*/ */

View File

@ -1,28 +0,0 @@
/*
* UltrasonicAppWidgetProvider4X1.kt
* Copyright (C) 2009-2022 Ultrasonic developers
*
* Distributed under terms of the GNU GPLv3 license.
*/
package org.moire.ultrasonic.provider
import org.moire.ultrasonic.R
class UltrasonicAppWidgetProvider4X1 : UltrasonicAppWidgetProvider() {
companion object {
@get:Synchronized
var instance: UltrasonicAppWidgetProvider4X1? = null
get() {
if (field == null) {
field = UltrasonicAppWidgetProvider4X1()
}
return field
}
private set
}
init {
layoutId = R.layout.appwidget4x1
}
}

View File

@ -1,28 +0,0 @@
/*
* UltrasonicAppWidgetProvider4X2.kt
* Copyright (C) 2009-2022 Ultrasonic developers
*
* Distributed under terms of the GNU GPLv3 license.
*/
package org.moire.ultrasonic.provider
import org.moire.ultrasonic.R
class UltrasonicAppWidgetProvider4X2 : UltrasonicAppWidgetProvider() {
companion object {
@get:Synchronized
var instance: UltrasonicAppWidgetProvider4X2? = null
get() {
if (field == null) {
field = UltrasonicAppWidgetProvider4X2()
}
return field
}
private set
}
init {
layoutId = R.layout.appwidget4x2
}
}

View File

@ -1,28 +0,0 @@
/*
* UltrasonicAppWidgetProvider4X3.kt
* Copyright (C) 2009-2022 Ultrasonic developers
*
* Distributed under terms of the GNU GPLv3 license.
*/
package org.moire.ultrasonic.provider
import org.moire.ultrasonic.R
class UltrasonicAppWidgetProvider4X3 : UltrasonicAppWidgetProvider() {
companion object {
@get:Synchronized
var instance: UltrasonicAppWidgetProvider4X3? = null
get() {
if (field == null) {
field = UltrasonicAppWidgetProvider4X3()
}
return field
}
private set
}
init {
layoutId = R.layout.appwidget4x3
}
}

View File

@ -1,28 +0,0 @@
/*
* UltrasonicAppWidgetProvider4X4.kt
* Copyright (C) 2009-2022 Ultrasonic developers
*
* Distributed under terms of the GNU GPLv3 license.
*/
package org.moire.ultrasonic.provider
import org.moire.ultrasonic.R
class UltrasonicAppWidgetProvider4X4 : UltrasonicAppWidgetProvider() {
companion object {
@get:Synchronized
var instance: UltrasonicAppWidgetProvider4X4? = null
get() {
if (field == null) {
field = UltrasonicAppWidgetProvider4X4()
}
return field
}
private set
}
init {
layoutId = R.layout.appwidget4x4
}
}

View File

@ -36,10 +36,6 @@ import org.moire.ultrasonic.data.ActiveServerProvider
import org.moire.ultrasonic.data.ActiveServerProvider.Companion.OFFLINE_DB_ID import org.moire.ultrasonic.data.ActiveServerProvider.Companion.OFFLINE_DB_ID
import org.moire.ultrasonic.domain.Track import org.moire.ultrasonic.domain.Track
import org.moire.ultrasonic.playback.PlaybackService import org.moire.ultrasonic.playback.PlaybackService
import org.moire.ultrasonic.provider.UltrasonicAppWidgetProvider4X1
import org.moire.ultrasonic.provider.UltrasonicAppWidgetProvider4X2
import org.moire.ultrasonic.provider.UltrasonicAppWidgetProvider4X3
import org.moire.ultrasonic.provider.UltrasonicAppWidgetProvider4X4
import org.moire.ultrasonic.service.MusicServiceFactory.getMusicService import org.moire.ultrasonic.service.MusicServiceFactory.getMusicService
import org.moire.ultrasonic.util.MainThreadExecutor import org.moire.ultrasonic.util.MainThreadExecutor
import org.moire.ultrasonic.util.Settings import org.moire.ultrasonic.util.Settings
@ -201,11 +197,6 @@ class MediaPlayerController(
} }
} }
rxBusSubscription += RxBus.stopServiceCommandObservable.subscribe {
// Clear the widget when we stop the service
updateWidget(null)
}
rxBusSubscription += RxBus.shutdownCommandObservable.subscribe { rxBusSubscription += RxBus.shutdownCommandObservable.subscribe {
playbackStateSerializer.serializeNow( playbackStateSerializer.serializeNow(
playlist.map { it.toTrack() }, playlist.map { it.toTrack() },
@ -236,9 +227,6 @@ class MediaPlayerController(
scrobbler.scrobble(currentPlaying, true) scrobbler.scrobble(currentPlaying, true)
} }
} }
// Update widget
updateWidget(currentPlaying)
} }
private fun clearBookmark() { private fun clearBookmark() {
@ -267,15 +255,6 @@ class MediaPlayerController(
Timber.i("New PlaybackState: %s", newState) Timber.i("New PlaybackState: %s", newState)
} }
private fun updateWidget(song: Track?) {
val context = UApp.applicationContext()
UltrasonicAppWidgetProvider4X1.instance?.notifyChange(context, song, isPlaying, false)
UltrasonicAppWidgetProvider4X2.instance?.notifyChange(context, song, isPlaying, true)
UltrasonicAppWidgetProvider4X3.instance?.notifyChange(context, song, isPlaying, true)
UltrasonicAppWidgetProvider4X4.instance?.notifyChange(context, song, isPlaying, true)
}
fun onDestroy() { fun onDestroy() {
if (!created) return if (!created) return

View File

@ -8,29 +8,45 @@
package org.moire.ultrasonic.util package org.moire.ultrasonic.util
import android.content.Context import android.content.Context
import androidx.annotation.ColorInt
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.graphics.ColorUtils import androidx.core.graphics.ColorUtils
import org.moire.ultrasonic.R import com.google.android.material.color.MaterialColors
private const val LUMINANCE_LIMIT = 0.5 private const val LUMINANCE_LIMIT = 0.5
private const val LUMINANCE_CORRECTION = -0.25
/** /**
* Contains functions for computing server display colors * Contains functions for computing server display colors
*/ */
object ServerColor { object ServerColor {
@ColorInt
fun getBackgroundColor(context: Context, serverColor: Int?): Int { fun getBackgroundColor(context: Context, serverColor: Int?): Int {
return serverColor ?: ContextCompat.getColor( return if (serverColor != null) {
context, Util.getResourceFromAttribute(context, R.attr.colorPrimary) MaterialColors.harmonizeWithPrimary(context, serverColor)
) } else {
MaterialColors.getColor(context, android.R.attr.colorPrimary, "")
}
} }
fun getForegroundColor(context: Context, serverColor: Int?): Int { @ColorInt
fun getForegroundColor(
context: Context,
serverColor: Int?,
showVectorBackground: Boolean = false
): Int {
val backgroundColor = getBackgroundColor(context, serverColor) val backgroundColor = getBackgroundColor(context, serverColor)
val luminance = ColorUtils.calculateLuminance(backgroundColor) var luminance = ColorUtils.calculateLuminance(backgroundColor)
// The actual luminance is a good bit lower
// when the background color is being overlayed by the vector
if (showVectorBackground) luminance += LUMINANCE_CORRECTION
return if (luminance < LUMINANCE_LIMIT) { return if (luminance < LUMINANCE_LIMIT) {
ContextCompat.getColor(context, R.color.selected_menu_dark) ContextCompat.getColor(context, org.moire.ultrasonic.R.color.selected_menu_dark)
} else { } else {
ContextCompat.getColor(context, R.color.selected_menu_light) ContextCompat.getColor(context, org.moire.ultrasonic.R.color.selected_menu_light)
} }
} }
} }

View File

@ -22,7 +22,7 @@ object Settings {
@JvmStatic @JvmStatic
var theme by StringSetting( var theme by StringSetting(
getKey(R.string.setting_key_theme), getKey(R.string.setting_key_theme),
getKey(R.string.setting_key_theme_dark) getKey(R.string.setting_key_theme_day_night)
) )
@JvmStatic @JvmStatic

View File

@ -31,7 +31,6 @@ import android.os.Build
import android.os.Environment import android.os.Environment
import android.text.TextUtils import android.text.TextUtils
import android.util.DisplayMetrics import android.util.DisplayMetrics
import android.util.TypedValue
import android.view.Gravity import android.view.Gravity
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.widget.Toast import android.widget.Toast
@ -94,21 +93,13 @@ object Util {
fun applyTheme(context: Context?) { fun applyTheme(context: Context?) {
if (context == null) return if (context == null) return
val style = getStyleFromSettings(context) val style = getStyleFromSettings(context)
// First set the theme (light, dark, etc.)
context.setTheme(style) context.setTheme(style)
// Then set an overlay controlling the status bar behaviour etc.
context.setTheme(R.style.UltrasonicTheme_Base)
} }
private fun getStyleFromSettings(context: Context): Int { private fun getStyleFromSettings(context: Context): Int {
// Migration
// TODO: Remove in June 2023
when (Settings.theme.lowercase()) {
"fullscreen" -> {
Settings.theme = context.getString(R.string.setting_key_theme_dark)
}
"fullscreenlight" -> {
Settings.theme = context.getString(R.string.setting_key_theme_light)
}
}
return when (Settings.theme.lowercase()) { return when (Settings.theme.lowercase()) {
context.getString(R.string.setting_key_theme_dark) -> { context.getString(R.string.setting_key_theme_dark) -> {
R.style.UltrasonicTheme_Dark R.style.UltrasonicTheme_Dark
@ -120,7 +111,7 @@ object Util {
R.style.UltrasonicTheme_Light R.style.UltrasonicTheme_Light
} }
else -> { else -> {
R.style.UltrasonicTheme_Dark R.style.UltrasonicTheme_DayNight
} }
} }
} }
@ -571,13 +562,6 @@ object Util {
) )
} }
fun getResourceFromAttribute(context: Context, resId: Int): Int {
val typedValue = TypedValue()
val theme = context.theme
theme.resolveAttribute(resId, typedValue, true)
return typedValue.resourceId
}
fun isFirstRun(): Boolean { fun isFirstRun(): Boolean {
if (Settings.firstRunExecuted) return false if (Settings.firstRunExecuted) return false

Binary file not shown.

Before

Width:  |  Height:  |  Size: 889 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Background for widgets to make the rounded corners based on the
appWidgetRadius attribute value
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"
android:theme="@style/Theme.AppWidget.AppWidgetContainer">
<corners android:radius="?attr/appWidgetRadius" />
<solid android:color="?attr/appWidgetBackground" />
</shape>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" <shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval"> android:shape="oval">
<solid android:color="?attr/colorOnPrimary" /> <solid android:color="#FFF" />
<size <size
android:width="120dp" android:width="120dp"
android:height="120dp"/> android:height="120dp"/>

View File

@ -5,7 +5,7 @@
<item android:id="@android:id/mask"> <item android:id="@android:id/mask">
<shape android:shape="rectangle"> <shape android:shape="rectangle">
<solid android:color="?android:colorAccent" /> <solid android:color="?attr/colorSecondary" />
</shape> </shape>
</item> </item>

View File

@ -2,8 +2,13 @@
android:width="24dp" android:width="24dp"
android:height="24dp" android:height="24dp"
android:viewportWidth="24" android:viewportWidth="24"
android:viewportHeight="24"> android:viewportHeight="24"
android:tint="?attr/colorControlHighlight">
<!-- Normally it would be unusal to set the fillColor to something else than #FFF here,
but applying the tint seems to take a short moment in the artist list, so setting it already
here once avoids a flash of the wrong color -->
<path <path
android:fillColor="#AAA" android:fillColor="?attr/colorOnPrimarySurface"
android:pathData="M3,5v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2L5,3c-1.11,0 -2,0.9 -2,2zM15,9c0,1.66 -1.34,3 -3,3s-3,-1.34 -3,-3 1.34,-3 3,-3 3,1.34 3,3zM6,17c0,-2 4,-3.1 6,-3.1s6,1.1 6,3.1v1L6,18v-1z"/> android:pathData="M3,5v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2L5,3c-1.11,0 -2,0.9 -2,2zM15,9c0,1.66 -1.34,3 -3,3s-3,-1.34 -3,-3 1.34,-3 3,-3 3,1.34 3,3zM6,17c0,-2 4,-3.1 6,-3.1s6,1.1 6,3.1v1L6,18v-1z"/>
</vector> </vector>

View File

@ -5,6 +5,6 @@
android:viewportHeight="24" android:viewportHeight="24"
android:tint="?attr/colorControlNormal"> android:tint="?attr/colorControlNormal">
<path <path
android:fillColor="#FFF" android:fillColor="#000"
android:pathData="M8,5v14l11,-7z"/> android:pathData="M8,5v14l11,-7z"/>
</vector> </vector>

View File

@ -3,10 +3,10 @@
android:shape="rectangle"> android:shape="rectangle">
<corners <corners
android:topLeftRadius="44dp" android:topLeftRadius="28dp"
android:topRightRadius="44dp" android:topRightRadius="28dp"
android:bottomRightRadius="44dp" android:bottomRightRadius="28dp"
android:bottomLeftRadius="44dp" /> android:bottomLeftRadius="28dp" />
<stroke android:width="2dp" android:color="?attr/colorOnBackground" /> <stroke android:width="1dp" android:color="?attr/colorOutline" />
</shape> </shape>

View File

@ -1,17 +1,16 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<ripple <ripple xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?android:colorControlHighlight"> android:color="?android:colorControlHighlight">
<item android:id="@android:id/mask"> <item android:id="@android:id/mask">
<shape android:shape="rectangle"> <shape android:shape="rectangle">
<solid android:color="?android:colorAccent" /> <solid android:color="?attr/colorSurfaceVariant" />
</shape> </shape>
</item> </item>
<item> <item>
<shape android:shape="rectangle"> <shape android:shape="rectangle">
<solid android:color="?attr/color_selected" /> <solid android:color="?attr/colorSurfaceVariant" />
</shape> </shape>
</item> </item>
</ripple> </ripple>

View File

@ -1,14 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="#FFF"
android:pathData="M15,5V3H9v2"/>
<path
android:fillColor="#FFF"
android:fillAlpha="150"
android:pathData="M9,5V9H5l7,7 7,-7H15V5M5,18v2h14v-2z"/>
</vector>

View File

@ -1,14 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="#FFF"
android:pathData="M15,7V3H9v4"/>
<path
android:fillColor="#FFF"
android:fillAlpha="150"
android:pathData="M9,7V9H5l7,7 7,-7H15V7M5,18v2h14v-2z"/>
</vector>

View File

@ -1,14 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="#FFF"
android:pathData="M15,9V3H9v6"/>
<path
android:fillColor="#FFF"
android:fillAlpha="150"
android:pathData="M9,9H5l7,7 7,-7H15M5,18v2h14v-2z"/>
</vector>

View File

@ -1,14 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="#FFF"
android:pathData="M17,11 L19,9H15V3H9V9H5l2,2"/>
<path
android:fillColor="#FFF"
android:fillAlpha="150"
android:pathData="m7,11 l5,5 5,-5M5,18v2h14v-2z"/>
</vector>

View File

@ -1,14 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="#FFF"
android:pathData="M15,13 L19,9H15V3H9V9H5l4,4"/>
<path
android:fillColor="#FFF"
android:fillAlpha="150"
android:pathData="m9,13 l3,3 3,-3M5,18v2h14v-2z"/>
</vector>

View File

@ -1,14 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="#FFF"
android:pathData="M13,15 L19,9H15V3H9V9H5l6,6"/>
<path
android:fillColor="#FFF"
android:fillAlpha="150"
android:pathData="m11,15 l1,1 1,-1m-8,3v2h14v-2z"/>
</vector>

View File

@ -1,14 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="#FFF"
android:pathData="M19,9H15V3H9V9H5l7,7z"/>
<path
android:fillColor="#FFF"
android:fillAlpha="150"
android:pathData="m5,18v2h14v-2z"/>
</vector>

View File

@ -0,0 +1,14 @@
<vector android:autoMirrored="true" android:height="41.69701dp"
android:viewportHeight="41.47" android:viewportWidth="158.93"
android:width="159.8dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#B5CAD7"
android:pathData="m39.46,0.01h108.88c5.85,0 10.58,4.74 10.58,10.58v20.29c0,5.85 -4.74,10.58 -10.58,10.58L39.46,41.46Z"
android:strokeColor="#00000000" android:strokeWidth="0.3"/>
<path android:fillColor="#003549"
android:pathData="M10.59,0.01L39.46,0.01v41.45L10.59,41.46c-5.85,0 -10.58,-4.74 -10.58,-10.58v-20.29c0,-5.85 4.74,-10.58 10.58,-10.58z"
android:strokeColor="#00000000" android:strokeWidth="0.3"/>
<path android:fillColor="#F2F4EE"
android:pathData="m121.38,11.09 l17.34,10.28 -17.34,10.23z"
android:strokeColor="#00000000" android:strokeLineCap="butt"
android:strokeLineJoin="miter" android:strokeWidth="0.20238"/>
</vector>

View File

@ -29,7 +29,7 @@
android:layout_weight="1" android:layout_weight="1"
android:visibility="gone" android:visibility="gone"
android:contentDescription="@string/common.play_now" android:contentDescription="@string/common.play_now"
app:icon="@drawable/media_start_normal" app:icon="@drawable/media_start"
app:iconGravity="textTop" app:iconGravity="textTop"
app:iconSize="26dp" /> app:iconSize="26dp" />
@ -120,7 +120,7 @@
android:layout_weight="1" android:layout_weight="1"
android:visibility="gone" android:visibility="gone"
android:contentDescription="@string/search.more" android:contentDescription="@string/search.more"
app:icon="@drawable/media_forward_normal" app:icon="@drawable/media_forward"
app:iconGravity="textTop" app:iconGravity="textTop"
app:iconSize="26dp" /> app:iconSize="26dp" />

View File

@ -1,105 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:a="http://schemas.android.com/apk/res/android"
a:layout_width="fill_parent"
a:layout_height="wrap_content"
a:minWidth="250dp"
a:minHeight="40dp"
a:background="@drawable/appwidget_dark_bg_trans"
a:orientation="horizontal" >
<ImageView
a:id="@+id/appwidget_coverart"
a:layout_width="80dp"
a:layout_height="80dp"
a:layout_gravity="center_vertical"
a:clickable="true"
a:focusable="true"
a:src="@drawable/unknown_album" />
<LinearLayout
a:id="@+id/linearLayout1"
a:layout_width="match_parent"
a:layout_height="match_parent"
a:orientation="vertical" >
<LinearLayout
a:id="@+id/appwidget_top"
a:layout_width="fill_parent"
a:layout_height="wrap_content"
a:clickable="true"
a:focusable="true"
a:orientation="vertical" >
<TextView
a:id="@+id/title"
a:layout_width="fill_parent"
a:layout_height="wrap_content"
a:ellipsize="none"
a:fadingEdge="horizontal"
a:fadingEdgeLength="20dip"
a:minHeight="16sp"
a:paddingStart="4dip"
a:paddingEnd="4dip"
a:paddingTop="4dip"
a:singleLine="true"
a:gravity="center_horizontal"
a:text="Title"
a:textColor="@color/appwidget_text"
a:textSize="16sp"
a:textStyle="bold" />
<TextView
a:id="@+id/artist"
a:layout_width="fill_parent"
a:layout_height="wrap_content"
a:ellipsize="none"
a:fadingEdge="horizontal"
a:fadingEdgeLength="10dip"
a:minHeight="12sp"
a:paddingBottom="4dip"
a:paddingStart="4dip"
a:paddingEnd="4dip"
a:singleLine="true"
a:gravity="center_horizontal"
a:text="Artist"
a:textColor="@color/appwidget_text"
a:textSize="12sp" />
</LinearLayout>
<LinearLayout
a:layout_width="fill_parent"
a:layout_height="fill_parent"
a:orientation="horizontal"
a:paddingBottom="4dip"
a:paddingTop="4dip" >
<ImageButton
a:id="@+id/control_previous"
a:layout_width="0dip"
a:layout_height="fill_parent"
a:layout_weight="1"
a:background="@color/appwidget_button_background"
a:scaleType="fitCenter"
a:src="@drawable/media_backward_normal" />
<ImageButton
a:id="@+id/control_play"
a:layout_width="0dip"
a:layout_height="fill_parent"
a:layout_weight="1"
a:background="@color/appwidget_button_background"
a:scaleType="fitCenter"
a:src="@drawable/media_start_normal" />
<ImageButton
a:id="@+id/control_next"
a:layout_width="0dip"
a:layout_height="fill_parent"
a:layout_weight="1"
a:background="@color/appwidget_button_background"
a:scaleType="fitCenter"
a:src="@drawable/media_forward_normal" />
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@ -1,129 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:a="http://schemas.android.com/apk/res/android"
a:layout_width="fill_parent"
a:layout_height="wrap_content"
a:minWidth="250dp"
a:minHeight="180dp"
a:background="@drawable/appwidget_dark_bg_trans"
a:orientation="vertical" >
<ImageView
a:id="@+id/appwidget_coverart"
a:layout_width="170dp"
a:layout_height="170dp"
a:layout_gravity="center_horizontal"
a:clickable="true"
a:focusable="true"
a:paddingTop="6dip"
a:paddingBottom="6dip"
a:src="@drawable/unknown_album" />
<LinearLayout
a:id="@+id/linearLayout1"
a:layout_width="match_parent"
a:layout_height="wrap_content"
a:orientation="vertical" >
<LinearLayout
a:id="@+id/appwidget_top"
a:layout_width="fill_parent"
a:layout_height="wrap_content"
a:clickable="true"
a:focusable="true"
a:orientation="vertical"
a:paddingBottom="4dip"
a:paddingTop="4dip" >
<TextView
a:id="@+id/title"
a:layout_width="fill_parent"
a:layout_height="wrap_content"
a:ellipsize="none"
a:fadingEdge="horizontal"
a:fadingEdgeLength="20dip"
a:minHeight="16sp"
a:paddingStart="5dip"
a:paddingEnd="5dip"
a:singleLine="true"
a:textColor="@color/appwidget_text"
a:textSize="16sp"
a:text="Title"
a:layout_gravity="center_horizontal"
a:gravity="center"
a:textStyle="bold" />
<TextView
a:id="@+id/artist"
a:layout_width="fill_parent"
a:layout_height="wrap_content"
a:ellipsize="none"
a:fadingEdge="horizontal"
a:fadingEdgeLength="10dip"
a:minHeight="12sp"
a:paddingBottom="2dip"
a:paddingStart="5dip"
a:singleLine="true"
a:text="Artist"
a:layout_gravity="center_horizontal"
a:gravity="center"
a:textColor="@color/appwidget_text"
a:textSize="12sp" />
<TextView
a:id="@+id/album"
a:layout_width="fill_parent"
a:layout_height="wrap_content"
a:ellipsize="none"
a:fadingEdge="horizontal"
a:fadingEdgeLength="10dip"
a:minHeight="12sp"
a:paddingBottom="2dip"
a:paddingStart="5dip"
a:singleLine="true"
a:text="Album"
a:layout_gravity="center_horizontal"
a:gravity="center"
a:textColor="@color/appwidget_text"
a:textSize="12sp" />
</LinearLayout>
<LinearLayout
a:layout_width="fill_parent"
a:layout_height="wrap_content"
a:layout_gravity="bottom"
a:gravity="bottom"
a:orientation="horizontal"
a:paddingBottom="4dip"
a:paddingTop="4dip" >
<ImageButton
a:id="@+id/control_previous"
a:layout_width="0dip"
a:layout_height="32dp"
a:layout_weight="1"
a:background="@color/appwidget_button_background"
a:scaleType="fitCenter"
a:src="@drawable/media_backward_normal" />
<ImageButton
a:id="@+id/control_play"
a:layout_width="0dip"
a:layout_height="32dp"
a:layout_weight="1"
a:background="@color/appwidget_button_background"
a:scaleType="fitCenter"
a:src="@drawable/media_start_normal" />
<ImageButton
a:id="@+id/control_next"
a:layout_width="0dip"
a:layout_height="32dp"
a:layout_weight="1"
a:background="@color/appwidget_button_background"
a:scaleType="fitCenter"
a:src="@drawable/media_forward_normal" />
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@ -1,130 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:a="http://schemas.android.com/apk/res/android"
a:layout_width="fill_parent"
a:layout_height="wrap_content"
a:minWidth="250dp"
a:minHeight="250dp"
a:orientation="vertical"
a:background="@drawable/appwidget_dark_bg_trans" >
<ImageView
a:id="@+id/appwidget_coverart"
a:layout_width="234dp"
a:layout_height="234dp"
a:layout_gravity="center_horizontal"
a:clickable="true"
a:focusable="true"
a:layout_margin="6dip"
a:paddingTop="6dip"
a:paddingBottom="6dip"
a:src="@drawable/unknown_album" />
<LinearLayout
a:id="@+id/linearLayout1"
a:layout_width="match_parent"
a:layout_height="wrap_content"
a:orientation="vertical" >
<LinearLayout
a:id="@+id/appwidget_top"
a:layout_width="fill_parent"
a:layout_height="wrap_content"
a:clickable="true"
a:focusable="true"
a:orientation="vertical"
a:paddingTop="4dip"
a:paddingBottom="4dip" >
<TextView
a:id="@+id/title"
a:layout_width="fill_parent"
a:layout_height="wrap_content"
a:ellipsize="none"
a:fadingEdge="horizontal"
a:fadingEdgeLength="20dip"
a:minHeight="16sp"
a:paddingStart="5dip"
a:paddingEnd="5dip"
a:singleLine="true"
a:textColor="@color/appwidget_text"
a:textSize="16sp"
a:text="Title"
a:layout_gravity="center_horizontal"
a:gravity="center"
a:textStyle="bold" />
<TextView
a:id="@+id/artist"
a:layout_width="fill_parent"
a:layout_height="wrap_content"
a:ellipsize="none"
a:fadingEdge="horizontal"
a:fadingEdgeLength="10dip"
a:minHeight="12sp"
a:paddingBottom="2dip"
a:paddingStart="5dip"
a:singleLine="true"
a:text="Artist"
a:layout_gravity="center_horizontal"
a:gravity="center"
a:textColor="@color/appwidget_text"
a:textSize="12sp" />
<TextView
a:id="@+id/album"
a:layout_width="fill_parent"
a:layout_height="wrap_content"
a:ellipsize="none"
a:fadingEdge="horizontal"
a:fadingEdgeLength="10dip"
a:minHeight="12sp"
a:paddingBottom="2dip"
a:paddingStart="5dip"
a:singleLine="true"
a:text="Album"
a:layout_gravity="center_horizontal"
a:gravity="center"
a:textColor="@color/appwidget_text"
a:textSize="12sp" />
</LinearLayout>
<LinearLayout
a:layout_width="fill_parent"
a:layout_height="wrap_content"
a:layout_gravity="bottom"
a:gravity="bottom"
a:orientation="horizontal"
a:paddingBottom="4dip"
a:paddingTop="4dip" >
<ImageButton
a:id="@+id/control_previous"
a:layout_width="0dip"
a:layout_height="32dp"
a:layout_weight="1"
a:background="@color/appwidget_button_background"
a:scaleType="fitCenter"
a:src="@drawable/media_backward_normal" />
<ImageButton
a:id="@+id/control_play"
a:layout_width="0dip"
a:layout_height="32dp"
a:layout_weight="1"
a:background="@color/appwidget_button_background"
a:scaleType="fitCenter"
a:src="@drawable/media_start_normal" />
<ImageButton
a:id="@+id/control_next"
a:layout_width="0dip"
a:layout_height="32dp"
a:layout_weight="1"
a:background="@color/appwidget_button_background"
a:scaleType="fitCenter"
a:src="@drawable/media_forward_normal" />
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@ -1,26 +1,35 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:a="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:a="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
style="@style/Widget.AppWidget.AppWidget.Container"
a:layout_width="fill_parent" a:layout_width="fill_parent"
a:layout_height="wrap_content" a:layout_height="fill_parent"
a:minWidth="250dp" a:minWidth="250dp"
a:minHeight="110dp" a:minHeight="110dp"
a:background="@drawable/appwidget_dark_bg_trans" a:orientation="horizontal"
a:orientation="horizontal" > a:theme="@style/Theme.AppWidget.AppWidgetContainerCropped"
tools:ignore="UseAppTint">
<ImageView <ImageView
a:id="@+id/appwidget_coverart" a:id="@+id/appwidget_coverart"
a:layout_width="110dp" a:layout_width="wrap_content"
a:layout_height="110dp" a:layout_height="match_parent"
a:layout_gravity="center_vertical" a:layout_gravity="center_vertical"
a:adjustViewBounds="true"
a:clickable="true" a:clickable="true"
a:focusable="true" a:focusable="true"
a:importantForAccessibility="no"
a:scaleType="fitXY"
a:src="@drawable/unknown_album" /> a:src="@drawable/unknown_album" />
<LinearLayout <LinearLayout
a:id="@+id/linearLayout1" a:id="@+id/linearLayout1"
a:layout_width="match_parent" a:layout_width="match_parent"
a:layout_height="match_parent" a:layout_height="match_parent"
a:orientation="vertical" > a:layout_gravity="center_vertical"
a:layout_marginHorizontal="5dp"
a:gravity="center"
a:orientation="vertical">
<LinearLayout <LinearLayout
a:id="@+id/appwidget_top" a:id="@+id/appwidget_top"
@ -28,98 +37,88 @@
a:layout_height="wrap_content" a:layout_height="wrap_content"
a:clickable="true" a:clickable="true"
a:focusable="true" a:focusable="true"
a:orientation="vertical" a:gravity="center"
a:paddingTop="4dip" a:orientation="vertical">
a:paddingBottom="4dip" >
<TextView <TextView
a:id="@+id/title" a:id="@+id/title"
a:layout_width="fill_parent" a:layout_width="fill_parent"
a:layout_height="wrap_content" a:layout_height="wrap_content"
a:ellipsize="none" a:ellipsize="none"
a:fadingEdge="horizontal" a:gravity="center_horizontal"
a:fadingEdgeLength="20dip"
a:minHeight="16sp" a:minHeight="16sp"
a:paddingStart="4dip" a:paddingStart="4dip"
a:paddingEnd="4dip"
a:paddingTop="4dip" a:paddingTop="4dip"
a:singleLine="true" a:paddingEnd="4dip"
a:gravity="center_horizontal" a:textAppearance="@style/TextAppearance.Material3.TitleMedium"
a:text="Title" tools:text="Title" />
a:textColor="@color/appwidget_text"
a:textSize="16sp"
a:textStyle="bold" />
<TextView <TextView
a:id="@+id/artist" a:id="@+id/artist"
a:layout_width="fill_parent" a:layout_width="fill_parent"
a:layout_height="wrap_content" a:layout_height="wrap_content"
a:ellipsize="none" a:ellipsize="none"
a:fadingEdge="horizontal" a:gravity="center_horizontal"
a:fadingEdgeLength="10dip"
a:minHeight="12sp" a:minHeight="12sp"
a:paddingStart="4dip" a:paddingStart="4dip"
a:paddingEnd="4dip" a:paddingEnd="4dip"
a:singleLine="true" a:singleLine="true"
a:gravity="center_horizontal" a:textAppearance="@style/TextAppearance.Material3.BodyLarge"
a:text="Artist" tools:text="Artist" />
a:textColor="@color/appwidget_text"
a:textSize="12sp" /> <TextView
<TextView
a:id="@+id/album" a:id="@+id/album"
a:layout_width="fill_parent" a:layout_width="fill_parent"
a:layout_height="wrap_content" a:layout_height="wrap_content"
a:ellipsize="none" a:ellipsize="none"
a:fadingEdge="horizontal" a:gravity="center_horizontal"
a:fadingEdgeLength="10dip"
a:minHeight="12sp" a:minHeight="12sp"
a:paddingBottom="4dip"
a:paddingStart="4dip" a:paddingStart="4dip"
a:paddingEnd="4dip" a:paddingEnd="4dip"
a:singleLine="true" a:singleLine="true"
a:gravity="center_horizontal" a:textAppearance="@style/TextAppearance.Material3.BodyLarge"
a:text="Album" tools:text="Album" />
a:textColor="@color/appwidget_text"
a:textSize="12sp" />
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout
a:layout_width="fill_parent" a:layout_width="fill_parent"
a:layout_height="wrap_content" a:layout_height="wrap_content"
a:orientation="horizontal"
a:layout_gravity="bottom"
a:gravity="bottom" a:gravity="bottom"
a:paddingBottom="4dip" a:orientation="horizontal"
a:paddingTop="4dip" > a:paddingTop="4dip"
a:paddingBottom="4dip">
<ImageButton <ImageButton
a:id="@+id/control_previous" a:id="@+id/control_previous"
a:layout_width="0dip" a:layout_width="0dp"
a:layout_height="32dp" a:layout_height="40dp"
a:layout_weight="1" a:layout_gravity="center"
a:background="@color/appwidget_button_background" a:layout_weight="0.6"
a:scaleType="fitCenter" a:importantForAccessibility="no"
a:src="@drawable/media_backward_normal" /> a:src="@drawable/media_backward"
/>
<ImageButton <ImageButton
a:id="@+id/control_play" a:id="@+id/control_play"
a:layout_width="0dip" a:theme="@style/Theme.AppWidget.ImageButton"
a:layout_height="32dp" a:layout_width="0dp"
a:layout_height="70dp"
a:layout_gravity="center"
a:layout_weight="1" a:layout_weight="1"
a:background="@color/appwidget_button_background" a:importantForAccessibility="no"
a:scaleType="fitCenter" a:src="@drawable/media_start"
a:src="@drawable/media_start_normal" /> />
<ImageButton <ImageButton
a:id="@+id/control_next" a:id="@+id/control_next"
a:layout_width="0dip" a:layout_width="0dp"
a:layout_height="32dp" a:layout_height="40dp"
a:layout_weight="1" a:layout_gravity="center"
a:background="@color/appwidget_button_background" a:layout_weight="0.6"
a:scaleType="fitCenter" a:importantForAccessibility="no"
a:src="@drawable/media_forward_normal" /> a:src="@drawable/media_forward"
/>
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>

View File

@ -0,0 +1,112 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:a="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
style="@style/Widget.AppWidget.AppWidget.Container"
a:layout_width="fill_parent"
a:layout_height="wrap_content"
a:minWidth="220dp"
a:minHeight="80dp"
a:orientation="horizontal"
a:theme="@style/Theme.AppWidget.AppWidgetContainerCropped"
tools:ignore="UseAppTint,NestedWeights"
>
<ImageView
a:id="@+id/appwidget_coverart"
a:layout_width="wrap_content"
a:layout_height="match_parent"
a:layout_gravity="center_vertical"
a:layout_margin="0dp"
a:adjustViewBounds="true"
a:clickable="true"
a:focusable="true"
a:importantForAccessibility="no"
a:maxWidth="150dp"
a:scaleType="fitCenter"
a:src="@drawable/unknown_album" />
<LinearLayout
a:id="@+id/linearLayout1"
a:layout_width="match_parent"
a:layout_height="match_parent"
a:layout_marginHorizontal="5dp"
a:orientation="horizontal"
a:padding="12dp"
a:baselineAligned="false">
<LinearLayout
a:id="@+id/appwidget_top"
a:layout_width="0dp"
a:layout_height="fill_parent"
a:layout_weight="1"
a:clickable="true"
a:focusable="true"
a:gravity="center_vertical"
a:orientation="vertical">
<TextView
a:id="@+id/title"
a:layout_width="fill_parent"
a:layout_height="wrap_content"
a:layout_weight="1"
a:ellipsize="marquee"
a:gravity="bottom"
a:textAppearance="@style/TextAppearance.Material3.TitleMedium"
tools:text="Title"
/>
<TextView
a:id="@+id/artist"
a:layout_width="fill_parent"
a:layout_height="wrap_content"
a:layout_weight="1"
a:ellipsize="marquee"
a:gravity="left"
a:singleLine="true"
a:textAppearance="@style/TextAppearance.Material3.BodyLarge"
tools:text="Artist" />
</LinearLayout>
<LinearLayout
a:layout_width="0dp"
a:layout_height="match_parent"
a:layout_weight="1"
a:gravity="end">
<ImageButton
a:id="@+id/control_previous"
a:layout_width="0dp"
a:layout_height="match_parent"
a:layout_weight="0.6"
a:importantForAccessibility="no"
a:scaleType="fitCenter"
a:src="@drawable/media_backward"
a:background="@null"
/>
<ImageButton
a:id="@+id/control_play"
a:layout_width="0dp"
a:layout_height="match_parent"
a:layout_weight="1"
a:importantForAccessibility="no"
a:scaleType="fitCenter"
a:src="@drawable/media_start"
a:background="@null"/>
<ImageButton
a:id="@+id/control_next"
a:layout_width="0dp"
a:layout_height="match_parent"
a:layout_weight="0.6"
a:importantForAccessibility="no"
a:scaleType="fitCenter"
a:src="@drawable/media_forward"
a:background="@null"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,128 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:a="http://schemas.android.com/apk/res/android"
a:layout_width="fill_parent"
a:layout_height="fill_parent"
xmlns:tools="http://schemas.android.com/tools"
a:minWidth="250dp"
a:minHeight="180dp"
style="@style/Widget.AppWidget.AppWidget.Container"
a:theme="@style/Theme.AppWidget.AppWidgetContainerCropped"
a:orientation="vertical"
tools:ignore="UseAppTint">
<ImageView
a:id="@+id/appwidget_coverart"
a:layout_width="match_parent"
a:layout_height="wrap_content"
a:layout_gravity="center_horizontal"
a:layout_alignParentTop="true"
a:layout_above="@id/linearLayout1"
a:scaleType="centerCrop"
a:adjustViewBounds="true"
a:clickable="true"
a:focusable="true"
a:importantForAccessibility="no"
a:clipToOutline="true"
a:src="@drawable/unknown_album"
tools:ignore="UnusedAttribute" />
<LinearLayout
a:id="@+id/linearLayout1"
a:layout_width="match_parent"
a:layout_height="wrap_content"
a:layout_above="@id/layout_buttons"
a:paddingVertical="6dp"
a:orientation="vertical" >
<TextView
a:id="@+id/title"
a:layout_width="fill_parent"
a:layout_height="wrap_content"
a:ellipsize="none"
a:minHeight="16sp"
a:paddingStart="5dip"
a:paddingEnd="5dip"
a:textAppearance="@style/TextAppearance.Material3.TitleMedium"
tools:text="Title"
a:layout_gravity="center_horizontal"
a:gravity="center"
/>
<TextView
a:id="@+id/artist"
a:layout_width="fill_parent"
a:layout_height="wrap_content"
a:ellipsize="none"
a:fadingEdge="horizontal"
a:fadingEdgeLength="10dip"
a:minHeight="12sp"
a:paddingBottom="2dip"
a:paddingStart="5dip"
a:singleLine="true"
tools:text="Artist"
a:layout_gravity="center_horizontal"
a:gravity="center"
a:textAppearance="@style/TextAppearance.Material3.BodyLarge" />
<TextView
a:id="@+id/album"
a:layout_width="fill_parent"
a:layout_height="wrap_content"
a:ellipsize="none"
a:fadingEdge="horizontal"
a:fadingEdgeLength="10dip"
a:minHeight="12sp"
a:paddingBottom="2dip"
a:paddingStart="5dip"
a:singleLine="true"
tools:text="Album"
a:layout_gravity="center_horizontal"
a:gravity="center"
a:textAppearance="@style/TextAppearance.Material3.BodyLarge" />
</LinearLayout>
<LinearLayout
a:id="@+id/layout_buttons"
a:layout_width="fill_parent"
a:layout_height="wrap_content"
a:layout_alignParentBottom="true"
a:layout_gravity="bottom"
a:gravity="bottom"
a:orientation="horizontal"
a:paddingTop="2dip"
a:paddingBottom="4dip">
<ImageButton
a:id="@+id/control_previous"
a:layout_width="0dp"
a:layout_height="40dp"
a:layout_gravity="center"
a:layout_weight="0.6"
a:scaleType="fitCenter"
a:importantForAccessibility="no"
a:src="@drawable/media_backward"
/>
<ImageButton
a:id="@+id/control_play"
a:layout_width="0dp"
a:layout_height="70dp"
a:layout_gravity="center"
a:layout_weight="1"
a:importantForAccessibility="no"
a:src="@drawable/media_start"
a:scaleType="fitCenter"/>
<ImageButton
a:id="@+id/control_next"
a:layout_width="0dp"
a:layout_height="40dp"
a:layout_gravity="center"
a:layout_weight="0.6"
a:importantForAccessibility="no"
a:scaleType="fitCenter"
a:src="@drawable/media_forward"
/>
</LinearLayout>
</RelativeLayout>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:a="http://schemas.android.com/apk/res/android" <RelativeLayout xmlns:a="http://schemas.android.com/apk/res/android"
a:layout_width="fill_parent" a:layout_width="fill_parent"
a:layout_height="wrap_content"> a:layout_height="wrap_content">
<ImageView <ImageView
a:id="@+id/select_album_art" a:id="@+id/select_album_art"
@ -12,7 +12,7 @@
a:layout_marginEnd="10dip" a:layout_marginEnd="10dip"
a:contentDescription="@null" a:contentDescription="@null"
a:scaleType="fitCenter" a:scaleType="fitCenter"
a:src="@drawable/unknown_album"/> a:src="@drawable/unknown_album" />
<TextView <TextView
a:id="@+id/select_album_title" a:id="@+id/select_album_title"
@ -20,10 +20,10 @@
a:layout_height="wrap_content" a:layout_height="wrap_content"
a:layout_toEndOf="@+id/select_album_art" a:layout_toEndOf="@+id/select_album_art"
a:ellipsize="end" a:ellipsize="end"
a:paddingEnd="4dip"
a:paddingTop="10dip" a:paddingTop="10dip"
a:paddingEnd="4dip"
a:singleLine="true" a:singleLine="true"
a:textAppearance="?android:attr/textAppearanceMedium"/> a:textAppearance="?android:attr/textAppearanceMedium" />
<TextView <TextView
a:id="@+id/select_album_artist" a:id="@+id/select_album_artist"
@ -34,7 +34,7 @@
a:ellipsize="end" a:ellipsize="end"
a:paddingEnd="4dip" a:paddingEnd="4dip"
a:singleLine="true" a:singleLine="true"
a:textAppearance="?android:attr/textAppearanceSmall"/> a:textAppearance="?android:attr/textAppearanceSmall" />
<TextView <TextView
a:id="@+id/select_album_genre" a:id="@+id/select_album_genre"
@ -45,7 +45,7 @@
a:ellipsize="end" a:ellipsize="end"
a:paddingEnd="4dip" a:paddingEnd="4dip"
a:singleLine="true" a:singleLine="true"
a:textAppearance="?android:attr/textAppearanceSmall"/> a:textAppearance="?android:attr/textAppearanceSmall" />
<TextView <TextView
a:id="@+id/select_album_year" a:id="@+id/select_album_year"
@ -56,7 +56,7 @@
a:ellipsize="end" a:ellipsize="end"
a:paddingEnd="4dip" a:paddingEnd="4dip"
a:singleLine="true" a:singleLine="true"
a:textAppearance="?android:attr/textAppearanceSmall"/> a:textAppearance="?android:attr/textAppearanceSmall" />
<TextView <TextView
a:id="@+id/select_album_song_count" a:id="@+id/select_album_song_count"
@ -67,7 +67,7 @@
a:ellipsize="none" a:ellipsize="none"
a:paddingEnd="4dip" a:paddingEnd="4dip"
a:singleLine="true" a:singleLine="true"
a:textAppearance="?android:attr/textAppearanceSmall"/> a:textAppearance="?android:attr/textAppearanceSmall" />
<TextView <TextView
a:id="@+id/select_album_duration" a:id="@+id/select_album_duration"
@ -78,6 +78,6 @@
a:ellipsize="none" a:ellipsize="none"
a:paddingEnd="4dip" a:paddingEnd="4dip"
a:singleLine="true" a:singleLine="true"
a:textAppearance="?android:attr/textAppearanceSmall"/> a:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout> </RelativeLayout>

View File

@ -11,9 +11,6 @@
a:layout_height="wrap_content" a:layout_height="wrap_content"
a:gravity="center_vertical" a:gravity="center_vertical"
a:paddingStart="6dp" a:paddingStart="6dp"
a:textAllCaps="true" a:theme="@style/Ultrasonic.AllCapsLabel" />
a:textAppearance="?android:attr/textAppearanceSmall"
a:textColor="?attr/colorPrimary"
a:textStyle="bold" />
</LinearLayout> </LinearLayout>

View File

@ -98,12 +98,12 @@
a:layout_marginTop="8dp" a:layout_marginTop="8dp"
a:layout_marginEnd="10dip" a:layout_marginEnd="10dip"
a:indeterminate="true" a:indeterminate="true"
app:indicatorColor="?attr/color_menu_selected" app:indicatorColor="?attr/colorOnSurface"
app:indicatorSize="20dp" app:indicatorSize="20dp"
app:layout_constraintBottom_toTopOf="@id/song_duration" app:layout_constraintBottom_toTopOf="@id/song_duration"
app:layout_constraintEnd_toEndOf="@id/star_barrier" app:layout_constraintEnd_toEndOf="@id/star_barrier"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:trackColor="?attr/color_selected" /> app:trackColor="?attr/colorSurfaceVariant" />
<TextView <TextView
a:id="@+id/song_duration" a:id="@+id/song_duration"

View File

@ -11,15 +11,13 @@
<TextView <TextView
android:id="@+id/main_music" android:id="@+id/main_music"
android:theme="@style/Ultrasonic.AllCapsLabel"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center_vertical" android:gravity="center_vertical"
android:paddingStart="6dp" android:paddingStart="6dp"
android:text="@string/main.music" android:text="@string/main.music"
android:textAllCaps="true" />
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?attr/colorPrimary"
android:textStyle="bold" />
<TextView <TextView
android:id="@+id/main_artists_button" android:id="@+id/main_artists_button"
@ -61,10 +59,7 @@
android:gravity="center_vertical" android:gravity="center_vertical"
android:paddingStart="6dp" android:paddingStart="6dp"
android:text="@string/main.songs_title" android:text="@string/main.songs_title"
android:textAllCaps="true" android:theme="@style/Ultrasonic.AllCapsLabel" />
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?attr/colorPrimary"
android:textStyle="bold" />
<TextView <TextView
android:id="@+id/main_songs_button" android:id="@+id/main_songs_button"
@ -90,15 +85,12 @@
<TextView <TextView
android:id="@+id/main_albums" android:id="@+id/main_albums"
android:theme="@style/Ultrasonic.AllCapsLabel"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center_vertical" android:gravity="center_vertical"
android:paddingStart="6dp" android:paddingStart="6dp"
android:text="@string/main.albums_title" android:text="@string/main.albums_title" />
android:textAllCaps="true"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?attr/colorPrimary"
android:textStyle="bold" />
<TextView <TextView
android:id="@+id/main_albums_newest" android:id="@+id/main_albums_newest"
@ -195,10 +187,7 @@
android:gravity="center_vertical" android:gravity="center_vertical"
android:paddingStart="6dp" android:paddingStart="6dp"
android:text="@string/main.videos" android:text="@string/main.videos"
android:textAllCaps="true" android:theme="@style/Ultrasonic.AllCapsLabel" />
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?attr/colorPrimary"
android:textStyle="bold" />
<TextView <TextView
android:id="@+id/main_videos" android:id="@+id/main_videos"

View File

@ -19,7 +19,7 @@
a:focusable="true" a:focusable="true"
a:scaleType="fitCenter" a:scaleType="fitCenter"
a:contentDescription="@string/buttons.shuffle" a:contentDescription="@string/buttons.shuffle"
app:icon="@drawable/media_shuffle_normal" app:icon="@drawable/media_shuffle"
app:iconGravity="textTop" /> app:iconGravity="textTop" />
<org.moire.ultrasonic.view.AutoRepeatButton <org.moire.ultrasonic.view.AutoRepeatButton
@ -33,7 +33,7 @@
a:contentDescription="@string/buttons.previous" a:contentDescription="@string/buttons.previous"
a:focusable="true" a:focusable="true"
a:scaleType="fitCenter" a:scaleType="fitCenter"
app:icon="@drawable/media_backward_normal" app:icon="@drawable/media_backward"
app:iconGravity="textTop" app:iconGravity="textTop"
app:iconSize="32dp" /> app:iconSize="32dp" />
@ -48,7 +48,7 @@
a:scaleType="fitCenter" a:scaleType="fitCenter"
tools:visibility="gone" tools:visibility="gone"
a:contentDescription="@string/buttons.play" a:contentDescription="@string/buttons.play"
app:icon="@drawable/media_start_normal" app:icon="@drawable/media_start"
app:iconGravity="textTop" app:iconGravity="textTop"
app:iconSize="48dp" /> app:iconSize="48dp" />
@ -62,7 +62,7 @@
a:contentDescription="@string/buttons.pause" a:contentDescription="@string/buttons.pause"
a:focusable="true" a:focusable="true"
a:scaleType="fitCenter" a:scaleType="fitCenter"
app:icon="@drawable/media_pause_normal" app:icon="@drawable/media_pause"
app:iconGravity="textTop" app:iconGravity="textTop"
app:iconSize="48dp" /> app:iconSize="48dp" />
@ -77,7 +77,7 @@
a:scaleType="fitCenter" a:scaleType="fitCenter"
tools:visibility="gone" tools:visibility="gone"
a:contentDescription="@string/buttons.stop" a:contentDescription="@string/buttons.stop"
app:icon="@drawable/media_stop_normal" app:icon="@drawable/media_stop"
app:iconGravity="textTop" app:iconGravity="textTop"
app:iconSize="48dp" /> app:iconSize="48dp" />
@ -92,7 +92,7 @@
a:contentDescription="@string/buttons.next" a:contentDescription="@string/buttons.next"
a:focusable="true" a:focusable="true"
a:scaleType="fitCenter" a:scaleType="fitCenter"
app:icon="@drawable/media_forward_normal" app:icon="@drawable/media_forward"
app:iconGravity="textTop" app:iconGravity="textTop"
app:iconSize="32dp" /> app:iconSize="32dp" />

View File

@ -6,7 +6,8 @@
a:id="@+id/drawer_layout" a:id="@+id/drawer_layout"
a:layout_width="match_parent" a:layout_width="match_parent"
a:layout_height="match_parent" a:layout_height="match_parent"
tools:context="org.moire.ultrasonic.activity.NavigationActivity"> tools:context="org.moire.ultrasonic.activity.NavigationActivity"
a:fitsSystemWindows="true">
<RelativeLayout <RelativeLayout
a:layout_width="match_parent" a:layout_width="match_parent"
@ -44,7 +45,6 @@
a:layout_height="match_parent" a:layout_height="match_parent"
a:layout_gravity="start" a:layout_gravity="start"
a:fitsSystemWindows="true" a:fitsSystemWindows="true"
a:background="?attr/color_menu_background"
app:headerLayout="@layout/navigation_header" app:headerLayout="@layout/navigation_header"
app:menu="@menu/navigation" /> app:menu="@menu/navigation" />
</androidx.drawerlayout.widget.DrawerLayout> </androidx.drawerlayout.widget.DrawerLayout>

View File

@ -24,19 +24,20 @@
style="@style/Widget.Material3.Button.TextButton.Icon" style="@style/Widget.Material3.Button.TextButton.Icon"
a:layout_width="match_parent" a:layout_width="match_parent"
a:layout_height="wrap_content" a:layout_height="wrap_content"
a:layout_marginTop="0dp" a:layout_marginStart="3dp"
a:layout_marginTop="24dp"
a:background="@drawable/default_ripple" a:background="@drawable/default_ripple"
a:gravity="center_vertical" a:gravity="center_vertical"
a:paddingHorizontal="22dp" a:paddingHorizontal="22dp"
a:paddingTop="16dp" a:paddingTop="16dp"
a:paddingBottom="16dp" a:paddingBottom="16dp"
a:text="@string/main.offline" a:text="@string/main.offline"
a:textAppearance="@style/MenuDrawer.Widget" a:textAppearance="@style/TextAppearance.Material3.TitleMedium"
a:textColor="?attr/colorOnPrimary" a:textColor="?attr/colorOnPrimary"
a:textSize="17sp" a:textSize="18sp"
a:textStyle="bold"
app:icon="@drawable/ic_menu_select_server" app:icon="@drawable/ic_menu_select_server"
app:iconPadding="12dp" app:iconPadding="12dp"
app:iconSize="24dp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"

View File

@ -6,16 +6,17 @@
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="?attr/colorSecondaryContainer" android:background="?attr/colorSecondaryContainer"
tools:ignore="overdraw"> tools:ignore="Overdraw">
<ImageView <ImageView
android:id="@+id/now_playing_image" android:id="@+id/now_playing_image"
android:layout_width="72.0dip" android:layout_width="72.0dip"
android:layout_height="72.0dip" android:layout_height="72.0dip"
android:layout_marginStart="0dp" android:layout_marginStart="0dp"
android:focusable="true" android:elevation="4dp"
android:gravity="center" android:focusable="true"
android:importantForAccessibility="no" /> android:gravity="center"
android:importantForAccessibility="no" />
<LinearLayout <LinearLayout
android:layout_width="0.0dp" android:layout_width="0.0dp"
@ -58,7 +59,7 @@
android:contentDescription="@string/buttons.play" android:contentDescription="@string/buttons.play"
android:focusable="false" android:focusable="false"
android:scaleType="fitCenter" android:scaleType="fitCenter"
app:icon="@drawable/media_pause_normal" app:icon="@drawable/media_pause"
app:iconGravity="textTop" app:iconGravity="textTop"
app:iconSize="42dp" /> app:iconSize="42dp" />

View File

@ -4,238 +4,252 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
a:layout_width="match_parent" a:layout_width="match_parent"
a:layout_height="match_parent" a:layout_height="match_parent"
a:fillViewport="true"> a:fillViewport="true"
tools:ignore="HardcodedText">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
a:layout_width="match_parent"
a:layout_height="wrap_content">
<com.google.android.material.textfield.TextInputLayout
a:id="@+id/edit_server_name"
a:layout_width="match_parent" a:layout_width="match_parent"
a:layout_height="wrap_content" a:layout_height="wrap_content"
a:hint="@string/settings.server_name" a:paddingStart="16dp"
app:layout_constraintEnd_toEndOf="parent" a:paddingEnd="16dp">
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputLayout
a:id="@+id/edit_server_name"
a:layout_width="match_parent" a:layout_width="match_parent"
a:layout_height="wrap_content" a:layout_height="wrap_content"
a:inputType="text" a:hint="@string/settings.server_name"
a:maxLines="1" /> app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
</com.google.android.material.textfield.TextInputLayout> <com.google.android.material.textfield.TextInputEditText
a:layout_width="match_parent"
a:layout_height="wrap_content"
a:layout_marginBottom="8dp"
a:inputType="text"
a:text=" "
a:maxLines="1" />
<com.google.android.material.textfield.TextInputLayout </com.google.android.material.textfield.TextInputLayout>
a:id="@+id/edit_server_address"
a:layout_width="match_parent"
a:layout_height="wrap_content"
a:layout_marginBottom="20dp"
a:hint="@string/settings.server_address"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/edit_server_name">
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputLayout
a:id="@+id/edit_server_address"
a:layout_width="match_parent" a:layout_width="match_parent"
a:layout_height="wrap_content" a:layout_height="wrap_content"
a:text="http://" a:layout_marginBottom="20dp"
a:inputType="textWebEmailAddress" /> a:hint="@string/settings.server_address"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/edit_server_name">
</com.google.android.material.textfield.TextInputLayout> <com.google.android.material.textfield.TextInputEditText
a:layout_width="match_parent"
a:layout_height="wrap_content"
a:inputType="textWebEmailAddress"
a:text="http://" />
<TextView </com.google.android.material.textfield.TextInputLayout>
a:id="@+id/edit_server_color_text"
style="@style/Widget.AppCompat.CompoundButton.Switch"
a:layout_width="0dp"
a:layout_height="wrap_content"
a:layout_marginStart="5dp"
a:layout_marginLeft="5dp"
a:text="@string/settings.server_color"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/edit_server_color_picker"
app:layout_constraintBottom_toBottomOf="@id/edit_server_color_picker"/>
<ImageView
a:id="@+id/edit_server_color_picker"
a:layout_width="48dp"
a:layout_height="32dp"
a:layout_margin="8dp"
a:src="@drawable/rounded_border"
app:layout_constraintTop_toBottomOf="@id/edit_server_address"
app:layout_constraintRight_toRightOf="parent"
tools:ignore="ContentDescription" />
<TextView <TextView
a:id="@+id/edit_authentication_header" a:id="@+id/edit_authentication_header"
style="@style/MenuDrawer.Widget.Category" style="@style/Ultrasonic.AllCapsLabel.Inset"
a:layout_width="wrap_content" a:layout_width="wrap_content"
a:layout_height="wrap_content" a:layout_height="wrap_content"
a:layout_marginTop="15dp" a:layout_marginTop="15dp"
a:text="@string/server_editor.authentication" a:layout_marginBottom="8dp"
app:layout_constraintBottom_toTopOf="@id/edit_server_username" a:text="@string/server_editor.authentication"
app:layout_constraintStart_toStartOf="@id/edit_server_username" app:layout_constraintBottom_toTopOf="@id/edit_server_username"
app:layout_constraintTop_toBottomOf="@id/edit_server_color_picker" /> app:layout_constraintStart_toStartOf="@id/edit_server_username"
app:layout_constraintTop_toBottomOf="@id/edit_server_address" />
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
a:id="@+id/edit_server_username" a:id="@+id/edit_server_username"
a:layout_width="match_parent"
a:layout_height="wrap_content"
a:hint="@string/settings.server_username"
app:layout_constraintBottom_toTopOf="@id/edit_server_password"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/edit_authentication_header">
<com.google.android.material.textfield.TextInputEditText
a:layout_width="match_parent" a:layout_width="match_parent"
a:layout_height="wrap_content" a:layout_height="wrap_content"
a:inputType="text" a:hint="@string/settings.server_username"
a:maxLines="1" /> app:layout_constraintBottom_toTopOf="@id/edit_server_password"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/edit_authentication_header">
</com.google.android.material.textfield.TextInputLayout> <com.google.android.material.textfield.TextInputEditText
a:layout_width="match_parent"
a:layout_height="wrap_content"
a:layout_marginTop="8dp"
a:layout_marginBottom="0dp"
a:inputType="text"
a:text=" "
a:maxLines="1" />
<com.google.android.material.textfield.TextInputLayout </com.google.android.material.textfield.TextInputLayout>
a:id="@+id/edit_server_password"
a:layout_width="match_parent"
a:layout_height="wrap_content"
a:layout_marginBottom="21dp"
a:hint="@string/settings.server_password"
app:endIconMode="password_toggle"
app:layout_constraintBottom_toTopOf="@id/edit_advanced_header"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/edit_server_username">
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputLayout
a:id="@+id/edit_server_password"
a:layout_width="match_parent" a:layout_width="match_parent"
a:layout_height="wrap_content" a:layout_height="wrap_content"
a:inputType="textPassword" /> a:layout_marginBottom="21dp"
a:hint="@string/settings.server_password"
app:endIconMode="password_toggle"
app:layout_constraintBottom_toTopOf="@id/edit_advanced_header"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/edit_server_username">
</com.google.android.material.textfield.TextInputLayout> <com.google.android.material.textfield.TextInputEditText
a:layout_width="match_parent"
a:layout_height="wrap_content"
a:inputType="textPassword"
a:text=" "
/>
<TextView </com.google.android.material.textfield.TextInputLayout>
a:id="@+id/edit_advanced_header"
style="@style/MenuDrawer.Widget.Category"
a:layout_width="wrap_content"
a:layout_height="wrap_content"
a:layout_marginBottom="8dp"
a:text="@string/server_editor.advanced"
app:layout_constraintBottom_toTopOf="@id/edit_self_signed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/edit_server_password" />
<TextView
a:id="@+id/edit_self_signed_title"
style="@style/Widget.AppCompat.CompoundButton.Switch"
a:layout_width="0dp"
a:layout_height="wrap_content"
a:layout_marginStart="5dp"
a:text="@string/settings.title.allow_self_signed_certificate"
app:layout_constraintBottom_toTopOf="@id/edit_ldap_title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/edit_advanced_header" />
<com.google.android.material.switchmaterial.SwitchMaterial
a:id="@+id/edit_self_signed"
a:layout_width="0dp"
a:layout_height="wrap_content"
a:layout_marginStart="8dp"
a:layout_marginEnd="5dp"
a:layout_marginBottom="8dp"
a:checked="false"
app:layout_constraintBottom_toTopOf="@id/edit_ldap_title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/edit_self_signed_title"
app:layout_constraintTop_toBottomOf="@id/edit_advanced_header" />
<TextView
a:id="@+id/edit_ldap_title"
style="@style/Widget.AppCompat.CompoundButton.Switch"
a:layout_width="0dp"
a:layout_height="wrap_content"
a:layout_marginStart="5dp"
a:text="@string/settings.title.force_plain_text_password"
app:layout_constraintBottom_toTopOf="@id/edit_ldap_description"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/edit_self_signed" />
<TextView
a:id="@+id/edit_ldap_description"
style="@style/TextAppearance.AppCompat.Small"
a:layout_width="0dp"
a:layout_height="wrap_content"
a:layout_marginStart="5dp"
a:text="@string/settings.summary.force_plain_text_password"
app:layout_constraintBottom_toTopOf="@id/edit_jukebox"
app:layout_constraintEnd_toStartOf="@id/edit_ldap"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/edit_ldap_title" />
<com.google.android.material.switchmaterial.SwitchMaterial
a:id="@+id/edit_ldap"
a:layout_width="wrap_content"
a:layout_height="wrap_content"
a:layout_marginEnd="5dp"
a:layout_marginStart="8dp"
a:checked="false"
app:layout_constraintBottom_toBottomOf="@id/edit_ldap_description"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/edit_ldap_description"
app:layout_constraintTop_toTopOf="@id/edit_ldap_title" />
<TextView
a:id="@+id/edit_jukebox_title"
style="@style/Widget.AppCompat.CompoundButton.Switch"
a:layout_width="0dp"
a:layout_height="wrap_content"
a:layout_marginStart="5dp"
a:layout_marginTop="8dp"
a:text="@string/jukebox.is_default"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/edit_ldap_description" />
<com.google.android.material.switchmaterial.SwitchMaterial
a:id="@+id/edit_jukebox"
a:layout_width="0dp"
a:layout_height="wrap_content"
a:layout_marginStart="8dp"
a:layout_marginTop="8dp"
a:layout_marginEnd="5dp"
a:checked="false"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/edit_jukebox_title"
app:layout_constraintTop_toBottomOf="@id/edit_ldap_description" />
<Button
a:id="@+id/edit_test"
style="?attr/materialButtonOutlinedStyle"
a:layout_width="0dp"
a:layout_height="wrap_content"
a:layout_marginTop="8dp"
a:layout_marginStart="5dp"
a:layout_marginEnd="2dp"
a:text="@string/settings.test_connection_title"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/edit_save"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/edit_jukebox"
app:layout_constraintVertical_bias="1.0" />
<Button
a:id="@+id/edit_save"
style="?attr/materialButtonOutlinedStyle"
a:layout_width="0dp"
a:layout_height="0dp"
a:layout_marginStart="2dp"
a:layout_marginEnd="5dp"
a:text="@string/common.save"
app:layout_constraintBottom_toBottomOf="@id/edit_test"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/edit_test"
app:layout_constraintTop_toTopOf="@id/edit_test" />
</androidx.constraintlayout.widget.ConstraintLayout> <TextView
a:id="@+id/edit_advanced_header"
style="@style/Ultrasonic.AllCapsLabel.Inset"
a:layout_width="wrap_content"
a:layout_height="wrap_content"
a:layout_marginBottom="8dp"
a:text="@string/server_editor.advanced"
app:layout_constraintBottom_toTopOf="@id/edit_server_color_text"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/edit_server_password" />
<ImageView
a:id="@+id/edit_server_color_picker"
a:layout_width="48dp"
a:layout_height="32dp"
a:layout_margin="8dp"
a:src="@drawable/rounded_border"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/edit_advanced_header"
tools:ignore="ContentDescription" />
<TextView
a:id="@+id/edit_server_color_text"
style="@style/Widget.AppCompat.CompoundButton.Switch"
a:layout_width="0dp"
a:layout_height="wrap_content"
a:layout_marginStart="5dp"
a:layout_marginLeft="5dp"
a:text="@string/settings.server_color"
app:layout_constraintBottom_toBottomOf="@id/edit_server_color_picker"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/edit_server_color_picker" />
<TextView
a:id="@+id/edit_self_signed_title"
style="@style/Widget.AppCompat.CompoundButton.Switch"
a:layout_width="0dp"
a:layout_height="wrap_content"
a:layout_marginStart="5dp"
a:text="@string/settings.title.allow_self_signed_certificate"
app:layout_constraintBottom_toTopOf="@id/edit_plaintext_title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/edit_server_color_text" />
<com.google.android.material.switchmaterial.SwitchMaterial
a:id="@+id/edit_self_signed"
a:layout_width="0dp"
a:layout_height="wrap_content"
a:layout_marginStart="8dp"
a:layout_marginEnd="5dp"
a:layout_marginBottom="8dp"
a:checked="false"
app:layout_constraintBottom_toTopOf="@id/edit_plaintext_title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/edit_self_signed_title"
app:layout_constraintTop_toBottomOf="@+id/edit_server_color_text" />
<TextView
a:id="@+id/edit_plaintext_title"
style="@style/Widget.AppCompat.CompoundButton.Switch"
a:layout_width="0dp"
a:layout_height="wrap_content"
a:layout_marginStart="5dp"
a:text="@string/settings.title.force_plain_text_password"
app:layout_constraintBottom_toTopOf="@id/edit_plaintext_description"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/edit_self_signed" />
<TextView
a:id="@+id/edit_plaintext_description"
style="@style/TextAppearance.AppCompat.Small"
a:layout_width="0dp"
a:layout_height="wrap_content"
a:layout_marginStart="5dp"
a:text="@string/settings.summary.force_plain_text_password"
app:layout_constraintBottom_toTopOf="@id/edit_jukebox"
app:layout_constraintEnd_toStartOf="@id/edit_plaintext"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/edit_plaintext_title" />
<com.google.android.material.switchmaterial.SwitchMaterial
a:id="@+id/edit_plaintext"
a:layout_width="wrap_content"
a:layout_height="wrap_content"
a:layout_marginStart="8dp"
a:layout_marginEnd="5dp"
a:checked="false"
app:layout_constraintBottom_toBottomOf="@id/edit_plaintext_description"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/edit_plaintext_description"
app:layout_constraintTop_toTopOf="@id/edit_plaintext_title" />
<TextView
a:id="@+id/edit_jukebox_title"
style="@style/Widget.AppCompat.CompoundButton.Switch"
a:layout_width="0dp"
a:layout_height="wrap_content"
a:layout_marginStart="5dp"
a:layout_marginTop="8dp"
a:text="@string/jukebox.is_default"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/edit_plaintext_description" />
<com.google.android.material.switchmaterial.SwitchMaterial
a:id="@+id/edit_jukebox"
a:layout_width="0dp"
a:layout_height="wrap_content"
a:layout_marginStart="8dp"
a:layout_marginTop="8dp"
a:layout_marginEnd="5dp"
a:checked="false"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/edit_jukebox_title"
app:layout_constraintTop_toBottomOf="@id/edit_plaintext_description" />
<Button
a:id="@+id/edit_test"
style="?attr/materialButtonOutlinedStyle"
a:layout_width="0dp"
a:layout_height="wrap_content"
a:layout_marginStart="5dp"
a:layout_marginTop="8dp"
a:layout_marginEnd="2dp"
a:text="@string/settings.test_connection_title"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/edit_save"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/edit_jukebox"
app:layout_constraintVertical_bias="1.0" />
<Button
a:id="@+id/edit_save"
style="?attr/materialButtonOutlinedStyle"
a:layout_width="0dp"
a:layout_height="0dp"
a:layout_marginStart="2dp"
a:layout_marginEnd="5dp"
a:text="@string/common.save"
app:layout_constraintBottom_toBottomOf="@id/edit_test"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/edit_test"
app:layout_constraintTop_toTopOf="@id/edit_test" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView> </ScrollView>

View File

@ -22,8 +22,7 @@
a:layout_width="wrap_content" a:layout_width="wrap_content"
a:layout_height="wrap_content" a:layout_height="wrap_content"
a:text="@string/share_comment" a:text="@string/share_comment"
a:textAllCaps="true" a:theme="@style/Ultrasonic.AllCapsLabel"
a:textColor="?attr/colorPrimary"
a:id="@+id/textViewComment" a:id="@+id/textViewComment"
a:layout_gravity="center_horizontal"/> a:layout_gravity="center_horizontal"/>
@ -40,8 +39,7 @@
a:layout_width="wrap_content" a:layout_width="wrap_content"
a:layout_height="wrap_content" a:layout_height="wrap_content"
a:text="@string/settings.share_expiration" a:text="@string/settings.share_expiration"
a:textAllCaps="true" a:theme="@style/Ultrasonic.AllCapsLabel"
a:textColor="?attr/colorPrimary"
a:id="@+id/textViewExpiration" a:id="@+id/textViewExpiration"
a:layout_gravity="center_horizontal"/> a:layout_gravity="center_horizontal"/>

View File

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<menu xmlns:app="http://schemas.android.com/apk/res-auto" <menu xmlns:a="http://schemas.android.com/apk/res/android">
xmlns:a="http://schemas.android.com/apk/res/android">
<group <group
a:checkableBehavior="none" a:checkableBehavior="none"
a:enabled="true" a:enabled="true"
@ -48,7 +47,7 @@
<item <item
a:id="@+id/playerFragment" a:id="@+id/playerFragment"
a:checkable="true" a:checkable="true"
a:icon="@drawable/media_start_normal" a:icon="@drawable/media_start"
a:title="@string/button_bar.now_playing" /> a:title="@string/button_bar.now_playing" />
<item <item
a:id="@+id/podcastFragment" a:id="@+id/podcastFragment"

View File

@ -6,7 +6,7 @@
<item <item
a:id="@+id/menu_item_toggle_list" a:id="@+id/menu_item_toggle_list"
a:icon="@drawable/media_toggle_list_normal" a:icon="@drawable/media_toggle_list"
app:showAsAction="always" app:showAsAction="always"
a:title="@string/download.toggle_playlist"/> a:title="@string/download.toggle_playlist"/>
<item <item

View File

@ -12,7 +12,7 @@
<item <item
a:id="@+id/select_album_play_all" a:id="@+id/select_album_play_all"
a:icon="@drawable/media_start_normal" a:icon="@drawable/media_start"
app:showAsAction="ifRoom|withText" app:showAsAction="ifRoom|withText"
a:title="@string/select_album.play_all"/> a:title="@string/select_album.play_all"/>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="UltrasonicTheme.DayNight" parent="Theme.Material3.DynamicColors.DayNight">
<item name="colorPrimaryDark">?attr/colorSurface</item>
<item name="colorControlNormal">?attr/colorOnSurface</item>
<item name="colorControlHighlight">?attr/colorSecondary</item>
</style>
</resources>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--
Having clipToOutline to true is needed for the background (that has id with
android:id/background so that the system knows the widget has been updated
and doesn't need to be clipped by the launcher.
-->
<style name="Widget.AppWidget.AppWidget.Container" parent="android:Widget">
<item name="android:id">@android:id/background</item>
<item name="android:padding">?attr/appWidgetPadding</item>
<item name="android:background">@drawable/app_widget_background</item>
<item name="android:clipToOutline">true</item>
</style>
</resources>

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--
Having themes.xml for v31 variant because @android:dimen/system_app_widget_background_radius
and @android:dimen/system_app_widget_inner_radius requires API level 31.
-->
<style name="Theme.AppWidget.AppWidgetContainer" parent="Theme.AppWidget.AppWidgetContainerParent">
<item name="appWidgetRadius">@android:dimen/system_app_widget_background_radius</item>
<item name="appWidgetInnerRadius">@android:dimen/system_app_widget_inner_radius</item>
<item name="appWidgetPadding">16dp</item>
<item name="android:imageButtonStyle">@style/Theme.AppWidget.ImageButton</item>
</style>
<style name="Theme.AppWidget.AppWidgetContainerCropped" parent="Theme.AppWidget.AppWidgetContainer">
<!-- Apply padding to avoid the content of the widget colliding with the rounded corners -->
<item name="appWidgetPadding">0dp</item>
</style>
</resources>

View File

@ -2,11 +2,13 @@
<resources> <resources>
<string-array name="themeValues" translatable="false"> <string-array name="themeValues" translatable="false">
<item>@string/preferences_key_theme_light</item> <item>@string/setting_key.theme_day_night</item>
<item>@string/preferences_key_theme_dark</item> <item>@string/setting_key.theme_light</item>
<item>@string/preferences_key_theme_black</item> <item>@string/setting_key.theme_dark</item>
<item>@string/setting_key.theme_black</item>
</string-array> </string-array>
<string-array name="themeNames" translatable="false"> <string-array name="themeNames" translatable="false">
<item>@string/settings.theme_day_night</item>
<item>@string/settings.theme_light</item> <item>@string/settings.theme_light</item>
<item>@string/settings.theme_dark</item> <item>@string/settings.theme_dark</item>
<item>@string/settings.theme_black</item> <item>@string/settings.theme_black</item>

View File

@ -0,0 +1,8 @@
<resources>
<declare-styleable name="AppWidgetAttrs">
<attr name="appWidgetPadding" format="dimension" />
<attr name="appWidgetInnerRadius" format="dimension" />
<attr name="appWidgetRadius" format="dimension" />
<attr name="appWidgetBackground" format="color" />
</declare-styleable>
</resources>

View File

@ -1,7 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<color name="appwidget_text">#FFFFFF</color>
<color name="appwidget_button_background">#00000000</color>
<color name="background_color_black">#000000</color> <color name="background_color_black">#000000</color>
<color name="selected_menu_dark">#F3F3F3</color> <color name="selected_menu_dark">#F3F3F3</color>
<color name="selected_menu_light">#000000</color> <color name="selected_menu_light">#000000</color>

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--
Do not forget to match string resources with constants
-->
<string translatable="false" name="preferences_key_theme_light">light</string>
<string translatable="false" name="preferences_key_theme_dark">dark</string>
<string translatable="false" name="preferences_key_theme_black">black</string>
</resources>

View File

@ -6,6 +6,7 @@
<string name="setting_key.theme_light" translatable="false">light</string> <string name="setting_key.theme_light" translatable="false">light</string>
<string name="setting_key.theme_dark" translatable="false">dark</string> <string name="setting_key.theme_dark" translatable="false">dark</string>
<string name="setting_key.theme_black" translatable="false">black</string> <string name="setting_key.theme_black" translatable="false">black</string>
<string name="setting_key.theme_day_night" translatable="false">dayNight</string>
<string name="setting_key.display_bitrate_with_artist" translatable="false">displayBitrateWithArtist</string> <string name="setting_key.display_bitrate_with_artist" translatable="false">displayBitrateWithArtist</string>
<string name="setting_key.use_folder_for_album_artist" translatable="false">useFolderForAlbumArtist</string> <string name="setting_key.use_folder_for_album_artist" translatable="false">useFolderForAlbumArtist</string>
<string name="setting_key.show_track_number" translatable="false">showTrackNumber</string> <string name="setting_key.show_track_number" translatable="false">showTrackNumber</string>

View File

@ -302,6 +302,7 @@
<string name="settings.test_connection_title">Test Connection</string> <string name="settings.test_connection_title">Test Connection</string>
<string name="settings.testing_ok">Connection is OK</string> <string name="settings.testing_ok">Connection is OK</string>
<string name="settings.testing_unlicensed">Connection is OK. Server unlicensed.</string> <string name="settings.testing_unlicensed">Connection is OK. Server unlicensed.</string>
<string name="settings.theme_day_night">Day &amp; Night</string>
<string name="settings.theme_light">Light</string> <string name="settings.theme_light">Light</string>
<string name="settings.theme_dark">Dark</string> <string name="settings.theme_dark">Dark</string>
<string name="settings.theme_black">Black</string> <string name="settings.theme_black">Black</string>

View File

@ -1,20 +1,20 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<style name="MenuDrawer"/>
<style name="MenuDrawer.Widget"/> <style name="Ultrasonic.AllCapsLabel" parent="">
<style name="MenuDrawer.Widget.Category">
<item name="android:textStyle">bold</item> <item name="android:textStyle">bold</item>
<item name="android:textColor">?attr/colorPrimary</item> <item name="android:textColor">?attr/colorPrimary</item>
<item name="android:textSize">14sp</item> <item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
<item name="android:textAllCaps">true</item> <item name="android:textAllCaps">true</item>
<item name="android:gravity">center_vertical</item> <item name="android:gravity">center_vertical</item>
<item name="android:paddingStart">16dp</item>
<item name="android:singleLine">true</item> <item name="android:singleLine">true</item>
<item name="android:ellipsize">end</item> <item name="android:ellipsize">end</item>
</style> </style>
<style name="Ultrasonic.AllCapsLabel.Inset" parent="Ultrasonic.AllCapsLabel">
<item name="android:paddingStart">16dp</item>
</style>
<style name="roundedImageView" parent=""> <style name="roundedImageView" parent="">
<item name="cornerFamily">rounded</item> <item name="cornerFamily">rounded</item>
<item name="cornerSize">8dp</item> <item name="cornerSize">8dp</item>
@ -25,9 +25,44 @@
<item name="cornerSize">2dp</item> <item name="cornerSize">2dp</item>
</style> </style>
<attr name="color_background" format="reference"/> <style name="Widget.AppWidget.AppWidget.Container" parent="android:Widget">
<attr name="color_selected" format="reference"/> <item name="android:id">@android:id/background</item>
<attr name="color_menu_background" format="reference"/> <item name="android:padding">?attr/appWidgetPadding</item>
<attr name="color_menu_selected" format="reference"/> <item name="android:background">@drawable/app_widget_background</item>
</style>
<style name="Theme.AppWidget.AppWidgetContainerParent" parent="Theme.Material3.DynamicColors.DayNight">
<!-- Radius of the outer bound of widgets to make the rounded corners -->
<item name="appWidgetRadius">4dp</item>
<item name="appWidgetBackground">?attr/colorSurfaceVariant</item>
<item name="colorControlNormal">?attr/colorOnPrimary</item>
<item name="android:colorButtonNormal">?attr/colorPrimary</item>
<!--
Radius of the inner view's bound of widgets to make the rounded corners.
It needs to be 8dp or less than the value of appWidgetRadius
-->
<item name="appWidgetInnerRadius">8dp</item>
</style>
<style name="Theme.AppWidget.AppWidgetContainer" parent="Theme.AppWidget.AppWidgetContainerParent">
<item name="appWidgetRadius">0dp</item>
<item name="appWidgetInnerRadius">0dp</item>
<!-- Apply padding to avoid the content of the widget colliding with the rounded corners -->
<item name="appWidgetPadding">4dp</item>
<item name="android:imageButtonStyle">@style/Theme.AppWidget.ImageButton</item>
</style>
<style name="Theme.AppWidget.ImageButton" parent="Widget.AppCompat.ImageButton">
<item name="android:tint">?attr/colorPrimary</item>
<item name="android:background">@null</item>
<item name="android:scaleType">fitXY</item>
<item name="android:adjustViewBounds">true</item>
</style>
<style name="Theme.AppWidget.AppWidgetContainerCropped" parent="Theme.AppWidget.AppWidgetContainer">
<!-- Apply padding to avoid the content of the widget colliding with the rounded corners -->
<item name="appWidgetPadding">4dp</item>
</style>
</resources> </resources>

View File

@ -1,41 +1,41 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources xmlns:tools="http://schemas.android.com/tools">
<style name="CleanSearchStyle" parent="Widget.AppCompat.SearchView"> <style name="CleanSearchStyle" parent="Widget.AppCompat.SearchView">
<item name="queryBackground">@null</item> <item name="queryBackground">@null</item>
<item name="submitBackground">@null</item> <item name="submitBackground">@null</item>
</style> </style>
<style name="UltrasonicTheme.Black" parent="Theme.Material3.Dark"> <!-- Here we define some basic attributes that are the same for each theme -->
<style name="UltrasonicTheme.Base" parent="">
<item name="searchViewStyle">@style/CleanSearchStyle</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item> <item name="windowNoTitle">true</item>
</style>
<!-- The day & night theme. There is a night specific override in the values-night folder -->
<style name="UltrasonicTheme.DayNight" parent="Theme.Material3.DynamicColors.DayNight">
<item name="colorControlNormal">?attr/colorOnSurface</item>
<item name="colorControlHighlight">?attr/colorSecondary</item>
</style>
<style name="UltrasonicTheme.Black" parent="Theme.Material3.Dark">
<item name="colorPrimaryDark">?attr/backgroundColor</item> <item name="colorPrimaryDark">?attr/backgroundColor</item>
<item name="android:colorBackground">@color/background_color_black</item> <item name="android:colorBackground">@color/background_color_black</item>
<item name="color_background">?attr/colorSurface</item>
<item name="color_selected">?attr/colorPrimaryContainer</item>
<item name="color_menu_background">?attr/colorSurface</item>
<item name="color_menu_selected">?attr/colorOnSurface</item>
<item name="colorControlNormal">?attr/colorOnSurface</item> <item name="colorControlNormal">?attr/colorOnSurface</item>
<item name="searchViewStyle">@style/CleanSearchStyle</item>
</style> </style>
<style name="UltrasonicTheme.Dark" parent="Theme.Material3.DynamicColors.Dark"> <style name="UltrasonicTheme.Dark" parent="Theme.Material3.DynamicColors.Dark">
<item name="windowNoTitle">true</item> <item name="colorPrimaryDark">?attr/colorSurface</item>
<item name="colorPrimaryDark">?attr/backgroundColor</item>
<item name="color_background">?attr/colorSurface</item>
<item name="color_selected">?attr/colorPrimaryContainer</item>
<item name="color_menu_background">?attr/colorSurface</item>
<item name="color_menu_selected">?attr/colorOnSurface</item>
<item name="colorControlNormal">?attr/colorOnSurface</item> <item name="colorControlNormal">?attr/colorOnSurface</item>
<item name="searchViewStyle">@style/CleanSearchStyle</item> <item name="colorControlHighlight">?attr/colorSecondary</item>
</style> </style>
<style name="UltrasonicTheme.Light" parent="Theme.Material3.DynamicColors.Light"> <style name="UltrasonicTheme.Light" parent="Theme.Material3.DynamicColors.Light">
<item name="windowNoTitle">true</item>
<item name="color_background">?attr/colorSurface</item>
<item name="color_selected">?attr/colorPrimaryContainer</item>
<item name="color_menu_background">?attr/colorSurface</item>
<item name="color_menu_selected">?attr/colorOnSurface</item>
<item name="colorControlNormal">?attr/colorOnSurface</item> <item name="colorControlNormal">?attr/colorOnSurface</item>
<item name="searchViewStyle">@style/CleanSearchStyle</item> <item name="colorControlHighlight">?attr/colorSecondary</item>
</style> </style>
</resources> </resources>

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:a="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
a:minWidth="220dp"
a:minHeight="50dp"
a:targetCellWidth="4"
a:targetCellHeight="1"
a:minResizeWidth="220dp"
a:minResizeHeight="50dp"
a:maxResizeWidth="600dp"
a:maxResizeHeight="600dp"
a:updatePeriodMillis="86400000"
a:resizeMode="horizontal|vertical"
a:previewImage="@drawable/widget_preview"
a:widgetCategory="home_screen|keyguard"
a:initialLayout="@layout/appwidget_landscape_small"
tools:ignore="UnusedAttribute" />

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:a="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
a:minWidth="250dp"
a:minHeight="40dp"
a:updatePeriodMillis="0"
a:resizeMode="none"
a:previewImage="@drawable/preview4x1"
a:widgetCategory="home_screen|keyguard"
a:initialLayout="@layout/appwidget4x1"
tools:ignore="UnusedAttribute" />

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:a="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
a:minWidth="250dp"
a:minHeight="110dp"
a:updatePeriodMillis="0"
a:resizeMode="none"
a:previewImage="@drawable/preview4x2"
a:widgetCategory="home_screen|keyguard"
a:initialLayout="@layout/appwidget4x2"
tools:ignore="UnusedAttribute" />

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:a="http://schemas.android.com/apk/res/android"
a:minWidth="250dp"
a:minHeight="180dp"
a:updatePeriodMillis="0"
a:resizeMode="none"
a:previewImage="@drawable/preview4x3"
a:widgetCategory="home_screen|keyguard"
a:initialLayout="@layout/appwidget4x3"/>

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:a="http://schemas.android.com/apk/res/android"
a:minWidth="250dp"
a:minHeight="250dp"
a:updatePeriodMillis="0"
a:resizeMode="none"
a:previewImage="@drawable/preview4x3"
a:widgetCategory="home_screen|keyguard"
a:initialLayout="@layout/appwidget4x4" />

View File

@ -8,7 +8,7 @@
a:title="@string/settings.appearance_title" a:title="@string/settings.appearance_title"
app:iconSpaceReserved="false"> app:iconSpaceReserved="false">
<ListPreference <ListPreference
a:defaultValue="@string/preferences_key_theme_dark" a:defaultValue="@string/setting_key.theme_day_night"
a:entries="@array/themeNames" a:entries="@array/themeNames"
a:entryValues="@array/themeValues" a:entryValues="@array/themeValues"
a:key="@string/setting_key.theme" a:key="@string/setting_key.theme"