diff --git a/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt b/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt index d476a92b16cb..51aa55843538 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt @@ -18,6 +18,7 @@ import androidx.work.ListenableWorker import androidx.work.NetworkType import androidx.work.OneTimeWorkRequest import androidx.work.Operation +import androidx.work.OutOfQuotaPolicy import androidx.work.PeriodicWorkRequest import androidx.work.WorkInfo import androidx.work.WorkManager @@ -500,6 +501,7 @@ internal class BackgroundJobManagerImpl( TimeUnit.SECONDS ) .setInputData(arguments) + .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) .build() workManager.enqueueUniquePeriodicWork( @@ -681,6 +683,7 @@ internal class BackgroundJobManagerImpl( .addTag(tag) .setInputData(dataBuilder.build()) .setConstraints(constraints) + .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) .build() } @@ -728,6 +731,7 @@ internal class BackgroundJobManagerImpl( val request = oneTimeRequestBuilder(FileDownloadWorker::class, JOB_FILES_DOWNLOAD, user) .addTag(tag) .setInputData(data) + .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) .build() // Since for each file new FileDownloadWorker going to be scheduled, @@ -839,6 +843,7 @@ internal class BackgroundJobManagerImpl( .addTag(JOB_DOWNLOAD_FOLDER) .setInputData(data) .setConstraints(constraints) + .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) .build() workManager.enqueueUniqueWork(JOB_DOWNLOAD_FOLDER, ExistingWorkPolicy.APPEND_OR_REPLACE, request) diff --git a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt index 39856a809d1d..5a793e5f8191 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt @@ -12,6 +12,7 @@ import android.content.Context import android.content.res.Resources import androidx.exifinterface.media.ExifInterface import androidx.work.CoroutineWorker +import androidx.work.ForegroundInfo import androidx.work.WorkerParameters import com.nextcloud.client.account.User import com.nextcloud.client.account.UserAccountManager @@ -52,7 +53,7 @@ import java.text.SimpleDateFormat import java.util.Locale import java.util.TimeZone -@Suppress("LongParameterList", "TooManyFunctions") +@Suppress("LongParameterList", "TooManyFunctions", "TooGenericExceptionCaught") class AutoUploadWorker( private val context: Context, params: WorkerParameters, @@ -78,16 +79,14 @@ class AutoUploadWorker( private lateinit var syncedFolder: SyncedFolder private val notificationManager = AutoUploadNotificationManager(context, viewThemeUtils, NOTIFICATION_ID) - @Suppress("TooGenericExceptionCaught", "ReturnCount") + @Suppress("ReturnCount") override suspend fun doWork(): Result { return try { val syncFolderId = inputData.getLong(SYNCED_FOLDER_ID, -1) syncedFolder = syncedFolderProvider.getSyncedFolderByID(syncFolderId) ?.takeIf { it.isEnabled } ?: return Result.failure() - // initial notification - val notification = createNotification(context.getString(R.string.upload_files)) - updateForegroundInfo(notification) + trySetForeground() /** * Receives from [com.nextcloud.client.jobs.ContentObserverWork.checkAndTriggerAutoUpload] @@ -110,17 +109,42 @@ class AutoUploadWorker( } } + override suspend fun getForegroundInfo(): ForegroundInfo { + val notification = createNotification( + context.getString(R.string.upload_files) + ) + + return ForegroundServiceHelper.createWorkerForegroundInfo( + NOTIFICATION_ID, + notification, + ForegroundServiceType.DataSync + ) + } + private fun updateNotification() { getStartNotificationTitle()?.let { (localFolderName, remoteFolderName) -> - val startNotification = createNotification( - context.getString( - R.string.auto_upload_worker_start_text, - localFolderName, - remoteFolderName + try { + val startNotification = createNotification( + context.getString( + R.string.auto_upload_worker_start_text, + localFolderName, + remoteFolderName + ) ) - ) - notificationManager.showNotification(startNotification) + notificationManager.showNotification(startNotification) + } catch (e: Exception) { + Log_OC.w(TAG, "⚠️ Could not update notification: ${e.message}") + } + } + } + + private suspend fun trySetForeground() { + try { + val notification = createNotification(context.getString(R.string.upload_files)) + updateForegroundInfo(notification) + } catch (e: Exception) { + Log_OC.w(TAG, "⚠️ Could not set foreground service: ${e.message}") } } diff --git a/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadWorker.kt b/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadWorker.kt index e160e1dd4558..8f032fa01ad5 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadWorker.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadWorker.kt @@ -46,7 +46,7 @@ import java.util.Optional import java.util.Vector import kotlin.random.Random -@Suppress("LongParameterList", "TooManyFunctions") +@Suppress("LongParameterList", "TooManyFunctions", "TooGenericExceptionCaught") class FileDownloadWorker( viewThemeUtils: ViewThemeUtils, private val accountManager: UserAccountManager, @@ -58,7 +58,7 @@ class FileDownloadWorker( OnDatatransferProgressListener { companion object { - private val TAG = FileDownloadWorker::class.java.simpleName + private const val TAG = "🗄️" + "FileDownloadWorker" private val pendingDownloads = IndexedForest() @@ -111,10 +111,9 @@ class FileDownloadWorker( private var downloadError: FileDownloadError? = null - @Suppress("TooGenericExceptionCaught", "ReturnCount") + @Suppress("ReturnCount") override suspend fun doWork(): Result { - val foregroundInfo = createWorkerForegroundInfo() - setForeground(foregroundInfo) + trySetForeground() return try { setUser() @@ -132,19 +131,37 @@ class FileDownloadWorker( notificationManager.dismissNotification() } - Log_OC.e(TAG, "FilesDownloadWorker successfully completed") + Log_OC.d(TAG, "successfully completed") Result.success() } catch (t: Throwable) { notificationManager.showNewNotification(context.getString(R.string.downloader_unexpected_error)) - Log_OC.e(TAG, "Error caught at FilesDownloadWorker(): " + t.localizedMessage) + Log_OC.e(TAG, "exception: " + t.localizedMessage) Result.failure() } finally { - Log_OC.e(TAG, "FilesDownloadWorker cleanup") + Log_OC.d(TAG, "cleanup") notificationManager.dismissNotification() setIdleWorkerState() } } + override suspend fun getForegroundInfo(): ForegroundInfo { + val notification = notificationManager.getNotification() + return ForegroundServiceHelper.createWorkerForegroundInfo( + notificationManager.getId(), + notification, + ForegroundServiceType.DataSync + ) + } + + private suspend fun trySetForeground() { + try { + val foregroundInfo = createWorkerForegroundInfo() + setForeground(foregroundInfo) + } catch (e: Exception) { + Log_OC.w(TAG, "⚠️ Could not set foreground service: ${e.message}") + } + } + private fun createWorkerForegroundInfo(): ForegroundInfo = ForegroundServiceHelper.createWorkerForegroundInfo( notificationManager.getId(), notificationManager.getNotification(), @@ -250,7 +267,7 @@ class FileDownloadWorker( } setWorkerState(user) - Log_OC.e(TAG, "FilesDownloadWorker downloading: $downloadKey") + Log_OC.d(TAG, "downloading: $downloadKey") val isAccountExist = accountManager.exists(currentDownload?.user?.toPlatformAccount()) if (!isAccountExist) { @@ -277,7 +294,7 @@ class FileDownloadWorker( } } } catch (e: Exception) { - Log_OC.e(TAG, "Error downloading", e) + Log_OC.e(TAG, "exception downloading file: ", e) downloadResult = RemoteOperationResult(e) } finally { cleanupDownloadProcess(downloadResult) diff --git a/app/src/main/java/com/nextcloud/client/jobs/folderDownload/FolderDownloadWorker.kt b/app/src/main/java/com/nextcloud/client/jobs/folderDownload/FolderDownloadWorker.kt index 1ef24562c165..7096e4aeb12b 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/folderDownload/FolderDownloadWorker.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/folderDownload/FolderDownloadWorker.kt @@ -9,6 +9,7 @@ package com.nextcloud.client.jobs.folderDownload import android.content.Context import androidx.work.CoroutineWorker +import androidx.work.ForegroundInfo import androidx.work.WorkerParameters import com.nextcloud.client.account.UserAccountManager import com.nextcloud.client.jobs.download.FileDownloadHelper @@ -24,7 +25,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import java.util.concurrent.ConcurrentHashMap -@Suppress("LongMethod") +@Suppress("LongMethod", "TooGenericExceptionCaught") class FolderDownloadWorker( private val accountManager: UserAccountManager, private val context: Context, @@ -45,7 +46,7 @@ class FolderDownloadWorker( private var notificationManager: FolderDownloadWorkerNotificationManager? = null private lateinit var storageManager: FileDataStorageManager - @Suppress("TooGenericExceptionCaught", "ReturnCount", "DEPRECATION") + @Suppress("ReturnCount", "DEPRECATION") override suspend fun doWork(): Result { val folderID = inputData.getLong(FOLDER_ID, -1) if (folderID == -1L) { @@ -76,8 +77,7 @@ class FolderDownloadWorker( Log_OC.d(TAG, "🕒 started for ${user.accountName} downloading ${folder.fileName}") - val foregroundInfo = notificationManager?.getForegroundInfo(folder) ?: return Result.failure() - setForeground(foregroundInfo) + trySetForeground(folder) pendingDownloads.add(folder.fileId) @@ -96,7 +96,7 @@ class FolderDownloadWorker( } withContext(Dispatchers.Main) { - notificationManager?.showProgressNotification( + getNotificationManager().showProgressNotification( folder.fileName, file.fileName, index, @@ -118,7 +118,7 @@ class FolderDownloadWorker( } withContext(Dispatchers.Main) { - notificationManager?.showCompletionMessage(folder.fileName, result) + getNotificationManager().showCompletionMessage(folder.fileName, result) } if (result) { @@ -133,11 +133,44 @@ class FolderDownloadWorker( Result.failure() } finally { pendingDownloads.remove(folder.fileId) - notificationManager?.dismiss() + getNotificationManager().dismiss() } } } + @Suppress("ReturnCount") + override suspend fun getForegroundInfo(): ForegroundInfo { + return try { + val folderID = inputData.getLong(FOLDER_ID, -1) + val accountName = inputData.getString(ACCOUNT_NAME) + + if (folderID == -1L || accountName == null || !::storageManager.isInitialized) { + return createDefaultForegroundInfo() + } + + val folder = storageManager.getFileById(folderID) ?: return createDefaultForegroundInfo() + + return getNotificationManager().getForegroundInfo(folder) + } catch (e: Exception) { + Log_OC.w(TAG, "⚠️ Error getting foreground info: ${e.message}") + createDefaultForegroundInfo() + } + } + + private fun getNotificationManager(): FolderDownloadWorkerNotificationManager = + notificationManager ?: FolderDownloadWorkerNotificationManager(context, viewThemeUtils) + + private fun createDefaultForegroundInfo(): ForegroundInfo = getNotificationManager().getDefaultForegroundInfo() + + private suspend fun trySetForeground(folder: OCFile) { + try { + val foregroundInfo = getNotificationManager().getForegroundInfo(folder) + setForeground(foregroundInfo) + } catch (e: Exception) { + Log_OC.w(TAG, "⚠️ Could not set foreground service: ${e.message}") + } + } + private fun getOCFile(operation: DownloadFileOperation): OCFile? { val file = operation.file?.fileId?.let { storageManager.getFileById(it) } ?: storageManager.getFileByDecryptedRemotePath(operation.file?.remotePath) @@ -158,7 +191,7 @@ class FolderDownloadWorker( val availableDiskSpace = FileOperationsHelper.getAvailableSpaceOnDevice() return if (availableDiskSpace < fileSizeInByte) { - notificationManager?.showNotAvailableDiskSpace() + getNotificationManager().showNotAvailableDiskSpace() false } else { true diff --git a/app/src/main/java/com/nextcloud/client/jobs/folderDownload/FolderDownloadWorkerNotificationManager.kt b/app/src/main/java/com/nextcloud/client/jobs/folderDownload/FolderDownloadWorkerNotificationManager.kt index 6b2a1f4bb361..67252b639611 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/folderDownload/FolderDownloadWorkerNotificationManager.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/folderDownload/FolderDownloadWorkerNotificationManager.kt @@ -11,6 +11,7 @@ import android.app.Notification import android.app.PendingIntent import android.content.Context import android.content.Intent +import androidx.core.app.NotificationCompat import androidx.work.ForegroundInfo import com.nextcloud.client.jobs.notification.WorkerNotificationManager import com.nextcloud.utils.ForegroundServiceHelper @@ -70,11 +71,24 @@ class FolderDownloadWorkerNotificationManager(private val context: Context, view ) } + @Suppress("MagicNumber") fun showProgressNotification(folderName: String, filename: String, currentIndex: Int, totalFileSize: Int) { val currentFileIndex = (currentIndex + 1) val description = context.getString(R.string.folder_download_counter, currentFileIndex, totalFileSize, filename) val progress = (currentFileIndex * MAX_PROGRESS) / totalFileSize - val notification = getNotification(title = folderName, description = description, progress = progress) + val notification = notificationBuilder.apply { + setContentTitle(folderName) + setContentText(description) + setProgress(100, progress, false) + clearActions() + addAction( + android.R.drawable.ic_menu_close_clear_cancel, + context.getString(R.string.common_cancel), + getCancelPendingIntent() + ) + setOngoing(true) + setAutoCancel(false) + }.build() notificationManager.notify(NOTIFICATION_ID, notification) } @@ -110,4 +124,21 @@ class FolderDownloadWorkerNotificationManager(private val context: Context, view fun dismiss() { notificationManager.cancel(NOTIFICATION_ID) } + + fun getDefaultForegroundInfo(): ForegroundInfo { + val notification = createDefaultNotification() + return ForegroundServiceHelper.createWorkerForegroundInfo( + NOTIFICATION_ID, + notification, + ForegroundServiceType.DataSync + ) + } + + private fun createDefaultNotification(): Notification = + NotificationCompat.Builder(context, NotificationUtils.NOTIFICATION_CHANNEL_DOWNLOAD) + .setContentTitle(context.getString(R.string.downloader_download_in_progress_ticker)) + .setSmallIcon(R.drawable.ic_sync) + .setOngoing(true) + .setSilent(true) + .build() } diff --git a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadWorker.kt b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadWorker.kt index c29f9c6a63d9..5283f69f163f 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadWorker.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadWorker.kt @@ -12,6 +12,7 @@ import android.content.Context import androidx.core.app.NotificationCompat import androidx.localbroadcastmanager.content.LocalBroadcastManager import androidx.work.CoroutineWorker +import androidx.work.ForegroundInfo import androidx.work.WorkerParameters import com.nextcloud.client.account.User import com.nextcloud.client.account.UserAccountManager @@ -48,7 +49,7 @@ import kotlinx.coroutines.withContext import java.io.File import kotlin.random.Random -@Suppress("LongParameterList") +@Suppress("LongParameterList", "TooGenericExceptionCaught") class FileUploadWorker( val uploadsStorageManager: UploadsStorageManager, val connectivityService: ConnectivityService, @@ -132,16 +133,12 @@ class FileUploadWorker( private val intents = FileUploaderIntents(context) private val fileUploaderDelegate = FileUploaderDelegate() - @Suppress("TooGenericExceptionCaught") override suspend fun doWork(): Result = try { Log_OC.d(TAG, "FileUploadWorker started") val workerName = BackgroundJobManagerImpl.formatClassTag(this::class) backgroundJobManager.logStartOfWorker(workerName) - val notificationTitle = notificationManager.currentOperationTitle - ?: context.getString(R.string.foreground_service_upload) - val notification = createNotification(notificationTitle) - updateForegroundInfo(notification) + trySetForeground() val result = uploadFiles() backgroundJobManager.logEndOfWorker(workerName, result) @@ -156,6 +153,30 @@ class FileUploadWorker( Result.failure() } + private suspend fun trySetForeground() { + try { + val notificationTitle = notificationManager.currentOperationTitle + ?: context.getString(R.string.foreground_service_upload) + val notification = createNotification(notificationTitle) + updateForegroundInfo(notification) + } catch (e: Exception) { + // Continue without foreground service - uploads will still work + Log_OC.w(TAG, "Could not set foreground service: ${e.message}") + } + } + + override suspend fun getForegroundInfo(): ForegroundInfo { + val notificationTitle = notificationManager.currentOperationTitle + ?: context.getString(R.string.foreground_service_upload) + val notification = createNotification(notificationTitle) + + return ForegroundServiceHelper.createWorkerForegroundInfo( + notificationId, + notification, + ForegroundServiceType.DataSync + ) + } + private suspend fun updateForegroundInfo(notification: Notification) { val foregroundInfo = ForegroundServiceHelper.createWorkerForegroundInfo( notificationId, @@ -193,7 +214,7 @@ class FileUploadWorker( WorkerStateLiveData.instance().setWorkState(WorkerState.UploadFinished(currentUploadFileOperation?.file)) } - @Suppress("ReturnCount", "LongMethod") + @Suppress("ReturnCount", "LongMethod", "DEPRECATION") private suspend fun uploadFiles(): Result = withContext(Dispatchers.IO) { val accountName = inputData.getString(ACCOUNT) if (accountName == null) {