Skip to content

Commit 65dc58c

Browse files
committed
feat: moves library sync to work manager and adds progress
1 parent b1b45e5 commit 65dc58c

File tree

15 files changed

+460
-194
lines changed

15 files changed

+460
-194
lines changed

.github/workflows/flow.yml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,19 @@ concurrency:
1515
cancel-in-progress: true
1616

1717
env:
18-
JVM_OPTS: -Xmx3200m
18+
JVM_OPTS: -Xmx6g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError
19+
GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx6g -XX:MaxMetaspaceSize=512m" -Dorg.gradle.daemon=false
1920

2021
jobs:
2122
check:
22-
timeout-minutes: 30
23+
timeout-minutes: 45
2324
runs-on: ubuntu-latest
2425
steps:
2526
- name: Checkout
2627
uses: actions/checkout@v4
2728
with:
2829
persist-credentials: false
30+
fetch-depth: 0
2931

3032
- name: Set up JDK
3133
uses: actions/setup-java@v4
@@ -46,7 +48,7 @@ jobs:
4648
with:
4749
sarif_file: app/build/reports/sarif/
4850
test:
49-
timeout-minutes: 30
51+
timeout-minutes: 45
5052
runs-on: ubuntu-latest
5153
steps:
5254
- name: Checkout
@@ -72,8 +74,10 @@ jobs:
7274
with:
7375
files: ./app/build/reports/kover/project-xml/report.xml
7476
token: ${{ secrets.CODECOV_TOKEN }}
77+
fail_ci_if_error: false
7578

7679
- uses: actions/upload-artifact@v4
80+
if: always()
7781
with:
7882
name: test-results
7983
path: app/build/test-results

