Backup restorng fixes

This commit is contained in:
Koitharu
2025-06-14 15:04:10 +03:00
parent b9d4c070eb
commit 437e6809bf
4 changed files with 85 additions and 78 deletions

View File

@@ -20,7 +20,7 @@ class BookmarkBackup(
@SerialName("chapter_id") val chapterId: Long,
@SerialName("page") val page: Int,
@SerialName("scroll") val scroll: Int,
@SerialName("image") val imageUrl: String,
@SerialName("image_url") val imageUrl: String,
@SerialName("created_at") val createdAt: Long,
@SerialName("percent") val percent: Float,
) {

View File

@@ -1,20 +1,25 @@
package org.koitharu.kotatsu.backups.ui
import android.app.Notification
import android.net.Uri
import androidx.core.app.NotificationChannelCompat
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.app.PendingIntentCompat
import androidx.core.app.ShareCompat
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.ErrorReporterReceiver
import org.koitharu.kotatsu.core.nav.AppRouter
import org.koitharu.kotatsu.core.ui.CoroutineIntentService
import org.koitharu.kotatsu.core.util.CompositeResult
import org.koitharu.kotatsu.core.util.ext.checkNotificationPermission
import org.koitharu.kotatsu.core.util.ext.getDisplayMessage
import org.koitharu.kotatsu.core.util.ext.getFileDisplayName
import androidx.appcompat.R as appcompatR
abstract class BaseBackupRestoreService : CoroutineIntentService() {
protected abstract val notificationTag: String
protected abstract val isRestoreService: Boolean
protected lateinit var notificationManager: NotificationManagerCompat
private set
@@ -26,10 +31,7 @@ abstract class BaseBackupRestoreService : CoroutineIntentService() {
}
override fun IntentJobContext.onError(error: Throwable) {
if (applicationContext.checkNotificationPermission(CHANNEL_ID)) {
val notification = createErrorNotification(error)
notificationManager.notify(notificationTag, startId, notification)
}
showResultNotification(null, CompositeResult.failure(error))
}
private fun createNotificationChannel() {
@@ -43,33 +45,93 @@ abstract class BaseBackupRestoreService : CoroutineIntentService() {
notificationManager.createNotificationChannel(channel)
}
protected fun createErrorNotification(error: Throwable): Notification {
protected fun IntentJobContext.showResultNotification(
fileUri: Uri?,
result: CompositeResult,
) {
if (!applicationContext.checkNotificationPermission(CHANNEL_ID)) {
return
}
val notification = NotificationCompat.Builder(applicationContext, CHANNEL_ID)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setDefaults(0)
.setSilent(true)
.setAutoCancel(true)
.setContentText(error.getDisplayMessage(resources))
.setSmallIcon(android.R.drawable.stat_notify_error)
ErrorReporterReceiver.getPendingIntent(applicationContext, error)?.let { reportIntent ->
notification.addAction(
R.drawable.ic_alert_outline,
applicationContext.getString(R.string.report),
reportIntent,
)
.setSubText(fileUri?.let { contentResolver.getFileDisplayName(it) })
when {
result.isAllSuccess -> {
if (isRestoreService) {
notification
.setContentTitle(getString(R.string.restoring_backup))
.setContentText(getString(R.string.data_restored_success))
} else {
notification
.setContentTitle(getString(R.string.backup_saved))
.setContentText(fileUri?.let { contentResolver.getFileDisplayName(it) })
.setSubText(null)
}
notification.setSmallIcon(R.drawable.ic_stat_done)
}
result.isAllFailed || !isRestoreService -> {
val title = getString(if (isRestoreService) R.string.data_not_restored else R.string.error_occurred)
val message = result.failures.joinToString("\n") {
it.getDisplayMessage(applicationContext.resources)
}
notification
.setContentText(if (isRestoreService) getString(R.string.data_not_restored_text) else message)
.setBigText(title, message)
.setSmallIcon(android.R.drawable.stat_notify_error)
result.failures.firstNotNullOfOrNull { error ->
ErrorReporterReceiver.getPendingIntent(applicationContext, error)
}?.let { reportIntent ->
notification.addAction(
R.drawable.ic_alert_outline,
applicationContext.getString(R.string.report),
reportIntent,
)
}
}
else -> {
notification
.setContentTitle(getString(R.string.restoring_backup))
.setContentText(getString(R.string.data_restored_with_errors))
.setSmallIcon(R.drawable.ic_stat_done)
}
}
notification.setContentIntent(
PendingIntentCompat.getActivity(
applicationContext,
0,
AppRouter.homeIntent(this),
AppRouter.homeIntent(this@BaseBackupRestoreService),
0,
false,
),
)
return notification.build()
if (!isRestoreService && fileUri != null) {
val shareIntent = ShareCompat.IntentBuilder(this@BaseBackupRestoreService)
.setStream(fileUri)
.setType("application/zip")
.setChooserTitle(R.string.share_backup)
.createChooserIntent()
notification.addAction(
appcompatR.drawable.abc_ic_menu_share_mtrl_alpha,
getString(R.string.share),
PendingIntentCompat.getActivity(this@BaseBackupRestoreService, 0, shareIntent, 0, false),
)
}
notificationManager.notify(notificationTag, startId, notification.build())
}
protected fun NotificationCompat.Builder.setBigText(title: String, text: CharSequence) = setStyle(
NotificationCompat.BigTextStyle()
.bigText(text)
.setSummaryText(text)
.setBigContentTitle(title),
)
protected companion object {
const val CHANNEL_ID = "backup_restore"

View File

@@ -8,8 +8,6 @@ import android.content.pm.ServiceInfo
import android.net.Uri
import androidx.annotation.CheckResult
import androidx.core.app.NotificationCompat
import androidx.core.app.PendingIntentCompat
import androidx.core.app.ShareCompat
import androidx.core.content.ContextCompat
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.cancelAndJoin
@@ -19,6 +17,7 @@ import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.backups.data.BackupRepository
import org.koitharu.kotatsu.backups.ui.BaseBackupRestoreService
import org.koitharu.kotatsu.core.nav.AppRouter
import org.koitharu.kotatsu.core.util.CompositeResult
import org.koitharu.kotatsu.core.util.ext.checkNotificationPermission
import org.koitharu.kotatsu.core.util.ext.powerManager
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
@@ -35,6 +34,7 @@ import androidx.appcompat.R as appcompatR
class BackupService : BaseBackupRestoreService() {
override val notificationTag = TAG
override val isRestoreService = false
@Inject
lateinit var repository: BackupRepository
@@ -63,9 +63,7 @@ class BackupService : BaseBackupRestoreService() {
}
progressUpdateJob?.cancelAndJoin()
contentResolver.notifyChange(destination, null)
if (checkNotificationPermission(CHANNEL_ID)) {
notificationManager.notify(notificationTag, startId, createResultNotification(destination))
}
showResultNotification(destination, CompositeResult.success())
}
}
@@ -98,31 +96,6 @@ class BackupService : BaseBackupRestoreService() {
).build()
}
private fun createResultNotification(uri: Uri): Notification {
val notification = NotificationCompat.Builder(applicationContext, CHANNEL_ID)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setDefaults(0)
.setSilent(true)
.setAutoCancel(true)
.setContentText(getString(R.string.backup_saved))
.setSmallIcon(R.drawable.ic_stat_done)
val shareIntent = ShareCompat.IntentBuilder(this)
.setStream(uri)
.setType(contentResolver.getType(uri) ?: "application/zip")
.setChooserTitle(R.string.share_backup)
.createChooserIntent()
notification.setContentIntent(
PendingIntentCompat.getActivity(
applicationContext,
0,
shareIntent,
0,
false,
),
)
return notification.build()
}
companion object {
private const val TAG = "BACKUP"

View File

@@ -8,8 +8,6 @@ import android.content.pm.ServiceInfo
import android.net.Uri
import androidx.annotation.CheckResult
import androidx.core.app.NotificationCompat
import androidx.core.app.PendingIntentCompat
import androidx.core.app.ShareCompat
import androidx.core.content.ContextCompat
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.cancelAndJoin
@@ -37,6 +35,7 @@ import androidx.appcompat.R as appcompatR
class RestoreService : BaseBackupRestoreService() {
override val notificationTag = TAG
override val isRestoreService = true
@Inject
lateinit var repository: BackupRepository
@@ -62,13 +61,11 @@ class RestoreService : BaseBackupRestoreService() {
} else {
null
}
ZipInputStream(contentResolver.openInputStream(source)).use { input ->
val result = ZipInputStream(contentResolver.openInputStream(source)).use { input ->
repository.restoreBackup(input, sections, progress)
}
progressUpdateJob?.cancelAndJoin()
if (checkNotificationPermission(CHANNEL_ID)) {
notificationManager.notify(notificationTag, startId, createResultNotification(source))
}
showResultNotification(source, result)
}
}
@@ -101,31 +98,6 @@ class RestoreService : BaseBackupRestoreService() {
).build()
}
private fun createResultNotification(uri: Uri): Notification {
val notification = NotificationCompat.Builder(applicationContext, CHANNEL_ID)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setDefaults(0)
.setSilent(true)
.setAutoCancel(true)
.setContentText(getString(R.string.backup_saved))
.setSmallIcon(R.drawable.ic_stat_done)
val shareIntent = ShareCompat.IntentBuilder(this)
.setStream(uri)
.setType(contentResolver.getType(uri) ?: "application/zip")
.setChooserTitle(R.string.share_backup)
.createChooserIntent()
notification.setContentIntent(
PendingIntentCompat.getActivity(
applicationContext,
0,
shareIntent,
0,
false,
),
)
return notification.build()
}
companion object {
private const val TAG = "RESTORE"