From 6bb356f01a72d9dbd810cdf65dc5ed2e51f5c59d Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Thu, 19 Mar 2026 14:05:37 +0100 Subject: [PATCH 1/3] fix(file-export): notification Signed-off-by: alperozturk96 --- .../nextcloud/client/jobs/FilesExportWork.kt | 123 ++++++------------ 1 file changed, 37 insertions(+), 86 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/jobs/FilesExportWork.kt b/app/src/main/java/com/nextcloud/client/jobs/FilesExportWork.kt index 812be5cb75c6..531adda7606f 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/FilesExportWork.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/FilesExportWork.kt @@ -1,6 +1,7 @@ /* * Nextcloud - Android Client * + * SPDX-FileCopyrightText: 2026 Alper Ozturk * SPDX-FileCopyrightText: 2022 Tobias Kaminsky * SPDX-FileCopyrightText: 2022 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only @@ -21,14 +22,12 @@ import com.nextcloud.client.account.User import com.nextcloud.client.jobs.download.FileDownloadHelper import com.owncloud.android.R import com.owncloud.android.datamodel.FileDataStorageManager -import com.owncloud.android.datamodel.OCFile import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.operations.DownloadType import com.owncloud.android.ui.notifications.NotificationUtils import com.owncloud.android.utils.FileExportUtils import com.owncloud.android.utils.FileStorageUtils import com.owncloud.android.utils.theme.ViewThemeUtils -import java.security.SecureRandom class FilesExportWork( private val appContext: Context, @@ -49,118 +48,70 @@ class FilesExportWork( } storageManager = FileDataStorageManager(user, contentResolver) + val (succeeded, failed) = exportFiles(fileIDs, storageManager) - val successfulExports = exportFiles(fileIDs) - - showSuccessNotification(successfulExports) + showSummaryNotification(succeeded, failed) return Result.success() } - private fun exportFiles(fileIDs: LongArray): Int { + private fun exportFiles(fileIDs: LongArray, storageManager: FileDataStorageManager): Pair { val fileDownloadHelper = FileDownloadHelper.instance() + val fileExportUtils = FileExportUtils() + var succeeded = 0 + var failed = 0 - var successfulExports = 0 fileIDs .asSequence() - .map { storageManager.getFileById(it) } - .filterNotNull() + .mapNotNull { storageManager.getFileById(it) } .forEach { ocFile -> - if (!FileStorageUtils.checkIfEnoughSpace(ocFile)) { - showErrorNotification(successfulExports) - return@forEach - } + val exported = when { + !FileStorageUtils.checkIfEnoughSpace(ocFile) -> false + + ocFile.isDown -> runCatching { + fileExportUtils.exportFile(ocFile.fileName, ocFile.mimeType, contentResolver, ocFile, null) + }.onFailure { Log_OC.e(TAG, "Error exporting file", it) }.isSuccess - if (ocFile.isDown) { - try { - exportFile(ocFile) - } catch (e: IllegalStateException) { - Log_OC.e(TAG, "Error exporting file", e) - showErrorNotification(successfulExports) + else -> { + fileDownloadHelper.downloadFile(user, ocFile, downloadType = DownloadType.EXPORT) + true } - } else { - fileDownloadHelper.downloadFile( - user, - ocFile, - downloadType = DownloadType.EXPORT - ) } - successfulExports++ + if (exported) succeeded++ else failed++ } - return successfulExports - } - @Throws(IllegalStateException::class) - private fun exportFile(ocFile: OCFile) { - FileExportUtils().exportFile( - ocFile.fileName, - ocFile.mimeType, - contentResolver, - ocFile, - null - ) + return succeeded to failed } - private fun showErrorNotification(successfulExports: Int) { - val message = if (successfulExports == 0) { - appContext.resources.getQuantityString(R.plurals.export_failed, successfulExports, successfulExports) - } else { - appContext.resources.getQuantityString( - R.plurals.export_partially_failed, - successfulExports, - successfulExports - ) + private fun showSummaryNotification(succeeded: Int, failed: Int) { + val resources = appContext.resources + val message = when { + failed == 0 -> resources.getQuantityString(R.plurals.export_successful, succeeded, succeeded) + succeeded == 0 -> resources.getQuantityString(R.plurals.export_failed, failed, failed) + else -> resources.getQuantityString(R.plurals.export_partially_failed, succeeded, succeeded) } - showNotification(message) - } - - private fun showSuccessNotification(successfulExports: Int) { - showNotification( - appContext.resources.getQuantityString( - R.plurals.export_successful, - successfulExports, - successfulExports - ) - ) - } - private fun showNotification(message: String) { - val notificationId = SecureRandom().nextInt() - - val notificationBuilder = NotificationCompat.Builder( + val pendingIntent = PendingIntent.getActivity( appContext, - NotificationUtils.NOTIFICATION_CHANNEL_DOWNLOAD + NOTIFICATION_ID, + Intent(DownloadManager.ACTION_VIEW_DOWNLOADS).apply { flags = FLAG_ACTIVITY_NEW_TASK }, + PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE ) + + val notification = NotificationCompat.Builder(appContext, NotificationUtils.NOTIFICATION_CHANNEL_DOWNLOAD) .setSmallIcon(R.drawable.notification_icon) .setContentTitle(message) .setAutoCancel(true) + .addAction(NotificationCompat.Action(null, appContext.getString(R.string.locate_folder), pendingIntent)) + .also { viewThemeUtils.androidx.themeNotificationCompatBuilder(appContext, it) } + .build() - viewThemeUtils.androidx.themeNotificationCompatBuilder(appContext, notificationBuilder) - - val actionIntent = Intent(DownloadManager.ACTION_VIEW_DOWNLOADS).apply { - flags = FLAG_ACTIVITY_NEW_TASK - } - val actionPendingIntent = PendingIntent.getActivity( - appContext, - notificationId, - actionIntent, - PendingIntent.FLAG_CANCEL_CURRENT or - PendingIntent.FLAG_IMMUTABLE - ) - notificationBuilder.addAction( - NotificationCompat.Action( - null, - appContext.getString(R.string.locate_folder), - actionPendingIntent - ) - ) - - val notificationManager = appContext - .getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - notificationManager.notify(notificationId, notificationBuilder.build()) + (appContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager) + .notify(NOTIFICATION_ID, notification) } companion object { + private const val NOTIFICATION_ID = 179 const val FILES_TO_DOWNLOAD = "files_to_download" private val TAG = FilesExportWork::class.simpleName } From f36ec9d58a6f4d488478e14b053dd3027f5a97e4 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Thu, 19 Mar 2026 14:47:10 +0100 Subject: [PATCH 2/3] fix(file-export): worker Signed-off-by: alperozturk96 --- .../client/jobs/BackgroundJobManagerImpl.kt | 12 +- .../nextcloud/client/jobs/FilesExportWork.kt | 118 +++++++++++------- .../client/jobs/worker/WorkerFilesPayload.kt | 68 ++++++++++ .../operations/DownloadFileOperation.java | 4 + 4 files changed, 156 insertions(+), 46 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/client/jobs/worker/WorkerFilesPayload.kt 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 b465446e3f82..eb15648e7a26 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt @@ -8,6 +8,7 @@ package com.nextcloud.client.jobs import android.provider.MediaStore import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData import androidx.lifecycle.map import androidx.work.BackoffPolicy import androidx.work.Constraints @@ -34,10 +35,12 @@ import com.nextcloud.client.jobs.metadata.MetadataWorker import com.nextcloud.client.jobs.offlineOperations.OfflineOperationsWorker import com.nextcloud.client.jobs.upload.FileUploadHelper import com.nextcloud.client.jobs.upload.FileUploadWorker +import com.nextcloud.client.jobs.worker.WorkerFilesPayload import com.nextcloud.client.preferences.AppPreferences import com.nextcloud.utils.extensions.isWorkScheduled import com.owncloud.android.datamodel.OCFile import com.owncloud.android.datamodel.SyncedFolder +import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.operations.DownloadType import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -71,6 +74,8 @@ internal class BackgroundJobManagerImpl( Injectable { companion object { + private const val TAG = "BackgroundJobManagerImpl" + const val TAG_ALL = "*" // This tag allows us to retrieve list of all jobs run by Nextcloud client const val JOB_CONTENT_OBSERVER = "content_observer" const val JOB_PERIODIC_CONTACTS_BACKUP = "periodic_contacts_backup" @@ -359,10 +364,13 @@ internal class BackgroundJobManagerImpl( } override fun startImmediateFilesExportJob(files: Collection): LiveData { - val ids = files.map { it.fileId }.toLongArray() + val path = WorkerFilesPayload.write(files.toList()) ?: run { + Log_OC.w(TAG, "File export was started without any file") + return MutableLiveData(null) + } val data = Data.Builder() - .putLongArray(FilesExportWork.FILES_TO_DOWNLOAD, ids) + .putString(FilesExportWork.FILES_TO_DOWNLOAD, path) .build() val request = oneTimeRequestBuilder(FilesExportWork::class, JOB_IMMEDIATE_FILES_EXPORT) diff --git a/app/src/main/java/com/nextcloud/client/jobs/FilesExportWork.kt b/app/src/main/java/com/nextcloud/client/jobs/FilesExportWork.kt index 531adda7606f..684d9778af99 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/FilesExportWork.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/FilesExportWork.kt @@ -16,75 +16,105 @@ import android.content.Context import android.content.Intent import android.content.Intent.FLAG_ACTIVITY_NEW_TASK import androidx.core.app.NotificationCompat -import androidx.work.Worker +import androidx.work.CoroutineWorker import androidx.work.WorkerParameters import com.nextcloud.client.account.User -import com.nextcloud.client.jobs.download.FileDownloadHelper +import com.nextcloud.client.jobs.worker.WorkerFilesPayload import com.owncloud.android.R import com.owncloud.android.datamodel.FileDataStorageManager +import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.OwnCloudClientManagerFactory import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.operations.DownloadFileOperation import com.owncloud.android.operations.DownloadType import com.owncloud.android.ui.notifications.NotificationUtils import com.owncloud.android.utils.FileExportUtils import com.owncloud.android.utils.FileStorageUtils import com.owncloud.android.utils.theme.ViewThemeUtils +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext class FilesExportWork( - private val appContext: Context, + private val context: Context, private val user: User, private val contentResolver: ContentResolver, private val viewThemeUtils: ViewThemeUtils, params: WorkerParameters -) : Worker(appContext, params) { - - private lateinit var storageManager: FileDataStorageManager - - override fun doWork(): Result { - val fileIDs = inputData.getLongArray(FILES_TO_DOWNLOAD) ?: LongArray(0) - - if (fileIDs.isEmpty()) { - Log_OC.w(this, "File export was started without any file") +) : CoroutineWorker(context, params) { + + override suspend fun doWork(): Result { + val path = inputData.getString(FILES_TO_DOWNLOAD) + val fileIds = WorkerFilesPayload.read(path) + if (fileIds.isEmpty()) { + Log_OC.w(TAG, "File export was started without any file") + WorkerFilesPayload.cleanup(path) return Result.success() } - storageManager = FileDataStorageManager(user, contentResolver) - val (succeeded, failed) = exportFiles(fileIDs, storageManager) + val storageManager = FileDataStorageManager(user, contentResolver) + + try { + val (succeeded, failed) = exportFiles(fileIds, storageManager) + showSummaryNotification(succeeded, failed) + } finally { + WorkerFilesPayload.cleanup(path) + } - showSummaryNotification(succeeded, failed) return Result.success() } - private fun exportFiles(fileIDs: LongArray, storageManager: FileDataStorageManager): Pair { - val fileDownloadHelper = FileDownloadHelper.instance() - val fileExportUtils = FileExportUtils() - var succeeded = 0 - var failed = 0 - - fileIDs - .asSequence() - .mapNotNull { storageManager.getFileById(it) } - .forEach { ocFile -> - val exported = when { - !FileStorageUtils.checkIfEnoughSpace(ocFile) -> false - - ocFile.isDown -> runCatching { - fileExportUtils.exportFile(ocFile.fileName, ocFile.mimeType, contentResolver, ocFile, null) - }.onFailure { Log_OC.e(TAG, "Error exporting file", it) }.isSuccess - - else -> { - fileDownloadHelper.downloadFile(user, ocFile, downloadType = DownloadType.EXPORT) - true + @Suppress("DEPRECATION") + private suspend fun exportFiles(fileIDs: List, storageManager: FileDataStorageManager): Pair = + withContext(Dispatchers.IO) { + val client = runCatching { + OwnCloudClientManagerFactory.getDefaultSingleton() + .getClientFor(user.toOwnCloudAccount(), context) + }.onFailure { + Log_OC.e(TAG, "Failed to create OwnCloudClient", it) + }.getOrNull() + + val fileExportUtils = FileExportUtils() + var succeeded = 0 + var failed = 0 + + fileIDs + .mapNotNull { storageManager.getFileById(it) } + .forEach { ocFile -> + val exported = when { + !FileStorageUtils.checkIfEnoughSpace(ocFile) -> false + + ocFile.isDown -> runCatching { + fileExportUtils.exportFile(ocFile.fileName, ocFile.mimeType, contentResolver, ocFile, null) + }.onFailure { Log_OC.e(TAG, "Error exporting file", it) }.isSuccess + + client != null -> downloadFile(ocFile, client) + + else -> { + Log_OC.e(TAG, "Skipping download, client unavailable: ${ocFile.remotePath}") + false + } } + + if (exported) succeeded++ else failed++ } - if (exported) succeeded++ else failed++ - } + return@withContext succeeded to failed + } - return succeeded to failed + @Suppress("DEPRECATION") + private suspend fun downloadFile(file: OCFile, client: OwnCloudClient): Boolean = withContext(Dispatchers.IO) { + val operation = DownloadFileOperation(user, file, context) + operation.downloadType = DownloadType.EXPORT + return@withContext runCatching { + operation.execute(client)?.isSuccess == true + }.onFailure { + Log_OC.e(TAG, "Exception downloading file: ${file.remotePath}", it) + }.getOrDefault(false) } private fun showSummaryNotification(succeeded: Int, failed: Int) { - val resources = appContext.resources + val resources = context.resources val message = when { failed == 0 -> resources.getQuantityString(R.plurals.export_successful, succeeded, succeeded) succeeded == 0 -> resources.getQuantityString(R.plurals.export_failed, failed, failed) @@ -92,21 +122,21 @@ class FilesExportWork( } val pendingIntent = PendingIntent.getActivity( - appContext, + context, NOTIFICATION_ID, Intent(DownloadManager.ACTION_VIEW_DOWNLOADS).apply { flags = FLAG_ACTIVITY_NEW_TASK }, PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE ) - val notification = NotificationCompat.Builder(appContext, NotificationUtils.NOTIFICATION_CHANNEL_DOWNLOAD) + val notification = NotificationCompat.Builder(context, NotificationUtils.NOTIFICATION_CHANNEL_DOWNLOAD) .setSmallIcon(R.drawable.notification_icon) .setContentTitle(message) .setAutoCancel(true) - .addAction(NotificationCompat.Action(null, appContext.getString(R.string.locate_folder), pendingIntent)) - .also { viewThemeUtils.androidx.themeNotificationCompatBuilder(appContext, it) } + .addAction(NotificationCompat.Action(null, context.getString(R.string.locate_folder), pendingIntent)) + .also { viewThemeUtils.androidx.themeNotificationCompatBuilder(context, it) } .build() - (appContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager) + (context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager) .notify(NOTIFICATION_ID, notification) } diff --git a/app/src/main/java/com/nextcloud/client/jobs/worker/WorkerFilesPayload.kt b/app/src/main/java/com/nextcloud/client/jobs/worker/WorkerFilesPayload.kt new file mode 100644 index 000000000000..1591f9964c3a --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/jobs/worker/WorkerFilesPayload.kt @@ -0,0 +1,68 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.client.jobs.worker + +import com.owncloud.android.MainApp +import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.utils.FileStorageUtils +import java.io.File + +@Suppress("ReturnCount") +object WorkerFilesPayload { + private const val TAG = "WorkerFilesPayload" + private const val FILE_PREFIX = "worker_files_payload_" + private const val FILE_SUFFIX = ".tmp" + private const val SEPARATOR = "," + + fun write(files: List): String? { + val context = MainApp.getAppContext() ?: return null + if (files.isEmpty()) return null + + val dir = File(FileStorageUtils.getAppTempDirectoryPath(context)).also { + if (!it.exists() && !it.mkdirs()) { + Log_OC.e(TAG, "Failed to create temp directory: ${it.absolutePath}") + return null + } + } + + val file = File(dir, "$FILE_PREFIX${System.currentTimeMillis()}$FILE_SUFFIX") + return runCatching { + file.writeText(files.joinToString(SEPARATOR) { it.fileId.toString() }) + file.absolutePath + }.onFailure { + Log_OC.e(TAG, "Failed to write payload file", it) + }.getOrNull() + } + + fun read(path: String?): List { + if (path.isNullOrBlank()) return listOf() + + val file = File(path) + if (!file.exists()) { + Log_OC.e(TAG, "Payload file not found: $path") + return listOf() + } + + val ids = runCatching { + file.readText() + .split(SEPARATOR) + .mapNotNull { it.toLongOrNull() } + }.onFailure { + Log_OC.e(TAG, "Failed to read payload file", it) + }.getOrNull() ?: return listOf() + + return ids + } + + fun cleanup(path: String?) { + if (path.isNullOrBlank()) return + val deleted = File(path).delete() + if (!deleted) Log_OC.w(TAG, "Failed to delete payload file: $path") + } +} diff --git a/app/src/main/java/com/owncloud/android/operations/DownloadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/DownloadFileOperation.java index d7b50d7d7176..ca0cc8be1b69 100644 --- a/app/src/main/java/com/owncloud/android/operations/DownloadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/DownloadFileOperation.java @@ -350,4 +350,8 @@ public String getPackageName() { public DownloadType getDownloadType() { return downloadType; } + + public void setDownloadType(DownloadType type) { + downloadType = type; + } } From 5ade9c1b873cfe08073d919af386fd72ded96145 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Fri, 20 Mar 2026 12:26:56 +0100 Subject: [PATCH 3/3] add progress notification Signed-off-by: alperozturk96 --- .../nextcloud/client/jobs/FilesExportWork.kt | 69 ++++++++++++------- app/src/main/res/values/strings.xml | 1 + 2 files changed, 46 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/jobs/FilesExportWork.kt b/app/src/main/java/com/nextcloud/client/jobs/FilesExportWork.kt index 684d9778af99..de69651c67e0 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/FilesExportWork.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/FilesExportWork.kt @@ -43,11 +43,20 @@ class FilesExportWork( params: WorkerParameters ) : CoroutineWorker(context, params) { + companion object { + private const val NOTIFICATION_ID = 179 + const val FILES_TO_DOWNLOAD = "files_to_download" + private val TAG = FilesExportWork::class.simpleName + } + + private val notificationManager = + context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + override suspend fun doWork(): Result { val path = inputData.getString(FILES_TO_DOWNLOAD) val fileIds = WorkerFilesPayload.read(path) if (fileIds.isEmpty()) { - Log_OC.w(TAG, "File export was started without any file") + Log_OC.w(TAG, "file export was started without any file") WorkerFilesPayload.cleanup(path) return Result.success() } @@ -56,6 +65,7 @@ class FilesExportWork( try { val (succeeded, failed) = exportFiles(fileIds, storageManager) + notificationManager.cancel(NOTIFICATION_ID) showSummaryNotification(succeeded, failed) } finally { WorkerFilesPayload.cleanup(path) @@ -75,30 +85,32 @@ class FilesExportWork( }.getOrNull() val fileExportUtils = FileExportUtils() + val files = fileIDs.mapNotNull { storageManager.getFileById(it) } + val total = files.size var succeeded = 0 var failed = 0 - fileIDs - .mapNotNull { storageManager.getFileById(it) } - .forEach { ocFile -> - val exported = when { - !FileStorageUtils.checkIfEnoughSpace(ocFile) -> false + files.forEachIndexed { index, ocFile -> + showProgressNotification(index, total, ocFile.fileName) - ocFile.isDown -> runCatching { - fileExportUtils.exportFile(ocFile.fileName, ocFile.mimeType, contentResolver, ocFile, null) - }.onFailure { Log_OC.e(TAG, "Error exporting file", it) }.isSuccess + val exported = when { + !FileStorageUtils.checkIfEnoughSpace(ocFile) -> false - client != null -> downloadFile(ocFile, client) + ocFile.isDown -> runCatching { + fileExportUtils.exportFile(ocFile.fileName, ocFile.mimeType, contentResolver, ocFile, null) + }.onFailure { Log_OC.e(TAG, "Error exporting file", it) }.isSuccess - else -> { - Log_OC.e(TAG, "Skipping download, client unavailable: ${ocFile.remotePath}") - false - } - } + client != null -> downloadFile(ocFile, client) - if (exported) succeeded++ else failed++ + else -> { + Log_OC.e(TAG, "Skipping download, client unavailable: ${ocFile.remotePath}") + false + } } + if (exported) succeeded++ else failed++ + } + return@withContext succeeded to failed } @@ -113,6 +125,22 @@ class FilesExportWork( }.getOrDefault(false) } + private fun showProgressNotification(current: Int, total: Int, fileName: String) { + val title = context.getString(R.string.export_in_progress, current + 1, total) + + val notification = NotificationCompat.Builder(context, NotificationUtils.NOTIFICATION_CHANNEL_DOWNLOAD) + .setSmallIcon(R.drawable.notification_icon) + .setContentTitle(title) + .setContentText(fileName) + .setProgress(total, current + 1, false) + .setOngoing(true) + .setOnlyAlertOnce(true) + .also { viewThemeUtils.androidx.themeNotificationCompatBuilder(context, it) } + .build() + + notificationManager.notify(NOTIFICATION_ID, notification) + } + private fun showSummaryNotification(succeeded: Int, failed: Int) { val resources = context.resources val message = when { @@ -136,13 +164,6 @@ class FilesExportWork( .also { viewThemeUtils.androidx.themeNotificationCompatBuilder(context, it) } .build() - (context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager) - .notify(NOTIFICATION_ID, notification) - } - - companion object { - private const val NOTIFICATION_ID = 179 - const val FILES_TO_DOWNLOAD = "files_to_download" - private val TAG = FilesExportWork::class.simpleName + notificationManager.notify(NOTIFICATION_ID, notification) } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 33fac1911ebd..bb49c4f71061 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -373,6 +373,7 @@ You can upload only %d file at once. You can upload up to %d files at once. + Exporting %1$d of %2$d As of version 1.3.16, files uploaded from this device are copied into the local %1$s folder to prevent data loss when a single file is synced with multiple accounts.\n\nDue to this change, all files uploaded with earlier versions of this app were copied into the %2$s folder. However, an error prevented the completion of this operation during account synchronization. You may either leave the file(s) as is and delete the link to %3$s, or move the file(s) into the %1$s folder and retain the link to %4$s.\n\nListed below are the local file(s), and the remote file(s) in %5$s they were linked to. The folder %1$s does not exist anymore Move all