mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-11 15:29:15 +01:00
toast a friendly message for playback connection errors
Map the Media3 network/HTTP error codes to readable text and show it as a toast, instead of leaving an opaque ERR_ code on the now playing screen.
This commit is contained in:
@@ -45,7 +45,7 @@ fun appModule(
|
|||||||
single { SyncManager(get(), get(), get()) }
|
single { SyncManager(get(), get(), get()) }
|
||||||
single { QueueRepository(get()) }
|
single { QueueRepository(get()) }
|
||||||
single<PlayerConnection> { MediaSessionConnection(context.applicationContext, get()) }
|
single<PlayerConnection> { MediaSessionConnection(context.applicationContext, get()) }
|
||||||
single { PlaybackController(get(), get(), get()) }
|
single { PlaybackController(get(), get(), get(), showMessage = toast) }
|
||||||
viewModel { RootViewModel(get()) }
|
viewModel { RootViewModel(get()) }
|
||||||
viewModel { LibraryViewModel(get(), get(), get(), get(), get()) }
|
viewModel { LibraryViewModel(get(), get(), get(), get(), get()) }
|
||||||
viewModel { SettingsViewModel(get(), get()) }
|
viewModel { SettingsViewModel(get(), get()) }
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ class Media3PlayerHandle(
|
|||||||
"Playback error ${error.errorCodeName} at ${controller.currentPosition}/${controller.duration}",
|
"Playback error ${error.errorCodeName} at ${controller.currentPosition}/${controller.duration}",
|
||||||
error,
|
error,
|
||||||
)
|
)
|
||||||
listener.onError(error.errorCodeName)
|
listener.onError(playbackErrorMessage(error))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPlaybackStateChanged(playbackState: Int) {
|
override fun onPlaybackStateChanged(playbackState: Int) {
|
||||||
@@ -171,3 +171,14 @@ class Media3PlayerHandle(
|
|||||||
const val EXTRA_DURATION_MS = "durationMs"
|
const val EXTRA_DURATION_MS = "durationMs"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun playbackErrorMessage(error: PlaybackException): String =
|
||||||
|
when (error.errorCode) {
|
||||||
|
PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED,
|
||||||
|
PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_TIMEOUT,
|
||||||
|
-> "Can't reach the server. Check your connection."
|
||||||
|
|
||||||
|
PlaybackException.ERROR_CODE_IO_BAD_HTTP_STATUS -> "The server refused to stream this track."
|
||||||
|
|
||||||
|
else -> error.errorCodeName
|
||||||
|
}
|
||||||
|
|||||||
@@ -98,6 +98,7 @@ class PlaybackController(
|
|||||||
private val sourceRepository: SourceRepository,
|
private val sourceRepository: SourceRepository,
|
||||||
private val queueRepository: QueueRepository,
|
private val queueRepository: QueueRepository,
|
||||||
private val connection: PlayerConnection,
|
private val connection: PlayerConnection,
|
||||||
|
private val showMessage: (String) -> Unit = {},
|
||||||
dispatcher: CoroutineDispatcher = Dispatchers.Main.immediate,
|
dispatcher: CoroutineDispatcher = Dispatchers.Main.immediate,
|
||||||
) {
|
) {
|
||||||
private val scope = CoroutineScope(SupervisorJob() + dispatcher)
|
private val scope = CoroutineScope(SupervisorJob() + dispatcher)
|
||||||
@@ -870,6 +871,7 @@ class PlaybackController(
|
|||||||
|
|
||||||
override fun onError(message: String) {
|
override fun onError(message: String) {
|
||||||
lastError = message
|
lastError = message
|
||||||
|
showMessage(message)
|
||||||
refresh()
|
refresh()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.subtracks.playback
|
package com.subtracks.playback
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import androidx.media3.common.PlaybackException
|
||||||
import androidx.room3.Room
|
import androidx.room3.Room
|
||||||
import androidx.sqlite.driver.bundled.BundledSQLiteDriver
|
import androidx.sqlite.driver.bundled.BundledSQLiteDriver
|
||||||
import androidx.test.core.app.ApplicationProvider
|
import androidx.test.core.app.ApplicationProvider
|
||||||
@@ -45,6 +46,7 @@ class PlaybackControllerTest {
|
|||||||
private lateinit var queues: QueueRepository
|
private lateinit var queues: QueueRepository
|
||||||
private lateinit var handle: FakePlayerHandle
|
private lateinit var handle: FakePlayerHandle
|
||||||
private lateinit var controller: PlaybackController
|
private lateinit var controller: PlaybackController
|
||||||
|
private val messages = CopyOnWriteArrayList<String>()
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
@@ -59,7 +61,8 @@ class PlaybackControllerTest {
|
|||||||
sources = SourceRepository(db, OkHttpClient(), prefs, networkMode = networkMode)
|
sources = SourceRepository(db, OkHttpClient(), prefs, networkMode = networkMode)
|
||||||
queues = QueueRepository(db)
|
queues = QueueRepository(db)
|
||||||
handle = FakePlayerHandle()
|
handle = FakePlayerHandle()
|
||||||
controller = PlaybackController(sources, queues, FakePlayerConnection(handle), dispatcher = dispatcher)
|
controller =
|
||||||
|
PlaybackController(sources, queues, FakePlayerConnection(handle), showMessage = { messages += it }, dispatcher = dispatcher)
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
@@ -299,6 +302,29 @@ class PlaybackControllerTest {
|
|||||||
assertEquals(null, controller.state.value.error)
|
assertEquals(null, controller.state.value.error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun playbackErrorsAreShownAsAToast() {
|
||||||
|
seedAlbum(3, sourceId = 1)
|
||||||
|
controller.playAlbum(1, "al1", 0)
|
||||||
|
await { handle.operations.contains("play") }
|
||||||
|
|
||||||
|
handle.fail("Can't reach the server. Check your connection.")
|
||||||
|
await { messages.isNotEmpty() }
|
||||||
|
|
||||||
|
assertEquals(listOf("Can't reach the server. Check your connection."), messages.toList())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun networkErrorsAreReportedWithAFriendlyMessage() {
|
||||||
|
val failed = PlaybackException("net", null, PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED)
|
||||||
|
val timeout = PlaybackException("net", null, PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_TIMEOUT)
|
||||||
|
val refused = PlaybackException("http", null, PlaybackException.ERROR_CODE_IO_BAD_HTTP_STATUS)
|
||||||
|
|
||||||
|
assertEquals("Can't reach the server. Check your connection.", playbackErrorMessage(failed))
|
||||||
|
assertEquals("Can't reach the server. Check your connection.", playbackErrorMessage(timeout))
|
||||||
|
assertEquals("The server refused to stream this track.", playbackErrorMessage(refused))
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun startingAnEmptyQueueStopsThePlayer() {
|
fun startingAnEmptyQueueStopsThePlayer() {
|
||||||
seedAlbum(3, sourceId = 1)
|
seedAlbum(3, sourceId = 1)
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ class QueueViewModelTest {
|
|||||||
.build()
|
.build()
|
||||||
val sources = SourceRepository(db, OkHttpClient(), fakeUserPreferences())
|
val sources = SourceRepository(db, OkHttpClient(), fakeUserPreferences())
|
||||||
queues = QueueRepository(db)
|
queues = QueueRepository(db)
|
||||||
controller = PlaybackController(sources, queues, FakePlayerConnection(FakePlayerHandle()), dispatcher)
|
controller = PlaybackController(sources, queues, FakePlayerConnection(FakePlayerHandle()), dispatcher = dispatcher)
|
||||||
viewModel = QueueViewModel(queues, controller)
|
viewModel = QueueViewModel(queues, controller)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user