util: clean up context utils
Clean up the context utils to be more appropriately designed and efficient.
This commit is contained in:
parent
96be8cb6b7
commit
7d04aad9b7
48 changed files with 246 additions and 343 deletions
|
@ -55,6 +55,7 @@ class MainFragment :
|
|||
private val navModel: NavigationViewModel by activityViewModels()
|
||||
private var callback: DynamicBackPressedCallback? = null
|
||||
private var lastInsets: WindowInsets? = null
|
||||
private var elevationNormal = -1f
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
@ -66,9 +67,12 @@ class MainFragment :
|
|||
|
||||
override fun onBindingCreated(binding: FragmentMainBinding, savedInstanceState: Bundle?) {
|
||||
// --- UI SETUP ---
|
||||
requireActivity()
|
||||
.onBackPressedDispatcher.addCallback(
|
||||
viewLifecycleOwner, DynamicBackPressedCallback().also { callback = it })
|
||||
val context = requireActivity()
|
||||
|
||||
context.onBackPressedDispatcher.addCallback(
|
||||
viewLifecycleOwner, DynamicBackPressedCallback().also { callback = it })
|
||||
|
||||
elevationNormal = requireContext().getDimen(R.dimen.elevation_normal)
|
||||
|
||||
binding.root.setOnApplyWindowInsetsListener { _, insets ->
|
||||
lastInsets = insets
|
||||
|
@ -77,8 +81,9 @@ class MainFragment :
|
|||
|
||||
// Send meaningful accessibility events for bottom sheets
|
||||
ViewCompat.setAccessibilityPaneTitle(
|
||||
binding.playbackSheet, getString(R.string.lbl_playback))
|
||||
ViewCompat.setAccessibilityPaneTitle(binding.queueSheet, getString(R.string.lbl_queue))
|
||||
binding.playbackSheet, context.getString(R.string.lbl_playback))
|
||||
ViewCompat.setAccessibilityPaneTitle(
|
||||
binding.queueSheet, context.getString(R.string.lbl_queue))
|
||||
|
||||
val queueSheetBehavior = binding.queueSheet.coordinatorLayoutBehavior as QueueSheetBehavior?
|
||||
if (queueSheetBehavior != null) {
|
||||
|
@ -97,8 +102,8 @@ class MainFragment :
|
|||
binding.queueSheet.apply {
|
||||
background =
|
||||
MaterialShapeDrawable.createWithElevationOverlay(context).apply {
|
||||
fillColor = context.getAttrColorSafe(R.attr.colorSurface).stateList
|
||||
elevation = context.getDimenSafe(R.dimen.elevation_normal)
|
||||
fillColor = context.getAttrColorCompat(R.attr.colorSurface)
|
||||
elevation = context.getDimen(R.dimen.elevation_normal)
|
||||
}
|
||||
|
||||
setOnApplyWindowInsetsListener { v, insets ->
|
||||
|
@ -182,8 +187,7 @@ class MainFragment :
|
|||
isInvisible = alpha == 0f
|
||||
}
|
||||
|
||||
binding.playbackSheet.translationZ =
|
||||
requireContext().getDimenSafe(R.dimen.elevation_normal) * outPlaybackRatio
|
||||
binding.playbackSheet.translationZ = elevationNormal * outPlaybackRatio
|
||||
playbackSheetBehavior.sheetBackgroundDrawable.alpha = (outPlaybackRatio * 255).toInt()
|
||||
|
||||
binding.playbackBarFragment.apply {
|
||||
|
|
|
@ -43,7 +43,6 @@ import org.oxycblt.auxio.ui.Sort
|
|||
import org.oxycblt.auxio.ui.fragment.MenuFragment
|
||||
import org.oxycblt.auxio.ui.recycler.Header
|
||||
import org.oxycblt.auxio.ui.recycler.Item
|
||||
import org.oxycblt.auxio.util.applySpans
|
||||
import org.oxycblt.auxio.util.canScroll
|
||||
import org.oxycblt.auxio.util.collect
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
|
@ -88,9 +87,9 @@ class AlbumDetailFragment :
|
|||
|
||||
binding.detailRecycler.apply {
|
||||
adapter = detailAdapter
|
||||
applySpans { pos ->
|
||||
val item = detailAdapter.data.currentList[pos]
|
||||
item is Header || item is SortHeader || item is Album
|
||||
setSpanSizeLookup { pos ->
|
||||
val item = detailAdapter.data.getItem(pos)
|
||||
item is Album || item is Header || item is SortHeader
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -240,7 +239,7 @@ class AlbumDetailFragment :
|
|||
// If the recyclerview can scroll, its certain that it will have to scroll to
|
||||
// correctly center the playing item, so make sure that the Toolbar is lifted in
|
||||
// that case.
|
||||
binding.detailAppbar.isLifted = binding.detailRecycler.canScroll
|
||||
binding.detailAppbar.isLifted = binding.detailRecycler.canScroll()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,6 @@ import org.oxycblt.auxio.ui.Sort
|
|||
import org.oxycblt.auxio.ui.fragment.MenuFragment
|
||||
import org.oxycblt.auxio.ui.recycler.Header
|
||||
import org.oxycblt.auxio.ui.recycler.Item
|
||||
import org.oxycblt.auxio.util.applySpans
|
||||
import org.oxycblt.auxio.util.collect
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
import org.oxycblt.auxio.util.context
|
||||
|
@ -83,10 +82,9 @@ class ArtistDetailFragment :
|
|||
|
||||
binding.detailRecycler.apply {
|
||||
adapter = detailAdapter
|
||||
applySpans { pos ->
|
||||
// If the item is an ActionHeader we need to also make the item full-width
|
||||
val item = detailAdapter.data.currentList[pos]
|
||||
item is Header || item is SortHeader || item is Artist
|
||||
setSpanSizeLookup { pos ->
|
||||
val item = detailAdapter.data.getItem(pos)
|
||||
item is Artist || item is Header || item is SortHeader
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,13 +29,10 @@ import androidx.coordinatorlayout.widget.CoordinatorLayout
|
|||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.appbar.AppBarLayout
|
||||
import java.lang.Exception
|
||||
import java.lang.reflect.Field
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.ui.AuxioAppBarLayout
|
||||
import org.oxycblt.auxio.util.lazyReflectedField
|
||||
import org.oxycblt.auxio.util.logE
|
||||
import org.oxycblt.auxio.util.logTraceOrThrow
|
||||
|
||||
/**
|
||||
* An [AuxioAppBarLayout] variant that also shows the name of the toolbar whenever the detail
|
||||
|
@ -68,14 +65,7 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
|
|||
val toolbar = findViewById<Toolbar>(R.id.detail_toolbar)
|
||||
|
||||
// Reflect to get the actual title view to do transformations on
|
||||
val newTitleView =
|
||||
try {
|
||||
TOOLBAR_TITLE_TEXT_FIELD.get(toolbar) as AppCompatTextView
|
||||
} catch (e: Exception) {
|
||||
logE("Could not get toolbar title view (likely an internal code change)")
|
||||
e.logTraceOrThrow()
|
||||
return null
|
||||
}
|
||||
val newTitleView = TOOLBAR_TITLE_TEXT_FIELD.get(toolbar) as AppCompatTextView
|
||||
|
||||
newTitleView.alpha = 0f
|
||||
this.titleView = newTitleView
|
||||
|
|
|
@ -42,7 +42,6 @@ import org.oxycblt.auxio.ui.Sort
|
|||
import org.oxycblt.auxio.ui.fragment.MenuFragment
|
||||
import org.oxycblt.auxio.ui.recycler.Header
|
||||
import org.oxycblt.auxio.ui.recycler.Item
|
||||
import org.oxycblt.auxio.util.applySpans
|
||||
import org.oxycblt.auxio.util.collect
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
import org.oxycblt.auxio.util.context
|
||||
|
@ -84,9 +83,9 @@ class GenreDetailFragment :
|
|||
|
||||
binding.detailRecycler.apply {
|
||||
adapter = detailAdapter
|
||||
applySpans { pos ->
|
||||
val item = detailAdapter.data.currentList[pos]
|
||||
item is Header || item is SortHeader || item is Genre
|
||||
setSpanSizeLookup { pos ->
|
||||
val item = detailAdapter.data.getItem(pos)
|
||||
item is Genre || item is Header || item is SortHeader
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -64,14 +64,12 @@ class SongDetailDialog : ViewBindingDialogFragment<DialogSongDetailBinding>() {
|
|||
|
||||
if (song != null) {
|
||||
if (song.info != null) {
|
||||
val context = requireContext()
|
||||
binding.detailContainer.isGone = false
|
||||
binding.detailFileName.setText(song.song.path.name)
|
||||
binding.detailRelativeDir.setText(
|
||||
song.song.path.parent.resolveName(requireContext()))
|
||||
binding.detailFormat.setText(
|
||||
song.info.resolvedMimeType.resolveName(requireContext()))
|
||||
binding.detailSize.setText(
|
||||
Formatter.formatFileSize(requireContext(), song.song.size))
|
||||
binding.detailRelativeDir.setText(song.song.path.parent.resolveName(context))
|
||||
binding.detailFormat.setText(song.info.resolvedMimeType.resolveName(context))
|
||||
binding.detailSize.setText(Formatter.formatFileSize(context, song.song.size))
|
||||
binding.detailDuration.setText(song.song.durationSecs.formatDuration(true))
|
||||
|
||||
if (song.info.bitrateKbps != null) {
|
||||
|
|
|
@ -34,7 +34,7 @@ import org.oxycblt.auxio.ui.recycler.MenuItemListener
|
|||
import org.oxycblt.auxio.ui.recycler.SimpleItemCallback
|
||||
import org.oxycblt.auxio.util.context
|
||||
import org.oxycblt.auxio.util.formatDuration
|
||||
import org.oxycblt.auxio.util.getPluralSafe
|
||||
import org.oxycblt.auxio.util.getPlural
|
||||
import org.oxycblt.auxio.util.inflater
|
||||
|
||||
/**
|
||||
|
@ -127,7 +127,7 @@ private class AlbumDetailViewHolder private constructor(private val binding: Ite
|
|||
item.date?.let { context.getString(R.string.fmt_number, it.year) }
|
||||
?: context.getString(R.string.def_date)
|
||||
|
||||
val songCount = context.getPluralSafe(R.plurals.fmt_song_count, item.songs.size)
|
||||
val songCount = context.getPlural(R.plurals.fmt_song_count, item.songs.size)
|
||||
|
||||
val duration = item.durationSecs.formatDuration(true)
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ import org.oxycblt.auxio.ui.recycler.Item
|
|||
import org.oxycblt.auxio.ui.recycler.MenuItemListener
|
||||
import org.oxycblt.auxio.ui.recycler.SimpleItemCallback
|
||||
import org.oxycblt.auxio.util.context
|
||||
import org.oxycblt.auxio.util.getPluralSafe
|
||||
import org.oxycblt.auxio.util.getPlural
|
||||
import org.oxycblt.auxio.util.inflater
|
||||
|
||||
/**
|
||||
|
@ -138,8 +138,8 @@ private class ArtistDetailViewHolder private constructor(private val binding: It
|
|||
binding.detailInfo.text =
|
||||
binding.context.getString(
|
||||
R.string.fmt_two,
|
||||
binding.context.getPluralSafe(R.plurals.fmt_album_count, item.albums.size),
|
||||
binding.context.getPluralSafe(R.plurals.fmt_song_count, item.songs.size))
|
||||
binding.context.getPlural(R.plurals.fmt_album_count, item.albums.size),
|
||||
binding.context.getPlural(R.plurals.fmt_song_count, item.songs.size))
|
||||
|
||||
binding.detailPlayButton.setOnClickListener { listener.onPlayParent() }
|
||||
binding.detailShuffleButton.setOnClickListener { listener.onShuffleParent() }
|
||||
|
|
|
@ -32,7 +32,7 @@ import org.oxycblt.auxio.ui.recycler.SimpleItemCallback
|
|||
import org.oxycblt.auxio.ui.recycler.SongViewHolder
|
||||
import org.oxycblt.auxio.util.context
|
||||
import org.oxycblt.auxio.util.formatDuration
|
||||
import org.oxycblt.auxio.util.getPluralSafe
|
||||
import org.oxycblt.auxio.util.getPlural
|
||||
import org.oxycblt.auxio.util.inflater
|
||||
|
||||
/**
|
||||
|
@ -106,7 +106,7 @@ private class GenreDetailViewHolder private constructor(private val binding: Ite
|
|||
binding.detailCover.bind(item)
|
||||
binding.detailName.text = item.resolveName(binding.context)
|
||||
binding.detailSubhead.text =
|
||||
binding.context.getPluralSafe(R.plurals.fmt_song_count, item.songs.size)
|
||||
binding.context.getPlural(R.plurals.fmt_song_count, item.songs.size)
|
||||
binding.detailInfo.text = item.durationSecs.formatDuration(false)
|
||||
binding.detailPlayButton.setOnClickListener { listener.onPlayParent() }
|
||||
binding.detailShuffleButton.setOnClickListener { listener.onShuffleParent() }
|
||||
|
|
|
@ -106,7 +106,7 @@ class HomeFragment : ViewBindingFragment<FragmentHomeBinding>(), Toolbar.OnMenuI
|
|||
// Load the track color in manually as it's unclear whether the track actually supports
|
||||
// using a ColorStateList in the resources
|
||||
binding.homeIndexingProgress.trackColor =
|
||||
requireContext().getColorStateListSafe(R.color.sel_track).defaultColor
|
||||
requireContext().getColorCompat(R.color.sel_track).defaultColor
|
||||
|
||||
binding.homePager.apply {
|
||||
adapter = HomePagerAdapter()
|
||||
|
@ -279,6 +279,8 @@ class HomeFragment : ViewBindingFragment<FragmentHomeBinding>(), Toolbar.OnMenuI
|
|||
binding.homeFab.show()
|
||||
binding.homeIndexingContainer.visibility = View.INVISIBLE
|
||||
} else {
|
||||
val context = requireContext()
|
||||
|
||||
binding.homeIndexingContainer.visibility = View.VISIBLE
|
||||
|
||||
logD("Received non-ok response $response")
|
||||
|
@ -286,28 +288,28 @@ class HomeFragment : ViewBindingFragment<FragmentHomeBinding>(), Toolbar.OnMenuI
|
|||
when (response) {
|
||||
is Indexer.Response.Err -> {
|
||||
binding.homeIndexingProgress.visibility = View.INVISIBLE
|
||||
binding.homeIndexingStatus.text = getString(R.string.err_index_failed)
|
||||
binding.homeIndexingStatus.text = context.getString(R.string.err_index_failed)
|
||||
binding.homeIndexingAction.apply {
|
||||
visibility = View.VISIBLE
|
||||
text = getString(R.string.lbl_retry)
|
||||
text = context.getString(R.string.lbl_retry)
|
||||
setOnClickListener { musicModel.reindex() }
|
||||
}
|
||||
}
|
||||
is Indexer.Response.NoMusic -> {
|
||||
binding.homeIndexingProgress.visibility = View.INVISIBLE
|
||||
binding.homeIndexingStatus.text = getString(R.string.err_no_music)
|
||||
binding.homeIndexingStatus.text = context.getString(R.string.err_no_music)
|
||||
binding.homeIndexingAction.apply {
|
||||
visibility = View.VISIBLE
|
||||
text = getString(R.string.lbl_retry)
|
||||
text = context.getString(R.string.lbl_retry)
|
||||
setOnClickListener { musicModel.reindex() }
|
||||
}
|
||||
}
|
||||
is Indexer.Response.NoPerms -> {
|
||||
binding.homeIndexingProgress.visibility = View.INVISIBLE
|
||||
binding.homeIndexingStatus.text = getString(R.string.err_no_perms)
|
||||
binding.homeIndexingStatus.text = context.getString(R.string.err_no_perms)
|
||||
binding.homeIndexingAction.apply {
|
||||
visibility = View.VISIBLE
|
||||
text = getString(R.string.lbl_grant)
|
||||
text = context.getString(R.string.lbl_grant)
|
||||
setOnClickListener {
|
||||
storagePermissionLauncher.launch(
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||
|
@ -324,14 +326,16 @@ class HomeFragment : ViewBindingFragment<FragmentHomeBinding>(), Toolbar.OnMenuI
|
|||
binding.homeIndexingProgress.visibility = View.VISIBLE
|
||||
binding.homeIndexingAction.visibility = View.INVISIBLE
|
||||
|
||||
val context = requireContext()
|
||||
|
||||
when (indexing) {
|
||||
is Indexer.Indexing.Indeterminate -> {
|
||||
binding.homeIndexingStatus.text = getString(R.string.lng_indexing_desc)
|
||||
binding.homeIndexingStatus.text = context.getString(R.string.lng_indexing_desc)
|
||||
binding.homeIndexingProgress.isIndeterminate = true
|
||||
}
|
||||
is Indexer.Indexing.Songs -> {
|
||||
binding.homeIndexingStatus.text =
|
||||
getString(R.string.fmt_indexing, indexing.current, indexing.total)
|
||||
context.getString(R.string.fmt_indexing, indexing.current, indexing.total)
|
||||
binding.homeIndexingProgress.apply {
|
||||
isIndeterminate = false
|
||||
max = indexing.total
|
||||
|
@ -370,14 +374,9 @@ class HomeFragment : ViewBindingFragment<FragmentHomeBinding>(), Toolbar.OnMenuI
|
|||
* https://al-e-shevelev.medium.com/how-to-reduce-scroll-sensitivity-of-viewpager2-widget-87797ad02414
|
||||
*/
|
||||
private fun ViewPager2.reduceSensitivity(by: Int) {
|
||||
try {
|
||||
val recycler = VIEW_PAGER_RECYCLER_FIELD.get(this@reduceSensitivity)
|
||||
val slop = VIEW_PAGER_TOUCH_SLOP_FIELD.get(recycler) as Int
|
||||
VIEW_PAGER_TOUCH_SLOP_FIELD.set(recycler, slop * by)
|
||||
} catch (e: Exception) {
|
||||
logE("Unable to reduce ViewPager sensitivity (likely an internal code change)")
|
||||
e.logTraceOrThrow()
|
||||
}
|
||||
val recycler = VIEW_PAGER_RECYCLER_FIELD.get(this@reduceSensitivity)
|
||||
val slop = VIEW_PAGER_TOUCH_SLOP_FIELD.get(recycler) as Int
|
||||
VIEW_PAGER_TOUCH_SLOP_FIELD.set(recycler, slop * by)
|
||||
}
|
||||
|
||||
/** Forces the view to recreate all fragments contained within it. */
|
||||
|
|
|
@ -34,9 +34,8 @@ import android.view.Gravity
|
|||
import androidx.core.widget.TextViewCompat
|
||||
import com.google.android.material.textview.MaterialTextView
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.util.getAttrColorSafe
|
||||
import org.oxycblt.auxio.util.getDimenOffsetSafe
|
||||
import org.oxycblt.auxio.util.getDimenSizeSafe
|
||||
import org.oxycblt.auxio.util.getAttrColorCompat
|
||||
import org.oxycblt.auxio.util.getDimenSize
|
||||
import org.oxycblt.auxio.util.isRtl
|
||||
|
||||
class FastScrollPopupView
|
||||
|
@ -44,17 +43,17 @@ class FastScrollPopupView
|
|||
constructor(context: Context, attrs: AttributeSet? = null, defStyleRes: Int = 0) :
|
||||
MaterialTextView(context, attrs, defStyleRes) {
|
||||
init {
|
||||
minimumWidth = context.getDimenSizeSafe(R.dimen.fast_scroll_popup_min_width)
|
||||
minimumHeight = context.getDimenSizeSafe(R.dimen.fast_scroll_popup_min_height)
|
||||
minimumWidth = context.getDimenSize(R.dimen.fast_scroll_popup_min_width)
|
||||
minimumHeight = context.getDimenSize(R.dimen.fast_scroll_popup_min_height)
|
||||
|
||||
TextViewCompat.setTextAppearance(this, R.style.TextAppearance_Auxio_HeadlineLarge)
|
||||
setTextColor(context.getAttrColorSafe(R.attr.colorOnSecondary))
|
||||
setTextColor(context.getAttrColorCompat(R.attr.colorOnSecondary))
|
||||
ellipsize = TextUtils.TruncateAt.MIDDLE
|
||||
gravity = Gravity.CENTER
|
||||
includeFontPadding = false
|
||||
|
||||
alpha = 0f
|
||||
elevation = context.getDimenSizeSafe(R.dimen.elevation_normal).toFloat()
|
||||
elevation = context.getDimenSize(R.dimen.elevation_normal).toFloat()
|
||||
background = FastScrollPopupDrawable(context)
|
||||
}
|
||||
|
||||
|
@ -62,16 +61,15 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleRes: Int = 0)
|
|||
private val paint: Paint =
|
||||
Paint().apply {
|
||||
isAntiAlias = true
|
||||
color = context.getAttrColorSafe(R.attr.colorSecondary)
|
||||
color = context.getAttrColorCompat(R.attr.colorSecondary).defaultColor
|
||||
style = Paint.Style.FILL
|
||||
}
|
||||
|
||||
private val path = Path()
|
||||
private val matrix = Matrix()
|
||||
|
||||
private val paddingStart =
|
||||
context.getDimenOffsetSafe(R.dimen.fast_scroll_popup_padding_start)
|
||||
private val paddingEnd = context.getDimenOffsetSafe(R.dimen.fast_scroll_popup_padding_end)
|
||||
private val paddingStart = context.getDimenSize(R.dimen.fast_scroll_popup_padding_start)
|
||||
private val paddingEnd = context.getDimenSize(R.dimen.fast_scroll_popup_padding_end)
|
||||
|
||||
override fun draw(canvas: Canvas) {
|
||||
canvas.drawPath(path, paint)
|
||||
|
|
|
@ -34,11 +34,9 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
|||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlin.math.abs
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.ui.recycler.EdgeRecyclerView
|
||||
import org.oxycblt.auxio.util.canScroll
|
||||
import org.oxycblt.auxio.util.getDimenOffsetSafe
|
||||
import org.oxycblt.auxio.util.getDimenSizeSafe
|
||||
import org.oxycblt.auxio.util.getDrawableSafe
|
||||
import org.oxycblt.auxio.ui.recycler.AuxioRecyclerView
|
||||
import org.oxycblt.auxio.util.getDimenSize
|
||||
import org.oxycblt.auxio.util.getDrawableCompat
|
||||
import org.oxycblt.auxio.util.isRtl
|
||||
import org.oxycblt.auxio.util.isUnder
|
||||
import org.oxycblt.auxio.util.systemBarInsetsCompat
|
||||
|
@ -71,12 +69,12 @@ import org.oxycblt.auxio.util.systemBarInsetsCompat
|
|||
class FastScrollRecyclerView
|
||||
@JvmOverloads
|
||||
constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr: Int = 0) :
|
||||
EdgeRecyclerView(context, attrs, defStyleAttr) {
|
||||
AuxioRecyclerView(context, attrs, defStyleAttr) {
|
||||
// Thumb
|
||||
private val thumbView =
|
||||
View(context).apply {
|
||||
alpha = 0f
|
||||
background = context.getDrawableSafe(R.drawable.ui_scroll_thumb)
|
||||
background = context.getDrawableCompat(R.drawable.ui_scroll_thumb)
|
||||
}
|
||||
|
||||
private val thumbWidth = thumbView.background.intrinsicWidth
|
||||
|
@ -99,7 +97,7 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
|
|||
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||
.apply {
|
||||
gravity = Gravity.CENTER_HORIZONTAL or Gravity.TOP
|
||||
marginEnd = context.getDimenOffsetSafe(R.dimen.spacing_small)
|
||||
marginEnd = context.getDimenSize(R.dimen.spacing_small)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -107,7 +105,7 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
|
|||
|
||||
// Touch
|
||||
private val minTouchTargetSize =
|
||||
context.getDimenSizeSafe(R.dimen.fast_scroll_thumb_touch_target_size)
|
||||
context.getDimenSize(R.dimen.fast_scroll_thumb_touch_target_size)
|
||||
private val touchSlop = ViewConfiguration.get(context).scaledTouchSlop
|
||||
|
||||
private var downX = 0f
|
||||
|
@ -289,7 +287,7 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
|
|||
}
|
||||
|
||||
private fun updateScrollbarState() {
|
||||
if (!canScroll || childCount == 0) {
|
||||
if (computeVerticalScrollRange() <= height || childCount == 0) {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,6 @@ import org.oxycblt.auxio.ui.fragment.MenuFragment
|
|||
import org.oxycblt.auxio.ui.recycler.Item
|
||||
import org.oxycblt.auxio.ui.recycler.MenuItemListener
|
||||
import org.oxycblt.auxio.util.androidActivityViewModels
|
||||
import org.oxycblt.auxio.util.applySpans
|
||||
|
||||
/**
|
||||
* A Base [Fragment] implementing the base features shared across all list fragments in the home UI.
|
||||
|
@ -46,7 +45,6 @@ abstract class HomeListFragment<T : Item> :
|
|||
override fun onBindingCreated(binding: FragmentHomeListBinding, savedInstanceState: Bundle?) {
|
||||
binding.homeRecycler.popupProvider = this
|
||||
binding.homeRecycler.listener = this
|
||||
binding.homeRecycler.applySpans()
|
||||
}
|
||||
|
||||
override fun onDestroyBinding(binding: FragmentHomeListBinding) {
|
||||
|
|
|
@ -29,8 +29,8 @@ import org.oxycblt.auxio.music.Album
|
|||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.Genre
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.util.getColorStateListSafe
|
||||
import org.oxycblt.auxio.util.getDrawableSafe
|
||||
import org.oxycblt.auxio.util.getColorCompat
|
||||
import org.oxycblt.auxio.util.getDrawableCompat
|
||||
|
||||
/**
|
||||
* Effectively a super-charged [StyledImageView].
|
||||
|
@ -66,7 +66,7 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
|
|||
indicator =
|
||||
StyledImageView(context).apply {
|
||||
cornerRadius = this@ImageGroup.cornerRadius
|
||||
staticIcon = context.getDrawableSafe(R.drawable.ic_currently_playing_24)
|
||||
staticIcon = context.getDrawableCompat(R.drawable.ic_currently_playing_24)
|
||||
}
|
||||
|
||||
addView(inner)
|
||||
|
@ -83,7 +83,7 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
|
|||
getChildAt(1)?.apply {
|
||||
background =
|
||||
MaterialShapeDrawable().apply {
|
||||
fillColor = context.getColorStateListSafe(R.color.sel_cover_bg)
|
||||
fillColor = context.getColorCompat(R.color.sel_cover_bg)
|
||||
setCornerSize(cornerRadius)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,8 +39,8 @@ import org.oxycblt.auxio.music.Genre
|
|||
import org.oxycblt.auxio.music.Music
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.settings.Settings
|
||||
import org.oxycblt.auxio.util.getColorStateListSafe
|
||||
import org.oxycblt.auxio.util.getDrawableSafe
|
||||
import org.oxycblt.auxio.util.getColorCompat
|
||||
import org.oxycblt.auxio.util.getDrawableCompat
|
||||
|
||||
/**
|
||||
* An [AppCompatImageView] that applies many of the stylistic choices that Auxio uses regarding
|
||||
|
@ -87,7 +87,7 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
|
|||
clipToOutline = true
|
||||
background =
|
||||
MaterialShapeDrawable().apply {
|
||||
fillColor = context.getColorStateListSafe(R.color.sel_cover_bg)
|
||||
fillColor = context.getColorCompat(R.color.sel_cover_bg)
|
||||
setCornerSize(cornerRadius)
|
||||
}
|
||||
|
||||
|
@ -96,7 +96,7 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
|
|||
styledAttrs.getResourceId(
|
||||
R.styleable.StyledImageView_staticIcon, ResourcesCompat.ID_NULL)
|
||||
if (staticIcon != ResourcesCompat.ID_NULL) {
|
||||
this.staticIcon = context.getDrawableSafe(staticIcon)
|
||||
this.staticIcon = context.getDrawableCompat(staticIcon)
|
||||
}
|
||||
|
||||
useLargeIcon = styledAttrs.getBoolean(R.styleable.StyledImageView_useLargeIcon, false)
|
||||
|
@ -126,14 +126,14 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
|
|||
|
||||
dispose()
|
||||
load(music) {
|
||||
error(StyledDrawable(context, context.getDrawableSafe(error)))
|
||||
error(StyledDrawable(context, context.getDrawableCompat(error)))
|
||||
transformations(SquareFrameTransform.INSTANCE)
|
||||
}
|
||||
}
|
||||
|
||||
private class StyledDrawable(context: Context, private val src: Drawable) : Drawable() {
|
||||
init {
|
||||
DrawableCompat.setTintList(src, context.getColorStateListSafe(R.color.sel_on_cover_bg))
|
||||
DrawableCompat.setTintList(src, context.getColorCompat(R.color.sel_on_cover_bg))
|
||||
}
|
||||
|
||||
override fun draw(canvas: Canvas) {
|
||||
|
|
|
@ -32,7 +32,7 @@ import org.oxycblt.auxio.music.Directory
|
|||
import org.oxycblt.auxio.settings.Settings
|
||||
import org.oxycblt.auxio.ui.fragment.ViewBindingDialogFragment
|
||||
import org.oxycblt.auxio.util.context
|
||||
import org.oxycblt.auxio.util.getSystemServiceSafe
|
||||
import org.oxycblt.auxio.util.getSystemServiceCompat
|
||||
import org.oxycblt.auxio.util.logD
|
||||
import org.oxycblt.auxio.util.showToast
|
||||
|
||||
|
@ -45,7 +45,7 @@ class MusicDirsDialog :
|
|||
private val dirAdapter = MusicDirAdapter(this)
|
||||
private val settings: Settings by lifecycleObject { binding -> Settings(binding.context) }
|
||||
private val storageManager: StorageManager by lifecycleObject { binding ->
|
||||
binding.context.getSystemServiceSafe(StorageManager::class)
|
||||
binding.context.getSystemServiceCompat(StorageManager::class)
|
||||
}
|
||||
|
||||
override fun onCreateBinding(inflater: LayoutInflater) =
|
||||
|
|
|
@ -34,7 +34,7 @@ import org.oxycblt.auxio.playback.state.PlaybackStateManager
|
|||
import org.oxycblt.auxio.settings.Settings
|
||||
import org.oxycblt.auxio.ui.system.ForegroundManager
|
||||
import org.oxycblt.auxio.util.contentResolverSafe
|
||||
import org.oxycblt.auxio.util.getSystemServiceSafe
|
||||
import org.oxycblt.auxio.util.getSystemServiceCompat
|
||||
import org.oxycblt.auxio.util.logD
|
||||
|
||||
/**
|
||||
|
@ -73,7 +73,7 @@ class IndexerService : Service(), Indexer.Controller, Settings.Callback {
|
|||
observingNotification = ObservingNotification(this)
|
||||
|
||||
wakeLock =
|
||||
getSystemServiceSafe(PowerManager::class)
|
||||
getSystemServiceCompat(PowerManager::class)
|
||||
.newWakeLock(
|
||||
PowerManager.PARTIAL_WAKE_LOCK, BuildConfig.APPLICATION_ID + ":IndexerService")
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ import java.io.File
|
|||
import org.oxycblt.auxio.music.*
|
||||
import org.oxycblt.auxio.settings.Settings
|
||||
import org.oxycblt.auxio.util.contentResolverSafe
|
||||
import org.oxycblt.auxio.util.getSystemServiceSafe
|
||||
import org.oxycblt.auxio.util.getSystemServiceCompat
|
||||
import org.oxycblt.auxio.util.logD
|
||||
|
||||
/*
|
||||
|
@ -114,7 +114,7 @@ abstract class MediaStoreBackend : Indexer.Backend {
|
|||
|
||||
override fun query(context: Context): Cursor {
|
||||
val settings = Settings(context)
|
||||
val storageManager = context.getSystemServiceSafe(StorageManager::class)
|
||||
val storageManager = context.getSystemServiceCompat(StorageManager::class)
|
||||
volumes.addAll(storageManager.storageVolumesCompat)
|
||||
val dirs = settings.getMusicDirs(storageManager)
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ import org.oxycblt.auxio.ui.NavigationViewModel
|
|||
import org.oxycblt.auxio.ui.fragment.ViewBindingFragment
|
||||
import org.oxycblt.auxio.util.androidActivityViewModels
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
import org.oxycblt.auxio.util.getColorStateListSafe
|
||||
import org.oxycblt.auxio.util.getColorCompat
|
||||
|
||||
/**
|
||||
* A fragment showing the current playback state in a compact manner. Used as the bar for the
|
||||
|
@ -58,7 +58,7 @@ class PlaybackBarFragment : ViewBindingFragment<FragmentPlaybackBarBinding>() {
|
|||
// Load the track color in manually as it's unclear whether the track actually supports
|
||||
// using a ColorStateList in the resources
|
||||
binding.playbackProgressBar.trackColor =
|
||||
requireContext().getColorStateListSafe(R.color.sel_track).defaultColor
|
||||
requireContext().getColorCompat(R.color.sel_track).defaultColor
|
||||
|
||||
binding.playbackPlayPause.setOnClickListener { playbackModel.invertPlaying() }
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ import org.oxycblt.auxio.ui.NavigationViewModel
|
|||
import org.oxycblt.auxio.ui.fragment.ViewBindingFragment
|
||||
import org.oxycblt.auxio.util.androidActivityViewModels
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
import org.oxycblt.auxio.util.getDrawableSafe
|
||||
import org.oxycblt.auxio.util.getDrawableCompat
|
||||
import org.oxycblt.auxio.util.logD
|
||||
import org.oxycblt.auxio.util.systemBarInsetsCompat
|
||||
|
||||
|
@ -148,8 +148,11 @@ class PlaybackPanelFragment :
|
|||
}
|
||||
|
||||
private fun updateParent(parent: MusicParent?) {
|
||||
requireBinding().playbackToolbar.subtitle =
|
||||
parent?.resolveName(requireContext()) ?: getString(R.string.lbl_all_songs)
|
||||
val binding = requireBinding()
|
||||
val context = requireContext()
|
||||
|
||||
binding.playbackToolbar.subtitle =
|
||||
parent?.resolveName(context) ?: context.getString(R.string.lbl_all_songs)
|
||||
}
|
||||
|
||||
private fun updatePosition(positionSecs: Long) {
|
||||
|
@ -157,16 +160,16 @@ class PlaybackPanelFragment :
|
|||
}
|
||||
|
||||
private fun updateRepeat(repeatMode: RepeatMode) {
|
||||
requireBinding().playbackRepeat.apply {
|
||||
isActivated = repeatMode != RepeatMode.NONE
|
||||
val iconRes =
|
||||
when (repeatMode) {
|
||||
RepeatMode.NONE -> R.drawable.ic_repeat_off_24
|
||||
RepeatMode.ALL -> R.drawable.ic_repeat_on_24
|
||||
RepeatMode.TRACK -> R.drawable.ic_repeat_one_24
|
||||
}
|
||||
val iconRes =
|
||||
when (repeatMode) {
|
||||
RepeatMode.NONE -> R.drawable.ic_repeat_off_24
|
||||
RepeatMode.ALL -> R.drawable.ic_repeat_on_24
|
||||
RepeatMode.TRACK -> R.drawable.ic_repeat_one_24
|
||||
}
|
||||
|
||||
icon = requireContext().getDrawableSafe(iconRes)
|
||||
requireBinding().playbackRepeat.apply {
|
||||
icon = requireContext().getDrawableCompat(iconRes)
|
||||
isActivated = repeatMode != RepeatMode.NONE
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -87,8 +87,8 @@ private constructor(
|
|||
|
||||
val backgroundDrawable =
|
||||
MaterialShapeDrawable.createWithElevationOverlay(binding.root.context).apply {
|
||||
fillColor = binding.context.getAttrColorSafe(R.attr.colorSurface).stateList
|
||||
elevation = binding.context.getDimenSafe(R.dimen.elevation_normal) * 5
|
||||
fillColor = binding.context.getAttrColorCompat(R.attr.colorSurface)
|
||||
elevation = binding.context.getDimen(R.dimen.elevation_normal) * 5
|
||||
alpha = 0
|
||||
}
|
||||
|
||||
|
@ -114,8 +114,8 @@ private constructor(
|
|||
LayerDrawable(
|
||||
arrayOf(
|
||||
MaterialShapeDrawable.createWithElevationOverlay(binding.context).apply {
|
||||
fillColor = binding.context.getAttrColorSafe(R.attr.colorSurface).stateList
|
||||
elevation = binding.context.getDimenSafe(R.dimen.elevation_normal)
|
||||
fillColor = binding.context.getAttrColorCompat(R.attr.colorSurface)
|
||||
elevation = binding.context.getDimen(R.dimen.elevation_normal)
|
||||
},
|
||||
backgroundDrawable))
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ import androidx.core.view.isInvisible
|
|||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.util.getDimenSafe
|
||||
import org.oxycblt.auxio.util.getDimen
|
||||
import org.oxycblt.auxio.util.logD
|
||||
|
||||
/**
|
||||
|
@ -68,7 +68,7 @@ class QueueDragCallback(private val playbackModel: QueueViewModel) : ItemTouchHe
|
|||
logD("Lifting queue item")
|
||||
|
||||
val bg = holder.backgroundDrawable
|
||||
val elevation = recyclerView.context.getDimenSafe(R.dimen.elevation_normal)
|
||||
val elevation = recyclerView.context.getDimen(R.dimen.elevation_normal)
|
||||
holder.itemView
|
||||
.animate()
|
||||
.translationZ(elevation)
|
||||
|
@ -108,7 +108,7 @@ class QueueDragCallback(private val playbackModel: QueueViewModel) : ItemTouchHe
|
|||
logD("Dropping queue item")
|
||||
|
||||
val bg = holder.backgroundDrawable
|
||||
val elevation = recyclerView.context.getDimenSafe(R.dimen.elevation_normal)
|
||||
val elevation = recyclerView.context.getDimen(R.dimen.elevation_normal)
|
||||
holder.itemView
|
||||
.animate()
|
||||
.translationZ(0f)
|
||||
|
|
|
@ -33,11 +33,11 @@ import org.oxycblt.auxio.util.*
|
|||
class QueueSheetBehavior<V : View>(context: Context, attributeSet: AttributeSet?) :
|
||||
AuxioSheetBehavior<V>(context, attributeSet) {
|
||||
private var barHeight = 0
|
||||
private var barSpacing = context.getDimenSizeSafe(R.dimen.spacing_small)
|
||||
private var barSpacing = context.getDimenSize(R.dimen.spacing_small)
|
||||
|
||||
init {
|
||||
isHideable = false
|
||||
sheetBackgroundDrawable.setCornerSize(context.getDimenSafe(R.dimen.size_corners_medium))
|
||||
sheetBackgroundDrawable.setCornerSize(context.getDimen(R.dimen.size_corners_medium))
|
||||
}
|
||||
|
||||
override fun layoutDependsOn(parent: CoordinatorLayout, child: V, dependency: View) =
|
||||
|
|
|
@ -44,11 +44,10 @@ import org.oxycblt.auxio.ui.recycler.Header
|
|||
import org.oxycblt.auxio.ui.recycler.Item
|
||||
import org.oxycblt.auxio.ui.recycler.MenuItemListener
|
||||
import org.oxycblt.auxio.util.androidViewModels
|
||||
import org.oxycblt.auxio.util.applySpans
|
||||
import org.oxycblt.auxio.util.collect
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
import org.oxycblt.auxio.util.context
|
||||
import org.oxycblt.auxio.util.getSystemServiceSafe
|
||||
import org.oxycblt.auxio.util.getSystemServiceCompat
|
||||
import org.oxycblt.auxio.util.logW
|
||||
|
||||
/**
|
||||
|
@ -64,7 +63,7 @@ class SearchFragment :
|
|||
private val searchAdapter = SearchAdapter(this)
|
||||
private val settings: Settings by lifecycleObject { binding -> Settings(binding.context) }
|
||||
private val imm: InputMethodManager by lifecycleObject { binding ->
|
||||
binding.context.getSystemServiceSafe(InputMethodManager::class)
|
||||
binding.context.getSystemServiceCompat(InputMethodManager::class)
|
||||
}
|
||||
|
||||
private var launchedKeyboard = false
|
||||
|
@ -108,7 +107,7 @@ class SearchFragment :
|
|||
|
||||
binding.searchRecycler.apply {
|
||||
adapter = searchAdapter
|
||||
applySpans { pos -> searchAdapter.data.currentList[pos] is Header }
|
||||
setSpanSizeLookup { pos -> searchAdapter.data.getItem(pos) is Header }
|
||||
}
|
||||
|
||||
// --- VIEWMODEL SETUP ---
|
||||
|
|
|
@ -104,6 +104,8 @@ class AboutFragment : ViewBindingFragment<FragmentAboutBinding>() {
|
|||
private fun openLinkInBrowser(link: String) {
|
||||
logD("Opening $link")
|
||||
|
||||
val context = requireContext()
|
||||
|
||||
val browserIntent =
|
||||
Intent(Intent.ACTION_VIEW, link.toUri()).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
|
||||
|
@ -112,10 +114,10 @@ class AboutFragment : ViewBindingFragment<FragmentAboutBinding>() {
|
|||
// [along with adding a new permission that breaks the old manual code], so
|
||||
// we just do a typical activity launch.
|
||||
try {
|
||||
requireContext().startActivity(browserIntent)
|
||||
context.startActivity(browserIntent)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
// No app installed to open the link
|
||||
requireContext().showToast(R.string.err_no_app)
|
||||
context.showToast(R.string.err_no_app)
|
||||
}
|
||||
} else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
|
||||
// On older versions of android, opening links from an ACTION_VIEW intent might
|
||||
|
@ -123,8 +125,7 @@ class AboutFragment : ViewBindingFragment<FragmentAboutBinding>() {
|
|||
// case, we will try to manually handle these cases before we try to launch the
|
||||
// browser.
|
||||
val pkgName =
|
||||
requireContext()
|
||||
.packageManager
|
||||
context.packageManager
|
||||
.resolveActivity(browserIntent, PackageManager.MATCH_DEFAULT_ONLY)
|
||||
?.run { activityInfo.packageName }
|
||||
|
||||
|
@ -144,7 +145,7 @@ class AboutFragment : ViewBindingFragment<FragmentAboutBinding>() {
|
|||
}
|
||||
} else {
|
||||
// No app installed to open the link
|
||||
requireContext().showToast(R.string.err_no_app)
|
||||
context.showToast(R.string.err_no_app)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -95,41 +95,49 @@ class SettingsListFragment : PreferenceFragmentCompat() {
|
|||
dialog.setTargetFragment(this, 0)
|
||||
dialog.show(parentFragmentManager, IntListPreferenceDialog.TAG)
|
||||
}
|
||||
is WrappedDialogPreference ->
|
||||
is WrappedDialogPreference -> {
|
||||
val context = requireContext()
|
||||
when (preference.key) {
|
||||
getString(R.string.set_key_accent) ->
|
||||
context.getString(R.string.set_key_accent) ->
|
||||
AccentCustomizeDialog()
|
||||
.show(childFragmentManager, AccentCustomizeDialog.TAG)
|
||||
getString(R.string.set_key_lib_tabs) ->
|
||||
context.getString(R.string.set_key_lib_tabs) ->
|
||||
TabCustomizeDialog().show(childFragmentManager, TabCustomizeDialog.TAG)
|
||||
getString(R.string.set_key_pre_amp) ->
|
||||
context.getString(R.string.set_key_pre_amp) ->
|
||||
PreAmpCustomizeDialog()
|
||||
.show(childFragmentManager, PreAmpCustomizeDialog.TAG)
|
||||
getString(R.string.set_key_music_dirs) ->
|
||||
context.getString(R.string.set_key_music_dirs) ->
|
||||
MusicDirsDialog().show(childFragmentManager, MusicDirsDialog.TAG)
|
||||
else -> logEOrThrow("Unexpected dialog key ${preference.key}")
|
||||
}
|
||||
}
|
||||
else -> super.onDisplayPreferenceDialog(preference)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPreferenceTreeClick(preference: Preference): Boolean {
|
||||
val context = requireContext()
|
||||
|
||||
when (preference.key) {
|
||||
getString(R.string.set_key_save_state) -> {
|
||||
playbackModel.savePlaybackState { context?.showToast(R.string.lng_state_saved) }
|
||||
context.getString(R.string.set_key_save_state) -> {
|
||||
playbackModel.savePlaybackState {
|
||||
this.context?.showToast(R.string.lng_state_saved)
|
||||
}
|
||||
}
|
||||
getString(R.string.set_key_wipe_state) -> {
|
||||
playbackModel.wipePlaybackState { context?.showToast(R.string.lng_state_wiped) }
|
||||
context.getString(R.string.set_key_wipe_state) -> {
|
||||
playbackModel.wipePlaybackState {
|
||||
this.context?.showToast(R.string.lng_state_wiped)
|
||||
}
|
||||
}
|
||||
getString(R.string.set_key_restore_state) ->
|
||||
context.getString(R.string.set_key_restore_state) ->
|
||||
playbackModel.tryRestorePlaybackState { restored ->
|
||||
if (restored) {
|
||||
context?.showToast(R.string.lng_state_restored)
|
||||
this.context?.showToast(R.string.lng_state_restored)
|
||||
} else {
|
||||
context?.showToast(R.string.err_did_not_restore)
|
||||
this.context?.showToast(R.string.err_did_not_restore)
|
||||
}
|
||||
}
|
||||
getString(R.string.set_key_reindex) -> {
|
||||
context.getString(R.string.set_key_reindex) -> {
|
||||
musicModel.reindex()
|
||||
}
|
||||
else -> return super.onPreferenceTreeClick(preference)
|
||||
|
@ -139,7 +147,8 @@ class SettingsListFragment : PreferenceFragmentCompat() {
|
|||
}
|
||||
|
||||
private fun setupPreference(preference: Preference) {
|
||||
val settings = Settings(requireContext())
|
||||
val context = requireActivity()
|
||||
val settings = Settings(context)
|
||||
|
||||
if (!preference.isVisible) return
|
||||
|
||||
|
@ -151,31 +160,31 @@ class SettingsListFragment : PreferenceFragmentCompat() {
|
|||
|
||||
preference.apply {
|
||||
when (key) {
|
||||
getString(R.string.set_key_theme) -> {
|
||||
context.getString(R.string.set_key_theme) -> {
|
||||
onPreferenceChangeListener =
|
||||
Preference.OnPreferenceChangeListener { _, value ->
|
||||
AppCompatDelegate.setDefaultNightMode(value as Int)
|
||||
true
|
||||
}
|
||||
}
|
||||
getString(R.string.set_key_accent) -> {
|
||||
context.getString(R.string.set_key_accent) -> {
|
||||
summary = context.getString(settings.accent.name)
|
||||
}
|
||||
getString(R.string.set_key_black_theme) -> {
|
||||
context.getString(R.string.set_key_black_theme) -> {
|
||||
onPreferenceChangeListener =
|
||||
Preference.OnPreferenceChangeListener { _, _ ->
|
||||
if (requireContext().isNight) {
|
||||
requireActivity().recreate()
|
||||
if (context.isNight) {
|
||||
context.recreate()
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
getString(R.string.set_key_show_covers),
|
||||
getString(R.string.set_key_quality_covers) -> {
|
||||
context.getString(R.string.set_key_show_covers),
|
||||
context.getString(R.string.set_key_quality_covers) -> {
|
||||
onPreferenceChangeListener =
|
||||
Preference.OnPreferenceChangeListener { _, _ ->
|
||||
Coil.imageLoader(requireContext()).apply { this.memoryCache?.clear() }
|
||||
Coil.imageLoader(context).apply { this.memoryCache?.clear() }
|
||||
true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
package org.oxycblt.auxio.settings.ui
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import androidx.preference.PreferenceDialogFragmentCompat
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
|
@ -29,22 +28,20 @@ class IntListPreferenceDialog : PreferenceDialogFragmentCompat() {
|
|||
get() = (preference as IntListPreference)
|
||||
private var pendingValueIndex = -1
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?) =
|
||||
// PreferenceDialogFragmentCompat does not allow us to customize the actual creation
|
||||
// of the alert dialog, so we have to manually override onCreateDialog and customize it
|
||||
// ourselves.
|
||||
val builder = MaterialAlertDialogBuilder(requireContext(), theme)
|
||||
builder.setTitle(listPreference.title)
|
||||
builder.setPositiveButton(null, null)
|
||||
builder.setNegativeButton(R.string.lbl_cancel, null)
|
||||
builder.setSingleChoiceItems(listPreference.entries, listPreference.getValueIndex()) {
|
||||
_,
|
||||
index ->
|
||||
pendingValueIndex = index
|
||||
dismiss()
|
||||
}
|
||||
return builder.create()
|
||||
}
|
||||
MaterialAlertDialogBuilder(requireContext(), theme)
|
||||
.setTitle(listPreference.title)
|
||||
.setPositiveButton(null, null)
|
||||
.setNegativeButton(R.string.lbl_cancel, null)
|
||||
.setSingleChoiceItems(listPreference.entries, listPreference.getValueIndex()) { _, index
|
||||
->
|
||||
pendingValueIndex = index
|
||||
dismiss()
|
||||
}
|
||||
.create()
|
||||
|
||||
override fun onDialogClosed(positiveResult: Boolean) {
|
||||
if (pendingValueIndex > -1) {
|
||||
|
|
|
@ -26,6 +26,7 @@ import androidx.annotation.AttrRes
|
|||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import com.google.android.material.appbar.AppBarLayout
|
||||
import org.oxycblt.auxio.util.coordinatorLayoutBehavior
|
||||
|
||||
/**
|
||||
* An [AppBarLayout] that fixes a bug with the default implementation where the lifted state will
|
||||
|
@ -47,8 +48,8 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
|
|||
|
||||
if (child != null) {
|
||||
val coordinator = parent as CoordinatorLayout
|
||||
(layoutParams as CoordinatorLayout.LayoutParams)
|
||||
.behavior?.onNestedPreScroll(coordinator, this, coordinator, 0, 0, tConsumed, 0)
|
||||
coordinatorLayoutBehavior?.onNestedPreScroll(
|
||||
coordinator, this, coordinator, 0, 0, tConsumed, 0)
|
||||
}
|
||||
|
||||
true
|
||||
|
|
|
@ -40,8 +40,8 @@ abstract class AuxioSheetBehavior<V : View>(context: Context, attributeSet: Attr
|
|||
private var setup = false
|
||||
val sheetBackgroundDrawable =
|
||||
MaterialShapeDrawable.createWithElevationOverlay(context).apply {
|
||||
fillColor = context.getAttrColorSafe(R.attr.colorSurface).stateList
|
||||
elevation = context.getDimenSafe(R.dimen.elevation_normal)
|
||||
fillColor = context.getAttrColorCompat(R.attr.colorSurface)
|
||||
elevation = context.getDimen(R.dimen.elevation_normal)
|
||||
}
|
||||
|
||||
init {
|
||||
|
@ -75,7 +75,8 @@ abstract class AuxioSheetBehavior<V : View>(context: Context, attributeSet: Attr
|
|||
background =
|
||||
LayerDrawable(
|
||||
arrayOf(
|
||||
ColorDrawable(context.getAttrColorSafe(R.attr.colorSurface)),
|
||||
ColorDrawable(
|
||||
context.getAttrColorCompat(R.attr.colorSurface).defaultColor),
|
||||
sheetBackgroundDrawable))
|
||||
|
||||
// Try to disable drop shadows if possible.
|
||||
|
|
|
@ -37,8 +37,8 @@ class BottomSheetContentBehavior<V : View>(context: Context, attributeSet: Attri
|
|||
CoordinatorLayout.Behavior<V>(context, attributeSet) {
|
||||
private var dep: View? = null
|
||||
private var lastInsets: WindowInsets? = null
|
||||
private var lastConsumed: Int? = null
|
||||
private var setup: Boolean = false
|
||||
private var lastConsumed = -1
|
||||
private var setup = false
|
||||
|
||||
override fun layoutDependsOn(parent: CoordinatorLayout, child: V, dependency: View): Boolean {
|
||||
if (dependency.coordinatorLayoutBehavior is NeoBottomSheetBehavior) {
|
||||
|
@ -56,7 +56,7 @@ class BottomSheetContentBehavior<V : View>(context: Context, attributeSet: Attri
|
|||
): Boolean {
|
||||
val behavior = dependency.coordinatorLayoutBehavior as NeoBottomSheetBehavior
|
||||
val consumed = behavior.calculateConsumedByBar()
|
||||
if (consumed < Int.MIN_VALUE) {
|
||||
if (consumed == Int.MIN_VALUE) {
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
|
@ -350,6 +350,8 @@ data class Sort(val mode: Mode, val isAscending: Boolean) {
|
|||
}
|
||||
|
||||
private class BasicComparator<T : Music> private constructor() : Comparator<T> {
|
||||
// TODO: Use Collator for sorting?
|
||||
|
||||
override fun compare(a: T, b: T): Int {
|
||||
val aSortName = a.sortName
|
||||
val bSortName = b.sortName
|
||||
|
|
|
@ -24,10 +24,9 @@ import org.oxycblt.auxio.databinding.ItemAccentBinding
|
|||
import org.oxycblt.auxio.ui.recycler.BackingData
|
||||
import org.oxycblt.auxio.ui.recycler.BindingViewHolder
|
||||
import org.oxycblt.auxio.ui.recycler.MonoAdapter
|
||||
import org.oxycblt.auxio.util.getAttrColorSafe
|
||||
import org.oxycblt.auxio.util.getColorSafe
|
||||
import org.oxycblt.auxio.util.getAttrColorCompat
|
||||
import org.oxycblt.auxio.util.getColorCompat
|
||||
import org.oxycblt.auxio.util.inflater
|
||||
import org.oxycblt.auxio.util.stateList
|
||||
|
||||
/**
|
||||
* An adapter that displays the accent palette.
|
||||
|
@ -82,7 +81,7 @@ class AccentViewHolder private constructor(private val binding: ItemAccentBindin
|
|||
setSelected(false)
|
||||
|
||||
binding.accent.apply {
|
||||
backgroundTintList = context.getColorSafe(item.primary).stateList
|
||||
backgroundTintList = context.getColorCompat(item.primary)
|
||||
contentDescription = context.getString(item.name)
|
||||
TooltipCompat.setTooltipText(this, contentDescription)
|
||||
setOnClickListener { listener.onAccentSelected(item) }
|
||||
|
@ -94,9 +93,9 @@ class AccentViewHolder private constructor(private val binding: ItemAccentBindin
|
|||
isEnabled = !isSelected
|
||||
iconTint =
|
||||
if (isSelected) {
|
||||
context.getAttrColorSafe(R.attr.colorSurface).stateList
|
||||
context.getAttrColorCompat(R.attr.colorSurface)
|
||||
} else {
|
||||
context.getColorSafe(android.R.color.transparent).stateList
|
||||
context.getColorCompat(android.R.color.transparent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,8 @@ import android.util.AttributeSet
|
|||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlin.math.max
|
||||
import org.oxycblt.auxio.util.pxOfDp
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.util.getDimenSize
|
||||
|
||||
/**
|
||||
* A sub-class of [GridLayoutManager] that automatically sets the spans so that they fit the width
|
||||
|
@ -35,9 +36,9 @@ class AccentGridLayoutManager(
|
|||
defStyleAttr: Int,
|
||||
defStyleRes: Int
|
||||
) : GridLayoutManager(context, attrs, defStyleAttr, defStyleRes) {
|
||||
// We use 72dp here since that's the rough size of the accent item.
|
||||
// We use 56dp here since that's the rough size of the accent item.
|
||||
// This will need to be modified if this is used beyond the accent dialog.
|
||||
private var columnWidth = context.pxOfDp(56f)
|
||||
private var columnWidth = context.getDimenSize(R.dimen.size_accent_item)
|
||||
|
||||
private var lastWidth = -1
|
||||
private var lastHeight = -1
|
||||
|
@ -45,9 +46,7 @@ class AccentGridLayoutManager(
|
|||
override fun onLayoutChildren(recycler: RecyclerView.Recycler?, state: RecyclerView.State?) {
|
||||
if (width > 0 && height > 0 && (lastWidth != width || lastHeight != height)) {
|
||||
val totalSpace = width - paddingRight - paddingLeft
|
||||
val spanCount = max(1, totalSpace / columnWidth)
|
||||
|
||||
setSpanCount(spanCount)
|
||||
spanCount = max(1, totalSpace / columnWidth)
|
||||
}
|
||||
|
||||
lastWidth = width
|
||||
|
|
|
@ -23,11 +23,12 @@ import android.util.AttributeSet
|
|||
import android.view.WindowInsets
|
||||
import androidx.annotation.AttrRes
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.oxycblt.auxio.util.systemBarInsetsCompat
|
||||
|
||||
/** A [RecyclerView] that automatically applies insets to itself. */
|
||||
open class EdgeRecyclerView
|
||||
open class AuxioRecyclerView
|
||||
@JvmOverloads
|
||||
constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr: Int = 0) :
|
||||
RecyclerView(context, attrs, defStyleAttr) {
|
||||
|
@ -52,4 +53,13 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
|
|||
|
||||
return insets
|
||||
}
|
||||
|
||||
fun setSpanSizeLookup(lookup: (Int) -> Boolean) {
|
||||
val glm = layoutManager as GridLayoutManager
|
||||
val spanCount = glm.spanCount
|
||||
glm.spanSizeLookup =
|
||||
object : GridLayoutManager.SpanSizeLookup() {
|
||||
override fun getSpanSize(position: Int) = if (lookup(position)) spanCount else 1
|
||||
}
|
||||
}
|
||||
}
|
|
@ -27,21 +27,21 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
|||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.divider.MaterialDivider
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.util.getDimenSizeSafe
|
||||
import org.oxycblt.auxio.util.getDimenSize
|
||||
|
||||
/**
|
||||
* A RecyclerView that enables something resembling the android:scrollIndicators attribute. Only
|
||||
* used in dialogs.
|
||||
* @author OxygenCobalt
|
||||
*/
|
||||
class ScrollIndicatorRecyclerView
|
||||
class DialogRecyclerView
|
||||
@JvmOverloads
|
||||
constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr: Int = 0) :
|
||||
RecyclerView(context, attrs, defStyleAttr) {
|
||||
private val topDivider = MaterialDivider(context)
|
||||
private val bottomDivider = MaterialDivider(context)
|
||||
|
||||
private val spacingMedium = context.getDimenSizeSafe(R.dimen.spacing_medium)
|
||||
private val spacingMedium = context.getDimenSize(R.dimen.spacing_medium)
|
||||
|
||||
init {
|
||||
updatePadding(top = spacingMedium)
|
|
@ -26,7 +26,7 @@ import androidx.recyclerview.widget.AsyncListDiffer
|
|||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
||||
// TODO: Reunify music updates and sorts under replace
|
||||
// TODO: Unify music updates and sorts under replace
|
||||
|
||||
/**
|
||||
* An adapter for one viewholder tied to one type of data. All functionality is derived from the
|
||||
|
|
|
@ -28,7 +28,7 @@ import org.oxycblt.auxio.music.Artist
|
|||
import org.oxycblt.auxio.music.Genre
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.util.context
|
||||
import org.oxycblt.auxio.util.getPluralSafe
|
||||
import org.oxycblt.auxio.util.getPlural
|
||||
import org.oxycblt.auxio.util.inflater
|
||||
|
||||
/**
|
||||
|
@ -124,8 +124,8 @@ class ArtistViewHolder private constructor(private val binding: ItemParentBindin
|
|||
binding.parentInfo.text =
|
||||
binding.context.getString(
|
||||
R.string.fmt_two,
|
||||
binding.context.getPluralSafe(R.plurals.fmt_album_count, item.albums.size),
|
||||
binding.context.getPluralSafe(R.plurals.fmt_song_count, item.songs.size))
|
||||
binding.context.getPlural(R.plurals.fmt_album_count, item.albums.size),
|
||||
binding.context.getPlural(R.plurals.fmt_song_count, item.songs.size))
|
||||
binding.root.apply {
|
||||
setOnClickListener { listener.onItemClick(item) }
|
||||
setOnLongClickListener { view ->
|
||||
|
@ -168,7 +168,7 @@ private constructor(
|
|||
binding.parentImage.bind(item)
|
||||
binding.parentName.text = item.resolveName(binding.context)
|
||||
binding.parentInfo.text =
|
||||
binding.context.getPluralSafe(R.plurals.fmt_song_count, item.songs.size)
|
||||
binding.context.getPlural(R.plurals.fmt_song_count, item.songs.size)
|
||||
binding.root.apply {
|
||||
setOnClickListener { listener.onItemClick(item) }
|
||||
setOnLongClickListener { view ->
|
||||
|
|
|
@ -23,7 +23,7 @@ import android.content.Context
|
|||
import android.os.Build
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.core.app.NotificationCompat
|
||||
import org.oxycblt.auxio.util.getSystemServiceSafe
|
||||
import org.oxycblt.auxio.util.getSystemServiceCompat
|
||||
|
||||
/**
|
||||
* Wrapper around [NotificationCompat.Builder] that automates parts of the notification setup, under
|
||||
|
@ -32,7 +32,7 @@ import org.oxycblt.auxio.util.getSystemServiceSafe
|
|||
*/
|
||||
abstract class ServiceNotification(context: Context, info: ChannelInfo) :
|
||||
NotificationCompat.Builder(context, info.id) {
|
||||
private val notificationManager = context.getSystemServiceSafe(NotificationManager::class)
|
||||
private val notificationManager = context.getSystemServiceCompat(NotificationManager::class)
|
||||
|
||||
init {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
|
|
|
@ -23,14 +23,12 @@ import android.content.Context
|
|||
import android.content.Intent
|
||||
import android.content.res.ColorStateList
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Build
|
||||
import android.util.TypedValue
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.AttrRes
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.annotation.ColorRes
|
||||
import androidx.annotation.DimenRes
|
||||
import androidx.annotation.Dimension
|
||||
|
@ -51,7 +49,7 @@ val Context.inflater: LayoutInflater
|
|||
* Returns whether the current UI is in night mode or not. This will work if the theme is automatic
|
||||
* as well.
|
||||
*/
|
||||
val Context.isNight: Boolean
|
||||
val Context.isNight
|
||||
get() =
|
||||
resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK ==
|
||||
Configuration.UI_MODE_NIGHT_YES
|
||||
|
@ -73,48 +71,25 @@ val Context.contentResolverSafe: ContentResolver
|
|||
* @param value Int value for the plural.
|
||||
* @return The formatted string requested
|
||||
*/
|
||||
fun Context.getPluralSafe(@PluralsRes pluralsRes: Int, value: Int): String {
|
||||
return try {
|
||||
resources.getQuantityString(pluralsRes, value, value)
|
||||
} catch (e: Exception) {
|
||||
handleResourceFailure(e, "plural", "<plural error>")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method for getting a color safely.
|
||||
* @param color The color resource
|
||||
* @return The color integer requested, or black if an error occurred.
|
||||
*/
|
||||
@ColorInt
|
||||
fun Context.getColorSafe(@ColorRes color: Int): Int {
|
||||
return try {
|
||||
ContextCompat.getColor(this, color)
|
||||
} catch (e: Exception) {
|
||||
handleResourceFailure(e, "color", getColorSafe(android.R.color.black))
|
||||
}
|
||||
}
|
||||
fun Context.getPlural(@PluralsRes pluralsRes: Int, value: Int) =
|
||||
resources.getQuantityString(pluralsRes, value, value)
|
||||
|
||||
/**
|
||||
* Convenience method for getting a [ColorStateList] resource safely.
|
||||
* @param color The color resource
|
||||
* @return The [ColorStateList] requested, or black if an error occurred.
|
||||
* @return The [ColorStateList] requested
|
||||
*/
|
||||
fun Context.getColorStateListSafe(@ColorRes color: Int): ColorStateList {
|
||||
return try {
|
||||
unlikelyToBeNull(ContextCompat.getColorStateList(this, color))
|
||||
} catch (e: Exception) {
|
||||
handleResourceFailure(e, "color state list", getColorSafe(android.R.color.black).stateList)
|
||||
fun Context.getColorCompat(@ColorRes color: Int) =
|
||||
requireNotNull(ContextCompat.getColorStateList(this, color)) {
|
||||
"Invalid resource: State list was null"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method for getting a color attribute safely.
|
||||
* @param attr The color attribute
|
||||
* @return The attribute requested, or black if an error occurred.
|
||||
* @return The attribute requested
|
||||
*/
|
||||
@ColorInt
|
||||
fun Context.getAttrColorSafe(@AttrRes attr: Int): Int {
|
||||
fun Context.getAttrColorCompat(@AttrRes attr: Int): ColorStateList {
|
||||
// First resolve the attribute into its ID
|
||||
val resolvedAttr = TypedValue()
|
||||
theme.resolveAttribute(attr, resolvedAttr, true)
|
||||
|
@ -127,80 +102,32 @@ fun Context.getAttrColorSafe(@AttrRes attr: Int): Int {
|
|||
resolvedAttr.data
|
||||
}
|
||||
|
||||
return getColorSafe(color)
|
||||
return getColorCompat(color)
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method for getting a [Drawable] safely.
|
||||
* @param drawable The drawable resource
|
||||
* @return The drawable requested, or black if an error occurred.
|
||||
* @return The drawable requested
|
||||
*/
|
||||
fun Context.getDrawableSafe(@DrawableRes drawable: Int): Drawable {
|
||||
return try {
|
||||
requireNotNull(ContextCompat.getDrawable(this, drawable))
|
||||
} catch (e: Exception) {
|
||||
handleResourceFailure(e, "drawable", ColorDrawable(getColorSafe(android.R.color.black)))
|
||||
fun Context.getDrawableCompat(@DrawableRes drawable: Int) =
|
||||
requireNotNull(ContextCompat.getDrawable(this, drawable)) {
|
||||
"Invalid resource: Drawable was null"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method for getting a dimension safely.
|
||||
* @param dimen The dimension resource
|
||||
* @return The dimension requested, or 0 if an error occurred.
|
||||
* @return The dimension requested
|
||||
*/
|
||||
@Dimension
|
||||
fun Context.getDimenSafe(@DimenRes dimen: Int): Float {
|
||||
return try {
|
||||
resources.getDimension(dimen)
|
||||
} catch (e: Exception) {
|
||||
handleResourceFailure(e, "dimen", 0f)
|
||||
}
|
||||
}
|
||||
@Dimension fun Context.getDimen(@DimenRes dimen: Int) = resources.getDimension(dimen)
|
||||
|
||||
/**
|
||||
* Convenience method for getting a dimension pixel size safely.
|
||||
* @param dimen The dimension resource
|
||||
* @return The dimension requested, in pixels, or 0 if an error occurred.
|
||||
* @return The dimension requested, in pixels
|
||||
*/
|
||||
@Px
|
||||
fun Context.getDimenSizeSafe(@DimenRes dimen: Int): Int {
|
||||
return try {
|
||||
resources.getDimensionPixelSize(dimen)
|
||||
} catch (e: Exception) {
|
||||
handleResourceFailure(e, "dimen", 0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method for getting a dimension pixel offset safely.
|
||||
* @param dimen The dimension resource
|
||||
* @return The dimension requested, in pixels, or 0 if an error occurred.
|
||||
*/
|
||||
@Px
|
||||
fun Context.getDimenOffsetSafe(@DimenRes dimen: Int): Int {
|
||||
return try {
|
||||
resources.getDimensionPixelOffset(dimen)
|
||||
} catch (e: Exception) {
|
||||
handleResourceFailure(e, "dimen", 0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the pixels of the given dimension [dp].
|
||||
* @param dp the dimension value
|
||||
* @return The equivalent amount of pixels for [dp].
|
||||
*/
|
||||
@Px
|
||||
fun Context.pxOfDp(@Dimension dp: Float): Int {
|
||||
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, resources.displayMetrics)
|
||||
.toInt()
|
||||
}
|
||||
|
||||
private fun <T> Context.handleResourceFailure(e: Exception, what: String, default: T): T {
|
||||
logE("$what load failed")
|
||||
e.logTraceOrThrow()
|
||||
return default
|
||||
}
|
||||
@Px fun Context.getDimenSize(@DimenRes dimen: Int) = resources.getDimensionPixelSize(dimen)
|
||||
|
||||
/**
|
||||
* Convenience method for getting a system service without nullability issues.
|
||||
|
@ -209,11 +136,10 @@ private fun <T> Context.handleResourceFailure(e: Exception, what: String, defaul
|
|||
* @return The system service
|
||||
* @throws IllegalArgumentException If the system service cannot be retrieved.
|
||||
*/
|
||||
fun <T : Any> Context.getSystemServiceSafe(serviceClass: KClass<T>): T {
|
||||
return requireNotNull(ContextCompat.getSystemService(this, serviceClass.java)) {
|
||||
fun <T : Any> Context.getSystemServiceCompat(serviceClass: KClass<T>) =
|
||||
requireNotNull(ContextCompat.getSystemService(this, serviceClass.java)) {
|
||||
"System service ${serviceClass.simpleName} could not be instantiated"
|
||||
}
|
||||
}
|
||||
|
||||
/** Create a toast using the provided string resource. */
|
||||
fun Context.showToast(@StringRes str: Int) {
|
||||
|
|
|
@ -19,7 +19,6 @@ package org.oxycblt.auxio.util
|
|||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.database.Cursor
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import android.graphics.drawable.Drawable
|
||||
|
@ -27,7 +26,6 @@ import android.os.Build
|
|||
import android.view.View
|
||||
import android.view.WindowInsets
|
||||
import androidx.activity.viewModels
|
||||
import androidx.annotation.ColorRes
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
|
@ -41,7 +39,6 @@ import androidx.lifecycle.Lifecycle
|
|||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
@ -56,7 +53,7 @@ import org.oxycblt.auxio.R
|
|||
*/
|
||||
fun View.disableDropShadowCompat() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
val transparent = context.getColorSafe(android.R.color.transparent)
|
||||
val transparent = context.getColorCompat(android.R.color.transparent).defaultColor
|
||||
outlineAmbientShadowColor = transparent
|
||||
outlineSpotShadowColor = transparent
|
||||
}
|
||||
|
@ -113,42 +110,16 @@ val Drawable.isRtl: Boolean
|
|||
val ViewBinding.context: Context
|
||||
get() = root.context
|
||||
|
||||
/**
|
||||
* Apply the recommended spans for a [RecyclerView].
|
||||
*
|
||||
* @param shouldBeFullWidth Optional callback for determining whether an item should be full-width,
|
||||
* regardless of spans
|
||||
*/
|
||||
fun RecyclerView.applySpans(shouldBeFullWidth: ((Int) -> Boolean)? = null) {
|
||||
val spans = resources.getInteger(R.integer.recycler_spans)
|
||||
|
||||
if (spans > 1) {
|
||||
val mgr = GridLayoutManager(context, spans)
|
||||
|
||||
if (shouldBeFullWidth != null) {
|
||||
mgr.spanSizeLookup =
|
||||
object : GridLayoutManager.SpanSizeLookup() {
|
||||
override fun getSpanSize(position: Int): Int {
|
||||
return if (shouldBeFullWidth(position)) spans else 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layoutManager = mgr
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns whether a recyclerview can scroll. */
|
||||
val RecyclerView.canScroll: Boolean
|
||||
get() = computeVerticalScrollRange() > height
|
||||
fun RecyclerView.canScroll() = computeVerticalScrollRange() > height
|
||||
|
||||
val View.coordinatorLayoutBehavior: CoordinatorLayout.Behavior<*>?
|
||||
/**
|
||||
* Shortcut to obtain the CoordinatorLayout behavior of a view. Null if not from a coordinator
|
||||
* layout or if no behavior is present.
|
||||
*/
|
||||
val View.coordinatorLayoutBehavior: CoordinatorLayout.Behavior<View>?
|
||||
get() = (layoutParams as? CoordinatorLayout.LayoutParams)?.behavior
|
||||
|
||||
/** Converts this color to a single-color [ColorStateList]. */
|
||||
val @receiver:ColorRes Int.stateList
|
||||
get() = ColorStateList.valueOf(this)
|
||||
|
||||
/**
|
||||
* Collect a [stateFlow] into [block] eventually.
|
||||
*
|
||||
|
|
|
@ -31,7 +31,7 @@ import org.oxycblt.auxio.music.Song
|
|||
import org.oxycblt.auxio.playback.state.PlaybackStateManager
|
||||
import org.oxycblt.auxio.playback.state.RepeatMode
|
||||
import org.oxycblt.auxio.settings.Settings
|
||||
import org.oxycblt.auxio.util.getDimenSizeSafe
|
||||
import org.oxycblt.auxio.util.getDimenSize
|
||||
import org.oxycblt.auxio.util.logD
|
||||
|
||||
/**
|
||||
|
@ -85,10 +85,10 @@ class WidgetComponent(private val context: Context) :
|
|||
val cornerRadius =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
// Android 12, always round the cover with the app widget's inner radius
|
||||
context.getDimenSizeSafe(android.R.dimen.system_app_widget_inner_radius)
|
||||
context.getDimenSize(android.R.dimen.system_app_widget_inner_radius)
|
||||
} else if (settings.roundMode) {
|
||||
// < Android 12, but the user still enabled round mode.
|
||||
context.getDimenSizeSafe(R.dimen.size_corners_medium)
|
||||
context.getDimenSize(R.dimen.size_corners_medium)
|
||||
} else {
|
||||
// User did not enable round mode.
|
||||
0
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<org.oxycblt.auxio.ui.recycler.ScrollIndicatorRecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<org.oxycblt.auxio.ui.recycler.DialogRecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/accent_recycler"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<org.oxycblt.auxio.ui.recycler.ScrollIndicatorRecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<org.oxycblt.auxio.ui.recycler.DialogRecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/tab_recycler"
|
||||
|
|
|
@ -20,11 +20,12 @@
|
|||
|
||||
</org.oxycblt.auxio.detail.DetailAppBarLayout>
|
||||
|
||||
<org.oxycblt.auxio.ui.recycler.EdgeRecyclerView
|
||||
<org.oxycblt.auxio.ui.recycler.AuxioRecyclerView
|
||||
android:id="@+id/detail_recycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||
app:spanCount="@integer/recycler_spans"
|
||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
|
||||
tools:listitem="@layout/item_detail" />
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
style="@style/Widget.Auxio.RecyclerView.WithAdaptiveFab"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||
app:spanCount="@integer/recycler_spans"
|
||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
|
||||
tools:listitem="@layout/item_parent" />
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<org.oxycblt.auxio.ui.recycler.EdgeRecyclerView
|
||||
<org.oxycblt.auxio.ui.recycler.AuxioRecyclerView
|
||||
android:id="@+id/queue_recycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
|
|
|
@ -45,11 +45,12 @@
|
|||
|
||||
</org.oxycblt.auxio.ui.AuxioAppBarLayout>
|
||||
|
||||
<org.oxycblt.auxio.ui.recycler.EdgeRecyclerView
|
||||
<org.oxycblt.auxio.ui.recycler.AuxioRecyclerView
|
||||
android:id="@+id/search_recycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||
app:spanCount="@integer/recycler_spans"
|
||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
|
||||
tools:listitem="@layout/item_song" />
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
<dimen name="size_corners_mid_large">24dp</dimen>
|
||||
|
||||
<dimen name="size_btn">48dp</dimen>
|
||||
<dimen name="size_accent_item">56dp</dimen>
|
||||
<dimen name="size_bottom_sheet_bar">64dp</dimen>
|
||||
<dimen name="size_play_pause_button">72dp</dimen>
|
||||
|
||||
|
|
Loading…
Reference in a new issue