From 9825c6c2ee1df28e990511c65d6021256e4f2ea1 Mon Sep 17 00:00:00 2001 From: Moez Bhatti Date: Mon, 27 Aug 2018 20:37:16 -0400 Subject: [PATCH] Use foreground service to perform restore --- .../QKSMS/repository/BackupRepositoryImpl.kt | 4 +- .../QKSMS/service/RestoreBackupService.kt | 105 ++++++++++++++++++ .../moez/QKSMS/interactor/PerformRestore.kt | 5 +- .../moez/QKSMS/manager/NotificationManager.kt | 4 + .../moez/QKSMS/repository/BackupRepository.kt | 2 +- presentation/src/main/AndroidManifest.xml | 7 +- .../common/util/NotificationManagerImpl.kt | 20 +++- .../QKSMS/feature/backup/BackupPresenter.kt | 9 +- .../injection/android/ServiceBuilderModule.kt | 5 + presentation/src/main/res/values/strings.xml | 1 + 10 files changed, 148 insertions(+), 14 deletions(-) create mode 100644 data/src/main/java/com/moez/QKSMS/service/RestoreBackupService.kt diff --git a/data/src/main/java/com/moez/QKSMS/repository/BackupRepositoryImpl.kt b/data/src/main/java/com/moez/QKSMS/repository/BackupRepositoryImpl.kt index 52568daf..6009e383 100644 --- a/data/src/main/java/com/moez/QKSMS/repository/BackupRepositoryImpl.kt +++ b/data/src/main/java/com/moez/QKSMS/repository/BackupRepositoryImpl.kt @@ -148,13 +148,13 @@ class BackupRepositoryImpl @Inject constructor( } .map { files -> files.sortedByDescending { file -> file.date } } - override fun performRestore(backupFile: BackupFile) { + override fun performRestore(filePath: String) { // If a backupFile or restore is already running, don't do anything if (isBackupOrRestoreRunning()) return restoreProgress.onNext(BackupRepository.Progress.Running(0, "Parsing backup")) - val file = File(backupFile.path) + val file = File(filePath) val source = Okio.buffer(Okio.source(file)) val backup = moshi.adapter(Backup::class.java).fromJson(source) diff --git a/data/src/main/java/com/moez/QKSMS/service/RestoreBackupService.kt b/data/src/main/java/com/moez/QKSMS/service/RestoreBackupService.kt new file mode 100644 index 00000000..77b5193d --- /dev/null +++ b/data/src/main/java/com/moez/QKSMS/service/RestoreBackupService.kt @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2017 Moez Bhatti + * + * This file is part of QKSMS. + * + * QKSMS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * QKSMS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with QKSMS. If not, see . + */ +package com.moez.QKSMS.service + +import android.annotation.SuppressLint +import android.app.Service +import android.content.Context +import android.content.Intent +import android.os.IBinder +import androidx.core.app.NotificationManagerCompat +import androidx.core.content.ContextCompat +import com.moez.QKSMS.manager.NotificationManager +import com.moez.QKSMS.repository.BackupRepository +import dagger.android.AndroidInjection +import io.reactivex.Observable +import io.reactivex.schedulers.Schedulers +import java.util.concurrent.TimeUnit +import javax.inject.Inject + + +class RestoreBackupService : Service() { + + companion object { + private const val NOTIFICATION_ID = -1 + + private const val ACTION_START = "com.moez.QKSMS.ACTION_START" + private const val ACTION_STOP = "com.moez.QKSMS.ACTION_STOP" + private const val EXTRA_FILE_PATH = "com.moez.QKSMS.EXTRA_FILE_PATH" + + fun start(context: Context, filePath: String) { + val intent = Intent(context, RestoreBackupService::class.java) + .setAction(ACTION_START) + .putExtra(EXTRA_FILE_PATH, filePath) + + ContextCompat.startForegroundService(context, intent) + } + } + + @Inject lateinit var backupRepo: BackupRepository + @Inject lateinit var notificationManager: NotificationManager + + override fun onCreate() = AndroidInjection.inject(this) + + override fun onBind(intent: Intent?): IBinder? = null + + override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { + super.onStartCommand(intent, flags, startId) + when (intent.action) { + ACTION_START -> start(intent) + ACTION_STOP -> stop() + } + + return Service.START_STICKY + } + + @SuppressLint("CheckResult") + private fun start(intent: Intent) { + val notification = notificationManager.getNotificationForBackup() + val notificationManager = NotificationManagerCompat.from(this) + + startForeground(NOTIFICATION_ID, notification.build()) + + backupRepo.getRestoreProgress() + .sample(200, TimeUnit.MILLISECONDS, true) + .subscribeOn(Schedulers.io()) + .subscribe { progress -> + when (progress) { + is BackupRepository.Progress.Running -> { + notification.setProgress(100, progress.progress, false) + notificationManager.notify(NOTIFICATION_ID, notification.build()) + } + + is BackupRepository.Progress.Idle -> stop() + } + } + + // Start the restore + Observable.just(intent) + .map { it.getStringExtra(EXTRA_FILE_PATH) } + .subscribeOn(Schedulers.io()) + .subscribe(backupRepo::performRestore) + } + + private fun stop() { + stopForeground(true) + stopSelf() + } + +} \ No newline at end of file diff --git a/domain/src/main/java/com/moez/QKSMS/interactor/PerformRestore.kt b/domain/src/main/java/com/moez/QKSMS/interactor/PerformRestore.kt index 23dfe8a2..7f4c1d6b 100644 --- a/domain/src/main/java/com/moez/QKSMS/interactor/PerformRestore.kt +++ b/domain/src/main/java/com/moez/QKSMS/interactor/PerformRestore.kt @@ -18,16 +18,15 @@ */ package com.moez.QKSMS.interactor -import com.moez.QKSMS.model.BackupFile import com.moez.QKSMS.repository.BackupRepository import io.reactivex.Flowable import javax.inject.Inject class PerformRestore @Inject constructor( private val backupRepo: BackupRepository -) : Interactor() { +) : Interactor() { - override fun buildObservable(params: BackupFile): Flowable<*> { + override fun buildObservable(params: String): Flowable<*> { return Flowable.just(params) .doOnNext(backupRepo::performRestore) } diff --git a/domain/src/main/java/com/moez/QKSMS/manager/NotificationManager.kt b/domain/src/main/java/com/moez/QKSMS/manager/NotificationManager.kt index 5ebbdc02..0a31695a 100644 --- a/domain/src/main/java/com/moez/QKSMS/manager/NotificationManager.kt +++ b/domain/src/main/java/com/moez/QKSMS/manager/NotificationManager.kt @@ -18,6 +18,8 @@ */ package com.moez.QKSMS.manager +import androidx.core.app.NotificationCompat + interface NotificationManager { fun update(threadId: Long) @@ -28,4 +30,6 @@ interface NotificationManager { fun buildNotificationChannelId(threadId: Long): String + fun getNotificationForBackup(): NotificationCompat.Builder + } \ No newline at end of file diff --git a/domain/src/main/java/com/moez/QKSMS/repository/BackupRepository.kt b/domain/src/main/java/com/moez/QKSMS/repository/BackupRepository.kt index 113d5585..7bf83a57 100644 --- a/domain/src/main/java/com/moez/QKSMS/repository/BackupRepository.kt +++ b/domain/src/main/java/com/moez/QKSMS/repository/BackupRepository.kt @@ -37,7 +37,7 @@ interface BackupRepository { */ fun getBackups(): Observable> - fun performRestore(backupFile: BackupFile) + fun performRestore(filePath: String) fun getRestoreProgress(): Observable diff --git a/presentation/src/main/AndroidManifest.xml b/presentation/src/main/AndroidManifest.xml index 6f9e74fe..e0c0405b 100644 --- a/presentation/src/main/AndroidManifest.xml +++ b/presentation/src/main/AndroidManifest.xml @@ -153,6 +153,9 @@ + - + diff --git a/presentation/src/main/java/com/moez/QKSMS/common/util/NotificationManagerImpl.kt b/presentation/src/main/java/com/moez/QKSMS/common/util/NotificationManagerImpl.kt index 082f8a6d..0be7e2c3 100644 --- a/presentation/src/main/java/com/moez/QKSMS/common/util/NotificationManagerImpl.kt +++ b/presentation/src/main/java/com/moez/QKSMS/common/util/NotificationManagerImpl.kt @@ -38,7 +38,6 @@ import com.moez.QKSMS.common.util.extensions.dpToPx import com.moez.QKSMS.extensions.isImage import com.moez.QKSMS.feature.compose.ComposeActivity import com.moez.QKSMS.feature.qkreply.QkReplyActivity -import com.moez.QKSMS.interactor.DeleteMessages import com.moez.QKSMS.manager.PermissionManager import com.moez.QKSMS.mapper.CursorToPartImpl import com.moez.QKSMS.receiver.DeleteMessagesReceiver @@ -65,6 +64,8 @@ class NotificationManagerImpl @Inject constructor( companion object { const val DEFAULT_CHANNEL_ID = "notifications_default" + const val BACKUP_RESTORE_CHANNEL_ID = "notifications_backup_restore" + val VIBRATE_PATTERN = longArrayOf(0, 200, 0, 200) } @@ -374,4 +375,21 @@ class NotificationManagerImpl @Inject constructor( } } + override fun getNotificationForBackup(): NotificationCompat.Builder { + if (Build.VERSION.SDK_INT >= 26) { + val name = context.getString(R.string.backup_notification_channel_name) + val importance = NotificationManager.IMPORTANCE_LOW + val channel = NotificationChannel(BACKUP_RESTORE_CHANNEL_ID, name, importance) + val notificationManager = context.getSystemService(NotificationManager::class.java) + notificationManager.createNotificationChannel(channel) + } + + return NotificationCompat.Builder(context, BACKUP_RESTORE_CHANNEL_ID) + .setContentTitle(context.getString(R.string.backup_restoring)) + .setSmallIcon(R.drawable.ic_file_download_black_24dp) + .setPriority(NotificationCompat.PRIORITY_MIN) + .setProgress(0, 0, true) + .setOngoing(true) + } + } \ No newline at end of file diff --git a/presentation/src/main/java/com/moez/QKSMS/feature/backup/BackupPresenter.kt b/presentation/src/main/java/com/moez/QKSMS/feature/backup/BackupPresenter.kt index 69d918e9..ae2e5f1c 100644 --- a/presentation/src/main/java/com/moez/QKSMS/feature/backup/BackupPresenter.kt +++ b/presentation/src/main/java/com/moez/QKSMS/feature/backup/BackupPresenter.kt @@ -18,12 +18,13 @@ */ package com.moez.QKSMS.feature.backup +import android.content.Context import com.moez.QKSMS.common.Navigator import com.moez.QKSMS.common.base.QkPresenter import com.moez.QKSMS.common.util.BillingManager import com.moez.QKSMS.interactor.PerformBackup -import com.moez.QKSMS.interactor.PerformRestore import com.moez.QKSMS.repository.BackupRepository +import com.moez.QKSMS.service.RestoreBackupService import com.uber.autodispose.kotlin.autoDisposable import io.reactivex.rxkotlin.plusAssign import io.reactivex.rxkotlin.withLatestFrom @@ -33,9 +34,9 @@ import javax.inject.Inject class BackupPresenter @Inject constructor( backupRepo: BackupRepository, private val billingManager: BillingManager, + private val context: Context, private val navigator: Navigator, - private val performBackup: PerformBackup, - private val performRestore: PerformRestore + private val performBackup: PerformBackup ) : QkPresenter(BackupState()) { init { @@ -65,7 +66,7 @@ class BackupPresenter @Inject constructor( view.restoreFileSelected() .autoDisposable(view.scope()) - .subscribe { backup -> performRestore.execute(backup) } + .subscribe { backup -> RestoreBackupService.start(context, backup.path) } view.fabClicks() .withLatestFrom(billingManager.upgradeStatus) { _, upgraded -> upgraded } diff --git a/presentation/src/main/java/com/moez/QKSMS/injection/android/ServiceBuilderModule.kt b/presentation/src/main/java/com/moez/QKSMS/injection/android/ServiceBuilderModule.kt index 57ff40d2..142d1207 100644 --- a/presentation/src/main/java/com/moez/QKSMS/injection/android/ServiceBuilderModule.kt +++ b/presentation/src/main/java/com/moez/QKSMS/injection/android/ServiceBuilderModule.kt @@ -20,6 +20,7 @@ package com.moez.QKSMS.injection.android import com.moez.QKSMS.injection.scope.ActivityScope import com.moez.QKSMS.service.HeadlessSmsSendService +import com.moez.QKSMS.service.RestoreBackupService import com.moez.QKSMS.service.SendScheduledMessageService import com.moez.QKSMS.service.SendSmsService import dagger.Module @@ -32,6 +33,10 @@ abstract class ServiceBuilderModule { @ContributesAndroidInjector() abstract fun bindHeadlessSmsSendService(): HeadlessSmsSendService + @ActivityScope + @ContributesAndroidInjector() + abstract fun bindRestoreBackupService(): RestoreBackupService + @ActivityScope @ContributesAndroidInjector() abstract fun bindSendScheduledMessageReceiver(): SendScheduledMessageService diff --git a/presentation/src/main/res/values/strings.xml b/presentation/src/main/res/values/strings.xml index a1574b90..b43446dc 100644 --- a/presentation/src/main/res/values/strings.xml +++ b/presentation/src/main/res/values/strings.xml @@ -162,6 +162,7 @@ Currently, only SMS is supported by Backup and Restore. MMS support and scheduled backups will be coming soon! Backup now + Backup and restore Scheduled Automatically send a message, at the exact moment you\'d like