ui: use scroll indicators
Use basic scroll indicators when a dialog shows a list. Mostly for material guidelines. Excluded dialogs and int pref dialog have not been modified, as I am still working on revamping those.
This commit is contained in:
parent
48e6868f39
commit
5d124ce771
15 changed files with 141 additions and 17 deletions
|
@ -5,8 +5,9 @@
|
|||
#### What's New
|
||||
- Added a new view for song properties (Such as Bitrate)
|
||||
- Folders on external drives can now be excluded on Android Q+ [#134]
|
||||
- Playback bar now has a skip action
|
||||
- When playing, the cover now shows an animated indicator
|
||||
- The playback bar now has a new design, with an improved progress
|
||||
indicator and a skip action
|
||||
- When playing, covers now shows an animated indicator
|
||||
|
||||
#### What's Improved
|
||||
- The toolbar in the home UI now collapses when scrolling
|
||||
|
@ -20,7 +21,7 @@
|
|||
- Songs with no data (i.e size of 0) are now filtered out
|
||||
|
||||
#### Dev/Meta
|
||||
- New translations [Fjuro -> Czech]
|
||||
- New translations [Fjuro -> Czech, Konstantin Tutsch -> German]
|
||||
- Moved music loading to a foreground service
|
||||
- Phased out `ImageButton` for `MaterialButton`
|
||||
- Unified icon sizing
|
||||
|
|
|
@ -110,6 +110,10 @@ class DetailViewModel : ViewModel(), MusicStore.Callback {
|
|||
generateDetailSong(context, song)
|
||||
}
|
||||
|
||||
fun clearSong() {
|
||||
_currentSong.value = null
|
||||
}
|
||||
|
||||
fun setAlbumId(id: Long) {
|
||||
if (_currentAlbum.value?.id == id) return
|
||||
val library = unlikelyToBeNull(musicStore.library)
|
||||
|
|
|
@ -119,7 +119,6 @@ class GenreDetailFragment : DetailFragment(), DetailAdapter.Listener {
|
|||
logD("Navigating to another album")
|
||||
findNavController().navigate(GenreDetailFragmentDirections.actionShowAlbum(item.id))
|
||||
}
|
||||
// All items will launch new detail fragments.
|
||||
is Artist -> {
|
||||
logD("Navigating to another artist")
|
||||
findNavController()
|
||||
|
|
|
@ -44,6 +44,7 @@ class ReadOnlyTextInput : TextInputEditText {
|
|||
) : super(context, attrs, defStyleAttr)
|
||||
|
||||
init {
|
||||
setTextIsSelectable(true)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
focusable = View.FOCUSABLE_AUTO
|
||||
}
|
||||
|
|
|
@ -50,6 +50,11 @@ class SongDetailDialog : ViewBindingDialogFragment<DialogSongDetailBinding>() {
|
|||
launch { detailModel.currentSong.collect(::updateSong) }
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
detailModel.clearSong()
|
||||
}
|
||||
|
||||
private fun updateSong(song: DetailViewModel.DetailSong?) {
|
||||
val binding = requireBinding()
|
||||
|
||||
|
|
|
@ -95,6 +95,7 @@ abstract class DetailAdapter<L : DetailAdapter.Listener>(
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Pause indicator animation when not playing
|
||||
viewHolder.itemView.isActivated = shouldHighlightViewHolder(item)
|
||||
}
|
||||
|
||||
|
|
|
@ -115,7 +115,6 @@ class ExcludedDialog :
|
|||
|
||||
override fun onRemoveDirectory(dir: Dir.Relative) {
|
||||
excludedAdapter.data.remove(dir)
|
||||
requireBinding().excludedEmpty.isVisible = excludedAdapter.data.currentList.isEmpty()
|
||||
}
|
||||
|
||||
private fun addDocTreePath(uri: Uri?) {
|
||||
|
|
|
@ -37,6 +37,7 @@ class IntListPreferenceDialog : PreferenceDialogFragmentCompat() {
|
|||
builder.setTitle(listPreference.title)
|
||||
builder.setPositiveButton(null, null)
|
||||
builder.setNegativeButton(R.string.lbl_cancel, null)
|
||||
// TODO: Replace this with an in-house view
|
||||
builder.setSingleChoiceItems(listPreference.entries, listPreference.getValueIndex()) {
|
||||
_,
|
||||
index ->
|
||||
|
|
101
app/src/main/java/org/oxycblt/auxio/ui/DialogRecyclerView.kt
Normal file
101
app/src/main/java/org/oxycblt/auxio/ui/DialogRecyclerView.kt
Normal file
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Auxio Project
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.AttrRes
|
||||
import androidx.core.view.isInvisible
|
||||
import androidx.core.view.updatePadding
|
||||
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.logD
|
||||
|
||||
/**
|
||||
* A RecyclerView that enables something resembling the android:scrollIndicators attribute.
|
||||
* Only used in dialogs.
|
||||
* @author OxygenCobalt
|
||||
*/
|
||||
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)
|
||||
|
||||
init {
|
||||
updatePadding(top = spacingMedium)
|
||||
overScrollMode = OVER_SCROLL_NEVER
|
||||
|
||||
overlay.apply {
|
||||
add(topDivider)
|
||||
add(bottomDivider)
|
||||
}
|
||||
|
||||
addOnScrollListener(
|
||||
object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
super.onScrolled(recyclerView, dx, dy)
|
||||
|
||||
val manager = recyclerView.layoutManager as LinearLayoutManager
|
||||
logD("top invisible: ${manager.findFirstCompletelyVisibleItemPosition() < 1}")
|
||||
// logD(
|
||||
// "bottom invisible:
|
||||
// ${manager.findLastCompletelyVisibleItemPosition() < (manager.itemCount -
|
||||
// 1)}")
|
||||
topDivider.isInvisible = manager.findFirstCompletelyVisibleItemPosition() < 1
|
||||
bottomDivider.isInvisible =
|
||||
manager.findLastCompletelyVisibleItemPosition() == (manager.itemCount - 1)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onMeasure(widthSpec: Int, heightSpec: Int) {
|
||||
super.onMeasure(widthSpec, heightSpec)
|
||||
measureDivider(topDivider)
|
||||
measureDivider(bottomDivider)
|
||||
}
|
||||
|
||||
private fun measureDivider(divider: MaterialDivider) {
|
||||
val widthMeasureSpec =
|
||||
ViewGroup.getChildMeasureSpec(
|
||||
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
|
||||
0,
|
||||
divider.layoutParams.width)
|
||||
|
||||
val heightMeasureSpec =
|
||||
ViewGroup.getChildMeasureSpec(
|
||||
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY),
|
||||
0,
|
||||
divider.layoutParams.height)
|
||||
|
||||
divider.measure(widthMeasureSpec, heightMeasureSpec)
|
||||
}
|
||||
|
||||
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
|
||||
super.onLayout(changed, l, t, r, b)
|
||||
topDivider.layout(l, spacingMedium, r, spacingMedium + topDivider.measuredHeight)
|
||||
bottomDivider.layout(l, measuredHeight - bottomDivider.measuredHeight, r, b)
|
||||
}
|
||||
}
|
|
@ -1,15 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<org.oxycblt.auxio.ui.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"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:overScrollMode="never"
|
||||
android:paddingStart="@dimen/spacing_medium"
|
||||
android:paddingTop="@dimen/spacing_medium"
|
||||
android:paddingEnd="@dimen/spacing_medium"
|
||||
android:paddingBottom="@dimen/spacing_small"
|
||||
app:layoutManager="org.oxycblt.auxio.ui.accent.AccentGridLayoutManager"
|
||||
app:layout_constraintBottom_toTopOf="@+id/accent_cancel"
|
||||
app:layout_constraintTop_toBottomOf="@+id/accent_header"
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_height="match_parent"
|
||||
style="@style/Widget.Auxio.Dialog.NestedScrollView">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
@ -3,12 +3,12 @@
|
|||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingTop="@dimen/spacing_medium">
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_height="match_parent"
|
||||
style="@style/Widget.Auxio.Dialog.NestedScrollView">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/detail_container"
|
||||
|
@ -18,7 +18,8 @@
|
|||
android:paddingStart="@dimen/spacing_mid_large"
|
||||
android:paddingEnd="@dimen/spacing_mid_large"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible">
|
||||
tools:visibility="visible"
|
||||
android:showDividers="middle">
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<org.oxycblt.auxio.ui.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"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:overScrollMode="never"
|
||||
android:paddingTop="@dimen/spacing_medium"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_constraintBottom_toTopOf="@+id/accent_cancel"
|
||||
app:layout_constraintTop_toBottomOf="@+id/accent_header"
|
||||
|
|
9
app/src/main/res/values-v23/styles_android.xml
Normal file
9
app/src/main/res/values-v23/styles_android.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Dialog style that properly impelments dividers in a NestedScrollView -->
|
||||
<style name="Widget.Auxio.Dialog.NestedScrollView" parent="">
|
||||
<item name="android:overScrollMode">never</item>
|
||||
<item name="android:scrollIndicators">top|bottom</item>
|
||||
<item name="android:paddingTop">@dimen/spacing_medium</item>
|
||||
</style>
|
||||
</resources>
|
|
@ -33,6 +33,12 @@
|
|||
<item name="android:layout_marginStart">0dp</item>
|
||||
</style>
|
||||
|
||||
<!-- Dialog style that properly impelments dividers in a NestedScrollView -->
|
||||
<style name="Widget.Auxio.Dialog.NestedScrollView" parent="">
|
||||
<item name="android:overScrollMode">never</item>
|
||||
<item name="android:paddingTop">@dimen/spacing_medium</item>
|
||||
</style>
|
||||
|
||||
<!-- Widget TextView that mimics the main Auxio Primary/Secondary TextViews. -->
|
||||
<style name="Widget.Auxio.TextView.AppWidget" parent="Widget.Auxio.TextView.Base">
|
||||
<item name="android:singleLine">true</item>
|
||||
|
|
Loading…
Reference in a new issue