app/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ dependencies {
219219
implementation(libs.androidx.recyclerview)
220220
implementation(libs.androidx.core.splashscreen)
221221
implementation(libs.androidx.viewpager2)
222+
implementation(libs.androidx.work.ktx)
222223
implementation(libs.androidx.swiperefreshlayout)
223224
implementation(libs.bundles.androidx.lifecycle)
224225
implementation(libs.bundles.coroutines)

app/src/main/AndroidManifest.xml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
<uses-permission android:name="android.permission.INTERNET" />
99
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
1010
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" />
11+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
1112
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
1213
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
1314
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
@@ -44,6 +45,18 @@
4445
</intent-filter>
4546
</activity>
4647

48+
<provider
49+
android:name="androidx.startup.InitializationProvider"
50+
android:authorities="${applicationId}.androidx-startup"
51+
android:exported="false"
52+
tools:node="merge">
53+
<!-- If you are using androidx.startup to initialize other components -->
54+
<meta-data
55+
android:name="androidx.work.WorkManagerInitializer"
56+
android:value="androidx.startup"
57+
tools:node="remove" />
58+
</provider>
59+
4760
<activity
4861
android:name=".features.library.LibraryActivity"
4962
android:label="@string/nav_library"
@@ -180,6 +193,11 @@
180193
</intent-filter>
181194
</service>
182195

196+
<service
197+
android:name="androidx.work.impl.foreground.SystemForegroundService"
198+
android:foregroundServiceType="dataSync"
199+
tools:node="merge" />
200+
183201
<provider
184202
android:name="androidx.core.content.FileProvider"
185203
android:authorities="${applicationId}.fileprovider"

app/src/main/java/com/kelsos/mbrc/App.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import coil3.request.crossfade
1616
import com.kelsos.mbrc.common.utilities.CustomLoggingTree
1717
import org.koin.android.ext.koin.androidContext
1818
import org.koin.androidx.fragment.koin.fragmentFactory
19+
import org.koin.androidx.workmanager.koin.workManagerFactory
1920
import org.koin.core.context.GlobalContext.startKoin
2021
import org.koin.core.module.Module
2122
import org.koin.dsl.module
@@ -56,6 +57,7 @@ open class App : Application() {
5657
startKoin {
5758
androidContext(this@App)
5859
fragmentFactory()
60+
workManagerFactory()
5961
modules(appModules())
6062
}
6163
initializeTimber()

app/src/main/java/com/kelsos/mbrc/AppModule.kt

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.kelsos.mbrc
22

33
import androidx.core.app.NotificationManagerCompat
44
import androidx.room.Room
5+
import androidx.work.WorkManager
56
import com.kelsos.mbrc.common.state.AppState
67
import com.kelsos.mbrc.common.state.AppStateFlow
78
import com.kelsos.mbrc.common.state.AppStateManager
@@ -24,6 +25,9 @@ import com.kelsos.mbrc.features.library.LibraryActivity
2425
import com.kelsos.mbrc.features.library.LibrarySearchModel
2526
import com.kelsos.mbrc.features.library.LibrarySyncUseCase
2627
import com.kelsos.mbrc.features.library.LibrarySyncUseCaseImpl
28+
import com.kelsos.mbrc.features.library.LibrarySyncWorkHandler
29+
import com.kelsos.mbrc.features.library.LibrarySyncWorkHandlerImpl
30+
import com.kelsos.mbrc.features.library.LibrarySyncWorker
2731
import com.kelsos.mbrc.features.library.LibraryViewModel
2832
import com.kelsos.mbrc.features.library.albums.AlbumEntryAdapter
2933
import com.kelsos.mbrc.features.library.albums.AlbumRepository
@@ -152,6 +156,7 @@ import kotlinx.coroutines.CoroutineDispatcher
152156
import kotlinx.coroutines.Dispatchers
153157
import kotlinx.coroutines.ExecutorCoroutineDispatcher
154158
import kotlinx.coroutines.asCoroutineDispatcher
159+
import org.koin.androidx.workmanager.dsl.workerOf
155160
import org.koin.core.module.dsl.bind
156161
import org.koin.core.module.dsl.factoryOf
157162
import org.koin.core.module.dsl.scopedOf
@@ -196,9 +201,8 @@ val appModule =
196201

197202
singleOf(::QueueHandler)
198203

199-
single<NotificationManagerCompat> {
200-
NotificationManagerCompat.from(get())
201-
}
204+
single<NotificationManagerCompat> { NotificationManagerCompat.from(get()) }
205+
single { WorkManager.getInstance(get()) }
202206

203207
singleOf(::RemoteBroadcastReceiver)
204208
singleOf(::SettingsManagerImpl) { bind<SettingsManager>() }
@@ -256,8 +260,11 @@ val appModule =
256260
factoryOf(::SimpleLogCommand)
257261
factoryOf(::ProtocolVersionUpdate)
258262

263+
workerOf(::LibrarySyncWorker)
264+
259265
singleOf(::RemoteServiceDiscoveryImpl) { bind<RemoteServiceDiscovery>() }
260266
singleOf(::WidgetUpdaterImpl) { bind<WidgetUpdater>() }
267+
singleOf(::LibrarySyncWorkHandlerImpl) { bind<LibrarySyncWorkHandler>() }
261268

262269
val network = createDispatcher(name = "Network", threads = 2)
263270
val database = createDispatcher(name = "Database")

app/src/main/java/com/kelsos/mbrc/features/library/LibraryActivity.kt

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import com.kelsos.mbrc.BaseActivity
2121
import com.kelsos.mbrc.R
2222
import kotlinx.coroutines.launch
2323
import org.koin.android.ext.android.inject
24+
import timber.log.Timber
2425

2526
class LibraryActivity :
2627
BaseActivity(R.layout.activity_library),
@@ -111,6 +112,7 @@ class LibraryActivity :
111112
syncComplete(event.stats)
112113
hideSyncProgress()
113114
}
115+
114116
is LibraryUiEvent.UpdateAlbumArtistOnly -> updateArtistOnlyPreference(event.enabled)
115117
is LibraryUiEvent.LibrarySyncFailed -> {
116118
syncFailure()
@@ -120,6 +122,17 @@ class LibraryActivity :
120122
}
121123
}
122124
}
125+
126+
lifecycleScope.launch {
127+
viewModel.progress.collect { progress ->
128+
Timber.v("progress: $progress")
129+
if (progress.running) {
130+
showSyncProgress(progress)
131+
} else {
132+
hideSyncProgress()
133+
}
134+
}
135+
}
123136
}
124137

