mirror of
https://github.com/Michatec/Radio.git
synced 2026-03-31 23:46:28 +02:00
feat(ui): implement station reordering via DPAD for TV navigation
This commit is contained in:
@@ -77,11 +77,12 @@ You can help out the radio-browser.info community by [adding the missing station
|
|||||||
|
|
||||||
When **Edit Stations** is enabled:
|
When **Edit Stations** is enabled:
|
||||||
- Press **← (Left)** on the remote to open the detailed station editing area
|
- Press **← (Left)** on the remote to open the detailed station editing area
|
||||||
- Press **2** or **Back** to close the editing area
|
- Press **3** or **Back** to close the editing area
|
||||||
|
|
||||||
**General TV** Controls:
|
**General TV** Controls:
|
||||||
- Press **0** or **DEL** to remove the selected radio station
|
- Press **0** or **DEL** to remove the selected radio station
|
||||||
- Press **1** or **SPACE** to make the selected radio station favourite
|
- Press **1** or **SPACE** to make the selected radio station favourite
|
||||||
|
- Press **2** or **PLUS** to reorder the selected radio station
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ package com.michatec.radio.collection
|
|||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
|
import android.content.res.ColorStateList
|
||||||
import android.text.Editable
|
import android.text.Editable
|
||||||
import android.text.TextWatcher
|
import android.text.TextWatcher
|
||||||
import android.view.KeyEvent
|
import android.view.KeyEvent
|
||||||
@@ -28,17 +29,19 @@ import android.widget.ImageView
|
|||||||
import android.widget.ProgressBar
|
import android.widget.ProgressBar
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.cardview.widget.CardView
|
|
||||||
import androidx.constraintlayout.widget.Group
|
import androidx.constraintlayout.widget.Group
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import androidx.core.view.isGone
|
import androidx.core.view.isGone
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.LifecycleOwner
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.media3.common.text.TextAnnotation
|
||||||
import androidx.navigation.findNavController
|
import androidx.navigation.findNavController
|
||||||
import androidx.recyclerview.widget.DiffUtil
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
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.card.MaterialCardView
|
||||||
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||||
import com.google.android.material.textfield.TextInputEditText
|
import com.google.android.material.textfield.TextInputEditText
|
||||||
import com.michatec.radio.Keys
|
import com.michatec.radio.Keys
|
||||||
@@ -68,6 +71,7 @@ class CollectionAdapter(
|
|||||||
private var expandedStationUuid: String = PreferencesHelper.loadStationListStreamUuid()
|
private var expandedStationUuid: String = PreferencesHelper.loadStationListStreamUuid()
|
||||||
private var expandedStationPosition: Int = -1
|
private var expandedStationPosition: Int = -1
|
||||||
var isExpandedForEdit: Boolean = false
|
var isExpandedForEdit: Boolean = false
|
||||||
|
private var reorderStationUuid: String = ""
|
||||||
|
|
||||||
|
|
||||||
/* Listener Interface */
|
/* Listener Interface */
|
||||||
@@ -198,6 +202,11 @@ class CollectionAdapter(
|
|||||||
setPlaybackProgress(stationViewHolder, station)
|
setPlaybackProgress(stationViewHolder, station)
|
||||||
setDownloadProgress(stationViewHolder, station)
|
setDownloadProgress(stationViewHolder, station)
|
||||||
|
|
||||||
|
// highlight if reordering
|
||||||
|
if (reorderStationUuid == station.uuid) {
|
||||||
|
stationViewHolder.stationCardView.setStrokeColor(ColorStateList.valueOf(ContextCompat.getColor(context, R.color.cardview_reordering)))
|
||||||
|
}
|
||||||
|
|
||||||
// show / hide edit views
|
// show / hide edit views
|
||||||
when (expandedStationPosition) {
|
when (expandedStationPosition) {
|
||||||
// show edit views
|
// show edit views
|
||||||
@@ -324,6 +333,21 @@ class CollectionAdapter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Shows / hides the reorder highlight for a station */
|
||||||
|
private fun toggleReorderMode(position: Int, stationUuid: String) {
|
||||||
|
if (reorderStationUuid == stationUuid) {
|
||||||
|
reorderStationUuid = ""
|
||||||
|
saveCollectionAfterDragDrop()
|
||||||
|
} else {
|
||||||
|
// collapse edit views if necessary
|
||||||
|
if (isExpandedForEdit) {
|
||||||
|
toggleEditViews(expandedStationPosition, expandedStationUuid)
|
||||||
|
}
|
||||||
|
reorderStationUuid = stationUuid
|
||||||
|
}
|
||||||
|
notifyItemChanged(position)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Shows / hides the edit view for a station */
|
/* Shows / hides the edit view for a station */
|
||||||
@SuppressLint("NotifyDataSetChanged")
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
@@ -392,6 +416,7 @@ class CollectionAdapter(
|
|||||||
false -> stationViewHolder.playButtonView.visibility = View.INVISIBLE
|
false -> stationViewHolder.playButtonView.visibility = View.INVISIBLE
|
||||||
}
|
}
|
||||||
stationViewHolder.stationCardView.setOnClickListener {
|
stationViewHolder.stationCardView.setOnClickListener {
|
||||||
|
if (reorderStationUuid.isNotEmpty()) return@setOnClickListener
|
||||||
if (expandedStationPosition == stationViewHolder.bindingAdapterPosition) return@setOnClickListener
|
if (expandedStationPosition == stationViewHolder.bindingAdapterPosition) return@setOnClickListener
|
||||||
collectionAdapterListener.onPlayButtonTapped(station.uuid)
|
collectionAdapterListener.onPlayButtonTapped(station.uuid)
|
||||||
}
|
}
|
||||||
@@ -408,9 +433,35 @@ class CollectionAdapter(
|
|||||||
collectionAdapterListener.onPlayButtonTapped(station.uuid)
|
collectionAdapterListener.onPlayButtonTapped(station.uuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TV improvement: Allow opening edit view with DPAD_LEFT
|
// TV improvement: Allow reordering with DPAD
|
||||||
stationViewHolder.stationCardView.setOnKeyListener { _, keyCode, event ->
|
stationViewHolder.stationCardView.setOnKeyListener { _, keyCode, event ->
|
||||||
if (event.action == KeyEvent.ACTION_DOWN) {
|
if (event.action == KeyEvent.ACTION_DOWN) {
|
||||||
|
// Reorder mode handling
|
||||||
|
if (reorderStationUuid == station.uuid) {
|
||||||
|
when (keyCode) {
|
||||||
|
KeyEvent.KEYCODE_DPAD_UP -> {
|
||||||
|
val currentPos = stationViewHolder.bindingAdapterPosition
|
||||||
|
if (currentPos > 0) {
|
||||||
|
onItemMove(currentPos, currentPos - 1)
|
||||||
|
}
|
||||||
|
return@setOnKeyListener true
|
||||||
|
}
|
||||||
|
KeyEvent.KEYCODE_DPAD_DOWN -> {
|
||||||
|
val currentPos = stationViewHolder.bindingAdapterPosition
|
||||||
|
if (currentPos < collection.stations.size - 1) {
|
||||||
|
onItemMove(currentPos, currentPos + 1)
|
||||||
|
}
|
||||||
|
return@setOnKeyListener true
|
||||||
|
}
|
||||||
|
KeyEvent.KEYCODE_NUMPAD_2, KeyEvent.KEYCODE_2, KeyEvent.KEYCODE_PLUS,
|
||||||
|
KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_ENTER, KeyEvent.KEYCODE_DPAD_CENTER -> {
|
||||||
|
toggleReorderMode(stationViewHolder.bindingAdapterPosition, station.uuid)
|
||||||
|
return@setOnKeyListener true
|
||||||
|
}
|
||||||
|
else -> return@setOnKeyListener true // Consume other keys in reorder mode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
when (keyCode) {
|
when (keyCode) {
|
||||||
KeyEvent.KEYCODE_DPAD_LEFT -> {
|
KeyEvent.KEYCODE_DPAD_LEFT -> {
|
||||||
if (editStationsEnabled && expandedStationPosition != stationViewHolder.bindingAdapterPosition) {
|
if (editStationsEnabled && expandedStationPosition != stationViewHolder.bindingAdapterPosition) {
|
||||||
@@ -419,7 +470,7 @@ class CollectionAdapter(
|
|||||||
return@setOnKeyListener true
|
return@setOnKeyListener true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
KeyEvent.KEYCODE_NUMPAD_2, KeyEvent.KEYCODE_2, KeyEvent.KEYCODE_BACK -> {
|
KeyEvent.KEYCODE_NUMPAD_3, KeyEvent.KEYCODE_3, KeyEvent.KEYCODE_BACK -> {
|
||||||
if (expandedStationPosition == stationViewHolder.bindingAdapterPosition) {
|
if (expandedStationPosition == stationViewHolder.bindingAdapterPosition) {
|
||||||
val position: Int = stationViewHolder.bindingAdapterPosition
|
val position: Int = stationViewHolder.bindingAdapterPosition
|
||||||
toggleEditViews(position, station.uuid)
|
toggleEditViews(position, station.uuid)
|
||||||
@@ -434,6 +485,10 @@ class CollectionAdapter(
|
|||||||
toggleStarredStation(context, stationViewHolder.bindingAdapterPosition)
|
toggleStarredStation(context, stationViewHolder.bindingAdapterPosition)
|
||||||
return@setOnKeyListener true
|
return@setOnKeyListener true
|
||||||
}
|
}
|
||||||
|
KeyEvent.KEYCODE_NUMPAD_2, KeyEvent.KEYCODE_2, KeyEvent.KEYCODE_PLUS -> {
|
||||||
|
toggleReorderMode(stationViewHolder.bindingAdapterPosition, station.uuid)
|
||||||
|
return@setOnKeyListener true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
@@ -717,7 +772,7 @@ class CollectionAdapter(
|
|||||||
*/
|
*/
|
||||||
private class StationViewHolder(stationCardLayout: View) :
|
private class StationViewHolder(stationCardLayout: View) :
|
||||||
RecyclerView.ViewHolder(stationCardLayout) {
|
RecyclerView.ViewHolder(stationCardLayout) {
|
||||||
val stationCardView: CardView = stationCardLayout.findViewById(R.id.station_card)
|
val stationCardView: MaterialCardView = stationCardLayout.findViewById(R.id.station_card)
|
||||||
val stationImageView: ImageView = stationCardLayout.findViewById(R.id.station_icon)
|
val stationImageView: ImageView = stationCardLayout.findViewById(R.id.station_icon)
|
||||||
val stationNameView: TextView = stationCardLayout.findViewById(R.id.station_name)
|
val stationNameView: TextView = stationCardLayout.findViewById(R.id.station_name)
|
||||||
val stationStarredView: ImageView = stationCardLayout.findViewById(R.id.starred_icon)
|
val stationStarredView: ImageView = stationCardLayout.findViewById(R.id.starred_icon)
|
||||||
|
|||||||
@@ -32,4 +32,5 @@
|
|||||||
<color name="player_sheet_text_main">@android:color/system_neutral1_50</color>
|
<color name="player_sheet_text_main">@android:color/system_neutral1_50</color>
|
||||||
<color name="player_sheet_icon">@android:color/system_neutral1_50</color>
|
<color name="player_sheet_icon">@android:color/system_neutral1_50</color>
|
||||||
|
|
||||||
|
<color name="cardview_reordering">@android:color/system_accent1_100</color>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -31,4 +31,5 @@
|
|||||||
<color name="player_sheet_text_main">#FFFFFFFF</color>
|
<color name="player_sheet_text_main">#FFFFFFFF</color>
|
||||||
<color name="player_sheet_icon">#FFFFFFFF</color>
|
<color name="player_sheet_icon">#FFFFFFFF</color>
|
||||||
|
|
||||||
|
<color name="cardview_reordering">#FFDAE2FF</color>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -37,4 +37,5 @@
|
|||||||
<color name="default_neutral_medium_light">@android:color/system_neutral1_300</color>
|
<color name="default_neutral_medium_light">@android:color/system_neutral1_300</color>
|
||||||
<color name="default_neutral_dark">@android:color/system_neutral1_600</color>
|
<color name="default_neutral_dark">@android:color/system_neutral1_600</color>
|
||||||
|
|
||||||
|
<color name="cardview_reordering">@android:color/system_accent1_900</color>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -39,5 +39,5 @@
|
|||||||
<color name="default_neutral_85percent">#D9313033</color>
|
<color name="default_neutral_85percent">#D9313033</color>
|
||||||
|
|
||||||
<color name="splashBackgroundColor">#FF1D3E66</color>
|
<color name="splashBackgroundColor">#FF1D3E66</color>
|
||||||
|
<color name="cardview_reordering">#161C2C</color>
|
||||||
</resources>
|
</resources>
|
||||||
Reference in New Issue
Block a user