mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-10 23:21:59 +01:00
localize transient messages and the download notification
Route user-facing messages produced below the UI through a typed UiMessage (string-resource id plus format args) instead of pre-built English strings: showMessage, SyncStatus.Failed, playback errors and download toasts now resolve in the current locale at the UI edge. Subsonic and page-cap exceptions carry a UiMessage alongside their developer message, and the download progress notification is formatted from resources. Internal invariants and log tags stay plain strings.
This commit is contained in:
@@ -1,21 +1,29 @@
|
||||
package com.subtracks.data.download
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.subtracks.data.model.DownloadStatus
|
||||
import com.subtracks.data.model.SongDownload
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class DownloadNotificationTest {
|
||||
private val resources: Resources = ApplicationProvider.getApplicationContext<Context>().resources
|
||||
|
||||
@Test
|
||||
fun nothingInFlightMeansNoNotification() {
|
||||
assertNull(downloadNotification(emptyList()))
|
||||
assertNull(downloadNotification(listOf(row("s1", DownloadStatus.Completed), row("s2", DownloadStatus.Failed))))
|
||||
assertNull(downloadNotification(resources, emptyList()))
|
||||
assertNull(downloadNotification(resources, listOf(row("s1", DownloadStatus.Completed), row("s2", DownloadStatus.Failed))))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun oneRunningDownloadReportsItsShare() {
|
||||
val notification = downloadNotification(listOf(row("s1", DownloadStatus.Running, bytes = 50, total = 100)))
|
||||
val notification = downloadNotification(resources, listOf(row("s1", DownloadStatus.Running, bytes = 50, total = 100)))
|
||||
|
||||
assertEquals("Downloading 1 song", notification?.title)
|
||||
assertEquals("50%", notification?.text)
|
||||
@@ -26,6 +34,7 @@ class DownloadNotificationTest {
|
||||
fun theProgressIsTheShareOfEveryActiveDownload() {
|
||||
val notification =
|
||||
downloadNotification(
|
||||
resources,
|
||||
listOf(
|
||||
row("s1", DownloadStatus.Running, bytes = 50, total = 100),
|
||||
row("s2", DownloadStatus.Queued, bytes = 150, total = 300),
|
||||
@@ -39,7 +48,7 @@ class DownloadNotificationTest {
|
||||
|
||||
@Test
|
||||
fun anUnknownTotalLeavesTheProgressIndeterminate() {
|
||||
val notification = downloadNotification(listOf(row("s1", DownloadStatus.Running)))
|
||||
val notification = downloadNotification(resources, listOf(row("s1", DownloadStatus.Running)))
|
||||
|
||||
assertEquals("In progress", notification?.text)
|
||||
assertNull(notification?.progress)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.subtracks.data.repo
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import android.net.Uri
|
||||
import androidx.room3.Room
|
||||
import androidx.sqlite.driver.bundled.BundledSQLiteDriver
|
||||
@@ -60,6 +61,7 @@ class DownloadRepositoryTest {
|
||||
private lateinit var scope: CoroutineScope
|
||||
private val prefs = fakeUserPreferences()
|
||||
private val messages = CopyOnWriteArrayList<String>()
|
||||
private lateinit var resources: Resources
|
||||
private val requestedArt = CopyOnWriteArrayList<String>()
|
||||
private val artFetchedAfterTheEngineRequest = CopyOnWriteArrayList<Boolean>()
|
||||
private var failArtworkFetch = false
|
||||
@@ -74,6 +76,7 @@ class DownloadRepositoryTest {
|
||||
@Before
|
||||
fun setUp() {
|
||||
val context = ApplicationProvider.getApplicationContext<Context>()
|
||||
resources = context.resources
|
||||
db =
|
||||
Room
|
||||
.inMemoryDatabaseBuilder(context, SubtracksDatabase::class.java)
|
||||
@@ -92,7 +95,7 @@ class DownloadRepositoryTest {
|
||||
dir,
|
||||
artworkStore = artwork,
|
||||
artworkFetcher = fetcher,
|
||||
showMessage = { messages += it },
|
||||
showMessage = { messages += it.resolve(resources) },
|
||||
scope = scope,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.subtracks.data.repo
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import androidx.room3.Room
|
||||
import androidx.sqlite.driver.bundled.BundledSQLiteDriver
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
@@ -40,10 +41,12 @@ class LibraryRepositoryTest {
|
||||
private lateinit var repository: LibraryRepository
|
||||
private lateinit var scope: CoroutineScope
|
||||
private val messages = ArrayList<String>()
|
||||
private lateinit var resources: Resources
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
val context = ApplicationProvider.getApplicationContext<Context>()
|
||||
resources = context.resources
|
||||
db =
|
||||
Room
|
||||
.inMemoryDatabaseBuilder(context, SubtracksDatabase::class.java)
|
||||
@@ -57,7 +60,7 @@ class LibraryRepositoryTest {
|
||||
db,
|
||||
sourceRepository,
|
||||
NetworkServerActionSink(sourceRepository),
|
||||
showMessage = { messages += it },
|
||||
showMessage = { messages += it.resolve(resources) },
|
||||
scope = scope,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.subtracks.data.repo
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import androidx.room3.Room
|
||||
import androidx.sqlite.driver.bundled.BundledSQLiteDriver
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
@@ -49,10 +50,12 @@ class SourceRepositoryTest {
|
||||
private lateinit var artwork: ArtworkStore
|
||||
private lateinit var scope: CoroutineScope
|
||||
private val messages = ArrayList<String>()
|
||||
private lateinit var resources: Resources
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
val context = ApplicationProvider.getApplicationContext<Context>()
|
||||
resources = context.resources
|
||||
db =
|
||||
Room
|
||||
.inMemoryDatabaseBuilder(context, SubtracksDatabase::class.java)
|
||||
@@ -61,7 +64,8 @@ class SourceRepositoryTest {
|
||||
artwork = ArtworkStore(File(context.cacheDir, "art-${System.nanoTime()}"))
|
||||
prefs = fakeUserPreferences()
|
||||
scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
|
||||
repository = SourceRepository(db, OkHttpClient(), prefs, artwork, showMessage = { messages += it }, scope = scope)
|
||||
repository =
|
||||
SourceRepository(db, OkHttpClient(), prefs, artwork, showMessage = { messages += it.resolve(resources) }, scope = scope)
|
||||
}
|
||||
|
||||
@After
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.subtracks.data.sync
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import androidx.room3.Room
|
||||
import androidx.sqlite.driver.bundled.BundledSQLiteDriver
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
@@ -45,10 +46,12 @@ class SyncManagerTest {
|
||||
private lateinit var scope: CoroutineScope
|
||||
private val messages = CopyOnWriteArrayList<String>()
|
||||
private lateinit var server: MockWebServer
|
||||
private lateinit var resources: Resources
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
val context = ApplicationProvider.getApplicationContext<Context>()
|
||||
resources = context.resources
|
||||
db =
|
||||
Room
|
||||
.inMemoryDatabaseBuilder(context, SubtracksDatabase::class.java)
|
||||
@@ -58,7 +61,7 @@ class SyncManagerTest {
|
||||
sourceRepository =
|
||||
SourceRepository(db, OkHttpClient(), fakeUserPreferences(), ArtworkStore(File(context.cacheDir, "art")), scope = scope)
|
||||
queueRepository = QueueRepository(db)
|
||||
manager = SyncManager(db, sourceRepository, queueRepository, showMessage = { messages += it }, scope = scope)
|
||||
manager = SyncManager(db, sourceRepository, queueRepository, showMessage = { messages += it.resolve(resources) }, scope = scope)
|
||||
server = MockWebServer()
|
||||
server.start()
|
||||
}
|
||||
@@ -76,7 +79,7 @@ class SyncManagerTest {
|
||||
manager.requestSync()
|
||||
withTimeout(TEST_TIMEOUT_MS) { manager.status.first { it is SyncStatus.Failed } }
|
||||
|
||||
assertEquals(listOf("Sync failed: No server configured"), messages)
|
||||
assertEquals(listOf("No server configured"), messages)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -242,6 +245,6 @@ class SyncManagerTest {
|
||||
manager.requestSync()
|
||||
val status = withTimeout(TEST_TIMEOUT_MS) { manager.status.first { it is SyncStatus.Failed } }
|
||||
|
||||
assertEquals("Offline mode is on", (status as SyncStatus.Failed).message)
|
||||
assertEquals("Offline mode is on", (status as SyncStatus.Failed).message.resolve(resources))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.subtracks.playback
|
||||
|
||||
import com.subtracks.UiMessage
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
|
||||
class FakePlayerConnection(
|
||||
@@ -232,7 +233,7 @@ class FakePlayerHandle : PlayerHandle {
|
||||
notifyEvents()
|
||||
}
|
||||
|
||||
fun fail(message: String) = listeners.forEach { it.onError(message) }
|
||||
fun fail(message: UiMessage) = listeners.forEach { it.onError(message) }
|
||||
|
||||
fun startBuffering() {
|
||||
idle = false
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
package com.subtracks.playback
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import androidx.media3.common.PlaybackException
|
||||
import androidx.room3.Room
|
||||
import androidx.sqlite.driver.bundled.BundledSQLiteDriver
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.subtracks.R
|
||||
import com.subtracks.TEST_TIMEOUT_MS
|
||||
import com.subtracks.UiMessage
|
||||
import com.subtracks.awaitUntil
|
||||
import com.subtracks.cancelAndJoinBlocking
|
||||
import com.subtracks.data.db.SubtracksDatabase
|
||||
@@ -73,10 +76,12 @@ class PlaybackControllerTest {
|
||||
private lateinit var handle: FakePlayerHandle
|
||||
private lateinit var controller: PlaybackController
|
||||
private val messages = CopyOnWriteArrayList<String>()
|
||||
private lateinit var resources: Resources
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
val context = ApplicationProvider.getApplicationContext<Context>()
|
||||
resources = context.resources
|
||||
db =
|
||||
Room
|
||||
.inMemoryDatabaseBuilder(context, SubtracksDatabase::class.java)
|
||||
@@ -112,7 +117,7 @@ class PlaybackControllerTest {
|
||||
queues,
|
||||
FakePlayerConnection(handle),
|
||||
downloads,
|
||||
showMessage = { messages += it },
|
||||
showMessage = { messages += it.resolve(resources) },
|
||||
scope = controllerScope,
|
||||
)
|
||||
}
|
||||
@@ -1365,7 +1370,7 @@ class PlaybackControllerTest {
|
||||
controller.playAlbum(1, "al1", 0)
|
||||
await { handle.operations.contains("play") }
|
||||
|
||||
handle.fail("Can't reach the server. Check your connection.")
|
||||
handle.fail(UiMessage(R.string.playback_error_network))
|
||||
await { messages.isNotEmpty() }
|
||||
|
||||
assertEquals(listOf("Can't reach the server. Check your connection."), messages.toList())
|
||||
@@ -1377,9 +1382,9 @@ class PlaybackControllerTest {
|
||||
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))
|
||||
assertEquals("Can't reach the server. Check your connection.", playbackErrorMessage(failed).resolve(resources))
|
||||
assertEquals("Can't reach the server. Check your connection.", playbackErrorMessage(timeout).resolve(resources))
|
||||
assertEquals("The server refused to stream this track.", playbackErrorMessage(refused).resolve(resources))
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user