125138
override fun onCreateOptionsMenu(menu: Menu): Boolean {
@@ -141,16 +154,17 @@ class LibraryActivity :
141154
when (item.itemId) {
142155
R.id.library_refresh_item -> {
143156
viewModel.sync()
144-
showSyncProgress()
145157
true
146158
}
159+
147160
R.id.library_album_artist -> {
148161
albumArtistOnly?.let {
149162
it.isChecked = !it.isChecked
150163
viewModel.updateAlbumArtistOnly(it.isChecked)
151164
}
152165
true
153166
}
167+
154168
R.id.library_search_clear -> {
155169
supportActionBar?.setTitle(R.string.nav_library)
156170
supportActionBar?.subtitle = ""
@@ -159,6 +173,7 @@ class LibraryActivity :
159173
searchClear?.isVisible = false
160174
true
161175
}
176+
162177
R.id.library_sync_state -> {
163178
viewModel.displayLibraryStats()
164179
true
@@ -222,9 +237,25 @@ class LibraryActivity :
222237
Snackbar.make(pager, R.string.library__sync_failed, Snackbar.LENGTH_LONG).show()
223238
}
224239

225-
fun showSyncProgress() {
226-
findViewById<LinearProgressIndicator>(R.id.sync_progress).isGone = false
227-
findViewById<TextView>(R.id.sync_progress_text).isGone = false
240+
fun getCategoryText(mediaType: LibraryMediaType): String =
241+
when (mediaType) {
242+
LibraryMediaType.Albums -> getString(R.string.media__albums)
243+
LibraryMediaType.Artists -> getString(R.string.media__artists)
244+
LibraryMediaType.Genres -> getString(R.string.media__genres)
245+
LibraryMediaType.Playlists -> getString(R.string.media__playlists)
246+
LibraryMediaType.Tracks -> getString(R.string.media__tracks)
247+
LibraryMediaType.Covers -> getString(R.string.media__covers)
248+
}
249+
250+
fun showSyncProgress(progress: LibrarySyncProgress) {
251+
val syncText = findViewById<TextView>(R.id.sync_progress_text)
252+
val progressIndicator = findViewById<LinearProgressIndicator>(R.id.sync_progress)
253+
val categoryText = getCategoryText(progress.category)
254+
syncText.isGone = false
255+
syncText.text = getString(R.string.library__sync_progress, progress.current, progress.total, categoryText)
256+
progressIndicator.isGone = false
257+
progressIndicator.progress = progress.current
258+
progressIndicator.max = progress.total
228259
}
229260

230261
fun hideSyncProgress() {
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package com.kelsos.mbrc.features.library
2+
3+
import androidx.annotation.StringRes
4+
import com.kelsos.mbrc.R
5+
6+
sealed class LibraryMediaType(
7+
val code: Int,
8+
@StringRes val nameRes: Int = 0,
9+
) {
10+
object Genres : LibraryMediaType(GENRES, R.string.media__genres)
11+
12+
object Artists : LibraryMediaType(ARTISTS, R.string.media__artists)
13+
14+
object Albums : LibraryMediaType(ALBUMS, R.string.media__albums)
15+
16+
object Tracks : LibraryMediaType(TRACKS, R.string.media__tracks)
17+
18+
object Playlists : LibraryMediaType(PLAYLISTS, R.string.media__playlists)
19+
20+
object Covers : LibraryMediaType(COVERS, R.string.media__covers)
21+
22+
companion object {
23+
const val GENRES = 1
24+
const val ARTISTS = 2
25+
const val ALBUMS = 3
26+
const val TRACKS = 4
27+
const val PLAYLISTS = 5
28+
const val COVERS = 6
29+
30+
fun fromCode(code: Int): LibraryMediaType =
31+
when (code) {
32+
GENRES -> Genres
33+
ARTISTS -> Artists
34+
ALBUMS -> Albums
35+
TRACKS -> Tracks
36+
PLAYLISTS -> Playlists
37+
COVERS -> Covers
38+
else -> throw IllegalArgumentException("Unknown media type code: $code")
39+
}
40+
}
41+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,30 @@
11
package com.kelsos.mbrc.features.library
22

3+
import androidx.work.Data
4+
import androidx.work.workDataOf
5+
36
data class LibraryStats(
47
val genres: Long,
58
val artists: Long,
69
val albums: Long,
710
val tracks: Long,
811
val playlists: Long,
912
)
13+
14+
fun LibraryStats.toWorkData(): Data =
15+
workDataOf(
16+
"genres" to genres,
17+
"artists" to artists,
18+
"albums" to albums,
19+
"tracks" to tracks,
20+
"playlists" to playlists,
21+
)
22+
23+
fun Data.toLibraryStats(): LibraryStats =
24+
LibraryStats(
25+
getLong("genres", 0),
26+
getLong("artists", 0),
27+
getLong("albums", 0),
28+
getLong("tracks", 0),
29+
getLong("playlists", 0),
30+
)

0 commit comments

Comments
 (0)