diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 11fdfa1d941e..627b1e8a22ee 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -384,6 +384,12 @@ + diff --git a/app/src/main/java/com/nextcloud/client/jobs/FilesSyncWork.kt b/app/src/main/java/com/nextcloud/client/jobs/FilesSyncWork.kt index 8570240ba2fd..56982b5cd34e 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/FilesSyncWork.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/FilesSyncWork.kt @@ -25,9 +25,12 @@ package com.nextcloud.client.jobs import android.content.ContentResolver import android.content.Context import android.content.res.Resources +import android.os.Build import android.text.TextUtils +import androidx.core.app.NotificationCompat import androidx.exifinterface.media.ExifInterface -import androidx.work.Worker +import androidx.work.CoroutineWorker +import androidx.work.ForegroundInfo import androidx.work.WorkerParameters import com.nextcloud.client.account.UserAccountManager import com.nextcloud.client.device.PowerManagementService @@ -37,6 +40,7 @@ import com.owncloud.android.R import com.owncloud.android.datamodel.ArbitraryDataProvider import com.owncloud.android.datamodel.ArbitraryDataProviderImpl import com.owncloud.android.datamodel.FilesystemDataProvider +import com.owncloud.android.datamodel.ForegroundServiceType import com.owncloud.android.datamodel.MediaFolderType import com.owncloud.android.datamodel.SyncedFolder import com.owncloud.android.datamodel.SyncedFolderProvider @@ -45,6 +49,7 @@ import com.owncloud.android.files.services.FileUploader import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.operations.UploadFileOperation import com.owncloud.android.ui.activity.SettingsActivity +import com.owncloud.android.ui.notifications.NotificationUtils import com.owncloud.android.utils.FileStorageUtils import com.owncloud.android.utils.FilesSyncHelper import com.owncloud.android.utils.MimeType @@ -66,16 +71,38 @@ class FilesSyncWork( private val powerManagementService: PowerManagementService, private val syncedFolderProvider: SyncedFolderProvider, private val backgroundJobManager: BackgroundJobManager -) : Worker(context, params) { +) : CoroutineWorker(context, params) { companion object { const val TAG = "FilesSyncJob" const val SKIP_CUSTOM = "skipCustom" const val OVERRIDE_POWER_SAVING = "overridePowerSaving" + const val FOREGROUND_SERVICE_ID = 414 } - override fun doWork(): Result { + @Suppress("MagicNumber") + private fun createForegroundInfo(progressPercent: Int): ForegroundInfo { + // update throughout worker execution to give use feedback how far worker is + + val notification = NotificationCompat.Builder(context, NotificationUtils.NOTIFICATION_CHANNEL_FILE_SYNC) + .setTicker(context.getString(R.string.autoupload_worker_foreground_info)) + .setContentText(context.getString(R.string.autoupload_worker_foreground_info)) + .setSmallIcon(R.drawable.notification_icon) + .setContentTitle(context.getString(R.string.autoupload_worker_foreground_info)) + .setOngoing(true) + .setProgress(100, progressPercent, false) + .build() + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + ForegroundInfo(FOREGROUND_SERVICE_ID, notification, ForegroundServiceType.DataSync.getId()) + } else { + ForegroundInfo(FOREGROUND_SERVICE_ID, notification) + } + } + + @Suppress("MagicNumber") + override suspend fun doWork(): Result { backgroundJobManager.logStartOfWorker(BackgroundJobManagerImpl.formatClassTag(this::class)) + setForeground(createForegroundInfo(0)) val overridePowerSaving = inputData.getBoolean(OVERRIDE_POWER_SAVING, false) // If we are in power save mode, better to postpone upload @@ -93,13 +120,18 @@ class FilesSyncWork( connectivityService, powerManagementService ) + setForeground(createForegroundInfo(5)) FilesSyncHelper.insertAllDBEntries(skipCustom, syncedFolderProvider) + setForeground(createForegroundInfo(50)) // Create all the providers we'll need val filesystemDataProvider = FilesystemDataProvider(contentResolver) val currentLocale = resources.configuration.locale val dateFormat = SimpleDateFormat("yyyy:MM:dd HH:mm:ss", currentLocale) dateFormat.timeZone = TimeZone.getTimeZone(TimeZone.getDefault().id) - for (syncedFolder in syncedFolderProvider.syncedFolders) { + + val syncedFolders = syncedFolderProvider.syncedFolders + for ((index, syncedFolder) in syncedFolders.withIndex()) { + setForeground(createForegroundInfo((50 + (index.toDouble() / syncedFolders.size.toDouble()) * 50).toInt())) if (syncedFolder.isEnabled && (!skipCustom || MediaFolderType.CUSTOM != syncedFolder.type)) { syncFolder( context, diff --git a/app/src/main/java/com/owncloud/android/datamodel/FilesystemDataProvider.java b/app/src/main/java/com/owncloud/android/datamodel/FilesystemDataProvider.java index 9e91738bb962..92cc7b19c982 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/FilesystemDataProvider.java +++ b/app/src/main/java/com/owncloud/android/datamodel/FilesystemDataProvider.java @@ -55,10 +55,10 @@ public FilesystemDataProvider(ContentResolver contentResolver) { public int deleteAllEntriesForSyncedFolder(String syncedFolderId) { return contentResolver.delete( - ProviderMeta.ProviderTableMeta.CONTENT_URI_FILESYSTEM, - ProviderMeta.ProviderTableMeta.FILESYSTEM_SYNCED_FOLDER_ID + " = ?", - new String[]{syncedFolderId} - ); + ProviderMeta.ProviderTableMeta.CONTENT_URI_FILESYSTEM, + ProviderMeta.ProviderTableMeta.FILESYSTEM_SYNCED_FOLDER_ID + " = ?", + new String[]{syncedFolderId} + ); } public void updateFilesystemFileAsSentForUpload(String path, String syncedFolderId) { @@ -66,12 +66,12 @@ public void updateFilesystemFileAsSentForUpload(String path, String syncedFolder cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_SENT_FOR_UPLOAD, 1); contentResolver.update( - ProviderMeta.ProviderTableMeta.CONTENT_URI_FILESYSTEM, - cv, - ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH + " = ? and " + - ProviderMeta.ProviderTableMeta.FILESYSTEM_SYNCED_FOLDER_ID + " = ?", - new String[]{path, syncedFolderId} - ); + ProviderMeta.ProviderTableMeta.CONTENT_URI_FILESYSTEM, + cv, + ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH + " = ? and " + + ProviderMeta.ProviderTableMeta.FILESYSTEM_SYNCED_FOLDER_ID + " = ?", + new String[]{path, syncedFolderId} + ); } public Set getFilesForUpload(String localPath, String syncedFolderId) { @@ -80,20 +80,20 @@ public Set getFilesForUpload(String localPath, String syncedFolderId) { String likeParam = localPath + "%"; Cursor cursor = contentResolver.query( - ProviderMeta.ProviderTableMeta.CONTENT_URI_FILESYSTEM, - null, - ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH + " LIKE ? and " + - ProviderMeta.ProviderTableMeta.FILESYSTEM_SYNCED_FOLDER_ID + " = ? and " + - ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_SENT_FOR_UPLOAD + " = ? and " + - ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_IS_FOLDER + " = ?", - new String[]{likeParam, syncedFolderId, "0", "0"}, - null); + ProviderMeta.ProviderTableMeta.CONTENT_URI_FILESYSTEM, + null, + ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH + " LIKE ? and " + + ProviderMeta.ProviderTableMeta.FILESYSTEM_SYNCED_FOLDER_ID + " = ? and " + + ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_SENT_FOR_UPLOAD + " = ? and " + + ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_IS_FOLDER + " = ?", + new String[]{likeParam, syncedFolderId, "0", "0"}, + null); if (cursor != null) { if (cursor.moveToFirst()) { do { String value = cursor.getString(cursor.getColumnIndexOrThrow( - ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH)); + ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH)); if (value == null) { Log_OC.e(TAG, "Cannot get local path"); } else { @@ -159,11 +159,11 @@ public void storeOrUpdateFileValue(String localPath, long modifiedAt, boolean is int result = contentResolver.update( - ProviderMeta.ProviderTableMeta.CONTENT_URI_FILESYSTEM, - cv, - ProviderMeta.ProviderTableMeta._ID + "=?", - new String[]{String.valueOf(data.getId())} - ); + ProviderMeta.ProviderTableMeta.CONTENT_URI_FILESYSTEM, + cv, + ProviderMeta.ProviderTableMeta._ID + "=?", + new String[]{String.valueOf(data.getId())} + ); if (result == 0) { Log_OC.v(TAG, "Failed to update filesystem data with local path: " + localPath); @@ -174,33 +174,33 @@ public void storeOrUpdateFileValue(String localPath, long modifiedAt, boolean is private FileSystemDataSet getFilesystemDataSet(String localPathParam, SyncedFolder syncedFolder) { Cursor cursor = contentResolver.query( - ProviderMeta.ProviderTableMeta.CONTENT_URI_FILESYSTEM, - null, - ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH + " = ? and " + - ProviderMeta.ProviderTableMeta.FILESYSTEM_SYNCED_FOLDER_ID + " = ?", - new String[]{localPathParam, Long.toString(syncedFolder.getId())}, - null - ); + ProviderMeta.ProviderTableMeta.CONTENT_URI_FILESYSTEM, + null, + ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH + " = ? and " + + ProviderMeta.ProviderTableMeta.FILESYSTEM_SYNCED_FOLDER_ID + " = ?", + new String[]{localPathParam, Long.toString(syncedFolder.getId())}, + null + ); FileSystemDataSet dataSet = null; if (cursor != null) { if (cursor.moveToFirst()) { int id = cursor.getInt(cursor.getColumnIndexOrThrow(ProviderMeta.ProviderTableMeta._ID)); String localPath = cursor.getString(cursor.getColumnIndexOrThrow( - ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH)); + ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH)); long modifiedAt = cursor.getLong(cursor.getColumnIndexOrThrow( - ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_MODIFIED)); + ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_MODIFIED)); boolean isFolder = false; if (cursor.getInt(cursor.getColumnIndexOrThrow( - ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_IS_FOLDER)) != 0) { + ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_IS_FOLDER)) != 0) { isFolder = true; } long foundAt = cursor.getLong(cursor.getColumnIndexOrThrow(ProviderMeta. - ProviderTableMeta.FILESYSTEM_FILE_FOUND_RECENTLY)); + ProviderTableMeta.FILESYSTEM_FILE_FOUND_RECENTLY)); boolean isSentForUpload = false; if (cursor.getInt(cursor.getColumnIndexOrThrow( - ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_SENT_FOR_UPLOAD)) != 0) { + ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_SENT_FOR_UPLOAD)) != 0) { isSentForUpload = true; } @@ -210,7 +210,7 @@ private FileSystemDataSet getFilesystemDataSet(String localPathParam, SyncedFold Log_OC.e(TAG, "Arbitrary value could not be created from cursor"); } else { dataSet = new FileSystemDataSet(id, localPath, modifiedAt, isFolder, isSentForUpload, foundAt, - syncedFolder.getId(), crc32); + syncedFolder.getId(), crc32); } } cursor.close(); @@ -223,11 +223,12 @@ private FileSystemDataSet getFilesystemDataSet(String localPathParam, SyncedFold private long getFileChecksum(String filepath) { - try (InputStream inputStream = new BufferedInputStream(new FileInputStream(filepath))){ + try (InputStream inputStream = new BufferedInputStream(new FileInputStream(filepath))) { CRC32 crc = new CRC32(); - int cnt; - while ((cnt = inputStream.read()) != -1) { - crc.update(cnt); + byte[] buf = new byte[1024 * 64]; + int size; + while ((size = inputStream.read(buf)) > 0) { + crc.update(buf, 0, size); } return crc.getValue(); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7fc2bcfbbc2e..26a79a3a08c8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -628,6 +628,7 @@ Hide folder Configure Configure folders + Preparing auto upload Test server connection