queue: rework previous items
Rework previous items to be based off of adapter state. This improves the transitions between active and previous items and their overall efficiency.
This commit is contained in:
parent
b42dfd0b53
commit
f5542c65ba
8 changed files with 129 additions and 87 deletions
|
@ -720,7 +720,8 @@ public class NeoBottomSheetBehavior<V extends View> extends CoordinatorLayout.Be
|
||||||
}
|
}
|
||||||
} else if (dy < 0) { // Downward
|
} else if (dy < 0) { // Downward
|
||||||
if (!target.canScrollVertically(-1)) {
|
if (!target.canScrollVertically(-1)) {
|
||||||
if (newTop <= collapsedOffset || hideable) {
|
// MODIFICATION: Add enableHidingGestures method
|
||||||
|
if (newTop <= collapsedOffset || (hideable && enableHidingGestures())) {
|
||||||
if (!draggable) {
|
if (!draggable) {
|
||||||
// Prevent dragging
|
// Prevent dragging
|
||||||
return;
|
return;
|
||||||
|
@ -774,7 +775,8 @@ public class NeoBottomSheetBehavior<V extends View> extends CoordinatorLayout.Be
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (hideable && shouldHide(child, getYVelocity())) {
|
// MODIFICATION: Add enableHidingGestures method
|
||||||
|
} else if (hideable && shouldHide(child, getYVelocity()) && enableHidingGestures()) {
|
||||||
targetState = STATE_HIDDEN;
|
targetState = STATE_HIDDEN;
|
||||||
} else if (lastNestedScrollDy == 0) {
|
} else if (lastNestedScrollDy == 0) {
|
||||||
int currentTop = child.getTop();
|
int currentTop = child.getTop();
|
||||||
|
@ -1723,7 +1725,8 @@ public class NeoBottomSheetBehavior<V extends View> extends CoordinatorLayout.Be
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (hideable && shouldHide(releasedChild, yvel)) {
|
// MODIFICATION: Add enableHidingGestures method
|
||||||
|
} else if (hideable && shouldHide(releasedChild, yvel) && enableHidingGestures()) {
|
||||||
// Hide if the view was either released low or it was a significant vertical swipe
|
// Hide if the view was either released low or it was a significant vertical swipe
|
||||||
// otherwise settle to closest expanded state.
|
// otherwise settle to closest expanded state.
|
||||||
if ((Math.abs(xvel) < Math.abs(yvel) && yvel > SIGNIFICANT_VEL_THRESHOLD)
|
if ((Math.abs(xvel) < Math.abs(yvel) && yvel > SIGNIFICANT_VEL_THRESHOLD)
|
||||||
|
@ -1795,8 +1798,9 @@ public class NeoBottomSheetBehavior<V extends View> extends CoordinatorLayout.Be
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int clampViewPositionVertical(@NonNull View child, int top, int dy) {
|
public int clampViewPositionVertical(@NonNull View child, int top, int dy) {
|
||||||
|
// MODIFICATION: Add enableHidingGestures method
|
||||||
return MathUtils.clamp(
|
return MathUtils.clamp(
|
||||||
top, getExpandedOffset(), hideable ? parentHeight : collapsedOffset);
|
top, getExpandedOffset(), (hideable && enableHidingGestures()) ? parentHeight : collapsedOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1806,7 +1810,8 @@ public class NeoBottomSheetBehavior<V extends View> extends CoordinatorLayout.Be
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getViewVerticalDragRange(@NonNull View child) {
|
public int getViewVerticalDragRange(@NonNull View child) {
|
||||||
if (hideable) {
|
// MODIFICATION: Add enableHidingGestures method
|
||||||
|
if (hideable && enableHidingGestures()) {
|
||||||
return parentHeight;
|
return parentHeight;
|
||||||
} else {
|
} else {
|
||||||
return collapsedOffset;
|
return collapsedOffset;
|
||||||
|
@ -1876,6 +1881,15 @@ public class NeoBottomSheetBehavior<V extends View> extends CoordinatorLayout.Be
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether hiding gestures should be enabled if {@code isHideable} is true.
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
@RestrictTo(LIBRARY_GROUP)
|
||||||
|
public boolean enableHidingGestures() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether the bottom sheet should be expanded after it has been released after dragging.
|
* Checks whether the bottom sheet should be expanded after it has been released after dragging.
|
||||||
*
|
*
|
||||||
|
|
|
@ -68,6 +68,9 @@ class PlaybackSheetBehavior<V : View>(context: Context, attributeSet: AttributeS
|
||||||
return success
|
return success
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Note: This is an extension to Auxio's vendored BottomSheetBehavior
|
||||||
|
override fun enableHidingGestures() = true
|
||||||
|
|
||||||
fun hideSafe() {
|
fun hideSafe() {
|
||||||
if (state != STATE_HIDDEN) {
|
if (state != STATE_HIDDEN) {
|
||||||
isDraggable = false
|
isDraggable = false
|
||||||
|
|
|
@ -29,13 +29,46 @@ import java.util.*
|
||||||
import org.oxycblt.auxio.IntegerTable
|
import org.oxycblt.auxio.IntegerTable
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.databinding.ItemQueueSongBinding
|
import org.oxycblt.auxio.databinding.ItemQueueSongBinding
|
||||||
|
import org.oxycblt.auxio.music.Song
|
||||||
import org.oxycblt.auxio.ui.recycler.*
|
import org.oxycblt.auxio.ui.recycler.*
|
||||||
import org.oxycblt.auxio.util.*
|
import org.oxycblt.auxio.util.*
|
||||||
|
|
||||||
class QueueAdapter(listener: QueueItemListener) :
|
class QueueAdapter(listener: QueueItemListener) :
|
||||||
MonoAdapter<QueueViewModel.QueueSong, QueueItemListener, QueueSongViewHolder>(listener) {
|
MonoAdapter<Song, QueueItemListener, QueueSongViewHolder>(listener) {
|
||||||
|
private var currentIndex = 0
|
||||||
|
|
||||||
override val data = SyncBackingData(this, QueueSongViewHolder.DIFFER)
|
override val data = SyncBackingData(this, QueueSongViewHolder.DIFFER)
|
||||||
override val creator = QueueSongViewHolder.CREATOR
|
override val creator = QueueSongViewHolder.CREATOR
|
||||||
|
|
||||||
|
override fun onBindViewHolder(
|
||||||
|
viewHolder: QueueSongViewHolder,
|
||||||
|
position: Int,
|
||||||
|
payload: List<Any>
|
||||||
|
) {
|
||||||
|
if (payload.isEmpty()) {
|
||||||
|
super.onBindViewHolder(viewHolder, position, payload)
|
||||||
|
}
|
||||||
|
|
||||||
|
viewHolder.isPrevious = position <= currentIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateIndex(index: Int) {
|
||||||
|
when {
|
||||||
|
index < currentIndex -> {
|
||||||
|
val lastIndex = currentIndex
|
||||||
|
currentIndex = index
|
||||||
|
notifyItemRangeChanged(0, lastIndex + 1, PAYLOAD_UPDATE_INDEX)
|
||||||
|
}
|
||||||
|
index > currentIndex -> {
|
||||||
|
currentIndex = index
|
||||||
|
notifyItemRangeChanged(0, currentIndex + 1, PAYLOAD_UPDATE_INDEX)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val PAYLOAD_UPDATE_INDEX = Any()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface QueueItemListener {
|
interface QueueItemListener {
|
||||||
|
@ -46,7 +79,7 @@ interface QueueItemListener {
|
||||||
class QueueSongViewHolder
|
class QueueSongViewHolder
|
||||||
private constructor(
|
private constructor(
|
||||||
private val binding: ItemQueueSongBinding,
|
private val binding: ItemQueueSongBinding,
|
||||||
) : BindingViewHolder<QueueViewModel.QueueSong, QueueItemListener>(binding.root) {
|
) : BindingViewHolder<Song, QueueItemListener>(binding.root) {
|
||||||
val bodyView: View
|
val bodyView: View
|
||||||
get() = binding.body
|
get() = binding.body
|
||||||
val backgroundView: View
|
val backgroundView: View
|
||||||
|
@ -58,8 +91,15 @@ private constructor(
|
||||||
elevation = binding.context.getDimenSafe(R.dimen.elevation_normal) * 5
|
elevation = binding.context.getDimenSafe(R.dimen.elevation_normal) * 5
|
||||||
}
|
}
|
||||||
|
|
||||||
val isPrevious: Boolean
|
var isPrevious: Boolean
|
||||||
get() = binding.songDragHandle.alpha == 0.5f
|
get() = binding.songDragHandle.alpha == 0.5f
|
||||||
|
set(value) {
|
||||||
|
val alpha = if (value) 0.5f else 1f
|
||||||
|
binding.songAlbumCover.alpha = alpha
|
||||||
|
binding.songName.alpha = alpha
|
||||||
|
binding.songInfo.alpha = alpha
|
||||||
|
binding.songDragHandle.alpha = alpha
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
binding.body.background =
|
binding.body.background =
|
||||||
|
@ -75,10 +115,10 @@ private constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
override fun bind(item: QueueViewModel.QueueSong, listener: QueueItemListener) {
|
override fun bind(item: Song, listener: QueueItemListener) {
|
||||||
binding.songAlbumCover.bind(item.song)
|
binding.songAlbumCover.bind(item)
|
||||||
binding.songName.textSafe = item.song.resolveName(binding.context)
|
binding.songName.textSafe = item.resolveName(binding.context)
|
||||||
binding.songInfo.textSafe = item.song.resolveIndividualArtistName(binding.context)
|
binding.songInfo.textSafe = item.resolveIndividualArtistName(binding.context)
|
||||||
|
|
||||||
binding.background.isInvisible = true
|
binding.background.isInvisible = true
|
||||||
|
|
||||||
|
@ -87,12 +127,6 @@ private constructor(
|
||||||
|
|
||||||
binding.body.setOnClickListener { listener.onClick(this) }
|
binding.body.setOnClickListener { listener.onClick(this) }
|
||||||
|
|
||||||
val alpha = if (item.previous) 0.5f else 1f
|
|
||||||
binding.songAlbumCover.alpha = alpha
|
|
||||||
binding.songName.alpha = alpha
|
|
||||||
binding.songInfo.alpha = alpha
|
|
||||||
binding.songDragHandle.alpha = alpha
|
|
||||||
|
|
||||||
// Roll our own drag handlers as the default ones suck
|
// Roll our own drag handlers as the default ones suck
|
||||||
binding.songDragHandle.setOnTouchListener { _, motionEvent ->
|
binding.songDragHandle.setOnTouchListener { _, motionEvent ->
|
||||||
binding.songDragHandle.performClick()
|
binding.songDragHandle.performClick()
|
||||||
|
@ -113,19 +147,6 @@ private constructor(
|
||||||
QueueSongViewHolder(ItemQueueSongBinding.inflate(context.inflater))
|
QueueSongViewHolder(ItemQueueSongBinding.inflate(context.inflater))
|
||||||
}
|
}
|
||||||
|
|
||||||
val DIFFER =
|
val DIFFER = SongViewHolder.DIFFER
|
||||||
object : SimpleItemCallback<QueueViewModel.QueueSong>() {
|
|
||||||
override fun areContentsTheSame(
|
|
||||||
oldItem: QueueViewModel.QueueSong,
|
|
||||||
newItem: QueueViewModel.QueueSong
|
|
||||||
) =
|
|
||||||
super.areContentsTheSame(oldItem, newItem) &&
|
|
||||||
oldItem.previous == newItem.previous
|
|
||||||
|
|
||||||
override fun areItemsTheSame(
|
|
||||||
oldItem: QueueViewModel.QueueSong,
|
|
||||||
newItem: QueueViewModel.QueueSong
|
|
||||||
) = oldItem.song == newItem.song && oldItem.previous == newItem.previous
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,10 +25,11 @@ import androidx.fragment.app.activityViewModels
|
||||||
import androidx.recyclerview.widget.ItemTouchHelper
|
import androidx.recyclerview.widget.ItemTouchHelper
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import java.util.*
|
|
||||||
import org.oxycblt.auxio.databinding.FragmentQueueBinding
|
import org.oxycblt.auxio.databinding.FragmentQueueBinding
|
||||||
|
import org.oxycblt.auxio.music.Song
|
||||||
import org.oxycblt.auxio.ui.fragment.ViewBindingFragment
|
import org.oxycblt.auxio.ui.fragment.ViewBindingFragment
|
||||||
import org.oxycblt.auxio.util.collectImmediately
|
import org.oxycblt.auxio.util.collectImmediately
|
||||||
|
import org.oxycblt.auxio.util.logD
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A [Fragment] that shows the queue and enables editing as well.
|
* A [Fragment] that shows the queue and enables editing as well.
|
||||||
|
@ -66,7 +67,7 @@ class QueueFragment : ViewBindingFragment<FragmentQueueBinding>(), QueueItemList
|
||||||
|
|
||||||
// --- VIEWMODEL SETUP ----
|
// --- VIEWMODEL SETUP ----
|
||||||
|
|
||||||
collectImmediately(queueModel.queue, ::updateQueue)
|
collectImmediately(queueModel.queue, queueModel.index, ::updateQueue)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyBinding(binding: FragmentQueueBinding) {
|
override fun onDestroyBinding(binding: FragmentQueueBinding) {
|
||||||
|
@ -82,16 +83,20 @@ class QueueFragment : ViewBindingFragment<FragmentQueueBinding>(), QueueItemList
|
||||||
touchHelper.startDrag(viewHolder)
|
touchHelper.startDrag(viewHolder)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateQueue(queue: List<QueueViewModel.QueueSong>) {
|
private fun updateQueue(queue: List<Song>, index: Int) {
|
||||||
val instructions = queueModel.instructions
|
val replaceQueue = queueModel.replaceQueue
|
||||||
if (instructions != null) {
|
if (replaceQueue == true) {
|
||||||
if (instructions.replace) {
|
logD("Replacing queue")
|
||||||
queueAdapter.data.replaceList(queue)
|
queueAdapter.data.replaceList(queue)
|
||||||
} else {
|
} else {
|
||||||
|
logD("Diffing queue")
|
||||||
queueAdapter.data.submitList(queue)
|
queueAdapter.data.submitList(queue)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (instructions.scrollTo != null) {
|
queueModel.finishReplace()
|
||||||
|
|
||||||
|
val scrollTo = queueModel.scrollTo
|
||||||
|
if (scrollTo != null) {
|
||||||
val binding = requireBinding()
|
val binding = requireBinding()
|
||||||
val lmm = binding.queueRecycler.layoutManager as LinearLayoutManager
|
val lmm = binding.queueRecycler.layoutManager as LinearLayoutManager
|
||||||
val start = lmm.findFirstCompletelyVisibleItemPosition()
|
val start = lmm.findFirstCompletelyVisibleItemPosition()
|
||||||
|
@ -99,14 +104,13 @@ class QueueFragment : ViewBindingFragment<FragmentQueueBinding>(), QueueItemList
|
||||||
|
|
||||||
if (start != RecyclerView.NO_POSITION &&
|
if (start != RecyclerView.NO_POSITION &&
|
||||||
end != RecyclerView.NO_POSITION &&
|
end != RecyclerView.NO_POSITION &&
|
||||||
instructions.scrollTo !in start..end) {
|
scrollTo !in start..end) {
|
||||||
binding.queueRecycler.scrollToPosition(instructions.scrollTo)
|
binding.queueRecycler.scrollToPosition(scrollTo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
queueModel.finishInstructions()
|
queueModel.finishScrollTo()
|
||||||
} else {
|
|
||||||
queueAdapter.data.submitList(queue)
|
queueAdapter.updateIndex(index)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@ class QueueSheetBehavior<V : View>(context: Context, attributeSet: AttributeSet?
|
||||||
private var barSpacing = context.getDimenSizeSafe(R.dimen.spacing_small)
|
private var barSpacing = context.getDimenSizeSafe(R.dimen.spacing_small)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
isHideable = false
|
||||||
sheetBackgroundDrawable.setCornerSize(context.getDimenSafe(R.dimen.size_corners_medium))
|
sheetBackgroundDrawable.setCornerSize(context.getDimenSafe(R.dimen.size_corners_medium))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,21 +24,19 @@ import kotlinx.coroutines.flow.StateFlow
|
||||||
import org.oxycblt.auxio.music.MusicParent
|
import org.oxycblt.auxio.music.MusicParent
|
||||||
import org.oxycblt.auxio.music.Song
|
import org.oxycblt.auxio.music.Song
|
||||||
import org.oxycblt.auxio.playback.state.PlaybackStateManager
|
import org.oxycblt.auxio.playback.state.PlaybackStateManager
|
||||||
import org.oxycblt.auxio.ui.recycler.Item
|
|
||||||
|
|
||||||
class QueueViewModel : ViewModel(), PlaybackStateManager.Callback {
|
class QueueViewModel : ViewModel(), PlaybackStateManager.Callback {
|
||||||
private val playbackManager = PlaybackStateManager.getInstance()
|
private val playbackManager = PlaybackStateManager.getInstance()
|
||||||
|
|
||||||
data class QueueSong(val song: Song, val previous: Boolean) : Item() {
|
private val _queue = MutableStateFlow(listOf<Song>())
|
||||||
override val id: Long
|
val queue: StateFlow<List<Song>> = _queue
|
||||||
get() = song.id
|
|
||||||
}
|
|
||||||
|
|
||||||
private val _queue = MutableStateFlow(listOf<QueueSong>())
|
private val _index = MutableStateFlow(playbackManager.index)
|
||||||
val queue: StateFlow<List<QueueSong>> = _queue
|
val index: StateFlow<Int>
|
||||||
|
get() = _index
|
||||||
|
|
||||||
data class QueueInstructions(val replace: Boolean, val scrollTo: Int?)
|
var replaceQueue: Boolean? = null
|
||||||
var instructions: QueueInstructions? = null
|
var scrollTo: Int? = null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
playbackManager.addCallback(this)
|
playbackManager.addCallback(this)
|
||||||
|
@ -76,35 +74,38 @@ class QueueViewModel : ViewModel(), PlaybackStateManager.Callback {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun finishInstructions() {
|
fun finishReplace() {
|
||||||
instructions = null
|
replaceQueue = null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun finishScrollTo() {
|
||||||
|
scrollTo = null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onIndexMoved(index: Int) {
|
override fun onIndexMoved(index: Int) {
|
||||||
instructions = QueueInstructions(false, min(index + 1, playbackManager.queue.lastIndex))
|
replaceQueue = null
|
||||||
_queue.value = generateQueue(index, playbackManager.queue)
|
scrollTo = min(index + 1, playbackManager.queue.lastIndex)
|
||||||
|
_index.value = index
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onQueueChanged(queue: List<Song>) {
|
override fun onQueueChanged(queue: List<Song>) {
|
||||||
instructions = QueueInstructions(false, null)
|
replaceQueue = false
|
||||||
_queue.value = generateQueue(playbackManager.index, queue)
|
scrollTo = null
|
||||||
|
_queue.value = playbackManager.queue.toMutableList()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onQueueReworked(index: Int, queue: List<Song>) {
|
override fun onQueueReworked(index: Int, queue: List<Song>) {
|
||||||
instructions = QueueInstructions(true, min(index + 1, playbackManager.queue.lastIndex))
|
replaceQueue = true
|
||||||
_queue.value = generateQueue(index, queue)
|
scrollTo = min(index + 1, playbackManager.queue.lastIndex)
|
||||||
|
_queue.value = playbackManager.queue.toMutableList()
|
||||||
|
_index.value = index
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onNewPlayback(index: Int, queue: List<Song>, parent: MusicParent?) {
|
override fun onNewPlayback(index: Int, queue: List<Song>, parent: MusicParent?) {
|
||||||
instructions = QueueInstructions(true, min(index + 1, playbackManager.queue.lastIndex))
|
replaceQueue = true
|
||||||
_queue.value = generateQueue(index, queue)
|
scrollTo = min(index + 1, playbackManager.queue.lastIndex)
|
||||||
}
|
_queue.value = playbackManager.queue.toMutableList()
|
||||||
|
_index.value = index
|
||||||
private fun generateQueue(index: Int, queue: List<Song>): List<QueueSong> {
|
|
||||||
val before = queue.slice(0..index).map { QueueSong(it, true) }
|
|
||||||
val after =
|
|
||||||
queue.slice(index + 1..playbackManager.queue.lastIndex).map { QueueSong(it, false) }
|
|
||||||
return before + after
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCleared() {
|
override fun onCleared() {
|
||||||
|
|
|
@ -38,8 +38,7 @@
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
app:layout_constraintHorizontal_bias="0.5"
|
||||||
app:layout_constraintStart_toEndOf="@+id/playback_cover"
|
app:layout_constraintStart_toEndOf="@+id/playback_cover"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/playback_toolbar"
|
app:layout_constraintTop_toBottomOf="@+id/playback_toolbar"
|
||||||
app:layout_constraintVertical_chainStyle="packed"
|
app:layout_constraintVertical_chainStyle="packed">
|
||||||
app:trackColorInactive="@color/sel_track">
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/playback_song"
|
android:id="@+id/playback_song"
|
||||||
|
|
|
@ -87,8 +87,7 @@
|
||||||
android:indeterminate="true"
|
android:indeterminate="true"
|
||||||
app:indeterminateAnimationType="disjoint"
|
app:indeterminateAnimationType="disjoint"
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/home_indexing_action"
|
app:layout_constraintBottom_toBottomOf="@+id/home_indexing_action"
|
||||||
app:layout_constraintTop_toTopOf="@+id/home_indexing_action"
|
app:layout_constraintTop_toTopOf="@+id/home_indexing_action" />
|
||||||
app:trackColor="@color/sel_track" />
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/home_indexing_action"
|
android:id="@+id/home_indexing_action"
|
||||||
|
|
Loading…
Reference in a new issue