diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e2caa58f4ad7b577fcfda369d3654a990adcf113..b4bdbf181ae61d6c80d56f991db39da3b460e7e8 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,6 +4,7 @@ package="foundation.e.drive"> + diff --git a/app/src/main/java/foundation/e/drive/account/receivers/AccountRemoveCallbackReceiver.java b/app/src/main/java/foundation/e/drive/account/receivers/AccountRemoveCallbackReceiver.java index 668fb93d9f05257c6d556e2a18d91038e868b64c..23f8d392f28b6defaf08487c8d0bee3a50dbb2ea 100644 --- a/app/src/main/java/foundation/e/drive/account/receivers/AccountRemoveCallbackReceiver.java +++ b/app/src/main/java/foundation/e/drive/account/receivers/AccountRemoveCallbackReceiver.java @@ -29,6 +29,7 @@ import foundation.e.drive.EdriveApplication; import foundation.e.drive.R; import foundation.e.drive.database.DbHelper; import foundation.e.drive.database.FailedSyncPrefsManager; +import foundation.e.drive.synchronization.SyncProgressNotifier; import foundation.e.drive.synchronization.SyncWorker; import foundation.e.drive.utils.AppConstants; import foundation.e.drive.utils.DavClientProvider; @@ -153,7 +154,7 @@ public class AccountRemoveCallbackReceiver extends BroadcastReceiver { notificationManager.cancelAll(); try { notificationManager.deleteNotificationChannel(AppConstants.notificationChannelID); - notificationManager.deleteNotificationChannel(SyncWorker.NOTIF_CHANNEL_ID); + notificationManager.deleteNotificationChannel(SyncProgressNotifier.NOTIF_CHANNEL_ID); } catch (Exception exception) { Timber.e(exception, "Cannot delete notification Channel"); } diff --git a/app/src/main/java/foundation/e/drive/synchronization/SyncProgressNotifier.kt b/app/src/main/java/foundation/e/drive/synchronization/SyncProgressNotifier.kt new file mode 100644 index 0000000000000000000000000000000000000000..79772ea3784ff5b70a715e79d1899aecfc488cb1 --- /dev/null +++ b/app/src/main/java/foundation/e/drive/synchronization/SyncProgressNotifier.kt @@ -0,0 +1,73 @@ +/* + * Copyright © MURENA SAS 2023. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + */ + +package foundation.e.drive.synchronization + +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.content.Context +import android.content.pm.ServiceInfo +import android.os.Build +import androidx.core.app.NotificationCompat +import androidx.work.ForegroundInfo +import foundation.e.drive.R + +class SyncProgressNotifier(private val context: Context) { + companion object { + const val NOTIFICATION_ID = 2003004 + const val NOTIF_CHANNEL_ID = "syncChannelId" + } + + private val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + + private fun createNotification(requestCounter: Int): Notification { + val text = getNotificationText(requestCounter) + val title = context.getString(R.string.notif_sync_is_running_title) + + return NotificationCompat.Builder(context, NOTIF_CHANNEL_ID) + .setOngoing(true) + .setContentTitle(title) + .setContentText(text) + .setSmallIcon(R.drawable.ic_synchronization) + .build() + } + + fun notifyTaskFinished(requestCounter: Int) { + notificationManager.notify(NOTIFICATION_ID, createNotification(requestCounter)) + } + + fun cancelAllSyncNotifications() { + notificationManager.cancel(NOTIFICATION_ID) + } + + private fun getNotificationText(requestCount: Int): String { + return context.resources + .getQuantityString(R.plurals.notif_sync_is_running_txt, requestCount, requestCount) + } + + fun createNotificationChannel() { + val channelName = context.getString(R.string.notif_sync_channel_name) + val importance = NotificationManager.IMPORTANCE_MIN + val channel = NotificationChannel(NOTIF_CHANNEL_ID, channelName, importance) + val channelDescription = context.getString(R.string.notif_sync_channel_description) + channel.description = channelDescription + + notificationManager.createNotificationChannel(channel) + } + + internal fun createForegroundInfo(requestCount: Int): ForegroundInfo { + val notification = createNotification(requestCount) + + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + ForegroundInfo(NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC) + } else { + ForegroundInfo(NOTIFICATION_ID, notification) + } + } +} diff --git a/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt b/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt index 497d547b89a196712b4629ed5b939b24df0aa1c0..7a43489f3fd09f217f6d547edb77b47a71cc4a2f 100644 --- a/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt +++ b/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt @@ -38,6 +38,8 @@ class SyncTask( private val fileName = request.syncedFileState.name private val fileLocalPath = request.syncedFileState.localPath + private val syncNotifier = SyncProgressNotifier(context) + override fun run() { if (!canStart()) { @@ -55,6 +57,7 @@ class SyncTask( DISABLE_SYNCING -> runSyncDisabling() } + syncNotifier.notifyTaskFinished(SyncWorker.pendingTaskCounter.decrementAndGet()) updateFailureCounter(request, succeed) syncManager.removeStartedRequest(fileLocalPath) Timber.d("${request.operationType.name} finished for $fileLocalPath") diff --git a/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt b/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt index 861d7b7cfef126e7535c9eeb6b292a8261e7dae4..c394766783bfb9e1b41ac31e2b451df1da431e98 100644 --- a/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt +++ b/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt @@ -8,26 +8,19 @@ package foundation.e.drive.synchronization import android.accounts.Account -import android.app.NotificationChannel -import android.app.NotificationManager import android.content.Context -import android.content.Context.NOTIFICATION_SERVICE -import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC -import android.os.Build -import androidx.core.app.NotificationCompat -import androidx.work.ForegroundInfo import androidx.work.Worker import androidx.work.WorkerParameters import com.owncloud.android.lib.common.OwnCloudClient import foundation.e.drive.EdriveApplication -import foundation.e.drive.R import foundation.e.drive.account.AccountUtils import foundation.e.drive.utils.DavClientProvider import timber.log.Timber import java.util.concurrent.Executors import java.util.concurrent.Future import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicInteger class SyncWorker( context: Context, @@ -36,20 +29,19 @@ class SyncWorker( companion object { const val UNIQUE_WORK_NAME = "syncWorker" - const val NOTIF_CHANNEL_ID = "syncChannelId" - const val NOTIFICATION_ID = 2003004 + internal lateinit var pendingTaskCounter: AtomicInteger private const val threadAmount = 2 private val syncManager = SyncProxy as SyncManager } private var account: Account? = null private var ocClient: OwnCloudClient? = null - private val notificationManager = applicationContext.getSystemService(NOTIFICATION_SERVICE) as NotificationManager private var executor = Executors.newFixedThreadPool(threadAmount) + private val syncNotifier = SyncProgressNotifier(applicationContext) override fun onStopped() { Timber.d("SyncWorker has been stopped") - notificationManager.cancel(NOTIFICATION_ID) + syncNotifier.cancelAllSyncNotifications() executor.shutdownNow() super.onStopped() @@ -70,15 +62,16 @@ class SyncWorker( syncManager.startListeningFiles(applicationContext as EdriveApplication) return Result.failure() } - - createNotificationChannel() + syncNotifier.createNotificationChannel() while (!syncManager.isQueueEmpty()) { - setForegroundAsync(createForegroundInfo()) + val requestCount = syncManager.getQueueSize() + pendingTaskCounter = AtomicInteger(requestCount) + setForegroundAsync(syncNotifier.createForegroundInfo(requestCount)) executeRequests() } - notificationManager.cancel(NOTIFICATION_ID) + syncNotifier.cancelAllSyncNotifications() syncManager.startListeningFiles(applicationContext as EdriveApplication) } catch (exception: Exception) { Timber.w(exception) @@ -120,35 +113,4 @@ class SyncWorker( executor.awaitTermination(30, TimeUnit.SECONDS) } } - - private fun createForegroundInfo(): ForegroundInfo { - val title = applicationContext.getString(R.string.notif_sync_is_running_title) - val requestCount = syncManager.getQueueSize() - val text = applicationContext.resources - .getQuantityString(R.plurals.notif_sync_is_running_txt, requestCount, requestCount) - - val notification = NotificationCompat.Builder(applicationContext, NOTIF_CHANNEL_ID) - .setOngoing(true) - .setContentTitle(title) - .setContentText(text) - .setSmallIcon(R.drawable.ic_synchronization) - - .build() - - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - ForegroundInfo(NOTIFICATION_ID, notification, FOREGROUND_SERVICE_TYPE_DATA_SYNC) - } else { - ForegroundInfo(NOTIFICATION_ID, notification) - } - } - - private fun createNotificationChannel() { - val channelName = applicationContext.getString(R.string.notif_sync_channel_name) - val importance = NotificationManager.IMPORTANCE_MIN - val channel = NotificationChannel(NOTIF_CHANNEL_ID, channelName, importance) - val channelDescription = applicationContext.getString(R.string.notif_sync_channel_description) - channel.description = channelDescription - - notificationManager.createNotificationChannel(channel) - } } \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fda41306fe16f9966de42198a8c4576956b5c720..1ebbd1a25159a0a0efefca494dc111b8152bd573 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -31,7 +31,7 @@ 99% of your allotted cloud storage is used. Please take action. You\'ve filled your allotted cloud storage up to 90%. You\'ve filled your allotted cloud storage up to 80%. - File synchronization in progress + File synchronization %d file to sync %d files to sync