mirror of
https://gitlab.com/ultrasonic/ultrasonic.git
synced 2025-06-12 05:22:22 +03:00
Merge branch 'PerformanceFixes' into 'develop'
Performance fixes See merge request ultrasonic/ultrasonic!818
This commit is contained in:
commit
2ef7c651d2
@ -9,6 +9,8 @@ package org.moire.ultrasonic.fragment
|
|||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.AlertDialog
|
import android.app.AlertDialog
|
||||||
|
import android.graphics.Canvas
|
||||||
|
import android.graphics.Color.argb
|
||||||
import android.graphics.Point
|
import android.graphics.Point
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
@ -33,6 +35,7 @@ import android.widget.SeekBar.OnSeekBarChangeListener
|
|||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import android.widget.ViewFlipper
|
import android.widget.ViewFlipper
|
||||||
|
import androidx.core.content.res.ResourcesCompat
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.media3.common.MediaItem
|
import androidx.media3.common.MediaItem
|
||||||
@ -42,10 +45,12 @@ import androidx.navigation.Navigation
|
|||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.recyclerview.widget.ItemTouchHelper
|
import androidx.recyclerview.widget.ItemTouchHelper
|
||||||
import androidx.recyclerview.widget.ItemTouchHelper.ACTION_STATE_DRAG
|
import androidx.recyclerview.widget.ItemTouchHelper.ACTION_STATE_DRAG
|
||||||
|
import androidx.recyclerview.widget.ItemTouchHelper.ACTION_STATE_IDLE
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.LinearSmoothScroller
|
import androidx.recyclerview.widget.LinearSmoothScroller
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.google.android.material.button.MaterialButton
|
import com.google.android.material.button.MaterialButton
|
||||||
|
import com.google.android.material.internal.ViewUtils.dpToPx
|
||||||
import com.google.common.util.concurrent.FutureCallback
|
import com.google.common.util.concurrent.FutureCallback
|
||||||
import com.google.common.util.concurrent.Futures
|
import com.google.common.util.concurrent.Futures
|
||||||
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||||
@ -89,6 +94,8 @@ import org.moire.ultrasonic.util.Util
|
|||||||
import org.moire.ultrasonic.util.toTrack
|
import org.moire.ultrasonic.util.toTrack
|
||||||
import org.moire.ultrasonic.view.AutoRepeatButton
|
import org.moire.ultrasonic.view.AutoRepeatButton
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
import java.util.Collections
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains the Music Player screen of Ultrasonic with playback controls and the playlist
|
* Contains the Music Player screen of Ultrasonic with playback controls and the playlist
|
||||||
@ -358,18 +365,12 @@ class PlayerFragment :
|
|||||||
|
|
||||||
// Observe playlist changes and update the UI
|
// Observe playlist changes and update the UI
|
||||||
rxBusSubscription += RxBus.playlistObservable.subscribe {
|
rxBusSubscription += RxBus.playlistObservable.subscribe {
|
||||||
// Use launch to ensure running it in the main thread
|
onPlaylistChanged()
|
||||||
launch {
|
onSliderProgressChanged()
|
||||||
onPlaylistChanged()
|
|
||||||
onSliderProgressChanged()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rxBusSubscription += RxBus.playerStateObservable.subscribe {
|
rxBusSubscription += RxBus.playerStateObservable.subscribe {
|
||||||
// Use launch to ensure running it in the main thread
|
update()
|
||||||
launch {
|
|
||||||
update()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query the Jukebox state in an IO Context
|
// Query the Jukebox state in an IO Context
|
||||||
@ -855,6 +856,11 @@ class PlayerFragment :
|
|||||||
ItemTouchHelper.UP or ItemTouchHelper.DOWN,
|
ItemTouchHelper.UP or ItemTouchHelper.DOWN,
|
||||||
ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
|
ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
var dragging = false
|
||||||
|
var startPosition = 0
|
||||||
|
var endPosition = 0
|
||||||
|
|
||||||
override fun onMove(
|
override fun onMove(
|
||||||
recyclerView: RecyclerView,
|
recyclerView: RecyclerView,
|
||||||
viewHolder: RecyclerView.ViewHolder,
|
viewHolder: RecyclerView.ViewHolder,
|
||||||
@ -864,8 +870,18 @@ class PlayerFragment :
|
|||||||
val from = viewHolder.bindingAdapterPosition
|
val from = viewHolder.bindingAdapterPosition
|
||||||
val to = target.bindingAdapterPosition
|
val to = target.bindingAdapterPosition
|
||||||
|
|
||||||
// Move it in the data set
|
Timber.i("MOVING from %d to %d", from, to)
|
||||||
mediaPlayerController.moveItemInPlaylist(from, to)
|
val newList = viewAdapter.getCurrentList().toMutableList()
|
||||||
|
Collections.swap(newList, from, to)
|
||||||
|
viewAdapter.submitList(newList)
|
||||||
|
endPosition = to
|
||||||
|
|
||||||
|
// When the user moves an item, onMove may be called many times quickly,
|
||||||
|
// especially while scrolling. We only update the playlist when the item
|
||||||
|
// is released (see onSelectedChanged)
|
||||||
|
|
||||||
|
|
||||||
|
// It was moved, so return true
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -892,6 +908,15 @@ class PlayerFragment :
|
|||||||
|
|
||||||
if (actionState == ACTION_STATE_DRAG) {
|
if (actionState == ACTION_STATE_DRAG) {
|
||||||
viewHolder?.itemView?.alpha = ALPHA_DEACTIVATED
|
viewHolder?.itemView?.alpha = ALPHA_DEACTIVATED
|
||||||
|
dragging = true
|
||||||
|
startPosition = viewHolder!!.bindingAdapterPosition
|
||||||
|
}
|
||||||
|
|
||||||
|
// We only move the item in the playlist when the user finished dragging
|
||||||
|
if (actionState == ACTION_STATE_IDLE && dragging) {
|
||||||
|
dragging = false
|
||||||
|
// Move the item in the playlist separately
|
||||||
|
mediaPlayerController.moveItemInPlaylist(startPosition, endPosition)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -907,6 +932,61 @@ class PlayerFragment :
|
|||||||
override fun isLongPressDragEnabled(): Boolean {
|
override fun isLongPressDragEnabled(): Boolean {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onChildDraw(
|
||||||
|
canvas: Canvas,
|
||||||
|
recyclerView: RecyclerView,
|
||||||
|
viewHolder: RecyclerView.ViewHolder,
|
||||||
|
dX: Float,
|
||||||
|
dY: Float,
|
||||||
|
actionState: Int,
|
||||||
|
isCurrentlyActive: Boolean
|
||||||
|
) {
|
||||||
|
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
|
||||||
|
val itemView = viewHolder.itemView
|
||||||
|
val drawable = ResourcesCompat.getDrawable(
|
||||||
|
resources,
|
||||||
|
R.drawable.ic_menu_remove_all,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
val iconSize = dpToPx(context!!, ICON_SIZE).toInt()
|
||||||
|
val swipeRatio = abs(dX) / viewHolder.itemView.width.toFloat()
|
||||||
|
val itemAlpha = ALPHA_FULL - swipeRatio
|
||||||
|
val backgroundAlpha = min(ALPHA_HALF + swipeRatio, ALPHA_FULL)
|
||||||
|
val backgroundColor = argb((backgroundAlpha * 255).toInt(), 255, 0, 0)
|
||||||
|
|
||||||
|
if (dX > 0) {
|
||||||
|
canvas.clipRect(
|
||||||
|
itemView.left.toFloat(), itemView.top.toFloat(),
|
||||||
|
dX, itemView.bottom.toFloat()
|
||||||
|
)
|
||||||
|
canvas.drawColor(backgroundColor)
|
||||||
|
val left = itemView.left + dpToPx(context!!,16).toInt()
|
||||||
|
val top = itemView.top + (itemView.bottom - itemView.top - iconSize) / 2
|
||||||
|
drawable?.setBounds(left, top, left + iconSize, top + iconSize)
|
||||||
|
drawable?.draw(canvas)
|
||||||
|
} else {
|
||||||
|
canvas.clipRect(
|
||||||
|
itemView.right.toFloat() + dX, itemView.top.toFloat(),
|
||||||
|
itemView.right.toFloat(), itemView.bottom.toFloat(),
|
||||||
|
)
|
||||||
|
canvas.drawColor(backgroundColor)
|
||||||
|
val left = itemView.right - dpToPx(context!!,16).toInt() - iconSize
|
||||||
|
val top = itemView.top + (itemView.bottom - itemView.top - iconSize) / 2
|
||||||
|
drawable?.setBounds(left, top, left + iconSize, top + iconSize)
|
||||||
|
drawable?.draw(canvas)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fade out the view as it is swiped out of the parent's bounds
|
||||||
|
viewHolder.itemView.alpha = itemAlpha
|
||||||
|
viewHolder.itemView.translationX = dX
|
||||||
|
} else {
|
||||||
|
super.onChildDraw(
|
||||||
|
canvas, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dragTouchHelper = ItemTouchHelper(callback)
|
dragTouchHelper = ItemTouchHelper(callback)
|
||||||
@ -1193,5 +1273,8 @@ class PlayerFragment :
|
|||||||
private const val PERCENTAGE_OF_SCREEN_FOR_SWIPE = 5
|
private const val PERCENTAGE_OF_SCREEN_FOR_SWIPE = 5
|
||||||
private const val ALPHA_ACTIVATED = 1f
|
private const val ALPHA_ACTIVATED = 1f
|
||||||
private const val ALPHA_DEACTIVATED = 0.4f
|
private const val ALPHA_DEACTIVATED = 0.4f
|
||||||
|
private const val ALPHA_FULL = 1.0f
|
||||||
|
private const val ALPHA_HALF = 0.5f
|
||||||
|
private const val ICON_SIZE = 32
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,8 @@ class FileLoggerTree : Timber.DebugTree() {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes a log entry to file
|
* Writes a log entry to file
|
||||||
|
*
|
||||||
|
* TODO: This seems to be writing in the main thread. Should be done in background...
|
||||||
*/
|
*/
|
||||||
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
|
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
|
||||||
var writer: FileWriter? = null
|
var writer: FileWriter? = null
|
||||||
@ -34,9 +36,9 @@ class FileLoggerTree : Timber.DebugTree() {
|
|||||||
)
|
)
|
||||||
writer.flush()
|
writer.flush()
|
||||||
}
|
}
|
||||||
} catch (x: Throwable) {
|
} catch (all: Throwable) {
|
||||||
// Using base class DebugTree here, we don't want to try to log this into file
|
// Using base class DebugTree here, we don't want to try to log this into file
|
||||||
super.log(6, TAG, String.format("Failed to write log to %s", file), x)
|
super.log(6, TAG, String.format("Failed to write log to %s", file), all)
|
||||||
} finally {
|
} finally {
|
||||||
writer.safeClose()
|
writer.safeClose()
|
||||||
}
|
}
|
||||||
@ -113,7 +115,9 @@ class FileLoggerTree : Timber.DebugTree() {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val TAG = FileLoggerTree::class.simpleName
|
val TAG = FileLoggerTree::class.simpleName
|
||||||
@Volatile private var file: File? = null
|
|
||||||
|
@Volatile
|
||||||
|
private var file: File? = null
|
||||||
const val FILENAME = "ultrasonic.*.log"
|
const val FILENAME = "ultrasonic.*.log"
|
||||||
private val fileNameRegex = Regex(
|
private val fileNameRegex = Regex(
|
||||||
FILENAME.replace(".", "\\.").replace("*", "\\d*")
|
FILENAME.replace(".", "\\.").replace("*", "\\d*")
|
||||||
|
@ -11,6 +11,11 @@ import org.moire.ultrasonic.domain.Track
|
|||||||
|
|
||||||
class RxBus {
|
class RxBus {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO: mainThread() seems to be not equal to the "normal" main Thread, so it causes
|
||||||
|
* a lot of often unnecessary thread switching. It looks like observeOn can actually
|
||||||
|
* be removed in many cases
|
||||||
|
*/
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
private fun mainThread() = AndroidSchedulers.from(Looper.getMainLooper())
|
private fun mainThread() = AndroidSchedulers.from(Looper.getMainLooper())
|
||||||
@ -33,11 +38,11 @@ class RxBus {
|
|||||||
val playerStatePublisher: PublishSubject<StateWithTrack> =
|
val playerStatePublisher: PublishSubject<StateWithTrack> =
|
||||||
PublishSubject.create()
|
PublishSubject.create()
|
||||||
val playerStateObservable: Observable<StateWithTrack> =
|
val playerStateObservable: Observable<StateWithTrack> =
|
||||||
playerStatePublisher.observeOn(mainThread())
|
playerStatePublisher
|
||||||
.replay(1)
|
.replay(1)
|
||||||
.autoConnect(0)
|
.autoConnect(0)
|
||||||
val throttledPlayerStateObservable: Observable<StateWithTrack> =
|
val throttledPlayerStateObservable: Observable<StateWithTrack> =
|
||||||
playerStatePublisher.observeOn(mainThread())
|
playerStatePublisher
|
||||||
.replay(1)
|
.replay(1)
|
||||||
.autoConnect(0)
|
.autoConnect(0)
|
||||||
.throttleLatest(300, TimeUnit.MILLISECONDS)
|
.throttleLatest(300, TimeUnit.MILLISECONDS)
|
||||||
@ -45,11 +50,11 @@ class RxBus {
|
|||||||
val playlistPublisher: PublishSubject<List<Track>> =
|
val playlistPublisher: PublishSubject<List<Track>> =
|
||||||
PublishSubject.create()
|
PublishSubject.create()
|
||||||
val playlistObservable: Observable<List<Track>> =
|
val playlistObservable: Observable<List<Track>> =
|
||||||
playlistPublisher.observeOn(mainThread())
|
playlistPublisher
|
||||||
.replay(1)
|
.replay(1)
|
||||||
.autoConnect(0)
|
.autoConnect(0)
|
||||||
val throttledPlaylistObservable: Observable<List<Track>> =
|
val throttledPlaylistObservable: Observable<List<Track>> =
|
||||||
playlistPublisher.observeOn(mainThread())
|
playlistPublisher
|
||||||
.replay(1)
|
.replay(1)
|
||||||
.autoConnect(0)
|
.autoConnect(0)
|
||||||
.throttleLatest(300, TimeUnit.MILLISECONDS)
|
.throttleLatest(300, TimeUnit.MILLISECONDS)
|
||||||
|
@ -28,119 +28,104 @@
|
|||||||
a:layout_width="wrap_content"
|
a:layout_width="wrap_content"
|
||||||
a:layout_height="0dp"
|
a:layout_height="0dp"
|
||||||
a:layout_marginStart="4dp"
|
a:layout_marginStart="4dp"
|
||||||
a:layout_marginEnd="4dp"
|
|
||||||
a:checkMark="@drawable/btn_check_custom"
|
a:checkMark="@drawable/btn_check_custom"
|
||||||
a:gravity="center_vertical"
|
a:gravity="center_vertical"
|
||||||
a:paddingEnd="4dip"
|
a:paddingEnd="4dip"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/song_details"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/song_drag"
|
app:layout_constraintStart_toEndOf="@+id/song_drag"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
a:id="@+id/song_details"
|
<TextView
|
||||||
|
a:id="@+id/song_track"
|
||||||
|
a:layout_width="wrap_content"
|
||||||
|
a:layout_height="wrap_content"
|
||||||
|
a:layout_marginTop="8dp"
|
||||||
|
a:paddingEnd="6dip"
|
||||||
|
a:textAppearance="?android:attr/textAppearanceMedium"
|
||||||
|
a:visibility="visible"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/song_title"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/song_check"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:text="Track"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
a:id="@+id/song_title"
|
||||||
a:layout_width="0dp"
|
a:layout_width="0dp"
|
||||||
a:layout_height="wrap_content"
|
a:layout_height="wrap_content"
|
||||||
a:layout_gravity="center_vertical"
|
a:layout_marginTop="8dp"
|
||||||
a:layout_marginStart="4dp"
|
a:drawablePadding="4dip"
|
||||||
a:layout_marginEnd="4dp"
|
a:ellipsize="end"
|
||||||
a:layout_weight="1"
|
a:paddingEnd="4dip"
|
||||||
|
a:singleLine="true"
|
||||||
|
a:textAppearance="?android:attr/textAppearanceMedium"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/barrier"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/song_track"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:text="Title" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
a:id="@+id/song_artist"
|
||||||
|
a:layout_width="0dp"
|
||||||
|
a:layout_height="wrap_content"
|
||||||
|
a:layout_marginBottom="10dp"
|
||||||
|
a:ellipsize="middle"
|
||||||
|
a:paddingStart="1dip"
|
||||||
|
a:paddingEnd="4dip"
|
||||||
|
a:singleLine="true"
|
||||||
|
a:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/barrier"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/song_check"
|
||||||
|
tools:text="Artist" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
a:id="@+id/song_status_image"
|
||||||
|
a:layout_width="25dp"
|
||||||
|
a:layout_height="0dp"
|
||||||
|
a:layout_marginTop="8dp"
|
||||||
|
a:layout_marginEnd="10dip"
|
||||||
|
a:importantForAccessibility="no"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/song_duration"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/star_barrier"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<com.google.android.material.progressindicator.CircularProgressIndicator
|
||||||
|
a:id="@+id/song_status_progress"
|
||||||
|
a:layout_width="wrap_content"
|
||||||
|
a:layout_height="0dp"
|
||||||
|
a:layout_marginTop="8dp"
|
||||||
|
a:layout_marginEnd="10dip"
|
||||||
|
a:indeterminate="true"
|
||||||
|
app:indicatorColor="?attr/color_menu_selected"
|
||||||
|
app:indicatorSize="20dp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/song_duration"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/star_barrier"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:trackColor="?attr/color_selected" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
a:id="@+id/song_duration"
|
||||||
|
a:layout_width="wrap_content"
|
||||||
|
a:layout_height="wrap_content"
|
||||||
|
a:layout_marginBottom="10dp"
|
||||||
|
a:paddingStart="3dip"
|
||||||
|
a:paddingEnd="9dip"
|
||||||
|
a:singleLine="true"
|
||||||
|
a:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="@id/star_barrier"
|
app:layout_constraintEnd_toEndOf="@id/star_barrier"
|
||||||
app:layout_constraintStart_toEndOf="@+id/song_check"
|
tools:text="Duration" />
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.Barrier
|
||||||
|
a:id="@+id/barrier"
|
||||||
|
a:layout_width="wrap_content"
|
||||||
|
a:layout_height="wrap_content"
|
||||||
|
app:barrierDirection="left"
|
||||||
|
app:constraint_referenced_ids="song_duration"
|
||||||
|
tools:layout_editor_absoluteX="289dp" />
|
||||||
|
|
||||||
<TextView
|
|
||||||
a:id="@+id/song_track"
|
|
||||||
a:layout_width="wrap_content"
|
|
||||||
a:layout_height="wrap_content"
|
|
||||||
a:paddingEnd="6dip"
|
|
||||||
a:textAppearance="?android:attr/textAppearanceMedium"
|
|
||||||
a:visibility="visible"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/song_artist"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/song_title"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintVertical_chainStyle="packed"
|
|
||||||
tools:text="Track"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
a:id="@+id/song_title"
|
|
||||||
a:layout_width="0dp"
|
|
||||||
a:layout_height="wrap_content"
|
|
||||||
a:drawablePadding="4dip"
|
|
||||||
a:ellipsize="end"
|
|
||||||
a:paddingEnd="4dip"
|
|
||||||
a:singleLine="true"
|
|
||||||
a:textAppearance="?android:attr/textAppearanceMedium"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/song_artist"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/barrier"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/song_track"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintVertical_bias="1.0"
|
|
||||||
app:layout_constraintVertical_chainStyle="packed"
|
|
||||||
tools:text="Title" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
a:id="@+id/song_artist"
|
|
||||||
a:layout_width="0dp"
|
|
||||||
a:layout_height="wrap_content"
|
|
||||||
a:ellipsize="middle"
|
|
||||||
a:paddingStart="1dip"
|
|
||||||
a:paddingEnd="4dip"
|
|
||||||
a:singleLine="true"
|
|
||||||
a:textAppearance="?android:attr/textAppearanceSmall"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/barrier"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
tools:text="Artist" />
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
a:id="@+id/song_status_image"
|
|
||||||
a:layout_width="25dp"
|
|
||||||
a:layout_height="0dp"
|
|
||||||
a:layout_marginEnd="10dip"
|
|
||||||
app:layout_constraintTop_toTopOf="@id/song_details"
|
|
||||||
app:layout_constraintBottom_toTopOf="@id/song_duration"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
a:importantForAccessibility="no" />
|
|
||||||
|
|
||||||
<com.google.android.material.progressindicator.CircularProgressIndicator
|
|
||||||
a:id="@+id/song_status_progress"
|
|
||||||
a:layout_width="wrap_content"
|
|
||||||
a:layout_height="0dp"
|
|
||||||
a:layout_marginEnd="10dip"
|
|
||||||
app:indicatorSize="20dp"
|
|
||||||
app:indicatorColor="?attr/color_menu_selected"
|
|
||||||
app:trackColor="?attr/color_selected"
|
|
||||||
app:layout_constraintTop_toTopOf="@id/song_details"
|
|
||||||
app:layout_constraintBottom_toTopOf="@id/song_duration"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
a:indeterminate="true" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
a:id="@+id/song_duration"
|
|
||||||
a:layout_width="wrap_content"
|
|
||||||
a:layout_height="wrap_content"
|
|
||||||
a:paddingStart="3dip"
|
|
||||||
a:paddingEnd="9dip"
|
|
||||||
a:singleLine="true"
|
|
||||||
a:textAppearance="?android:attr/textAppearanceSmall"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
tools:text="Duration" />
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.Barrier
|
|
||||||
a:id="@+id/barrier"
|
|
||||||
a:layout_width="wrap_content"
|
|
||||||
a:layout_height="wrap_content"
|
|
||||||
app:barrierDirection="left"
|
|
||||||
app:constraint_referenced_ids="song_duration" />
|
|
||||||
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.Barrier
|
<androidx.constraintlayout.widget.Barrier
|
||||||
a:id="@+id/star_barrier"
|
a:id="@+id/star_barrier"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user