diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/adapters/TrackViewBinder.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/adapters/TrackViewBinder.kt
index 691eeee1..38903da6 100644
--- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/adapters/TrackViewBinder.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/adapters/TrackViewBinder.kt
@@ -1,9 +1,13 @@
 package org.moire.ultrasonic.adapters
 
+import android.annotation.SuppressLint
 import android.content.Context
 import android.view.LayoutInflater
 import android.view.MenuItem
+import android.view.MotionEvent
+import android.view.View
 import android.view.ViewGroup
+import androidx.core.view.MotionEventCompat
 import androidx.lifecycle.LifecycleOwner
 import com.drakeet.multitype.ItemViewBinder
 import org.koin.core.component.KoinComponent
@@ -23,6 +27,8 @@ class TrackViewBinder(
     val lifecycleOwner: LifecycleOwner,
 ) : ItemViewBinder<Identifiable, TrackViewHolder>(), KoinComponent {
 
+    var startDrag: ((TrackViewHolder) -> Unit)? = null
+
     // Set our layout files
     val layout = R.layout.list_item_track
     val contextMenuLayout = R.menu.context_menu_track
@@ -34,6 +40,7 @@ class TrackViewBinder(
         return TrackViewHolder(inflater.inflate(layout, parent, false))
     }
 
+    @SuppressLint("ClickableViewAccessibility")
     @Suppress("LongMethod")
     override fun onBindViewHolder(holder: TrackViewHolder, item: Identifiable) {
         val downloadFile: DownloadFile?
@@ -89,6 +96,13 @@ class TrackViewBinder(
             }
         }
 
+        holder.drag.setOnTouchListener { _, event ->
+            if (event.action == MotionEvent.ACTION_DOWN) {
+                startDrag?.invoke(holder)
+            }
+            false
+        }
+
         // Notify the adapter of selection changes
         holder.observableChecked.observe(
             lifecycleOwner,
@@ -127,4 +141,9 @@ class TrackViewBinder(
             }
         )
     }
+
+    override fun onViewRecycled(holder: TrackViewHolder) {
+        holder.dispose()
+        super.onViewRecycled(holder)
+    }
 }
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/adapters/TrackViewHolder.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/adapters/TrackViewHolder.kt
index fc5ba282..ee46f7dc 100644
--- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/adapters/TrackViewHolder.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/adapters/TrackViewHolder.kt
@@ -11,6 +11,7 @@ import android.widget.TextView
 import androidx.core.view.isVisible
 import androidx.lifecycle.MutableLiveData
 import androidx.recyclerview.widget.RecyclerView
+import io.reactivex.rxjava3.disposables.Disposable
 import org.koin.core.component.KoinComponent
 import org.koin.core.component.get
 import org.moire.ultrasonic.R
@@ -56,6 +57,8 @@ class TrackViewHolder(val view: View) : RecyclerView.ViewHolder(view), Checkable
     private var statusImage: Drawable? = null
     private var isPlayingCached = false
 
+    private var rxSubscription: Disposable? = null
+
     var observableChecked = MutableLiveData(false)
 
     private val useFiveStarRating: Boolean by lazy {
@@ -112,11 +115,15 @@ class TrackViewHolder(val view: View) : RecyclerView.ViewHolder(view), Checkable
             progress.isVisible = false
         }
 
-        RxBus.playerStateObservable.subscribe {
+        rxSubscription = RxBus.playerStateObservable.subscribe {
             setPlayIcon(it.track == downloadFile)
         }
     }
 
+    fun dispose() {
+        rxSubscription?.dispose()
+    }
+
     private fun setPlayIcon(isPlaying: Boolean) {
         if (isPlaying && !isPlayingCached) {
             isPlayingCached = true
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/PlayerFragment.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/PlayerFragment.kt
index 7e26c289..3c22a01b 100644
--- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/PlayerFragment.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/PlayerFragment.kt
@@ -864,7 +864,9 @@ class PlayerFragment :
                 draggable = true,
                 context = requireContext(),
                 lifecycleOwner = viewLifecycleOwner,
-            )
+            ).apply { this.startDrag = { holder ->
+                dragTouchHelper.startDrag(holder)
+            } }
         )
 
         dragTouchHelper = ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(
@@ -923,6 +925,10 @@ class PlayerFragment :
 
                     viewHolder.itemView.alpha = 1.0f
                 }
+
+                override fun isLongPressDragEnabled(): Boolean {
+                    return false
+                }
             }
         )
 
diff --git a/ultrasonic/src/main/res/layout/list_item_track_details.xml b/ultrasonic/src/main/res/layout/list_item_track_details.xml
index a2da6642..62473b67 100644
--- a/ultrasonic/src/main/res/layout/list_item_track_details.xml
+++ b/ultrasonic/src/main/res/layout/list_item_track_details.xml
@@ -16,12 +16,14 @@
         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:text="Track"
+        tools:visibility="visible" />
 
     <TextView
         a:id="@+id/song_title"
@@ -62,7 +64,7 @@
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintEnd_toStartOf="@+id/song_duration"
         app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toBottomOf="@+id/song_track"
+        app:layout_constraintTop_toBottomOf="@+id/song_title"
         tools:text="Artist" />
 
     <TextView