feat(ui): Add restoring & syncing banner
This commit is contained in:
parent
8c3d99889c
commit
274aefb3b3
7 changed files with 121 additions and 3 deletions
|
|
@ -63,6 +63,10 @@ fun AppStateBanners(
|
||||||
downloadedOnlyMode: Boolean,
|
downloadedOnlyMode: Boolean,
|
||||||
incognitoMode: Boolean,
|
incognitoMode: Boolean,
|
||||||
indexing: Boolean,
|
indexing: Boolean,
|
||||||
|
// KMK -->
|
||||||
|
restoring: Boolean,
|
||||||
|
syncing: Boolean,
|
||||||
|
// KMK <--
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val density = LocalDensity.current
|
val density = LocalDensity.current
|
||||||
|
|
@ -71,12 +75,21 @@ fun AppStateBanners(
|
||||||
SubcomposeLayout(modifier = modifier) { constraints ->
|
SubcomposeLayout(modifier = modifier) { constraints ->
|
||||||
val indexingPlaceable = subcompose(0) {
|
val indexingPlaceable = subcompose(0) {
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
visible = indexing,
|
// KMK -->
|
||||||
|
visible = indexing || restoring || syncing,
|
||||||
|
// KMK <--
|
||||||
enter = expandVertically(),
|
enter = expandVertically(),
|
||||||
exit = shrinkVertically(),
|
exit = shrinkVertically(),
|
||||||
) {
|
) {
|
||||||
IndexingDownloadBanner(
|
IndexingDownloadBanner(
|
||||||
modifier = Modifier.windowInsetsPadding(mainInsets),
|
modifier = Modifier.windowInsetsPadding(mainInsets),
|
||||||
|
// KMK -->
|
||||||
|
text = when {
|
||||||
|
syncing -> stringResource(MR.strings.syncing_library)
|
||||||
|
restoring -> stringResource(MR.strings.restoring_backup)
|
||||||
|
else -> stringResource(MR.strings.download_notifier_cache_renewal)
|
||||||
|
},
|
||||||
|
// KMK <--
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}.fastMap { it.measure(constraints) }
|
}.fastMap { it.measure(constraints) }
|
||||||
|
|
@ -155,7 +168,12 @@ private fun IncognitoModeBanner(modifier: Modifier = Modifier) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun IndexingDownloadBanner(modifier: Modifier = Modifier) {
|
private fun IndexingDownloadBanner(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
// KMK -->
|
||||||
|
text: String = stringResource(MR.strings.download_notifier_cache_renewal),
|
||||||
|
// KMK <--
|
||||||
|
) {
|
||||||
val density = LocalDensity.current
|
val density = LocalDensity.current
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
@ -173,7 +191,9 @@ private fun IndexingDownloadBanner(modifier: Modifier = Modifier) {
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.width(8.dp))
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(MR.strings.download_notifier_cache_renewal),
|
// KMK -->
|
||||||
|
text = text,
|
||||||
|
// KMK <--
|
||||||
color = MaterialTheme.colorScheme.onSecondary,
|
color = MaterialTheme.colorScheme.onSecondary,
|
||||||
textAlign = TextAlign.Center,
|
textAlign = TextAlign.Center,
|
||||||
style = MaterialTheme.typography.labelMedium,
|
style = MaterialTheme.typography.labelMedium,
|
||||||
|
|
|
||||||
|
|
@ -22,12 +22,18 @@ import logcat.LogPriority
|
||||||
import tachiyomi.core.common.i18n.stringResource
|
import tachiyomi.core.common.i18n.stringResource
|
||||||
import tachiyomi.core.common.util.system.logcat
|
import tachiyomi.core.common.util.system.logcat
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
class BackupRestoreJob(private val context: Context, workerParams: WorkerParameters) :
|
class BackupRestoreJob(private val context: Context, workerParams: WorkerParameters) :
|
||||||
CoroutineWorker(context, workerParams) {
|
CoroutineWorker(context, workerParams) {
|
||||||
|
|
||||||
private val notifier = BackupNotifier(context)
|
private val notifier = BackupNotifier(context)
|
||||||
|
|
||||||
|
// KMK -->
|
||||||
|
private val backupRestoreStatus: BackupRestoreStatus = Injekt.get()
|
||||||
|
// KMK <--
|
||||||
|
|
||||||
override suspend fun doWork(): Result {
|
override suspend fun doWork(): Result {
|
||||||
val uri = inputData.getString(LOCATION_URI_KEY)?.toUri()
|
val uri = inputData.getString(LOCATION_URI_KEY)?.toUri()
|
||||||
val options = inputData.getBooleanArray(OPTIONS_KEY)?.let { RestoreOptions.fromBooleanArray(it) }
|
val options = inputData.getBooleanArray(OPTIONS_KEY)?.let { RestoreOptions.fromBooleanArray(it) }
|
||||||
|
|
@ -36,6 +42,10 @@ class BackupRestoreJob(private val context: Context, workerParams: WorkerParamet
|
||||||
return Result.failure()
|
return Result.failure()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// KMK -->
|
||||||
|
backupRestoreStatus.start()
|
||||||
|
// KMK <--
|
||||||
|
|
||||||
val isSync = inputData.getBoolean(SYNC_KEY, false)
|
val isSync = inputData.getBoolean(SYNC_KEY, false)
|
||||||
|
|
||||||
setForegroundSafely()
|
setForegroundSafely()
|
||||||
|
|
@ -54,6 +64,9 @@ class BackupRestoreJob(private val context: Context, workerParams: WorkerParamet
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
context.cancelNotification(Notifications.ID_RESTORE_PROGRESS)
|
context.cancelNotification(Notifications.ID_RESTORE_PROGRESS)
|
||||||
|
// KMK -->
|
||||||
|
backupRestoreStatus.stop()
|
||||||
|
// KMK <--
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
package eu.kanade.tachiyomi.data.backup.restore
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
|
import kotlinx.coroutines.flow.debounce
|
||||||
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
|
||||||
|
class BackupRestoreStatus {
|
||||||
|
private val scope = CoroutineScope(Dispatchers.IO)
|
||||||
|
|
||||||
|
private val _isRunning = MutableStateFlow(false)
|
||||||
|
|
||||||
|
val isRunning = _isRunning
|
||||||
|
.debounce(1000L) // Don't notify if it finishes quickly enough
|
||||||
|
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
|
||||||
|
|
||||||
|
suspend fun start() {
|
||||||
|
_isRunning.emit(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun stop() {
|
||||||
|
_isRunning.emit(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -29,6 +29,10 @@ class SyncDataJob(private val context: Context, workerParams: WorkerParameters)
|
||||||
|
|
||||||
private val notifier = SyncNotifier(context)
|
private val notifier = SyncNotifier(context)
|
||||||
|
|
||||||
|
// KMK -->
|
||||||
|
private val syncStatus: SyncStatus = Injekt.get()
|
||||||
|
// KMK <--
|
||||||
|
|
||||||
override suspend fun doWork(): Result {
|
override suspend fun doWork(): Result {
|
||||||
if (tags.contains(TAG_AUTO)) {
|
if (tags.contains(TAG_AUTO)) {
|
||||||
// Find a running manual worker. If exists, try again later
|
// Find a running manual worker. If exists, try again later
|
||||||
|
|
@ -37,6 +41,10 @@ class SyncDataJob(private val context: Context, workerParams: WorkerParameters)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// KMK -->
|
||||||
|
syncStatus.start()
|
||||||
|
// KMK <--
|
||||||
|
|
||||||
setForegroundSafely()
|
setForegroundSafely()
|
||||||
|
|
||||||
return try {
|
return try {
|
||||||
|
|
@ -48,6 +56,9 @@ class SyncDataJob(private val context: Context, workerParams: WorkerParameters)
|
||||||
Result.success() // try again next time
|
Result.success() // try again next time
|
||||||
} finally {
|
} finally {
|
||||||
context.cancelNotification(Notifications.ID_RESTORE_PROGRESS)
|
context.cancelNotification(Notifications.ID_RESTORE_PROGRESS)
|
||||||
|
// KMK -->
|
||||||
|
syncStatus.stop()
|
||||||
|
// KMK <--
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
package eu.kanade.tachiyomi.data.sync
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
|
import kotlinx.coroutines.flow.debounce
|
||||||
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
|
||||||
|
class SyncStatus {
|
||||||
|
private val scope = CoroutineScope(Dispatchers.IO)
|
||||||
|
|
||||||
|
private val _isRunning = MutableStateFlow(false)
|
||||||
|
|
||||||
|
val isRunning = _isRunning
|
||||||
|
.debounce(1000L) // Don't notify if it finishes quickly enough
|
||||||
|
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
|
||||||
|
|
||||||
|
suspend fun start() {
|
||||||
|
_isRunning.emit(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun stop() {
|
||||||
|
_isRunning.emit(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -10,6 +10,7 @@ import app.cash.sqldelight.driver.android.AndroidSqliteDriver
|
||||||
import eu.kanade.domain.track.store.DelayedTrackingStore
|
import eu.kanade.domain.track.store.DelayedTrackingStore
|
||||||
import eu.kanade.tachiyomi.BuildConfig
|
import eu.kanade.tachiyomi.BuildConfig
|
||||||
import eu.kanade.tachiyomi.core.security.SecurityPreferences
|
import eu.kanade.tachiyomi.core.security.SecurityPreferences
|
||||||
|
import eu.kanade.tachiyomi.data.backup.restore.BackupRestoreStatus
|
||||||
import eu.kanade.tachiyomi.data.cache.ChapterCache
|
import eu.kanade.tachiyomi.data.cache.ChapterCache
|
||||||
import eu.kanade.tachiyomi.data.cache.CoverCache
|
import eu.kanade.tachiyomi.data.cache.CoverCache
|
||||||
import eu.kanade.tachiyomi.data.cache.PagePreviewCache
|
import eu.kanade.tachiyomi.data.cache.PagePreviewCache
|
||||||
|
|
@ -17,6 +18,7 @@ import eu.kanade.tachiyomi.data.download.DownloadCache
|
||||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||||
import eu.kanade.tachiyomi.data.download.DownloadProvider
|
import eu.kanade.tachiyomi.data.download.DownloadProvider
|
||||||
import eu.kanade.tachiyomi.data.saver.ImageSaver
|
import eu.kanade.tachiyomi.data.saver.ImageSaver
|
||||||
|
import eu.kanade.tachiyomi.data.sync.SyncStatus
|
||||||
import eu.kanade.tachiyomi.data.sync.service.GoogleDriveService
|
import eu.kanade.tachiyomi.data.sync.service.GoogleDriveService
|
||||||
import eu.kanade.tachiyomi.data.track.TrackerManager
|
import eu.kanade.tachiyomi.data.track.TrackerManager
|
||||||
import eu.kanade.tachiyomi.extension.ExtensionManager
|
import eu.kanade.tachiyomi.extension.ExtensionManager
|
||||||
|
|
@ -172,6 +174,11 @@ class AppModule(val app: Application) : InjektModule {
|
||||||
addSingletonFactory { PagePreviewCache(app) }
|
addSingletonFactory { PagePreviewCache(app) }
|
||||||
// SY <--
|
// SY <--
|
||||||
|
|
||||||
|
// KMK -->
|
||||||
|
addSingletonFactory { BackupRestoreStatus() }
|
||||||
|
addSingletonFactory { SyncStatus() }
|
||||||
|
// KMK <--
|
||||||
|
|
||||||
// Asynchronously init expensive components for a faster cold start
|
// Asynchronously init expensive components for a faster cold start
|
||||||
ContextCompat.getMainExecutor(app).execute {
|
ContextCompat.getMainExecutor(app).execute {
|
||||||
get<NetworkHelper>()
|
get<NetworkHelper>()
|
||||||
|
|
|
||||||
|
|
@ -61,10 +61,12 @@ import eu.kanade.presentation.more.settings.screen.data.RestoreBackupScreen
|
||||||
import eu.kanade.presentation.util.AssistContentScreen
|
import eu.kanade.presentation.util.AssistContentScreen
|
||||||
import eu.kanade.presentation.util.DefaultNavigatorScreenTransition
|
import eu.kanade.presentation.util.DefaultNavigatorScreenTransition
|
||||||
import eu.kanade.tachiyomi.BuildConfig
|
import eu.kanade.tachiyomi.BuildConfig
|
||||||
|
import eu.kanade.tachiyomi.data.backup.restore.BackupRestoreStatus
|
||||||
import eu.kanade.tachiyomi.data.cache.ChapterCache
|
import eu.kanade.tachiyomi.data.cache.ChapterCache
|
||||||
import eu.kanade.tachiyomi.data.coil.MangaCoverMetadata
|
import eu.kanade.tachiyomi.data.coil.MangaCoverMetadata
|
||||||
import eu.kanade.tachiyomi.data.download.DownloadCache
|
import eu.kanade.tachiyomi.data.download.DownloadCache
|
||||||
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
|
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
|
||||||
|
import eu.kanade.tachiyomi.data.sync.SyncStatus
|
||||||
import eu.kanade.tachiyomi.data.updater.AppUpdateChecker
|
import eu.kanade.tachiyomi.data.updater.AppUpdateChecker
|
||||||
import eu.kanade.tachiyomi.extension.api.ExtensionApi
|
import eu.kanade.tachiyomi.extension.api.ExtensionApi
|
||||||
import eu.kanade.tachiyomi.ui.base.activity.BaseActivity
|
import eu.kanade.tachiyomi.ui.base.activity.BaseActivity
|
||||||
|
|
@ -122,6 +124,11 @@ class MainActivity : BaseActivity() {
|
||||||
private val unsortedPreferences: UnsortedPreferences by injectLazy()
|
private val unsortedPreferences: UnsortedPreferences by injectLazy()
|
||||||
// SY <--
|
// SY <--
|
||||||
|
|
||||||
|
// KMK -->
|
||||||
|
private val backupRestoreStatus: BackupRestoreStatus by injectLazy()
|
||||||
|
private val syncStatus: SyncStatus by injectLazy()
|
||||||
|
// KMK <--
|
||||||
|
|
||||||
private val downloadCache: DownloadCache by injectLazy()
|
private val downloadCache: DownloadCache by injectLazy()
|
||||||
private val chapterCache: ChapterCache by injectLazy()
|
private val chapterCache: ChapterCache by injectLazy()
|
||||||
|
|
||||||
|
|
@ -189,6 +196,10 @@ class MainActivity : BaseActivity() {
|
||||||
val incognito by preferences.incognitoMode().collectAsState()
|
val incognito by preferences.incognitoMode().collectAsState()
|
||||||
val downloadOnly by preferences.downloadedOnly().collectAsState()
|
val downloadOnly by preferences.downloadedOnly().collectAsState()
|
||||||
val indexing by downloadCache.isInitializing.collectAsState()
|
val indexing by downloadCache.isInitializing.collectAsState()
|
||||||
|
// KMK -->
|
||||||
|
val syncing by syncStatus.isRunning.collectAsState()
|
||||||
|
val restoring by backupRestoreStatus.isRunning.collectAsState()
|
||||||
|
// KMK <--
|
||||||
|
|
||||||
// Set status bar color considering the top app state banner
|
// Set status bar color considering the top app state banner
|
||||||
val systemUiController = rememberSystemUiController()
|
val systemUiController = rememberSystemUiController()
|
||||||
|
|
@ -260,6 +271,10 @@ class MainActivity : BaseActivity() {
|
||||||
downloadedOnlyMode = downloadOnly,
|
downloadedOnlyMode = downloadOnly,
|
||||||
incognitoMode = incognito,
|
incognitoMode = incognito,
|
||||||
indexing = indexing,
|
indexing = indexing,
|
||||||
|
// KMK -->
|
||||||
|
restoring = restoring,
|
||||||
|
syncing = syncing,
|
||||||
|
// KMK <--
|
||||||
modifier = Modifier.windowInsetsPadding(scaffoldInsets),
|
modifier = Modifier.windowInsetsPadding(scaffoldInsets),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue