From c831bea22d718dca5a9fe7df10ac5216c1eb4024 Mon Sep 17 00:00:00 2001 From: Nite Date: Mon, 3 Oct 2022 13:39:09 +0000 Subject: [PATCH] Updated widget handling and layouts, add Day&Night theme --- ultrasonic/build.gradle | 2 + ultrasonic/lint-baseline.xml | 347 +------------- ultrasonic/src/main/AndroidManifest.xml | 42 +- .../ultrasonic/activity/NavigationActivity.kt | 15 +- .../ultrasonic/adapters/TrackViewBinder.kt | 6 - .../ultrasonic/adapters/TrackViewHolder.kt | 23 +- .../org/moire/ultrasonic/adapters/Utils.kt | 39 -- .../ultrasonic/fragment/DownloadsFragment.kt | 1 - .../ultrasonic/fragment/EditServerFragment.kt | 16 +- .../ultrasonic/fragment/NowPlayingFragment.kt | 4 +- .../ultrasonic/fragment/PlayerFragment.kt | 6 +- .../ultrasonic/fragment/SearchFragment.kt | 1 - .../fragment/ServerSelectorFragment.kt | 1 - .../fragment/TrackCollectionFragment.kt | 1 - .../ultrasonic/imageloader/ImageLoader.kt | 3 +- .../playback/AutoMediaBrowserCallback.kt | 2 +- .../ultrasonic/playback/PlaybackService.kt | 18 + .../provider/UltrasonicAppWidgetProvider.kt | 302 +++++++----- .../UltrasonicAppWidgetProvider4X1.kt | 28 -- .../UltrasonicAppWidgetProvider4X2.kt | 28 -- .../UltrasonicAppWidgetProvider4X3.kt | 28 -- .../UltrasonicAppWidgetProvider4X4.kt | 28 -- .../service/MediaPlayerController.kt | 21 - .../org/moire/ultrasonic/util/ServerColor.kt | 32 +- .../org/moire/ultrasonic/util/Settings.kt | 2 +- .../kotlin/org/moire/ultrasonic/util/Util.kt | 24 +- .../appwidget_dark_bg_trans.9.png | Bin 889 -> 0 bytes .../main/res/drawable-nodpi/preview4x1.png | Bin 8850 -> 0 bytes .../main/res/drawable-nodpi/preview4x2.png | Bin 12161 -> 0 bytes .../main/res/drawable-nodpi/preview4x3.png | Bin 16775 -> 0 bytes .../res/drawable/app_widget_background.xml | 12 + ultrasonic/src/main/res/drawable/circle.xml | 2 +- .../src/main/res/drawable/default_ripple.xml | 2 +- .../main/res/drawable/ic_contact_picture.xml | 9 +- ..._download_anim_0.xml => ic_downloaded.xml} | 0 ...backward_normal.xml => media_backward.xml} | 0 ...a_forward_normal.xml => media_forward.xml} | 0 ...media_pause_normal.xml => media_pause.xml} | 0 ...a_shuffle_normal.xml => media_shuffle.xml} | 0 ...media_start_normal.xml => media_start.xml} | 2 +- .../{media_stop_normal.xml => media_stop.xml} | 0 ..._list_normal.xml => media_toggle_list.xml} | 0 .../src/main/res/drawable/rounded_border.xml | 10 +- .../src/main/res/drawable/select_ripple.xml | 7 +- .../res/drawable/stat_sys_download_anim_1.xml | 14 - .../res/drawable/stat_sys_download_anim_2.xml | 14 - .../res/drawable/stat_sys_download_anim_3.xml | 14 - .../res/drawable/stat_sys_download_anim_4.xml | 14 - .../res/drawable/stat_sys_download_anim_5.xml | 14 - .../res/drawable/stat_sys_download_anim_6.xml | 14 - .../res/drawable/stat_sys_download_anim_7.xml | 14 - .../src/main/res/drawable/widget_preview.xml | 14 + .../src/main/res/layout/album_buttons.xml | 4 +- .../src/main/res/layout/appwidget4x1.xml | 105 ----- .../src/main/res/layout/appwidget4x3.xml | 129 ------ .../src/main/res/layout/appwidget4x4.xml | 130 ------ ...pwidget4x2.xml => appwidget_landscape.xml} | 109 +++-- .../res/layout/appwidget_landscape_small.xml | 112 +++++ .../main/res/layout/appwidget_portrait.xml | 128 ++++++ .../src/main/res/layout/list_header_album.xml | 20 +- .../src/main/res/layout/list_item_divider.xml | 5 +- .../src/main/res/layout/list_item_track.xml | 4 +- ultrasonic/src/main/res/layout/main.xml | 23 +- .../src/main/res/layout/media_buttons.xml | 12 +- .../main/res/layout/navigation_activity.xml | 4 +- .../src/main/res/layout/navigation_header.xml | 9 +- .../src/main/res/layout/now_playing.xml | 19 +- .../src/main/res/layout/server_edit.xml | 428 +++++++++--------- .../src/main/res/layout/share_details.xml | 6 +- ultrasonic/src/main/res/menu/navigation.xml | 5 +- ultrasonic/src/main/res/menu/nowplaying.xml | 2 +- ultrasonic/src/main/res/menu/select_album.xml | 2 +- .../src/main/res/values-night/themes.xml | 8 + ultrasonic/src/main/res/values-v31/styles.xml | 14 + ultrasonic/src/main/res/values-v31/themes.xml | 17 + ultrasonic/src/main/res/values/arrays.xml | 8 +- ultrasonic/src/main/res/values/attrs.xml | 8 + ultrasonic/src/main/res/values/colors.xml | 2 - ultrasonic/src/main/res/values/constants.xml | 10 - .../src/main/res/values/setting_keys.xml | 1 + ultrasonic/src/main/res/values/strings.xml | 1 + ultrasonic/src/main/res/values/styles.xml | 55 ++- ultrasonic/src/main/res/values/themes.xml | 40 +- .../src/main/res/xml/appwidget_info.xml | 17 + .../src/main/res/xml/appwidget_info_4x1.xml | 11 - .../src/main/res/xml/appwidget_info_4x2.xml | 11 - .../src/main/res/xml/appwidget_info_4x3.xml | 9 - .../src/main/res/xml/appwidget_info_4x4.xml | 9 - ultrasonic/src/main/res/xml/settings.xml | 2 +- 89 files changed, 1023 insertions(+), 1632 deletions(-) delete mode 100644 ultrasonic/src/main/kotlin/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider4X1.kt delete mode 100644 ultrasonic/src/main/kotlin/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider4X2.kt delete mode 100644 ultrasonic/src/main/kotlin/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider4X3.kt delete mode 100644 ultrasonic/src/main/kotlin/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider4X4.kt delete mode 100644 ultrasonic/src/main/res/drawable-hdpi/appwidget_dark_bg_trans.9.png delete mode 100644 ultrasonic/src/main/res/drawable-nodpi/preview4x1.png delete mode 100644 ultrasonic/src/main/res/drawable-nodpi/preview4x2.png delete mode 100644 ultrasonic/src/main/res/drawable-nodpi/preview4x3.png create mode 100644 ultrasonic/src/main/res/drawable/app_widget_background.xml rename ultrasonic/src/main/res/drawable/{stat_sys_download_anim_0.xml => ic_downloaded.xml} (100%) rename ultrasonic/src/main/res/drawable/{media_backward_normal.xml => media_backward.xml} (100%) rename ultrasonic/src/main/res/drawable/{media_forward_normal.xml => media_forward.xml} (100%) rename ultrasonic/src/main/res/drawable/{media_pause_normal.xml => media_pause.xml} (100%) rename ultrasonic/src/main/res/drawable/{media_shuffle_normal.xml => media_shuffle.xml} (100%) rename ultrasonic/src/main/res/drawable/{media_start_normal.xml => media_start.xml} (90%) rename ultrasonic/src/main/res/drawable/{media_stop_normal.xml => media_stop.xml} (100%) rename ultrasonic/src/main/res/drawable/{media_toggle_list_normal.xml => media_toggle_list.xml} (100%) delete mode 100644 ultrasonic/src/main/res/drawable/stat_sys_download_anim_1.xml delete mode 100644 ultrasonic/src/main/res/drawable/stat_sys_download_anim_2.xml delete mode 100644 ultrasonic/src/main/res/drawable/stat_sys_download_anim_3.xml delete mode 100644 ultrasonic/src/main/res/drawable/stat_sys_download_anim_4.xml delete mode 100644 ultrasonic/src/main/res/drawable/stat_sys_download_anim_5.xml delete mode 100644 ultrasonic/src/main/res/drawable/stat_sys_download_anim_6.xml delete mode 100644 ultrasonic/src/main/res/drawable/stat_sys_download_anim_7.xml create mode 100644 ultrasonic/src/main/res/drawable/widget_preview.xml delete mode 100644 ultrasonic/src/main/res/layout/appwidget4x1.xml delete mode 100644 ultrasonic/src/main/res/layout/appwidget4x3.xml delete mode 100644 ultrasonic/src/main/res/layout/appwidget4x4.xml rename ultrasonic/src/main/res/layout/{appwidget4x2.xml => appwidget_landscape.xml} (54%) create mode 100644 ultrasonic/src/main/res/layout/appwidget_landscape_small.xml create mode 100644 ultrasonic/src/main/res/layout/appwidget_portrait.xml create mode 100644 ultrasonic/src/main/res/values-night/themes.xml create mode 100644 ultrasonic/src/main/res/values-v31/styles.xml create mode 100644 ultrasonic/src/main/res/values-v31/themes.xml create mode 100644 ultrasonic/src/main/res/values/attrs.xml delete mode 100644 ultrasonic/src/main/res/values/constants.xml create mode 100644 ultrasonic/src/main/res/xml/appwidget_info.xml delete mode 100644 ultrasonic/src/main/res/xml/appwidget_info_4x1.xml delete mode 100644 ultrasonic/src/main/res/xml/appwidget_info_4x2.xml delete mode 100644 ultrasonic/src/main/res/xml/appwidget_info_4x3.xml delete mode 100644 ultrasonic/src/main/res/xml/appwidget_info_4x4.xml diff --git a/ultrasonic/build.gradle b/ultrasonic/build.gradle index 0ae0cb25..5639d78a 100644 --- a/ultrasonic/build.gradle +++ b/ultrasonic/build.gradle @@ -77,6 +77,8 @@ android { ignore 'MissingTranslation', 'UnusedQuantity', 'MissingQuantity' warning 'ImpliedQuantity' disable 'ObsoleteLintCustomCheck' + textReport true + checkDependencies true } namespace 'org.moire.ultrasonic' diff --git a/ultrasonic/lint-baseline.xml b/ultrasonic/lint-baseline.xml index f3794053..bb988f23 100644 --- a/ultrasonic/lint-baseline.xml +++ b/ultrasonic/lint-baseline.xml @@ -1,5 +1,5 @@ - + @@ -55,7 +55,7 @@ errorLine2=" ~~~~~~~~"> @@ -103,50 +103,6 @@ column="9"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -117,43 +117,7 @@ - - - - - - - - - - - - - - - - - - - - - + android:resource="@xml/appwidget_info"/> diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/activity/NavigationActivity.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/activity/NavigationActivity.kt index 23b00828..6206816e 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/activity/NavigationActivity.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/activity/NavigationActivity.kt @@ -13,6 +13,7 @@ import android.content.Intent import android.content.res.ColorStateList import android.content.res.Resources import android.media.AudioManager +import android.os.Build import android.os.Bundle import android.provider.MediaStore import android.provider.SearchRecentSuggestions @@ -246,14 +247,19 @@ class NavigationActivity : AppCompatActivity() { } 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() if (cachedServerCount == 0) selectServerButton?.text = getString(R.string.main_setup_server, activeServer.name) else selectServerButton?.text = activeServer.name - val foregroundColor = ServerColor.getForegroundColor(this, activeServer.color) - val backgroundColor = ServerColor.getBackgroundColor(this, activeServer.color) + val foregroundColor = + ServerColor.getForegroundColor(this, activeServer.color, showVectorBackground) + val backgroundColor = + ServerColor.getBackgroundColor(this, activeServer.color) if (activeServer.index == 0) selectServerButton?.icon = @@ -265,6 +271,11 @@ class NavigationActivity : AppCompatActivity() { selectServerButton?.iconTint = ColorStateList.valueOf(foregroundColor) selectServerButton?.setTextColor(foregroundColor) 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 { diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/adapters/TrackViewBinder.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/adapters/TrackViewBinder.kt index a182166e..bca305aa 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/adapters/TrackViewBinder.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/adapters/TrackViewBinder.kt @@ -1,7 +1,6 @@ package org.moire.ultrasonic.adapters import android.annotation.SuppressLint -import android.content.Context import android.view.LayoutInflater import android.view.MenuItem import android.view.MotionEvent @@ -21,7 +20,6 @@ class TrackViewBinder( val onContextMenuClick: ((MenuItem, Track) -> Boolean)? = null, val checkable: Boolean, val draggable: Boolean, - context: Context, val lifecycleOwner: LifecycleOwner, val createContextMenu: (View, Track) -> PopupMenu = { view, _ -> Utils.createPopupMenu( @@ -36,8 +34,6 @@ class TrackViewBinder( // Set our layout files val layout = R.layout.list_item_track - private val imageHelper: Utils.ImageHelper = Utils.ImageHelper(context) - override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup): TrackViewHolder { return TrackViewHolder(inflater.inflate(layout, parent, false)) } @@ -56,8 +52,6 @@ class TrackViewBinder( } } - holder.imageHelper = imageHelper - // Remove observer before binding holder.observableChecked.removeObservers(lifecycleOwner) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/adapters/TrackViewHolder.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/adapters/TrackViewHolder.kt index 140848e8..9d69167b 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/adapters/TrackViewHolder.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/adapters/TrackViewHolder.kt @@ -1,12 +1,12 @@ package org.moire.ultrasonic.adapters -import android.graphics.drawable.Drawable import android.view.View import android.widget.Checkable import android.widget.CheckedTextView import android.widget.ImageView import android.widget.LinearLayout import android.widget.TextView +import androidx.core.content.ContextCompat import androidx.core.view.isVisible import androidx.lifecycle.MutableLiveData 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 drag: ImageView = view.findViewById(R.id.song_drag) var observableChecked = MutableLiveData(false) - lateinit var imageHelper: Utils.ImageHelper private var rating: LinearLayout = view.findViewById(R.id.song_five_star) 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() } + private val playingIcon by lazy { + ContextCompat.getDrawable(view.context, R.drawable.ic_stat_play)!! + } + private fun setPlayIcon(isPlaying: Boolean) { if (isPlaying && !isPlayingCached) { isPlayingCached = true title.setCompoundDrawablesWithIntrinsicBounds( - imageHelper.playingImage, null, null, null + playingIcon, null, null, null ) } else if (!isPlaying && isPlayingCached) { isPlayingCached = false @@ -214,13 +217,13 @@ class TrackViewHolder(val view: View) : RecyclerView.ViewHolder(view), Checkable when (status) { DownloadState.DONE -> { - showStatusImage(imageHelper.downloadedImage) + showStatusImage(R.drawable.ic_downloaded) } DownloadState.PINNED -> { - showStatusImage(imageHelper.pinImage) + showStatusImage(R.drawable.ic_menu_pin) } DownloadState.FAILED -> { - showStatusImage(imageHelper.errorImage) + showStatusImage(R.drawable.ic_baseline_error) } DownloadState.DOWNLOADING -> { 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 statusImage.isVisible = true - statusImage.setImageDrawable(image) + if (image != null) { + statusImage.setImageResource(image) + } else { + statusImage.setImageDrawable(null) + } } private fun showIndefiniteProgress() { diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/adapters/Utils.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/adapters/Utils.kt index 6fb37037..f7eeb386 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/adapters/Utils.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/adapters/Utils.kt @@ -1,15 +1,11 @@ package org.moire.ultrasonic.adapters -import android.content.Context -import android.graphics.drawable.Drawable import android.view.MenuInflater import android.view.View import android.widget.PopupMenu -import androidx.core.content.ContextCompat import org.moire.ultrasonic.R import org.moire.ultrasonic.data.ActiveServerProvider import org.moire.ultrasonic.domain.Identifiable -import org.moire.ultrasonic.util.Settings object Utils { @JvmStatic @@ -31,41 +27,6 @@ object Utils { 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 - 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 { fun getSectionName(item: Identifiable): String } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/DownloadsFragment.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/DownloadsFragment.kt index e8b453d0..2f7a0832 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/DownloadsFragment.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/DownloadsFragment.kt @@ -57,7 +57,6 @@ class DownloadsFragment : MultiListFragment() { { _, _ -> true }, checkable = false, draggable = false, - context = requireContext(), lifecycleOwner = viewLifecycleOwner ) ) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/EditServerFragment.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/EditServerFragment.kt index 55cdce69..8b253627 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/EditServerFragment.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/EditServerFragment.kt @@ -69,7 +69,7 @@ class EditServerFragment : Fragment(), OnBackPressedHandler { private var userNameEditText: TextInputLayout? = null private var passwordEditText: TextInputLayout? = null private var selfSignedSwitch: SwitchMaterial? = null - private var ldapSwitch: SwitchMaterial? = null + private var plaintextSwitch: SwitchMaterial? = null private var jukeboxSwitch: SwitchMaterial? = null private var saveButton: Button? = null private var testButton: Button? = null @@ -102,7 +102,7 @@ class EditServerFragment : Fragment(), OnBackPressedHandler { userNameEditText = view.findViewById(R.id.edit_server_username) passwordEditText = view.findViewById(R.id.edit_server_password) 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) saveButton = view.findViewById(R.id.edit_save) testButton = view.findViewById(R.id.edit_test) @@ -195,7 +195,7 @@ class EditServerFragment : Fragment(), OnBackPressedHandler { private fun updateColor(color: Int?) { val image = ContextCompat.getDrawable(requireContext(), R.drawable.thumb_drawable) - currentColor = ServerColor.getBackgroundColor(requireContext(), color) + currentColor = color ?: ServerColor.getBackgroundColor(requireContext(), null) image?.setTint(currentColor) serverColorImageView?.background = image } @@ -221,7 +221,7 @@ class EditServerFragment : Fragment(), OnBackPressedHandler { ::selfSignedSwitch.name, selfSignedSwitch!!.isChecked ) savedInstanceState.putBoolean( - ::ldapSwitch.name, ldapSwitch!!.isChecked + ::plaintextSwitch.name, plaintextSwitch!!.isChecked ) savedInstanceState.putBoolean( ::jukeboxSwitch.name, jukeboxSwitch!!.isChecked @@ -258,7 +258,7 @@ class EditServerFragment : Fragment(), OnBackPressedHandler { savedInstanceState.getString(::passwordEditText.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) updateColor(savedInstanceState.getInt(::serverColorImageView.name)) if (savedInstanceState.containsKey(::selectedColor.name)) @@ -277,7 +277,7 @@ class EditServerFragment : Fragment(), OnBackPressedHandler { userNameEditText!!.editText?.setText(currentServerSetting!!.userName) passwordEditText!!.editText?.setText(currentServerSetting!!.password) selfSignedSwitch!!.isChecked = currentServerSetting!!.allowSelfSignedCertificate - ldapSwitch!!.isChecked = currentServerSetting!!.forcePlainTextPassword + plaintextSwitch!!.isChecked = currentServerSetting!!.forcePlainTextPassword jukeboxSwitch!!.isChecked = currentServerSetting!!.jukeboxByDefault updateColor(currentServerSetting!!.color) } @@ -331,7 +331,7 @@ class EditServerFragment : Fragment(), OnBackPressedHandler { currentServerSetting!!.userName = userNameEditText!!.editText?.text.toString() currentServerSetting!!.password = passwordEditText!!.editText?.text.toString() currentServerSetting!!.allowSelfSignedCertificate = selfSignedSwitch!!.isChecked - currentServerSetting!!.forcePlainTextPassword = ldapSwitch!!.isChecked + currentServerSetting!!.forcePlainTextPassword = plaintextSwitch!!.isChecked currentServerSetting!!.jukeboxByDefault = jukeboxSwitch!!.isChecked } @@ -354,7 +354,7 @@ class EditServerFragment : Fragment(), OnBackPressedHandler { currentServerSetting!!.userName != userNameEditText!!.editText?.text.toString() || currentServerSetting!!.password != passwordEditText!!.editText?.text.toString() || currentServerSetting!!.allowSelfSignedCertificate != selfSignedSwitch!!.isChecked || - currentServerSetting!!.forcePlainTextPassword != ldapSwitch!!.isChecked || + currentServerSetting!!.forcePlainTextPassword != plaintextSwitch!!.isChecked || currentServerSetting!!.jukeboxByDefault != jukeboxSwitch!!.isChecked } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/NowPlayingFragment.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/NowPlayingFragment.kt index 04c03efe..87c63d62 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/NowPlayingFragment.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/NowPlayingFragment.kt @@ -86,9 +86,9 @@ class NowPlayingFragment : Fragment() { private fun update() { try { if (mediaPlayerController.isPlaying) { - playButton!!.setIconResource(R.drawable.media_pause_normal) + playButton!!.setIconResource(R.drawable.media_pause) } else { - playButton!!.setIconResource(R.drawable.media_start_normal) + playButton!!.setIconResource(R.drawable.media_start) } val file = mediaPlayerController.currentMediaItem?.toTrack() diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/PlayerFragment.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/PlayerFragment.kt index e3309031..6881fe95 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/PlayerFragment.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/PlayerFragment.kt @@ -838,13 +838,11 @@ class PlayerFragment : viewAdapter.register( TrackViewBinder( onItemClick = clickHandler, + onContextMenuClick = { menu, id -> onContextMenuItemSelected(menu, id) }, checkable = false, draggable = true, - context = requireContext(), lifecycleOwner = viewLifecycleOwner, - createContextMenu = { view, track -> onCreateContextMenu(view, track) }, - onContextMenuClick = { menu, id -> onContextMenuItemSelected(menu, id) }, - ).apply { + ) { view, track -> onCreateContextMenu(view, track) }.apply { this.startDrag = { holder -> dragTouchHelper.startDrag(holder) } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/SearchFragment.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/SearchFragment.kt index cfb8d0d8..e90e06b5 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/SearchFragment.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/SearchFragment.kt @@ -120,7 +120,6 @@ class SearchFragment : MultiListFragment(), KoinComponent { onContextMenuClick = ::onContextMenuItemSelected, checkable = false, draggable = false, - context = requireContext(), lifecycleOwner = viewLifecycleOwner ) ) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ServerSelectorFragment.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ServerSelectorFragment.kt index 9db2cb6b..bb5a9227 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ServerSelectorFragment.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ServerSelectorFragment.kt @@ -63,7 +63,6 @@ class ServerSelectorFragment : Fragment() { listView?.adapter = serverRowAdapter listView?.onItemClickListener = AdapterView.OnItemClickListener { parent, _, position, _ -> - val server = parent.getItemAtPosition(position) as ServerSetting ActiveServerProvider.setActiveServerById(server.id) findNavController().popBackStack(R.id.mainFragment, false) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/TrackCollectionFragment.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/TrackCollectionFragment.kt index a2188078..2f16475c 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/TrackCollectionFragment.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/TrackCollectionFragment.kt @@ -133,7 +133,6 @@ open class TrackCollectionFragment : MultiListFragment() { onContextMenuClick = { menu, id -> onContextMenuItemSelected(menu, id) }, checkable = true, draggable = false, - context = requireContext(), lifecycleOwner = viewLifecycleOwner ) ) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/imageloader/ImageLoader.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/imageloader/ImageLoader.kt index 59be1edc..40908d1b 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/imageloader/ImageLoader.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/imageloader/ImageLoader.kt @@ -16,7 +16,6 @@ import java.io.InputStream import java.io.OutputStream import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.CountDownLatch -import org.moire.ultrasonic.BuildConfig import org.moire.ultrasonic.R import org.moire.ultrasonic.api.subsonic.SubsonicAPIClient import org.moire.ultrasonic.api.subsonic.throwOnFailure @@ -46,7 +45,7 @@ class ImageLoader( .addRequestHandler(AvatarRequestHandler(apiClient)) .memoryCache(LruCache(calculateMemoryCacheSize(context))) .build().apply { - setIndicatorsEnabled(BuildConfig.DEBUG) + // setIndicatorsEnabled(BuildConfig.DEBUG) } private fun load(request: ImageRequest) = when (request) { diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt index d2c861c4..fd8943e3 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt @@ -1203,7 +1203,7 @@ class AutoMediaBrowserCallback(var player: Player, val libraryService: MediaLibr mediaId, null, false, - icon = R.drawable.media_start_normal + icon = R.drawable.media_start ) } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/PlaybackService.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/PlaybackService.kt index 7f3c8145..ad9ceacd 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/PlaybackService.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/PlaybackService.kt @@ -30,6 +30,8 @@ import org.moire.ultrasonic.activity.NavigationActivity import org.moire.ultrasonic.app.UApp import org.moire.ultrasonic.audiofx.EqualizerController 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.MusicServiceFactory.getMusicService import org.moire.ultrasonic.service.RxBus @@ -172,6 +174,12 @@ class PlaybackService : MediaLibraryService(), KoinComponent { } override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) { + updateWidgetTrack(mediaItem?.toTrack()) + cacheNextSongs() + } + + override fun onIsPlayingChanged(isPlaying: Boolean) { + updateWidgetPlayerState(isPlaying) cacheNextSongs() } } @@ -205,4 +213,14 @@ class PlaybackService : MediaLibraryService(), KoinComponent { .setContentType(C.AUDIO_CONTENT_TYPE_MUSIC) .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) + } } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider.kt index ffd579cc..337f91f0 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider.kt @@ -14,10 +14,12 @@ import android.content.ComponentName import android.content.Context import android.content.Intent import android.os.Build +import android.os.Bundle import android.os.Environment import android.view.KeyEvent +import android.view.View.GONE +import android.view.View.VISIBLE import android.widget.RemoteViews -import java.lang.Exception import org.moire.ultrasonic.R import org.moire.ultrasonic.activity.NavigationActivity import org.moire.ultrasonic.domain.Track @@ -27,141 +29,197 @@ import org.moire.ultrasonic.util.Constants import timber.log.Timber /** - * Widget Provider for the Ultrasonic Widgets + * Widget Provider for the Ultrasonic Widget */ @Suppress("MagicNumber") open class UltrasonicAppWidgetProvider : AppWidgetProvider() { - @JvmField - protected var layoutId = 0 + override fun onUpdate( context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray ) { - defaultAppWidget(context, appWidgetIds) + updateTrackAndState(context, appWidgetIds) } - /** - * Initialize given widgets to default state, where we launch Ultrasonic on default click - * and hide actions if service not running. - */ - private fun defaultAppWidget(context: Context, appWidgetIds: IntArray) { - 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 + override fun onAppWidgetOptionsChanged( + context: Context?, + appWidgetManager: AppWidgetManager?, + appWidgetId: Int, + newOptions: Bundle? ) { - Timber.d("Updating Widget") - 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) + updateTrackAndState(context!!, intArrayOf(appWidgetId)) } 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]. */ diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider4X1.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider4X1.kt deleted file mode 100644 index 5efd3ca8..00000000 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider4X1.kt +++ /dev/null @@ -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 - } -} diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider4X2.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider4X2.kt deleted file mode 100644 index 7235a998..00000000 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider4X2.kt +++ /dev/null @@ -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 - } -} diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider4X3.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider4X3.kt deleted file mode 100644 index 7b9187cf..00000000 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider4X3.kt +++ /dev/null @@ -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 - } -} diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider4X4.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider4X4.kt deleted file mode 100644 index d641ff4a..00000000 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider4X4.kt +++ /dev/null @@ -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 - } -} diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt index 64aee927..29208e5b 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt @@ -36,10 +36,6 @@ import org.moire.ultrasonic.data.ActiveServerProvider import org.moire.ultrasonic.data.ActiveServerProvider.Companion.OFFLINE_DB_ID import org.moire.ultrasonic.domain.Track 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.util.MainThreadExecutor 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 { playbackStateSerializer.serializeNow( playlist.map { it.toTrack() }, @@ -236,9 +227,6 @@ class MediaPlayerController( scrobbler.scrobble(currentPlaying, true) } } - - // Update widget - updateWidget(currentPlaying) } private fun clearBookmark() { @@ -267,15 +255,6 @@ class MediaPlayerController( 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() { if (!created) return diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/ServerColor.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/ServerColor.kt index 565e3aa7..7d4d35d7 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/ServerColor.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/ServerColor.kt @@ -8,29 +8,45 @@ package org.moire.ultrasonic.util import android.content.Context +import androidx.annotation.ColorInt import androidx.core.content.ContextCompat 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_CORRECTION = -0.25 /** * Contains functions for computing server display colors */ object ServerColor { + + @ColorInt fun getBackgroundColor(context: Context, serverColor: Int?): Int { - return serverColor ?: ContextCompat.getColor( - context, Util.getResourceFromAttribute(context, R.attr.colorPrimary) - ) + return if (serverColor != null) { + 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 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) { - ContextCompat.getColor(context, R.color.selected_menu_dark) + ContextCompat.getColor(context, org.moire.ultrasonic.R.color.selected_menu_dark) } else { - ContextCompat.getColor(context, R.color.selected_menu_light) + ContextCompat.getColor(context, org.moire.ultrasonic.R.color.selected_menu_light) } } } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/Settings.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/Settings.kt index bce819ad..a1c81eb7 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/Settings.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/Settings.kt @@ -22,7 +22,7 @@ object Settings { @JvmStatic var theme by StringSetting( getKey(R.string.setting_key_theme), - getKey(R.string.setting_key_theme_dark) + getKey(R.string.setting_key_theme_day_night) ) @JvmStatic diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/Util.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/Util.kt index eeada628..4a043715 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/Util.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/Util.kt @@ -31,7 +31,6 @@ import android.os.Build import android.os.Environment import android.text.TextUtils import android.util.DisplayMetrics -import android.util.TypedValue import android.view.Gravity import android.view.inputmethod.InputMethodManager import android.widget.Toast @@ -94,21 +93,13 @@ object Util { fun applyTheme(context: Context?) { if (context == null) return val style = getStyleFromSettings(context) + // First set the theme (light, dark, etc.) 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 { - // 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()) { context.getString(R.string.setting_key_theme_dark) -> { R.style.UltrasonicTheme_Dark @@ -120,7 +111,7 @@ object Util { R.style.UltrasonicTheme_Light } 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 { if (Settings.firstRunExecuted) return false diff --git a/ultrasonic/src/main/res/drawable-hdpi/appwidget_dark_bg_trans.9.png b/ultrasonic/src/main/res/drawable-hdpi/appwidget_dark_bg_trans.9.png deleted file mode 100644 index f4fcb51be7b1d369d08cece3c5bf4221ddfc261c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 889 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 diff --git a/ultrasonic/src/main/res/drawable-nodpi/preview4x3.png b/ultrasonic/src/main/res/drawable-nodpi/preview4x3.png deleted file mode 100644 index 665aa40c599d0479d168240c8bdb1dec501be8a2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16775 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 diff --git a/ultrasonic/src/main/res/drawable/app_widget_background.xml b/ultrasonic/src/main/res/drawable/app_widget_background.xml new file mode 100644 index 00000000..d1807771 --- /dev/null +++ b/ultrasonic/src/main/res/drawable/app_widget_background.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/ultrasonic/src/main/res/drawable/circle.xml b/ultrasonic/src/main/res/drawable/circle.xml index bda25c31..23c1d9cd 100644 --- a/ultrasonic/src/main/res/drawable/circle.xml +++ b/ultrasonic/src/main/res/drawable/circle.xml @@ -1,7 +1,7 @@ - + diff --git a/ultrasonic/src/main/res/drawable/default_ripple.xml b/ultrasonic/src/main/res/drawable/default_ripple.xml index 4390ea04..cd11a5d8 100644 --- a/ultrasonic/src/main/res/drawable/default_ripple.xml +++ b/ultrasonic/src/main/res/drawable/default_ripple.xml @@ -5,7 +5,7 @@ - + diff --git a/ultrasonic/src/main/res/drawable/ic_contact_picture.xml b/ultrasonic/src/main/res/drawable/ic_contact_picture.xml index ab3465e4..904f6690 100644 --- a/ultrasonic/src/main/res/drawable/ic_contact_picture.xml +++ b/ultrasonic/src/main/res/drawable/ic_contact_picture.xml @@ -2,8 +2,13 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24"> + android:viewportHeight="24" + android:tint="?attr/colorControlHighlight"> + + diff --git a/ultrasonic/src/main/res/drawable/stat_sys_download_anim_0.xml b/ultrasonic/src/main/res/drawable/ic_downloaded.xml similarity index 100% rename from ultrasonic/src/main/res/drawable/stat_sys_download_anim_0.xml rename to ultrasonic/src/main/res/drawable/ic_downloaded.xml diff --git a/ultrasonic/src/main/res/drawable/media_backward_normal.xml b/ultrasonic/src/main/res/drawable/media_backward.xml similarity index 100% rename from ultrasonic/src/main/res/drawable/media_backward_normal.xml rename to ultrasonic/src/main/res/drawable/media_backward.xml diff --git a/ultrasonic/src/main/res/drawable/media_forward_normal.xml b/ultrasonic/src/main/res/drawable/media_forward.xml similarity index 100% rename from ultrasonic/src/main/res/drawable/media_forward_normal.xml rename to ultrasonic/src/main/res/drawable/media_forward.xml diff --git a/ultrasonic/src/main/res/drawable/media_pause_normal.xml b/ultrasonic/src/main/res/drawable/media_pause.xml similarity index 100% rename from ultrasonic/src/main/res/drawable/media_pause_normal.xml rename to ultrasonic/src/main/res/drawable/media_pause.xml diff --git a/ultrasonic/src/main/res/drawable/media_shuffle_normal.xml b/ultrasonic/src/main/res/drawable/media_shuffle.xml similarity index 100% rename from ultrasonic/src/main/res/drawable/media_shuffle_normal.xml rename to ultrasonic/src/main/res/drawable/media_shuffle.xml diff --git a/ultrasonic/src/main/res/drawable/media_start_normal.xml b/ultrasonic/src/main/res/drawable/media_start.xml similarity index 90% rename from ultrasonic/src/main/res/drawable/media_start_normal.xml rename to ultrasonic/src/main/res/drawable/media_start.xml index 04b5b2bd..cc4ff3f6 100644 --- a/ultrasonic/src/main/res/drawable/media_start_normal.xml +++ b/ultrasonic/src/main/res/drawable/media_start.xml @@ -5,6 +5,6 @@ android:viewportHeight="24" android:tint="?attr/colorControlNormal"> diff --git a/ultrasonic/src/main/res/drawable/media_stop_normal.xml b/ultrasonic/src/main/res/drawable/media_stop.xml similarity index 100% rename from ultrasonic/src/main/res/drawable/media_stop_normal.xml rename to ultrasonic/src/main/res/drawable/media_stop.xml diff --git a/ultrasonic/src/main/res/drawable/media_toggle_list_normal.xml b/ultrasonic/src/main/res/drawable/media_toggle_list.xml similarity index 100% rename from ultrasonic/src/main/res/drawable/media_toggle_list_normal.xml rename to ultrasonic/src/main/res/drawable/media_toggle_list.xml diff --git a/ultrasonic/src/main/res/drawable/rounded_border.xml b/ultrasonic/src/main/res/drawable/rounded_border.xml index 5c60efee..2f2a9842 100644 --- a/ultrasonic/src/main/res/drawable/rounded_border.xml +++ b/ultrasonic/src/main/res/drawable/rounded_border.xml @@ -3,10 +3,10 @@ android:shape="rectangle"> + android:topLeftRadius="28dp" + android:topRightRadius="28dp" + android:bottomRightRadius="28dp" + android:bottomLeftRadius="28dp" /> - + \ No newline at end of file diff --git a/ultrasonic/src/main/res/drawable/select_ripple.xml b/ultrasonic/src/main/res/drawable/select_ripple.xml index f2b692e8..ce8c6310 100644 --- a/ultrasonic/src/main/res/drawable/select_ripple.xml +++ b/ultrasonic/src/main/res/drawable/select_ripple.xml @@ -1,17 +1,16 @@ - - + - + \ No newline at end of file diff --git a/ultrasonic/src/main/res/drawable/stat_sys_download_anim_1.xml b/ultrasonic/src/main/res/drawable/stat_sys_download_anim_1.xml deleted file mode 100644 index 34504676..00000000 --- a/ultrasonic/src/main/res/drawable/stat_sys_download_anim_1.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/ultrasonic/src/main/res/drawable/stat_sys_download_anim_2.xml b/ultrasonic/src/main/res/drawable/stat_sys_download_anim_2.xml deleted file mode 100644 index 7c3b29d9..00000000 --- a/ultrasonic/src/main/res/drawable/stat_sys_download_anim_2.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/ultrasonic/src/main/res/drawable/stat_sys_download_anim_3.xml b/ultrasonic/src/main/res/drawable/stat_sys_download_anim_3.xml deleted file mode 100644 index 2d55a557..00000000 --- a/ultrasonic/src/main/res/drawable/stat_sys_download_anim_3.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/ultrasonic/src/main/res/drawable/stat_sys_download_anim_4.xml b/ultrasonic/src/main/res/drawable/stat_sys_download_anim_4.xml deleted file mode 100644 index 8ab08780..00000000 --- a/ultrasonic/src/main/res/drawable/stat_sys_download_anim_4.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/ultrasonic/src/main/res/drawable/stat_sys_download_anim_5.xml b/ultrasonic/src/main/res/drawable/stat_sys_download_anim_5.xml deleted file mode 100644 index da7a3cce..00000000 --- a/ultrasonic/src/main/res/drawable/stat_sys_download_anim_5.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/ultrasonic/src/main/res/drawable/stat_sys_download_anim_6.xml b/ultrasonic/src/main/res/drawable/stat_sys_download_anim_6.xml deleted file mode 100644 index ce14d3c4..00000000 --- a/ultrasonic/src/main/res/drawable/stat_sys_download_anim_6.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/ultrasonic/src/main/res/drawable/stat_sys_download_anim_7.xml b/ultrasonic/src/main/res/drawable/stat_sys_download_anim_7.xml deleted file mode 100644 index 9255455d..00000000 --- a/ultrasonic/src/main/res/drawable/stat_sys_download_anim_7.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/ultrasonic/src/main/res/drawable/widget_preview.xml b/ultrasonic/src/main/res/drawable/widget_preview.xml new file mode 100644 index 00000000..16635c3f --- /dev/null +++ b/ultrasonic/src/main/res/drawable/widget_preview.xml @@ -0,0 +1,14 @@ + + + + + diff --git a/ultrasonic/src/main/res/layout/album_buttons.xml b/ultrasonic/src/main/res/layout/album_buttons.xml index 87bb262a..391f946c 100644 --- a/ultrasonic/src/main/res/layout/album_buttons.xml +++ b/ultrasonic/src/main/res/layout/album_buttons.xml @@ -29,7 +29,7 @@ android:layout_weight="1" android:visibility="gone" android:contentDescription="@string/common.play_now" - app:icon="@drawable/media_start_normal" + app:icon="@drawable/media_start" app:iconGravity="textTop" app:iconSize="26dp" /> @@ -120,7 +120,7 @@ android:layout_weight="1" android:visibility="gone" android:contentDescription="@string/search.more" - app:icon="@drawable/media_forward_normal" + app:icon="@drawable/media_forward" app:iconGravity="textTop" app:iconSize="26dp" /> diff --git a/ultrasonic/src/main/res/layout/appwidget4x1.xml b/ultrasonic/src/main/res/layout/appwidget4x1.xml deleted file mode 100644 index 2c51a624..00000000 --- a/ultrasonic/src/main/res/layout/appwidget4x1.xml +++ /dev/null @@ -1,105 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/ultrasonic/src/main/res/layout/appwidget4x3.xml b/ultrasonic/src/main/res/layout/appwidget4x3.xml deleted file mode 100644 index c752f346..00000000 --- a/ultrasonic/src/main/res/layout/appwidget4x3.xml +++ /dev/null @@ -1,129 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/ultrasonic/src/main/res/layout/appwidget4x4.xml b/ultrasonic/src/main/res/layout/appwidget4x4.xml deleted file mode 100644 index d4fa7a40..00000000 --- a/ultrasonic/src/main/res/layout/appwidget4x4.xml +++ /dev/null @@ -1,130 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/ultrasonic/src/main/res/layout/appwidget4x2.xml b/ultrasonic/src/main/res/layout/appwidget_landscape.xml similarity index 54% rename from ultrasonic/src/main/res/layout/appwidget4x2.xml rename to ultrasonic/src/main/res/layout/appwidget_landscape.xml index fc59026c..cdc82fcc 100644 --- a/ultrasonic/src/main/res/layout/appwidget4x2.xml +++ b/ultrasonic/src/main/res/layout/appwidget_landscape.xml @@ -1,26 +1,35 @@ + a:orientation="horizontal" + a:theme="@style/Theme.AppWidget.AppWidgetContainerCropped" + tools:ignore="UseAppTint"> + a:layout_gravity="center_vertical" + a:layout_marginHorizontal="5dp" + a:gravity="center" + a:orientation="vertical"> + a:gravity="center" + a:orientation="vertical"> + a:paddingEnd="4dip" + a:textAppearance="@style/TextAppearance.Material3.TitleMedium" + tools:text="Title" /> - - + + - + a:textAppearance="@style/TextAppearance.Material3.BodyLarge" + tools:text="Album" /> + + a:orientation="horizontal" + a:paddingTop="4dip" + a:paddingBottom="4dip"> + a:layout_width="0dp" + a:layout_height="40dp" + a:layout_gravity="center" + a:layout_weight="0.6" + a:importantForAccessibility="no" + a:src="@drawable/media_backward" + /> + a:importantForAccessibility="no" + a:src="@drawable/media_start" + /> + a:layout_width="0dp" + a:layout_height="40dp" + a:layout_gravity="center" + a:layout_weight="0.6" + a:importantForAccessibility="no" + a:src="@drawable/media_forward" + /> diff --git a/ultrasonic/src/main/res/layout/appwidget_landscape_small.xml b/ultrasonic/src/main/res/layout/appwidget_landscape_small.xml new file mode 100644 index 00000000..8fc7f14e --- /dev/null +++ b/ultrasonic/src/main/res/layout/appwidget_landscape_small.xml @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ultrasonic/src/main/res/layout/appwidget_portrait.xml b/ultrasonic/src/main/res/layout/appwidget_portrait.xml new file mode 100644 index 00000000..b2cea920 --- /dev/null +++ b/ultrasonic/src/main/res/layout/appwidget_portrait.xml @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ultrasonic/src/main/res/layout/list_header_album.xml b/ultrasonic/src/main/res/layout/list_header_album.xml index c33df633..256082f0 100644 --- a/ultrasonic/src/main/res/layout/list_header_album.xml +++ b/ultrasonic/src/main/res/layout/list_header_album.xml @@ -1,7 +1,7 @@ + a:layout_width="fill_parent" + a:layout_height="wrap_content"> + a:src="@drawable/unknown_album" /> + a:textAppearance="?android:attr/textAppearanceMedium" /> + a:textAppearance="?android:attr/textAppearanceSmall" /> + a:textAppearance="?android:attr/textAppearanceSmall" /> + a:textAppearance="?android:attr/textAppearanceSmall" /> + a:textAppearance="?android:attr/textAppearanceSmall" /> + a:textAppearance="?android:attr/textAppearanceSmall" /> \ No newline at end of file diff --git a/ultrasonic/src/main/res/layout/list_item_divider.xml b/ultrasonic/src/main/res/layout/list_item_divider.xml index 1f71852c..289c9ec4 100644 --- a/ultrasonic/src/main/res/layout/list_item_divider.xml +++ b/ultrasonic/src/main/res/layout/list_item_divider.xml @@ -11,9 +11,6 @@ a:layout_height="wrap_content" a:gravity="center_vertical" a:paddingStart="6dp" - a:textAllCaps="true" - a:textAppearance="?android:attr/textAppearanceSmall" - a:textColor="?attr/colorPrimary" - a:textStyle="bold" /> + a:theme="@style/Ultrasonic.AllCapsLabel" /> diff --git a/ultrasonic/src/main/res/layout/list_item_track.xml b/ultrasonic/src/main/res/layout/list_item_track.xml index ad096bdd..55ebfb86 100644 --- a/ultrasonic/src/main/res/layout/list_item_track.xml +++ b/ultrasonic/src/main/res/layout/list_item_track.xml @@ -98,12 +98,12 @@ a:layout_marginTop="8dp" a:layout_marginEnd="10dip" a:indeterminate="true" - app:indicatorColor="?attr/color_menu_selected" + app:indicatorColor="?attr/colorOnSurface" app:indicatorSize="20dp" app:layout_constraintBottom_toTopOf="@id/song_duration" app:layout_constraintEnd_toEndOf="@id/star_barrier" app:layout_constraintTop_toTopOf="parent" - app:trackColor="?attr/color_selected" /> + app:trackColor="?attr/colorSurfaceVariant" /> + /> + android:theme="@style/Ultrasonic.AllCapsLabel" /> + android:text="@string/main.albums_title" /> + android:theme="@style/Ultrasonic.AllCapsLabel" /> @@ -48,7 +48,7 @@ a:scaleType="fitCenter" tools:visibility="gone" a:contentDescription="@string/buttons.play" - app:icon="@drawable/media_start_normal" + app:icon="@drawable/media_start" app:iconGravity="textTop" app:iconSize="48dp" /> @@ -62,7 +62,7 @@ a:contentDescription="@string/buttons.pause" a:focusable="true" a:scaleType="fitCenter" - app:icon="@drawable/media_pause_normal" + app:icon="@drawable/media_pause" app:iconGravity="textTop" app:iconSize="48dp" /> @@ -77,7 +77,7 @@ a:scaleType="fitCenter" tools:visibility="gone" a:contentDescription="@string/buttons.stop" - app:icon="@drawable/media_stop_normal" + app:icon="@drawable/media_stop" app:iconGravity="textTop" app:iconSize="48dp" /> @@ -92,7 +92,7 @@ a:contentDescription="@string/buttons.next" a:focusable="true" a:scaleType="fitCenter" - app:icon="@drawable/media_forward_normal" + app:icon="@drawable/media_forward" app:iconGravity="textTop" app:iconSize="32dp" /> diff --git a/ultrasonic/src/main/res/layout/navigation_activity.xml b/ultrasonic/src/main/res/layout/navigation_activity.xml index a2f722e4..b9c38450 100644 --- a/ultrasonic/src/main/res/layout/navigation_activity.xml +++ b/ultrasonic/src/main/res/layout/navigation_activity.xml @@ -6,7 +6,8 @@ a:id="@+id/drawer_layout" a:layout_width="match_parent" a:layout_height="match_parent" - tools:context="org.moire.ultrasonic.activity.NavigationActivity"> + tools:context="org.moire.ultrasonic.activity.NavigationActivity" + a:fitsSystemWindows="true"> \ No newline at end of file diff --git a/ultrasonic/src/main/res/layout/navigation_header.xml b/ultrasonic/src/main/res/layout/navigation_header.xml index 11ec6a96..2f8abec8 100644 --- a/ultrasonic/src/main/res/layout/navigation_header.xml +++ b/ultrasonic/src/main/res/layout/navigation_header.xml @@ -24,19 +24,20 @@ style="@style/Widget.Material3.Button.TextButton.Icon" a:layout_width="match_parent" a:layout_height="wrap_content" - a:layout_marginTop="0dp" + a:layout_marginStart="3dp" + a:layout_marginTop="24dp" a:background="@drawable/default_ripple" a:gravity="center_vertical" a:paddingHorizontal="22dp" a:paddingTop="16dp" a:paddingBottom="16dp" a:text="@string/main.offline" - a:textAppearance="@style/MenuDrawer.Widget" + a:textAppearance="@style/TextAppearance.Material3.TitleMedium" a:textColor="?attr/colorOnPrimary" - a:textSize="17sp" - a:textStyle="bold" + a:textSize="18sp" app:icon="@drawable/ic_menu_select_server" app:iconPadding="12dp" + app:iconSize="24dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" diff --git a/ultrasonic/src/main/res/layout/now_playing.xml b/ultrasonic/src/main/res/layout/now_playing.xml index 639a14fd..0e671c40 100644 --- a/ultrasonic/src/main/res/layout/now_playing.xml +++ b/ultrasonic/src/main/res/layout/now_playing.xml @@ -6,16 +6,17 @@ android:layout_width="fill_parent" android:layout_height="wrap_content" android:background="?attr/colorSecondaryContainer" - tools:ignore="overdraw"> + tools:ignore="Overdraw"> + android:id="@+id/now_playing_image" + android:layout_width="72.0dip" + android:layout_height="72.0dip" + android:layout_marginStart="0dp" + android:elevation="4dp" + android:focusable="true" + android:gravity="center" + android:importantForAccessibility="no" /> diff --git a/ultrasonic/src/main/res/layout/server_edit.xml b/ultrasonic/src/main/res/layout/server_edit.xml index e025c7ff..3c53d649 100644 --- a/ultrasonic/src/main/res/layout/server_edit.xml +++ b/ultrasonic/src/main/res/layout/server_edit.xml @@ -4,238 +4,252 @@ xmlns:tools="http://schemas.android.com/tools" a:layout_width="match_parent" a:layout_height="match_parent" - a:fillViewport="true"> + a:fillViewport="true" + tools:ignore="HardcodedText"> - - - + a:paddingStart="16dp" + a:paddingEnd="16dp"> - + a:hint="@string/settings.server_name" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent"> - + - + - + 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"> - + - + - - + - - - + 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"> - + - + - + 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"> - + - - - - - - - - - - - - - - - - -