mirror of
https://github.com/mihonapp/mihon.git
synced 2025-02-07 15:54:58 +01:00
feat: add notification and sync job
This commit is contained in:
parent
2d44f57e7b
commit
dd1a9a7216
@ -9,6 +9,7 @@ import androidx.core.net.toUri
|
|||||||
import eu.kanade.tachiyomi.data.backup.restore.BackupRestoreJob
|
import eu.kanade.tachiyomi.data.backup.restore.BackupRestoreJob
|
||||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
|
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
|
||||||
|
import eu.kanade.tachiyomi.data.sync.SyncDataJob
|
||||||
import eu.kanade.tachiyomi.data.updater.AppUpdateDownloadJob
|
import eu.kanade.tachiyomi.data.updater.AppUpdateDownloadJob
|
||||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
||||||
@ -70,6 +71,8 @@ class NotificationReceiver : BroadcastReceiver() {
|
|||||||
"application/x-protobuf+gzip",
|
"application/x-protobuf+gzip",
|
||||||
)
|
)
|
||||||
ACTION_CANCEL_RESTORE -> cancelRestore(context)
|
ACTION_CANCEL_RESTORE -> cancelRestore(context)
|
||||||
|
|
||||||
|
ACTION_CANCEL_SYNC -> cancelSync(context)
|
||||||
// Cancel library update and dismiss notification
|
// Cancel library update and dismiss notification
|
||||||
ACTION_CANCEL_LIBRARY_UPDATE -> cancelLibraryUpdate(context)
|
ACTION_CANCEL_LIBRARY_UPDATE -> cancelLibraryUpdate(context)
|
||||||
// Start downloading app update
|
// Start downloading app update
|
||||||
@ -187,6 +190,15 @@ class NotificationReceiver : BroadcastReceiver() {
|
|||||||
AppUpdateDownloadJob.stop(context)
|
AppUpdateDownloadJob.stop(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method called when user wants to stop a backup restore job.
|
||||||
|
*
|
||||||
|
* @param context context of application
|
||||||
|
*/
|
||||||
|
private fun cancelSync(context: Context) {
|
||||||
|
SyncDataJob.stop(context)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method called when user wants to mark manga chapters as read
|
* Method called when user wants to mark manga chapters as read
|
||||||
*
|
*
|
||||||
@ -239,6 +251,8 @@ class NotificationReceiver : BroadcastReceiver() {
|
|||||||
|
|
||||||
private const val ACTION_CANCEL_RESTORE = "$ID.$NAME.CANCEL_RESTORE"
|
private const val ACTION_CANCEL_RESTORE = "$ID.$NAME.CANCEL_RESTORE"
|
||||||
|
|
||||||
|
private const val ACTION_CANCEL_SYNC = "$ID.$NAME.CANCEL_SYNC"
|
||||||
|
|
||||||
private const val ACTION_CANCEL_LIBRARY_UPDATE = "$ID.$NAME.CANCEL_LIBRARY_UPDATE"
|
private const val ACTION_CANCEL_LIBRARY_UPDATE = "$ID.$NAME.CANCEL_LIBRARY_UPDATE"
|
||||||
|
|
||||||
private const val ACTION_START_APP_UPDATE = "$ID.$NAME.ACTION_START_APP_UPDATE"
|
private const val ACTION_START_APP_UPDATE = "$ID.$NAME.ACTION_START_APP_UPDATE"
|
||||||
@ -615,5 +629,25 @@ class NotificationReceiver : BroadcastReceiver() {
|
|||||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns [PendingIntent] that cancels a sync restore job.
|
||||||
|
*
|
||||||
|
* @param context context of application
|
||||||
|
* @param notificationId id of notification
|
||||||
|
* @return [PendingIntent]
|
||||||
|
*/
|
||||||
|
internal fun cancelSyncPendingBroadcast(context: Context, notificationId: Int): PendingIntent {
|
||||||
|
val intent = Intent(context, NotificationReceiver::class.java).apply {
|
||||||
|
action = ACTION_CANCEL_SYNC
|
||||||
|
putExtra(EXTRA_NOTIFICATION_ID, notificationId)
|
||||||
|
}
|
||||||
|
return PendingIntent.getBroadcast(
|
||||||
|
context,
|
||||||
|
0,
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
103
app/src/main/java/eu/kanade/tachiyomi/data/sync/SyncDataJob.kt
Normal file
103
app/src/main/java/eu/kanade/tachiyomi/data/sync/SyncDataJob.kt
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
package eu.kanade.tachiyomi.data.sync
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.pm.ServiceInfo
|
||||||
|
import android.os.Build
|
||||||
|
import androidx.work.CoroutineWorker
|
||||||
|
import androidx.work.ExistingPeriodicWorkPolicy
|
||||||
|
import androidx.work.ExistingWorkPolicy
|
||||||
|
import androidx.work.ForegroundInfo
|
||||||
|
import androidx.work.OneTimeWorkRequestBuilder
|
||||||
|
import androidx.work.PeriodicWorkRequestBuilder
|
||||||
|
import androidx.work.WorkerParameters
|
||||||
|
import eu.kanade.domain.sync.SyncPreferences
|
||||||
|
import eu.kanade.tachiyomi.data.notification.Notifications
|
||||||
|
import eu.kanade.tachiyomi.util.system.cancelNotification
|
||||||
|
import eu.kanade.tachiyomi.util.system.isRunning
|
||||||
|
import eu.kanade.tachiyomi.util.system.workManager
|
||||||
|
import logcat.LogPriority
|
||||||
|
import tachiyomi.core.util.system.logcat
|
||||||
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
class SyncDataJob(private val context: Context, workerParams: WorkerParameters) :
|
||||||
|
CoroutineWorker(context, workerParams) {
|
||||||
|
|
||||||
|
private val notifier = SyncNotifier(context)
|
||||||
|
|
||||||
|
override suspend fun doWork(): Result {
|
||||||
|
try {
|
||||||
|
setForeground(getForegroundInfo())
|
||||||
|
} catch (e: IllegalStateException) {
|
||||||
|
logcat(LogPriority.ERROR, e) { "Not allowed to run on foreground service" }
|
||||||
|
}
|
||||||
|
|
||||||
|
return try {
|
||||||
|
//TODO: Uncomment this when the rest of sync PR is merged.
|
||||||
|
// SyncManager(context).syncData()
|
||||||
|
Result.success()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logcat(LogPriority.ERROR, e)
|
||||||
|
notifier.showSyncError(e.message)
|
||||||
|
Result.failure()
|
||||||
|
} finally {
|
||||||
|
context.cancelNotification(Notifications.ID_RESTORE_PROGRESS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getForegroundInfo(): ForegroundInfo {
|
||||||
|
return ForegroundInfo(
|
||||||
|
Notifications.ID_RESTORE_PROGRESS,
|
||||||
|
notifier.showSyncProgress().build(),
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
|
ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG_JOB = "SyncDataJob"
|
||||||
|
private const val TAG_AUTO = "$TAG_JOB:auto"
|
||||||
|
const val TAG_MANUAL = "$TAG_JOB:manual"
|
||||||
|
|
||||||
|
private val jobTagList = listOf(TAG_AUTO, TAG_MANUAL)
|
||||||
|
|
||||||
|
fun isAnyJobRunning(context: Context): Boolean {
|
||||||
|
return jobTagList.any { context.workManager.isRunning(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setupTask(context: Context, prefInterval: Int? = null) {
|
||||||
|
val syncPreferences = Injekt.get<SyncPreferences>()
|
||||||
|
val interval = prefInterval ?: syncPreferences.syncInterval().get()
|
||||||
|
|
||||||
|
if (interval > 0) {
|
||||||
|
val request = PeriodicWorkRequestBuilder<SyncDataJob>(
|
||||||
|
interval.toLong(),
|
||||||
|
TimeUnit.MINUTES,
|
||||||
|
10,
|
||||||
|
TimeUnit.MINUTES,
|
||||||
|
)
|
||||||
|
.addTag(TAG_AUTO)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
context.workManager.enqueueUniquePeriodicWork(TAG_AUTO, ExistingPeriodicWorkPolicy.UPDATE, request)
|
||||||
|
} else {
|
||||||
|
context.workManager.cancelUniqueWork(TAG_AUTO)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun startNow(context: Context) {
|
||||||
|
val request = OneTimeWorkRequestBuilder<SyncDataJob>()
|
||||||
|
.addTag(TAG_MANUAL)
|
||||||
|
.build()
|
||||||
|
context.workManager.enqueueUniqueWork(TAG_MANUAL, ExistingWorkPolicy.KEEP, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stop(context: Context) {
|
||||||
|
context.workManager.cancelUniqueWork(TAG_MANUAL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,86 @@
|
|||||||
|
package eu.kanade.tachiyomi.data.sync
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
|
import androidx.core.app.NotificationCompat
|
||||||
|
import eu.kanade.tachiyomi.R
|
||||||
|
import eu.kanade.tachiyomi.core.security.SecurityPreferences
|
||||||
|
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
|
||||||
|
import eu.kanade.tachiyomi.data.notification.Notifications
|
||||||
|
import eu.kanade.tachiyomi.util.system.cancelNotification
|
||||||
|
import eu.kanade.tachiyomi.util.system.notificationBuilder
|
||||||
|
import eu.kanade.tachiyomi.util.system.notify
|
||||||
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
|
||||||
|
class SyncNotifier(private val context: Context) {
|
||||||
|
|
||||||
|
private val preferences: SecurityPreferences by injectLazy()
|
||||||
|
|
||||||
|
private val progressNotificationBuilder = context.notificationBuilder(
|
||||||
|
Notifications.CHANNEL_BACKUP_RESTORE_PROGRESS,
|
||||||
|
) {
|
||||||
|
setLargeIcon(BitmapFactory.decodeResource(context.resources, R.mipmap.ic_launcher))
|
||||||
|
setSmallIcon(R.drawable.ic_mihon)
|
||||||
|
setAutoCancel(false)
|
||||||
|
setOngoing(true)
|
||||||
|
setOnlyAlertOnce(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val completeNotificationBuilder = context.notificationBuilder(
|
||||||
|
Notifications.CHANNEL_BACKUP_RESTORE_PROGRESS,
|
||||||
|
) {
|
||||||
|
setLargeIcon(BitmapFactory.decodeResource(context.resources, R.mipmap.ic_launcher))
|
||||||
|
setSmallIcon(R.drawable.ic_mihon)
|
||||||
|
setAutoCancel(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun NotificationCompat.Builder.show(id: Int) {
|
||||||
|
context.notify(id, build())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showSyncProgress(content: String = "", progress: Int = 0, maxAmount: Int = 100): NotificationCompat.Builder {
|
||||||
|
val builder = with(progressNotificationBuilder) {
|
||||||
|
setContentTitle(context.getString(R.string.syncing_library))
|
||||||
|
|
||||||
|
if (!preferences.hideNotificationContent().get()) {
|
||||||
|
setContentText(content)
|
||||||
|
}
|
||||||
|
|
||||||
|
setProgress(maxAmount, progress, true)
|
||||||
|
setOnlyAlertOnce(true)
|
||||||
|
|
||||||
|
clearActions()
|
||||||
|
addAction(
|
||||||
|
R.drawable.ic_close_24dp,
|
||||||
|
context.getString(R.string.action_cancel),
|
||||||
|
NotificationReceiver.cancelSyncPendingBroadcast(context, Notifications.ID_RESTORE_PROGRESS),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.show(Notifications.ID_RESTORE_PROGRESS)
|
||||||
|
|
||||||
|
return builder
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showSyncError(error: String?) {
|
||||||
|
context.cancelNotification(Notifications.ID_RESTORE_PROGRESS)
|
||||||
|
|
||||||
|
with(completeNotificationBuilder) {
|
||||||
|
setContentTitle(context.getString(R.string.sync_error))
|
||||||
|
setContentText(error)
|
||||||
|
|
||||||
|
show(Notifications.ID_RESTORE_COMPLETE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showSyncSuccess(message: String?) {
|
||||||
|
context.cancelNotification(Notifications.ID_RESTORE_PROGRESS)
|
||||||
|
|
||||||
|
with(completeNotificationBuilder) {
|
||||||
|
setContentTitle(context.getString(R.string.sync_complete))
|
||||||
|
setContentText(message)
|
||||||
|
|
||||||
|
show(Notifications.ID_RESTORE_COMPLETE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user