From 79e651874a9469a404fb39ef1f731f9b3192f745 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Fri, 5 Apr 2024 09:58:19 +0200 Subject: [PATCH 01/74] Delete temp encryptedFile in all cases Signed-off-by: alperozturk --- .../com/owncloud/android/util/EncryptionTestIT.java | 2 +- .../android/operations/UploadFileOperation.java | 13 +++++++------ .../com/owncloud/android/utils/EncryptionUtils.java | 3 +-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/src/androidTest/java/com/owncloud/android/util/EncryptionTestIT.java b/app/src/androidTest/java/com/owncloud/android/util/EncryptionTestIT.java index d1100a8b97..b077e8b9c0 100644 --- a/app/src/androidTest/java/com/owncloud/android/util/EncryptionTestIT.java +++ b/app/src/androidTest/java/com/owncloud/android/util/EncryptionTestIT.java @@ -836,7 +836,7 @@ public class EncryptionTestIT extends AbstractIT { // Encryption Cipher encryptorCipher = EncryptionUtils.getCipher(Cipher.ENCRYPT_MODE, key, iv); - EncryptionUtils.encryptFile(file, encryptorCipher); + EncryptionUtils.encryptFile(targetContext, file, encryptorCipher); String encryptorCipherAuthTag = EncryptionUtils.getAuthenticationTag(encryptorCipher); // Decryption diff --git a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index 5c2e793001..e2f8e03ac1 100644 --- a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -442,6 +442,7 @@ public class UploadFileOperation extends SyncOperation { File temporalFile = null; File originalFile = new File(mOriginalStoragePath); File expectedFile = null; + File encryptedTempFile = null; FileLock fileLock = null; long size; @@ -552,7 +553,7 @@ public class UploadFileOperation extends SyncOperation { byte[] iv = EncryptionUtils.randomBytes(EncryptionUtils.ivLength); Cipher cipher = EncryptionUtils.getCipher(Cipher.ENCRYPT_MODE, key, iv); File file = new File(mFile.getStoragePath()); - EncryptedFile encryptedFile = EncryptionUtils.encryptFile(file, cipher); + EncryptedFile encryptedFile = EncryptionUtils.encryptFile(getContext(), file, cipher); // new random file name, check if it exists in metadata String encryptedFileName = EncryptionUtils.generateUid(); @@ -567,9 +568,7 @@ public class UploadFileOperation extends SyncOperation { } } - File encryptedTempFile = encryptedFile.getEncryptedFile(); - - /***** E2E *****/ + encryptedTempFile = encryptedFile.getEncryptedFile(); FileChannel channel = null; try { @@ -712,8 +711,6 @@ public class UploadFileOperation extends SyncOperation { user, getStorageManager()); } - - encryptedTempFile.delete(); } } catch (FileNotFoundException e) { Log_OC.d(TAG, mFile.getStoragePath() + " not exists anymore"); @@ -755,6 +752,10 @@ public class UploadFileOperation extends SyncOperation { if (unlockFolderResult != null && !unlockFolderResult.isSuccess()) { result = unlockFolderResult; } + + boolean isTempEncryptedFileDeleted = encryptedTempFile.delete(); + Log_OC.e(TAG, "isTempEncryptedFileDeleted: " + isTempEncryptedFileDeleted); + } if (result.isSuccess()) { diff --git a/app/src/main/java/com/owncloud/android/utils/EncryptionUtils.java b/app/src/main/java/com/owncloud/android/utils/EncryptionUtils.java index aebe37816a..a91b038fd8 100644 --- a/app/src/main/java/com/owncloud/android/utils/EncryptionUtils.java +++ b/app/src/main/java/com/owncloud/android/utils/EncryptionUtils.java @@ -547,8 +547,7 @@ public final class EncryptionUtils { return Base64.decode(string, Base64.NO_WRAP); } - public static EncryptedFile encryptFile(File file, Cipher cipher) throws InvalidParameterSpecException { - // FIXME this won't work on low or write-protected storage + public static EncryptedFile encryptFile(Context context, File file, Cipher cipher) throws InvalidParameterSpecException { File encryptedFile = new File(file.getAbsolutePath() + ".enc.jpg"); encryptFileWithGivenCipher(file, encryptedFile, cipher); String authenticationTagString = getAuthenticationTag(cipher); From c6c646063027ca04fe770e9e3c55772a19b1948e Mon Sep 17 00:00:00 2001 From: alperozturk Date: Fri, 5 Apr 2024 10:13:59 +0200 Subject: [PATCH 02/74] Use temp directory for tempEncryptedFile creation Signed-off-by: alperozturk --- .../java/com/owncloud/android/utils/EncryptionUtils.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/utils/EncryptionUtils.java b/app/src/main/java/com/owncloud/android/utils/EncryptionUtils.java index a91b038fd8..f49991524d 100644 --- a/app/src/main/java/com/owncloud/android/utils/EncryptionUtils.java +++ b/app/src/main/java/com/owncloud/android/utils/EncryptionUtils.java @@ -547,11 +547,11 @@ public final class EncryptionUtils { return Base64.decode(string, Base64.NO_WRAP); } - public static EncryptedFile encryptFile(Context context, File file, Cipher cipher) throws InvalidParameterSpecException { - File encryptedFile = new File(file.getAbsolutePath() + ".enc.jpg"); - encryptFileWithGivenCipher(file, encryptedFile, cipher); + public static EncryptedFile encryptFile(Context context, File file, Cipher cipher) throws InvalidParameterSpecException, IOException { + File tempEncryptedFile = File.createTempFile(file.getName(), "", context.getCacheDir()); + encryptFileWithGivenCipher(file, tempEncryptedFile, cipher); String authenticationTagString = getAuthenticationTag(cipher); - return new EncryptedFile(encryptedFile, authenticationTagString); + return new EncryptedFile(tempEncryptedFile, authenticationTagString); } public static String getAuthenticationTag(Cipher cipher) throws InvalidParameterSpecException { From 6515602ee61b0a8062470a4b8a2a8b517464a3c4 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Fri, 5 Apr 2024 10:39:45 +0200 Subject: [PATCH 03/74] Fix file name in notification Signed-off-by: alperozturk --- .../client/jobs/upload/FileUploadWorker.kt | 41 ++++++++++--------- .../jobs/upload/UploadNotificationManager.kt | 12 +++--- .../operations/UploadFileOperation.java | 6 ++- .../android/utils/EncryptionUtils.java | 2 +- 4 files changed, 32 insertions(+), 29 deletions(-) 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 e0c50de8c3..5ea59f0660 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 @@ -165,16 +165,16 @@ class FileUploadWorker( } if (user.isPresent) { - val operation = createUploadFileOperation(upload, user.get()) + val uploadFileOperation = createUploadFileOperation(upload, user.get()) - currentUploadFileOperation = operation - val result = upload(operation, user.get()) + currentUploadFileOperation = uploadFileOperation + val result = upload(uploadFileOperation, user.get()) currentUploadFileOperation = null fileUploaderDelegate.sendBroadcastUploadFinished( - operation, + uploadFileOperation, result, - operation.oldFile?.storagePath, + uploadFileOperation.oldFile?.storagePath, context, localBroadcastManager ) @@ -205,39 +205,39 @@ class FileUploadWorker( } @Suppress("TooGenericExceptionCaught", "DEPRECATION") - private fun upload(operation: UploadFileOperation, user: User): RemoteOperationResult { + private fun upload(uploadFileOperation: UploadFileOperation, user: User): RemoteOperationResult { lateinit var result: RemoteOperationResult notificationManager.prepareForStart( - operation, - intents.startIntent(operation), - intents.notificationStartIntent(operation) + uploadFileOperation, + intents.startIntent(uploadFileOperation), + intents.notificationStartIntent(uploadFileOperation) ) try { - val storageManager = operation.storageManager + val storageManager = uploadFileOperation.storageManager val ocAccount = OwnCloudAccount(user.toPlatformAccount(), context) val uploadClient = OwnCloudClientManagerFactory.getDefaultSingleton().getClientFor(ocAccount, context) - result = operation.execute(uploadClient) + result = uploadFileOperation.execute(uploadClient) val task = ThumbnailsCacheManager.ThumbnailGenerationTask(storageManager, user) - val file = File(operation.originalStoragePath) - val remoteId: String? = operation.file.remoteId + val file = File(uploadFileOperation.originalStoragePath) + val remoteId: String? = uploadFileOperation.file.remoteId task.execute(ThumbnailsCacheManager.ThumbnailGenerationTaskObject(file, remoteId)) } catch (e: Exception) { Log_OC.e(TAG, "Error uploading", e) result = RemoteOperationResult(e) } finally { - cleanupUploadProcess(result, operation) + cleanupUploadProcess(result, uploadFileOperation) } return result } - private fun cleanupUploadProcess(result: RemoteOperationResult, operation: UploadFileOperation) { + private fun cleanupUploadProcess(result: RemoteOperationResult, uploadFileOperation: UploadFileOperation) { if (!isStopped || !result.isCancelled) { - uploadsStorageManager.updateDatabaseUploadResult(result, operation) - notifyUploadResult(operation, result) + uploadsStorageManager.updateDatabaseUploadResult(result, uploadFileOperation) + notifyUploadResult(uploadFileOperation, result) notificationManager.dismissWorkerNotifications() } } @@ -315,10 +315,11 @@ class FileUploadWorker( if (percent != lastPercent) { notificationManager.run { - updateUploadProgress(fileAbsoluteName, percent, currentUploadFileOperation) - val accountName = currentUploadFileOperation?.user?.accountName val remotePath = currentUploadFileOperation?.remotePath + val filename = currentUploadFileOperation?.fileName ?: "" + + updateUploadProgress(filename, percent, currentUploadFileOperation) if (accountName != null && remotePath != null) { val key: String = @@ -329,7 +330,7 @@ class FileUploadWorker( progressRate, totalTransferredSoFar, totalToTransfer, - fileAbsoluteName + filename ) } diff --git a/app/src/main/java/com/nextcloud/client/jobs/upload/UploadNotificationManager.kt b/app/src/main/java/com/nextcloud/client/jobs/upload/UploadNotificationManager.kt index 23392fa65a..a4c3f28fee 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/upload/UploadNotificationManager.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/upload/UploadNotificationManager.kt @@ -16,7 +16,6 @@ import android.os.Build import androidx.core.app.NotificationCompat import com.owncloud.android.R import com.owncloud.android.lib.common.operations.RemoteOperationResult -import com.owncloud.android.lib.resources.files.FileUtils import com.owncloud.android.operations.UploadFileOperation import com.owncloud.android.ui.notifications.NotificationUtils import com.owncloud.android.utils.theme.ViewThemeUtils @@ -44,14 +43,14 @@ class UploadNotificationManager(private val context: Context, viewThemeUtils: Vi } @Suppress("MagicNumber") - fun prepareForStart(upload: UploadFileOperation, pendingIntent: PendingIntent, startIntent: PendingIntent) { + fun prepareForStart(uploadFileOperation: UploadFileOperation, pendingIntent: PendingIntent, startIntent: PendingIntent) { notificationBuilder.run { setContentTitle(context.getString(R.string.uploader_upload_in_progress_ticker)) setContentText( String.format( context.getString(R.string.uploader_upload_in_progress), 0, - upload.fileName + uploadFileOperation.fileName ) ) setTicker(context.getString(R.string.foreground_service_upload)) @@ -68,7 +67,7 @@ class UploadNotificationManager(private val context: Context, viewThemeUtils: Vi setContentIntent(startIntent) } - if (!upload.isInstantPicture && !upload.isInstantVideo) { + if (!uploadFileOperation.isInstantPicture && !uploadFileOperation.isInstantVideo) { showNotification() } } @@ -138,11 +137,10 @@ class UploadNotificationManager(private val context: Context, viewThemeUtils: Vi } @Suppress("MagicNumber") - fun updateUploadProgress(filePath: String, percent: Int, currentOperation: UploadFileOperation?) { + fun updateUploadProgress(filename: String, percent: Int, currentOperation: UploadFileOperation?) { notificationBuilder.run { setProgress(100, percent, false) - val fileName = filePath.substring(filePath.lastIndexOf(FileUtils.PATH_SEPARATOR) + 1) - val text = String.format(context.getString(R.string.uploader_upload_in_progress), percent, fileName) + val text = String.format(context.getString(R.string.uploader_upload_in_progress), percent, filename) setContentText(text) showNotification() diff --git a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index e2f8e03ac1..61a2f2e4fa 100644 --- a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -272,7 +272,11 @@ public class UploadFileOperation extends SyncOperation { } public String getFileName() { - return (mFile != null) ? mFile.getFileName() : null; + if (mFile == null) { + return null; + } + + return mFile.getFileName(); } public OCFile getFile() { diff --git a/app/src/main/java/com/owncloud/android/utils/EncryptionUtils.java b/app/src/main/java/com/owncloud/android/utils/EncryptionUtils.java index f49991524d..dfe8fdfa1a 100644 --- a/app/src/main/java/com/owncloud/android/utils/EncryptionUtils.java +++ b/app/src/main/java/com/owncloud/android/utils/EncryptionUtils.java @@ -548,7 +548,7 @@ public final class EncryptionUtils { } public static EncryptedFile encryptFile(Context context, File file, Cipher cipher) throws InvalidParameterSpecException, IOException { - File tempEncryptedFile = File.createTempFile(file.getName(), "", context.getCacheDir()); + File tempEncryptedFile = File.createTempFile(file.getName(), ".", context.getCacheDir()); encryptFileWithGivenCipher(file, tempEncryptedFile, cipher); String authenticationTagString = getAuthenticationTag(cipher); return new EncryptedFile(tempEncryptedFile, authenticationTagString); From 0e47ad737f6f82c4965c08dca964efb938467b41 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Fri, 5 Apr 2024 10:48:42 +0200 Subject: [PATCH 04/74] Use single line code for getFileName Signed-off-by: alperozturk --- .../owncloud/android/operations/UploadFileOperation.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index 61a2f2e4fa..e2f8e03ac1 100644 --- a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -272,11 +272,7 @@ public class UploadFileOperation extends SyncOperation { } public String getFileName() { - if (mFile == null) { - return null; - } - - return mFile.getFileName(); + return (mFile != null) ? mFile.getFileName() : null; } public OCFile getFile() { From 17f7676abbc782d79c2cb279e5b6bdf31e7f2976 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 8 Apr 2024 16:32:10 +0200 Subject: [PATCH 05/74] Fix code analytics Signed-off-by: alperozturk --- .../client/jobs/upload/UploadNotificationManager.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/nextcloud/client/jobs/upload/UploadNotificationManager.kt b/app/src/main/java/com/nextcloud/client/jobs/upload/UploadNotificationManager.kt index a4c3f28fee..a6d74ce7e1 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/upload/UploadNotificationManager.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/upload/UploadNotificationManager.kt @@ -43,7 +43,11 @@ class UploadNotificationManager(private val context: Context, viewThemeUtils: Vi } @Suppress("MagicNumber") - fun prepareForStart(uploadFileOperation: UploadFileOperation, pendingIntent: PendingIntent, startIntent: PendingIntent) { + fun prepareForStart( + uploadFileOperation: UploadFileOperation, + pendingIntent: PendingIntent, + startIntent: PendingIntent + ) { notificationBuilder.run { setContentTitle(context.getString(R.string.uploader_upload_in_progress_ticker)) setContentText( From 9c5d39f30118452e1fba622df3818be89d38aa7a Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 9 Apr 2024 09:31:27 +0200 Subject: [PATCH 06/74] Fix code analytics Signed-off-by: alperozturk --- .../owncloud/android/operations/UploadFileOperation.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index e2f8e03ac1..1e805c244e 100644 --- a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -753,9 +753,12 @@ public class UploadFileOperation extends SyncOperation { result = unlockFolderResult; } - boolean isTempEncryptedFileDeleted = encryptedTempFile.delete(); - Log_OC.e(TAG, "isTempEncryptedFileDeleted: " + isTempEncryptedFileDeleted); - + if (encryptedTempFile != null) { + boolean isTempEncryptedFileDeleted = encryptedTempFile.delete(); + Log_OC.e(TAG, "isTempEncryptedFileDeleted: " + isTempEncryptedFileDeleted); + } else { + Log_OC.e(TAG, "Encrypted temp file cannot be found"); + } } if (result.isSuccess()) { From 14d3257036c8c22c6872912a597f923eb791bb92 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Thu, 11 Apr 2024 11:49:09 +0200 Subject: [PATCH 07/74] Remove "." suffix Signed-off-by: alperozturk --- .../main/java/com/owncloud/android/utils/EncryptionUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/owncloud/android/utils/EncryptionUtils.java b/app/src/main/java/com/owncloud/android/utils/EncryptionUtils.java index dfe8fdfa1a..47817eb71d 100644 --- a/app/src/main/java/com/owncloud/android/utils/EncryptionUtils.java +++ b/app/src/main/java/com/owncloud/android/utils/EncryptionUtils.java @@ -548,7 +548,7 @@ public final class EncryptionUtils { } public static EncryptedFile encryptFile(Context context, File file, Cipher cipher) throws InvalidParameterSpecException, IOException { - File tempEncryptedFile = File.createTempFile(file.getName(), ".", context.getCacheDir()); + File tempEncryptedFile = File.createTempFile(file.getName(), null, context.getCacheDir()); encryptFileWithGivenCipher(file, tempEncryptedFile, cipher); String authenticationTagString = getAuthenticationTag(cipher); return new EncryptedFile(tempEncryptedFile, authenticationTagString); From a563c87b0116418eada2ab4f3c008d92d1746cbc Mon Sep 17 00:00:00 2001 From: alperozturk Date: Thu, 11 Apr 2024 11:52:46 +0200 Subject: [PATCH 08/74] Delete temporal file usage for e2e Signed-off-by: alperozturk --- .../operations/UploadFileOperation.java | 29 +++++++------------ 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index 1e805c244e..1629b5f784 100644 --- a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -439,7 +439,6 @@ public class UploadFileOperation extends SyncOperation { @SuppressLint("AndroidLintUseSparseArrays") // gson cannot handle sparse arrays easily, therefore use hashmap private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile parentFile) { RemoteOperationResult result = null; - File temporalFile = null; File originalFile = new File(mOriginalStoragePath); File expectedFile = null; File encryptedTempFile = null; @@ -580,14 +579,14 @@ public class UploadFileOperation extends SyncOperation { String temporalPath = FileStorageUtils.getInternalTemporalPath(user.getAccountName(), mContext) + mFile.getRemotePath(); mFile.setStoragePath(temporalPath); - temporalFile = new File(temporalPath); + encryptedTempFile = new File(temporalPath); Files.deleteIfExists(Paths.get(temporalPath)); - result = copy(originalFile, temporalFile); + result = copy(originalFile, encryptedTempFile); if (result.isSuccess()) { - if (temporalFile.length() == originalFile.length()) { - channel = new RandomAccessFile(temporalFile.getAbsolutePath(), "rw").getChannel(); + if (encryptedTempFile.length() == originalFile.length()) { + channel = new RandomAccessFile(encryptedTempFile.getAbsolutePath(), "rw").getChannel(); fileLock = channel.tryLock(); } else { result = new RemoteOperationResult(ResultCode.LOCK_FAILED); @@ -732,9 +731,6 @@ public class UploadFileOperation extends SyncOperation { } } - if (temporalFile != null && !originalFile.equals(temporalFile)) { - temporalFile.delete(); - } if (result == null) { result = new RemoteOperationResult(ResultCode.UNKNOWN_ERROR); } @@ -753,6 +749,12 @@ public class UploadFileOperation extends SyncOperation { result = unlockFolderResult; } + if (result.isSuccess()) { + handleSuccessfulUpload(encryptedTempFile, expectedFile, originalFile, client); + } else if (result.getCode() == ResultCode.SYNC_CONFLICT) { + getStorageManager().saveConflict(mFile, mFile.getEtagInConflict()); + } + if (encryptedTempFile != null) { boolean isTempEncryptedFileDeleted = encryptedTempFile.delete(); Log_OC.e(TAG, "isTempEncryptedFileDeleted: " + isTempEncryptedFileDeleted); @@ -761,17 +763,6 @@ public class UploadFileOperation extends SyncOperation { } } - if (result.isSuccess()) { - handleSuccessfulUpload(temporalFile, expectedFile, originalFile, client); - } else if (result.getCode() == ResultCode.SYNC_CONFLICT) { - getStorageManager().saveConflict(mFile, mFile.getEtagInConflict()); - } - - // delete temporal file - if (temporalFile != null && temporalFile.exists() && !temporalFile.delete()) { - Log_OC.e(TAG, "Could not delete temporal file " + temporalFile.getAbsolutePath()); - } - return result; } From d583a2f62c598ca96582b08e2ad0d4805ad370d9 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Thu, 11 Apr 2024 12:37:12 +0200 Subject: [PATCH 09/74] Revert changes Signed-off-by: alperozturk --- .../operations/UploadFileOperation.java | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index 1629b5f784..1e805c244e 100644 --- a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -439,6 +439,7 @@ public class UploadFileOperation extends SyncOperation { @SuppressLint("AndroidLintUseSparseArrays") // gson cannot handle sparse arrays easily, therefore use hashmap private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile parentFile) { RemoteOperationResult result = null; + File temporalFile = null; File originalFile = new File(mOriginalStoragePath); File expectedFile = null; File encryptedTempFile = null; @@ -579,14 +580,14 @@ public class UploadFileOperation extends SyncOperation { String temporalPath = FileStorageUtils.getInternalTemporalPath(user.getAccountName(), mContext) + mFile.getRemotePath(); mFile.setStoragePath(temporalPath); - encryptedTempFile = new File(temporalPath); + temporalFile = new File(temporalPath); Files.deleteIfExists(Paths.get(temporalPath)); - result = copy(originalFile, encryptedTempFile); + result = copy(originalFile, temporalFile); if (result.isSuccess()) { - if (encryptedTempFile.length() == originalFile.length()) { - channel = new RandomAccessFile(encryptedTempFile.getAbsolutePath(), "rw").getChannel(); + if (temporalFile.length() == originalFile.length()) { + channel = new RandomAccessFile(temporalFile.getAbsolutePath(), "rw").getChannel(); fileLock = channel.tryLock(); } else { result = new RemoteOperationResult(ResultCode.LOCK_FAILED); @@ -731,6 +732,9 @@ public class UploadFileOperation extends SyncOperation { } } + if (temporalFile != null && !originalFile.equals(temporalFile)) { + temporalFile.delete(); + } if (result == null) { result = new RemoteOperationResult(ResultCode.UNKNOWN_ERROR); } @@ -749,12 +753,6 @@ public class UploadFileOperation extends SyncOperation { result = unlockFolderResult; } - if (result.isSuccess()) { - handleSuccessfulUpload(encryptedTempFile, expectedFile, originalFile, client); - } else if (result.getCode() == ResultCode.SYNC_CONFLICT) { - getStorageManager().saveConflict(mFile, mFile.getEtagInConflict()); - } - if (encryptedTempFile != null) { boolean isTempEncryptedFileDeleted = encryptedTempFile.delete(); Log_OC.e(TAG, "isTempEncryptedFileDeleted: " + isTempEncryptedFileDeleted); @@ -763,6 +761,17 @@ public class UploadFileOperation extends SyncOperation { } } + if (result.isSuccess()) { + handleSuccessfulUpload(temporalFile, expectedFile, originalFile, client); + } else if (result.getCode() == ResultCode.SYNC_CONFLICT) { + getStorageManager().saveConflict(mFile, mFile.getEtagInConflict()); + } + + // delete temporal file + if (temporalFile != null && temporalFile.exists() && !temporalFile.delete()) { + Log_OC.e(TAG, "Could not delete temporal file " + temporalFile.getAbsolutePath()); + } + return result; } From 123c576399d1e1782e3a621416bbc5e8485dd088 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Thu, 11 Apr 2024 16:38:22 +0200 Subject: [PATCH 10/74] Dont use cache dir. cache dir might be deleted during big file uploads Signed-off-by: alperozturk --- .../main/java/com/owncloud/android/utils/EncryptionUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/owncloud/android/utils/EncryptionUtils.java b/app/src/main/java/com/owncloud/android/utils/EncryptionUtils.java index 47817eb71d..acb9da6c72 100644 --- a/app/src/main/java/com/owncloud/android/utils/EncryptionUtils.java +++ b/app/src/main/java/com/owncloud/android/utils/EncryptionUtils.java @@ -548,7 +548,7 @@ public final class EncryptionUtils { } public static EncryptedFile encryptFile(Context context, File file, Cipher cipher) throws InvalidParameterSpecException, IOException { - File tempEncryptedFile = File.createTempFile(file.getName(), null, context.getCacheDir()); + File tempEncryptedFile = File.createTempFile(file.getName(), null, context.getFilesDir()); encryptFileWithGivenCipher(file, tempEncryptedFile, cipher); String authenticationTagString = getAuthenticationTag(cipher); return new EncryptedFile(tempEncryptedFile, authenticationTagString); From 39e9916732ad4e32d9dc84bc0de1e5c5c4826ca0 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Thu, 11 Apr 2024 16:43:13 +0200 Subject: [PATCH 11/74] Use tempEncryptedFolder Signed-off-by: alperozturk --- .../owncloud/android/utils/EncryptionUtils.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/utils/EncryptionUtils.java b/app/src/main/java/com/owncloud/android/utils/EncryptionUtils.java index acb9da6c72..875309fe77 100644 --- a/app/src/main/java/com/owncloud/android/utils/EncryptionUtils.java +++ b/app/src/main/java/com/owncloud/android/utils/EncryptionUtils.java @@ -548,7 +548,17 @@ public final class EncryptionUtils { } public static EncryptedFile encryptFile(Context context, File file, Cipher cipher) throws InvalidParameterSpecException, IOException { - File tempEncryptedFile = File.createTempFile(file.getName(), null, context.getFilesDir()); + String dirPath = context.getFilesDir().getAbsolutePath() + File.separator + "temp_encrypted_folder"; + File tempEncryptedFolder = new File(dirPath); + + if (!tempEncryptedFolder.exists()) { + boolean isTempEncryptedFolderCreated = tempEncryptedFolder.mkdirs(); + Log_OC.d(TAG, "tempEncryptedFolder created" + isTempEncryptedFolderCreated); + } else { + Log_OC.d(TAG, "tempEncryptedFolder already exists"); + } + + File tempEncryptedFile = File.createTempFile(file.getName(), null, tempEncryptedFolder); encryptFileWithGivenCipher(file, tempEncryptedFile, cipher); String authenticationTagString = getAuthenticationTag(cipher); return new EncryptedFile(tempEncryptedFile, authenticationTagString); @@ -568,7 +578,7 @@ public final class EncryptionUtils { } public static void encryptFileWithGivenCipher(File inputFile, File encryptedFile, Cipher cipher) { - try( FileInputStream inputStream = new FileInputStream(inputFile); + try (FileInputStream inputStream = new FileInputStream(inputFile); FileOutputStream fileOutputStream = new FileOutputStream(encryptedFile); CipherOutputStream outputStream = new CipherOutputStream(fileOutputStream, cipher)) { byte[] buffer = new byte[4096]; From 7470fef2d6065fea4518ceaf7151b6ddf47baefd Mon Sep 17 00:00:00 2001 From: alperozturk Date: Thu, 11 Apr 2024 16:55:35 +0200 Subject: [PATCH 12/74] Clear temp directory for cancelling all uploads Signed-off-by: alperozturk --- .../datamodel/FileDataStorageManager.java | 40 +++++++++++++++++++ .../android/ui/adapter/UploadListAdapter.java | 4 ++ .../android/utils/EncryptionUtils.java | 11 +---- 3 files changed, 45 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java b/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java index 6245958506..9a86353ff0 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java +++ b/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java @@ -56,7 +56,10 @@ import com.owncloud.android.utils.FileStorageUtils; import com.owncloud.android.utils.MimeType; import com.owncloud.android.utils.MimeTypeUtil; +import org.apache.commons.io.FileUtils; + import java.io.File; +import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -338,6 +341,43 @@ public class FileDataStorageManager { return ocFile; } + private static final String tempEncryptedFolderPath = "temp_encrypted_folder"; + + public static void clearTempEncryptedFolder(Context context) { + File tempEncryptedFolder = getTempEncryptedFolder(context); + + if (!tempEncryptedFolder.exists()) { + Log_OC.d(TAG,"tempEncryptedFolder not exists"); + return; + } + + try { + FileUtils.cleanDirectory(tempEncryptedFolder); + + Log_OC.d(TAG,"tempEncryptedFolder cleared"); + } catch (IOException exception) { + Log_OC.d(TAG,"Error caught at clearTempEncryptedFolder: " + exception); + } + } + + public static File getTempEncryptedFolder(Context context) { + String dirPath = context.getFilesDir().getAbsolutePath() + File.separator + tempEncryptedFolderPath; + return new File(dirPath); + } + + public static File createTempEncryptedFolder(Context context) { + File tempEncryptedFolder = getTempEncryptedFolder(context); + + if (!tempEncryptedFolder.exists()) { + boolean isTempEncryptedFolderCreated = tempEncryptedFolder.mkdirs(); + Log_OC.d(TAG, "tempEncryptedFolder created" + isTempEncryptedFolderCreated); + } else { + Log_OC.d(TAG, "tempEncryptedFolder already exists"); + } + + return tempEncryptedFolder; + } + public void saveNewFile(OCFile newFile) { String remoteParentPath = new File(newFile.getRemotePath()).getParent(); remoteParentPath = remoteParentPath.endsWith(OCFile.PATH_SEPARATOR) ? diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java b/app/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java index 2a747669bd..3d5a5ad610 100755 --- a/app/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java @@ -54,6 +54,8 @@ import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.MimeTypeUtil; import com.owncloud.android.utils.theme.ViewThemeUtils; +import org.apache.commons.io.FileUtils; + import java.io.File; import java.util.Arrays; import java.util.Optional; @@ -169,6 +171,8 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter Date: Fri, 12 Apr 2024 09:03:27 +0200 Subject: [PATCH 13/74] Use more clear argument names Signed-off-by: alperozturk --- .../java/com/nextcloud/client/jobs/upload/FileUploadWorker.kt | 2 +- .../nextcloud/client/jobs/upload/UploadNotificationManager.kt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) 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 5ea59f0660..80cb01ee4e 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 @@ -210,7 +210,7 @@ class FileUploadWorker( notificationManager.prepareForStart( uploadFileOperation, - intents.startIntent(uploadFileOperation), + cancelPendingIntent = intents.startIntent(uploadFileOperation), intents.notificationStartIntent(uploadFileOperation) ) diff --git a/app/src/main/java/com/nextcloud/client/jobs/upload/UploadNotificationManager.kt b/app/src/main/java/com/nextcloud/client/jobs/upload/UploadNotificationManager.kt index a6d74ce7e1..4c2a9c6656 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/upload/UploadNotificationManager.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/upload/UploadNotificationManager.kt @@ -45,7 +45,7 @@ class UploadNotificationManager(private val context: Context, viewThemeUtils: Vi @Suppress("MagicNumber") fun prepareForStart( uploadFileOperation: UploadFileOperation, - pendingIntent: PendingIntent, + cancelPendingIntent: PendingIntent, startIntent: PendingIntent ) { notificationBuilder.run { @@ -65,7 +65,7 @@ class UploadNotificationManager(private val context: Context, viewThemeUtils: Vi addAction( R.drawable.ic_action_cancel_grey, context.getString(R.string.common_cancel), - pendingIntent + cancelPendingIntent ) setContentIntent(startIntent) From 24b2d033f18eb59aa7e7ba029dc60a2d4f8d58f6 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Fri, 12 Apr 2024 09:28:43 +0200 Subject: [PATCH 14/74] Clear temp encrypted folder for clear failed uploads actions Signed-off-by: alperozturk --- .../java/com/owncloud/android/ui/adapter/UploadListAdapter.java | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java b/app/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java index 3d5a5ad610..378d1995bb 100755 --- a/app/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java @@ -143,6 +143,7 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter Date: Fri, 12 Apr 2024 09:29:57 +0200 Subject: [PATCH 15/74] Specifically make visible retry failed uploads action Signed-off-by: alperozturk --- .../com/owncloud/android/ui/adapter/UploadListAdapter.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java b/app/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java index 378d1995bb..3436e6e96d 100755 --- a/app/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java @@ -145,8 +145,7 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter { FileUploadHelper.Companion.instance().retryFailedUploads( uploadsStorageManager, @@ -155,8 +154,8 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter Date: Fri, 12 Apr 2024 09:31:26 +0200 Subject: [PATCH 16/74] Add fixme comments Signed-off-by: alperozturk --- .../com/owncloud/android/ui/adapter/UploadListAdapter.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java b/app/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java index 3436e6e96d..ab27688bd5 100755 --- a/app/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java @@ -146,6 +146,8 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter { FileUploadHelper.Companion.instance().retryFailedUploads( uploadsStorageManager, @@ -158,6 +160,7 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter { boolean showNotExistMessage = FileUploadHelper.Companion.instance().retryCancelledUploads( From 7342b54f42b53cb106a7f50d390375fd47cb3792 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Fri, 12 Apr 2024 09:53:47 +0200 Subject: [PATCH 17/74] Add log for failed uploads Signed-off-by: alperozturk --- .../java/com/nextcloud/client/jobs/upload/FileUploadHelper.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadHelper.kt b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadHelper.kt index 1b5f1c8667..2e231b2def 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadHelper.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadHelper.kt @@ -75,8 +75,10 @@ class FileUploadHelper { ) { val failedUploads = uploadsStorageManager.failedUploads if (failedUploads == null || failedUploads.isEmpty()) { + Log_OC.d(TAG, "Failed uploads are empty or null") return } + retryUploads( uploadsStorageManager, connectivityService, @@ -120,6 +122,7 @@ class FileUploadHelper { val charging = batteryStatus.isCharging || batteryStatus.isFull val isPowerSaving = powerManagementService.isPowerSavingEnabled var uploadUser = Optional.empty() + for (failedUpload in failedUploads) { // 1. extract failed upload owner account and cache it between loops (expensive query) if (!uploadUser.isPresent || !uploadUser.get().nameEquals(failedUpload.accountName)) { From 80e581bcd38db7f1d714f3ba23c2940076860436 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Fri, 12 Apr 2024 12:23:19 +0200 Subject: [PATCH 18/74] Use path for each user Signed-off-by: alperozturk --- .../owncloud/android/util/EncryptionTestIT.java | 2 +- .../android/datamodel/FileDataStorageManager.java | 15 ++++----------- .../android/operations/UploadFileOperation.java | 2 +- .../android/ui/adapter/UploadListAdapter.java | 12 +++++++----- .../owncloud/android/utils/EncryptionUtils.java | 4 ++-- .../owncloud/android/utils/FileStorageUtils.java | 11 +++++++++++ 6 files changed, 26 insertions(+), 20 deletions(-) diff --git a/app/src/androidTest/java/com/owncloud/android/util/EncryptionTestIT.java b/app/src/androidTest/java/com/owncloud/android/util/EncryptionTestIT.java index b077e8b9c0..8c81d28e5c 100644 --- a/app/src/androidTest/java/com/owncloud/android/util/EncryptionTestIT.java +++ b/app/src/androidTest/java/com/owncloud/android/util/EncryptionTestIT.java @@ -836,7 +836,7 @@ public class EncryptionTestIT extends AbstractIT { // Encryption Cipher encryptorCipher = EncryptionUtils.getCipher(Cipher.ENCRYPT_MODE, key, iv); - EncryptionUtils.encryptFile(targetContext, file, encryptorCipher); + EncryptionUtils.encryptFile(user.getAccountName(), file, encryptorCipher); String encryptorCipherAuthTag = EncryptionUtils.getAuthenticationTag(encryptorCipher); // Decryption diff --git a/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java b/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java index 9a86353ff0..260524248c 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java +++ b/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java @@ -341,10 +341,8 @@ public class FileDataStorageManager { return ocFile; } - private static final String tempEncryptedFolderPath = "temp_encrypted_folder"; - - public static void clearTempEncryptedFolder(Context context) { - File tempEncryptedFolder = getTempEncryptedFolder(context); + public static void clearTempEncryptedFolder(String accountName) { + File tempEncryptedFolder = new File(FileStorageUtils.getTemporalEncryptedFolderPath(accountName)); if (!tempEncryptedFolder.exists()) { Log_OC.d(TAG,"tempEncryptedFolder not exists"); @@ -360,13 +358,8 @@ public class FileDataStorageManager { } } - public static File getTempEncryptedFolder(Context context) { - String dirPath = context.getFilesDir().getAbsolutePath() + File.separator + tempEncryptedFolderPath; - return new File(dirPath); - } - - public static File createTempEncryptedFolder(Context context) { - File tempEncryptedFolder = getTempEncryptedFolder(context); + public static File createTempEncryptedFolder(String accountName) { + File tempEncryptedFolder = new File(FileStorageUtils.getTemporalEncryptedFolderPath(accountName)); if (!tempEncryptedFolder.exists()) { boolean isTempEncryptedFolderCreated = tempEncryptedFolder.mkdirs(); diff --git a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index 1e805c244e..18de79ad31 100644 --- a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -553,7 +553,7 @@ public class UploadFileOperation extends SyncOperation { byte[] iv = EncryptionUtils.randomBytes(EncryptionUtils.ivLength); Cipher cipher = EncryptionUtils.getCipher(Cipher.ENCRYPT_MODE, key, iv); File file = new File(mFile.getStoragePath()); - EncryptedFile encryptedFile = EncryptionUtils.encryptFile(getContext(), file, cipher); + EncryptedFile encryptedFile = EncryptionUtils.encryptFile(user.getAccountName(), file, cipher); // new random file name, check if it exists in metadata String encryptedFileName = EncryptionUtils.generateUid(); diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java b/app/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java index ab27688bd5..2ee4e4e205 100755 --- a/app/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java @@ -54,8 +54,6 @@ import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.MimeTypeUtil; import com.owncloud.android.utils.theme.ViewThemeUtils; -import org.apache.commons.io.FileUtils; - import java.io.File; import java.util.Arrays; import java.util.Optional; @@ -143,7 +141,7 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter user = parentActivity.getUser(); + user.ifPresent(value -> FileDataStorageManager.clearTempEncryptedFolder(value.getAccountName())); + } + // FIXME For e2e resume is not working private void retryCancelledUploads() { new Thread(() -> { diff --git a/app/src/main/java/com/owncloud/android/utils/EncryptionUtils.java b/app/src/main/java/com/owncloud/android/utils/EncryptionUtils.java index 0a10af4f80..1ca499e077 100644 --- a/app/src/main/java/com/owncloud/android/utils/EncryptionUtils.java +++ b/app/src/main/java/com/owncloud/android/utils/EncryptionUtils.java @@ -547,8 +547,8 @@ public final class EncryptionUtils { return Base64.decode(string, Base64.NO_WRAP); } - public static EncryptedFile encryptFile(Context context, File file, Cipher cipher) throws InvalidParameterSpecException, IOException { - File tempEncryptedFolder = FileDataStorageManager.createTempEncryptedFolder(context); + public static EncryptedFile encryptFile(String accountName, File file, Cipher cipher) throws InvalidParameterSpecException, IOException { + File tempEncryptedFolder = FileDataStorageManager.createTempEncryptedFolder(accountName); File tempEncryptedFile = File.createTempFile(file.getName(), null, tempEncryptedFolder); encryptFileWithGivenCipher(file, tempEncryptedFile, cipher); String authenticationTagString = getAuthenticationTag(cipher); diff --git a/app/src/main/java/com/owncloud/android/utils/FileStorageUtils.java b/app/src/main/java/com/owncloud/android/utils/FileStorageUtils.java index 878784b8ed..630a1e3def 100644 --- a/app/src/main/java/com/owncloud/android/utils/FileStorageUtils.java +++ b/app/src/main/java/com/owncloud/android/utils/FileStorageUtils.java @@ -106,6 +106,17 @@ public final class FileStorageUtils { // that can be in the accountName since 0.1.190B } + public static String getTemporalEncryptedFolderPath(String accountName) { + return MainApp + .getAppContext() + .getFilesDir() + .getAbsolutePath() + + File.separator + + accountName + + File.separator + + "temp_encrypted_folder"; + } + /** * Get absolute path to tmp folder inside app folder for given accountName. */ From 3bcd2b3703ce6a4a9c2577904f4c44bacd14c660 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 9 Apr 2024 14:25:57 +0200 Subject: [PATCH 19/74] Dont use null path Signed-off-by: alperozturk --- app/src/main/java/com/owncloud/android/datamodel/OCFile.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/java/com/owncloud/android/datamodel/OCFile.java b/app/src/main/java/com/owncloud/android/datamodel/OCFile.java index 48108dc91f..d294f9c0e7 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/OCFile.java +++ b/app/src/main/java/com/owncloud/android/datamodel/OCFile.java @@ -265,6 +265,11 @@ public class OCFile implements Parcelable, Comparable, ServerFileInterfa } if (isFolder()) { + if (decryptedRemotePath.equals("/null/")) { + Log_OC.d(TAG, "Null folder path found"); + return remotePath; + } + if (decryptedRemotePath.endsWith(PATH_SEPARATOR)) { return decryptedRemotePath; } else { From d9d9cf56b27db0f1e912513490ad5337deddcc5c Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 10 Apr 2024 09:37:16 +0200 Subject: [PATCH 20/74] Check existence of decryptedRemotePath Signed-off-by: alperozturk --- .../operations/RefreshFolderOperation.java | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java b/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java index 8223c22a9f..691f09fb55 100644 --- a/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java @@ -553,6 +553,7 @@ public class RefreshFolderOperation extends RemoteOperation { updatedFiles.add(updatedFile); } + // save updated contents in local database // update file name for encrypted files if (e2EVersion == E2EVersion.V1_2) { @@ -636,20 +637,35 @@ public class RefreshFolderOperation extends RemoteOperation { mimetype = MimeType.DIRECTORY; } else { DecryptedFile decryptedFile = metadata.getMetadata().getFiles().get(updatedFile.getFileName()); + + if (decryptedFile == null) { + throw new NullPointerException("decryptedFile cannot be null"); + } + decryptedFileName = decryptedFile.getFilename(); mimetype = decryptedFile.getMimetype(); } OCFile parentFile = storageManager.getFileById(updatedFile.getParentId()); - String decryptedRemotePath = parentFile.getDecryptedRemotePath() + decryptedFileName; + + if (parentFile == null) { + throw new NullPointerException("parentFile cannot be null"); + } + + String decryptedRemotePath; + if (decryptedFileName != null) { + decryptedRemotePath = parentFile.getDecryptedRemotePath() + decryptedFileName; + } else { + decryptedRemotePath = parentFile.getRemotePath() + updatedFile.getFileName(); + } if (updatedFile.isFolder()) { decryptedRemotePath += "/"; } updatedFile.setDecryptedRemotePath(decryptedRemotePath); - if (mimetype == null || mimetype.isEmpty()) { + if (mimetype.isEmpty()) { if (updatedFile.isFolder()) { updatedFile.setMimeType(MimeType.DIRECTORY); } else { From 82c1c25a2f9afc2aab023a022d7c0e8c2a816dbc Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 10 Apr 2024 11:59:55 +0200 Subject: [PATCH 21/74] Use encrypted remote path for file conflict resolver dialog Signed-off-by: alperozturk --- .../datamodel/FileDataStorageManager.java | 42 +++++++++++++++++++ .../operations/RefreshFolderOperation.java | 1 + .../ui/activity/ConflictsResolveActivity.kt | 13 +++++- .../ui/activity/FileDisplayActivity.java | 21 ++++++---- 4 files changed, 68 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java b/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java index 6245958506..175c2c2f7c 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java +++ b/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java @@ -66,11 +66,14 @@ import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import kotlin.Pair; public class FileDataStorageManager { private static final String TAG = FileDataStorageManager.class.getSimpleName(); @@ -2297,6 +2300,45 @@ public class FileDataStorageManager { } } + public String getFolderName(String path) { + return "/" + path.split("/")[1] + "/"; + } + + public String getEncryptedRemotePath(String decryptedRemotePath) { + String folderName = getFolderName(decryptedRemotePath); + + if (folderName == null) { + throw new NullPointerException("folderName cannot be null"); + } + + OCFile folder = getFileByDecryptedRemotePath(folderName); + List files = getAllFilesRecursivelyInsideFolder(folder); + List> decryptedFileNamesAndEncryptedRemotePaths = getDecryptedFileNamesAndEncryptedRemotePaths(files); + + String decryptedFileName = decryptedRemotePath.substring( decryptedRemotePath.lastIndexOf('/') + 1); + + for (Pair item : decryptedFileNamesAndEncryptedRemotePaths) { + if (item.getFirst().equals(decryptedFileName)) { + return item.getSecond(); + } + } + + return null; + } + + private List> getDecryptedFileNamesAndEncryptedRemotePaths(List fileList) { + List> result = new ArrayList<>(); + + for (OCFile file : fileList) { + if (file.isEncrypted()) { + Pair fileNameAndEncryptedRemotePath = new Pair<>(file.getDecryptedFileName(), file.getRemotePath()); + result.add(fileNameAndEncryptedRemotePath); + } + } + + return result; + } + public void removeLocalFiles(User user, FileDataStorageManager storageManager) { File tempDir = new File(FileStorageUtils.getTemporalPath(user.getAccountName())); File saveDir = new File(FileStorageUtils.getSavePath(user.getAccountName())); diff --git a/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java b/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java index 691f09fb55..f5778685d8 100644 --- a/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java @@ -625,6 +625,7 @@ public class RefreshFolderOperation extends RemoteOperation { } } + // TODO write test for decryptedRemotePath existence... public static void updateFileNameForEncryptedFile(FileDataStorageManager storageManager, @NonNull DecryptedFolderMetadataFile metadata, OCFile updatedFile) { diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.kt index 31a85c9eeb..3409519384 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.kt @@ -21,6 +21,7 @@ import com.nextcloud.client.jobs.upload.UploadNotificationManager import com.nextcloud.model.HTTPStatusCodes import com.nextcloud.utils.extensions.getParcelableArgument import com.owncloud.android.R +import com.owncloud.android.datamodel.FileDataStorageManager import com.owncloud.android.datamodel.OCFile import com.owncloud.android.datamodel.UploadsStorageManager import com.owncloud.android.db.OCUpload @@ -42,6 +43,10 @@ class ConflictsResolveActivity : FileActivity(), OnConflictDecisionMadeListener @Inject var uploadsStorageManager: UploadsStorageManager? = null + @JvmField + @Inject + var fileStorageManager: FileDataStorageManager? = null + private var conflictUploadId: Long = 0 private var existingFile: OCFile? = null private var newFile: OCFile? = null @@ -159,8 +164,12 @@ class ConflictsResolveActivity : FileActivity(), OnConflictDecisionMadeListener return } if (existingFile == null) { - // fetch info of existing file from server - val operation = ReadFileRemoteOperation(newFile!!.remotePath) + var remotePath = newFile!!.remotePath + if (newFile?.isEncrypted == true) { + remotePath = fileStorageManager?.getEncryptedRemotePath(newFile!!.remotePath) + } + + val operation = ReadFileRemoteOperation(remotePath) @Suppress("TooGenericExceptionCaught") Thread { diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java index 94a4ed270d..2c0da6cb52 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -54,7 +54,6 @@ import com.nextcloud.client.jobs.download.FileDownloadHelper; import com.nextcloud.client.jobs.download.FileDownloadWorker; import com.nextcloud.client.jobs.upload.FileUploadHelper; import com.nextcloud.client.jobs.upload.FileUploadWorker; -import com.nextcloud.client.jobs.upload.UploadNotificationManager; import com.nextcloud.client.media.PlayerServiceConnection; import com.nextcloud.client.network.ClientFactory; import com.nextcloud.client.network.ConnectivityService; @@ -135,6 +134,7 @@ import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.Objects; import java.util.Optional; import javax.inject.Inject; @@ -149,6 +149,7 @@ import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import kotlin.Pair; import kotlin.Unit; import static com.owncloud.android.datamodel.OCFile.PATH_SEPARATOR; @@ -878,18 +879,24 @@ public class FileDisplayActivity extends FileActivity requestUploadOfFilesFromFileSystem(basePath, filePaths, resultCode); } + private String[] getRemotePaths(String directory, String[] filePaths, String localBasePath) { + String[] remotePaths = new String[filePaths.length]; + for (int j = 0; j < remotePaths.length; j++) { + String relativePath = StringUtils.removePrefix(filePaths[j], localBasePath); + remotePaths[j] = directory + relativePath; + } + + return remotePaths; + } + private void requestUploadOfFilesFromFileSystem(String localBasePath, String[] filePaths, int resultCode) { if (localBasePath != null && filePaths != null) { if (!localBasePath.endsWith("/")) { localBasePath = localBasePath + "/"; } - String[] remotePaths = new String[filePaths.length]; String remotePathBase = getCurrentDir().getRemotePath(); - for (int j = 0; j < remotePaths.length; j++) { - String relativePath = StringUtils.removePrefix(filePaths[j], localBasePath); - remotePaths[j] = remotePathBase + relativePath; - } + String[] decryptedRemotePaths = getRemotePaths(remotePathBase, filePaths, localBasePath); int behaviour = switch (resultCode) { case UploadFilesActivity.RESULT_OK_AND_MOVE -> FileUploadWorker.LOCAL_BEHAVIOUR_MOVE; @@ -899,7 +906,7 @@ public class FileDisplayActivity extends FileActivity FileUploadHelper.Companion.instance().uploadNewFiles(getUser().orElseThrow(RuntimeException::new), filePaths, - remotePaths, + decryptedRemotePaths, behaviour, true, UploadFileOperation.CREATED_BY_USER, From eaa773d5a994b7d3f3d460adecb116e49fb00879 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 10 Apr 2024 12:05:33 +0200 Subject: [PATCH 22/74] Use better message Signed-off-by: alperozturk --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a2777a201e..1fb9802c13 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -994,7 +994,7 @@ Thumbnail for new file Thumbnail for existing file Invalid URL - Error creating conflict dialog! + Conflict resolver dialog cannot able to created QR code could not be read! Note icon Add another link From affc77715cb1b8dcdce13e450661e88e1cd40e57 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 10 Apr 2024 12:05:43 +0200 Subject: [PATCH 23/74] Use remote path correctly Signed-off-by: alperozturk --- .../android/ui/activity/ConflictsResolveActivity.kt | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.kt index 3409519384..10315d624c 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.kt @@ -178,7 +178,7 @@ class ConflictsResolveActivity : FileActivity(), OnConflictDecisionMadeListener if (result.isSuccess) { existingFile = FileStorageUtils.fillOCFile(result.data[0] as RemoteFile) existingFile?.lastSyncDateForProperties = System.currentTimeMillis() - startDialog() + startDialog(remotePath) } else { Log_OC.e(TAG, "ReadFileRemoteOp returned failure with code: " + result.httpCode) showErrorAndFinish(result.httpCode) @@ -189,11 +189,16 @@ class ConflictsResolveActivity : FileActivity(), OnConflictDecisionMadeListener } }.start() } else { - startDialog() + var remotePath = existingFile!!.remotePath + if (newFile?.isEncrypted == true) { + remotePath = fileStorageManager?.getEncryptedRemotePath(existingFile!!.remotePath) + } + + startDialog(remotePath) } } - private fun startDialog() { + private fun startDialog(remotePath: String) { val userOptional = user if (!userOptional.isPresent) { Log_OC.e(TAG, "User not present") @@ -206,7 +211,7 @@ class ConflictsResolveActivity : FileActivity(), OnConflictDecisionMadeListener if (prev != null) { fragmentTransaction.remove(prev) } - if (existingFile != null && storageManager.fileExists(newFile?.remotePath)) { + if (existingFile != null && storageManager.fileExists(remotePath)) { val dialog = ConflictsResolveDialog.newInstance( existingFile, newFile, From 7c15334dc0bf697270924c4ab00401a8a0b60e59 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 10 Apr 2024 12:07:56 +0200 Subject: [PATCH 24/74] Remove unused imports and logic Signed-off-by: alperozturk --- .../owncloud/android/datamodel/FileDataStorageManager.java | 2 -- app/src/main/java/com/owncloud/android/datamodel/OCFile.java | 5 ----- .../owncloud/android/ui/activity/FileDisplayActivity.java | 2 -- 3 files changed, 9 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java b/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java index 175c2c2f7c..6026f30d6a 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java +++ b/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java @@ -66,8 +66,6 @@ import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import androidx.annotation.NonNull; import androidx.annotation.Nullable; diff --git a/app/src/main/java/com/owncloud/android/datamodel/OCFile.java b/app/src/main/java/com/owncloud/android/datamodel/OCFile.java index d294f9c0e7..48108dc91f 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/OCFile.java +++ b/app/src/main/java/com/owncloud/android/datamodel/OCFile.java @@ -265,11 +265,6 @@ public class OCFile implements Parcelable, Comparable, ServerFileInterfa } if (isFolder()) { - if (decryptedRemotePath.equals("/null/")) { - Log_OC.d(TAG, "Null folder path found"); - return remotePath; - } - if (decryptedRemotePath.endsWith(PATH_SEPARATOR)) { return decryptedRemotePath; } else { diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java index 2c0da6cb52..4e7496dc62 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -134,7 +134,6 @@ import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.List; -import java.util.Objects; import java.util.Optional; import javax.inject.Inject; @@ -149,7 +148,6 @@ import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import kotlin.Pair; import kotlin.Unit; import static com.owncloud.android.datamodel.OCFile.PATH_SEPARATOR; From 7d9a8f1d9fd56d822e83c23f8efaa0110206ff8b Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 10 Apr 2024 12:25:21 +0200 Subject: [PATCH 25/74] Add correct decryptedRemotePath for v1 Signed-off-by: alperozturk --- .../operations/RefreshFolderOperation.java | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java b/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java index f5778685d8..f71e52cf43 100644 --- a/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java @@ -585,6 +585,7 @@ public class RefreshFolderOperation extends RemoteOperation { return metadata; } + // TODO write test for decryptedRemotePath existence... public static void updateFileNameForEncryptedFileV1(FileDataStorageManager storageManager, @NonNull DecryptedFolderMetadataFileV1 metadata, OCFile updatedFile) { @@ -598,13 +599,28 @@ public class RefreshFolderOperation extends RemoteOperation { } else { com.owncloud.android.datamodel.e2e.v1.decrypted.DecryptedFile decryptedFile = metadata.getFiles().get(updatedFile.getFileName()); + + if (decryptedFile == null) { + throw new NullPointerException("decryptedFile cannot be null"); + } + decryptedFileName = decryptedFile.getEncrypted().getFilename(); mimetype = decryptedFile.getEncrypted().getMimetype(); } OCFile parentFile = storageManager.getFileById(updatedFile.getParentId()); - String decryptedRemotePath = parentFile.getDecryptedRemotePath() + decryptedFileName; + + if (parentFile == null) { + throw new NullPointerException("parentFile cannot be null"); + } + + String decryptedRemotePath; + if (decryptedFileName != null) { + decryptedRemotePath = parentFile.getDecryptedRemotePath() + decryptedFileName; + } else { + decryptedRemotePath = parentFile.getRemotePath() + updatedFile.getFileName(); + } if (updatedFile.isFolder()) { decryptedRemotePath += "/"; From 9fd1ef520508a69ddb04e11fcc5f4cf97e692589 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 10 Apr 2024 13:04:12 +0200 Subject: [PATCH 26/74] Add testUpdateFileNameForEncryptedFileWhenEncryptedFileUploadRemotePathShouldSetAsEncrypted Signed-off-by: alperozturk --- .../android/utils/EncryptionUtilsV2IT.kt | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/app/src/androidTest/java/com/owncloud/android/utils/EncryptionUtilsV2IT.kt b/app/src/androidTest/java/com/owncloud/android/utils/EncryptionUtilsV2IT.kt index b52460e30e..06a81e31cf 100644 --- a/app/src/androidTest/java/com/owncloud/android/utils/EncryptionUtilsV2IT.kt +++ b/app/src/androidTest/java/com/owncloud/android/utils/EncryptionUtilsV2IT.kt @@ -21,11 +21,15 @@ import com.owncloud.android.datamodel.e2e.v2.decrypted.DecryptedUser import com.owncloud.android.datamodel.e2e.v2.encrypted.EncryptedFiledrop import com.owncloud.android.datamodel.e2e.v2.encrypted.EncryptedFiledropUser import com.owncloud.android.datamodel.e2e.v2.encrypted.EncryptedFolderMetadataFile +import com.owncloud.android.lib.resources.status.CapabilityBooleanType +import com.owncloud.android.lib.resources.status.OCCapability +import com.owncloud.android.operations.RefreshFolderOperation import com.owncloud.android.util.EncryptionTestIT import junit.framework.TestCase.assertEquals import junit.framework.TestCase.assertTrue import org.junit.Assert.assertNotEquals import org.junit.Test +import java.security.SecureRandom class EncryptionUtilsV2IT : AbstractIT() { private val encryptionTestUtils = EncryptionTestUtils() @@ -781,6 +785,44 @@ class EncryptionUtilsV2IT : AbstractIT() { assertTrue(encryptionUtilsV2.verifySignedMessage(base64Ans, jsonBase64, certs)) } + @Test + @Throws(Exception::class) + fun testUpdateFileNameForEncryptedFileWhenEncryptedFileUploadRemotePathShouldSetAsEncrypted() { + OCFile("/").apply { + storageManager.saveFile(this) + } + + val folder = OCFile("/TestFolder/").apply { + decryptedRemotePath = "/TestFolder/" + isEncrypted = true + fileLength = SecureRandom().nextLong() + setFolder() + parentId = storageManager.getFileByDecryptedRemotePath("/")!!.fileId + storageManager.saveFile(this) + } + + val imageFile = OCFile("/TestFolder/test_pic.png").apply { + mimeType = "image/png" + fileName = "test_pic.png" + isEncrypted = true + fileLength = 1024000 + modificationTimestamp = 1188206955000 + parentId = storageManager.getFileByEncryptedRemotePath("/TestFolder/").fileId + storageManager.saveFile(this) + } + + val remotePathBeforeUpdate = imageFile.remotePath + + val metadata = EncryptionTestUtils().generateFolderMetadataV2( + client.userId, + EncryptionTestIT.publicKey + ) + + RefreshFolderOperation.updateFileNameForEncryptedFile(storageManager, metadata, folder) + + assertNotEquals(remotePathBeforeUpdate, imageFile.remotePath) + } + /** * DecryptedFolderMetadata -> EncryptedFolderMetadata -> JSON -> encrypt -> decrypt -> JSON -> * EncryptedFolderMetadata -> DecryptedFolderMetadata From 182673672fe3bc5338190a5e820c304a38a4f398 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 10 Apr 2024 15:58:54 +0200 Subject: [PATCH 27/74] Add test Signed-off-by: alperozturk --- .../android/utils/EncryptionUtilsV2IT.kt | 36 ++--- .../e2e/v2/decrypted/DecryptedMetadata.kt | 2 +- .../operations/RefreshFolderOperation.java | 135 ++++++++++-------- 3 files changed, 93 insertions(+), 80 deletions(-) diff --git a/app/src/androidTest/java/com/owncloud/android/utils/EncryptionUtilsV2IT.kt b/app/src/androidTest/java/com/owncloud/android/utils/EncryptionUtilsV2IT.kt index 06a81e31cf..96cf6f04d3 100644 --- a/app/src/androidTest/java/com/owncloud/android/utils/EncryptionUtilsV2IT.kt +++ b/app/src/androidTest/java/com/owncloud/android/utils/EncryptionUtilsV2IT.kt @@ -21,8 +21,6 @@ import com.owncloud.android.datamodel.e2e.v2.decrypted.DecryptedUser import com.owncloud.android.datamodel.e2e.v2.encrypted.EncryptedFiledrop import com.owncloud.android.datamodel.e2e.v2.encrypted.EncryptedFiledropUser import com.owncloud.android.datamodel.e2e.v2.encrypted.EncryptedFolderMetadataFile -import com.owncloud.android.lib.resources.status.CapabilityBooleanType -import com.owncloud.android.lib.resources.status.OCCapability import com.owncloud.android.operations.RefreshFolderOperation import com.owncloud.android.util.EncryptionTestIT import junit.framework.TestCase.assertEquals @@ -788,39 +786,43 @@ class EncryptionUtilsV2IT : AbstractIT() { @Test @Throws(Exception::class) fun testUpdateFileNameForEncryptedFileWhenEncryptedFileUploadRemotePathShouldSetAsEncrypted() { - OCFile("/").apply { + val rootPath = "/" + val folderPath = "/TestFolder/" + + OCFile(rootPath).apply { storageManager.saveFile(this) } - val folder = OCFile("/TestFolder/").apply { - decryptedRemotePath = "/TestFolder/" + OCFile(folderPath).apply { + decryptedRemotePath = folderPath isEncrypted = true fileLength = SecureRandom().nextLong() setFolder() - parentId = storageManager.getFileByDecryptedRemotePath("/")!!.fileId + parentId = storageManager.getFileByDecryptedRemotePath(rootPath)!!.fileId storageManager.saveFile(this) } - val imageFile = OCFile("/TestFolder/test_pic.png").apply { + val decryptedFilename = "image.png" + val mockEncryptedFilename = "encrypted_file_name.png" + + val imageFile = OCFile(folderPath + decryptedFilename).apply { mimeType = "image/png" - fileName = "test_pic.png" + fileName = mockEncryptedFilename isEncrypted = true fileLength = 1024000 modificationTimestamp = 1188206955000 - parentId = storageManager.getFileByEncryptedRemotePath("/TestFolder/").fileId + parentId = storageManager.getFileByEncryptedRemotePath(folderPath).fileId storageManager.saveFile(this) } - val remotePathBeforeUpdate = imageFile.remotePath + val decryptedMetadata = DecryptedMetadata().apply { + folders = mutableMapOf(mockEncryptedFilename to decryptedFilename) + } + val metadata = DecryptedFolderMetadataFile(decryptedMetadata) - val metadata = EncryptionTestUtils().generateFolderMetadataV2( - client.userId, - EncryptionTestIT.publicKey - ) + RefreshFolderOperation.updateFileNameForEncryptedFile(storageManager, metadata, imageFile) - RefreshFolderOperation.updateFileNameForEncryptedFile(storageManager, metadata, folder) - - assertNotEquals(remotePathBeforeUpdate, imageFile.remotePath) + assertNotEquals(decryptedFilename, imageFile.fileName) } /** diff --git a/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/decrypted/DecryptedMetadata.kt b/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/decrypted/DecryptedMetadata.kt index 88099b6702..f27cd440bb 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/decrypted/DecryptedMetadata.kt +++ b/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/decrypted/DecryptedMetadata.kt @@ -13,7 +13,7 @@ data class DecryptedMetadata( val keyChecksums: MutableList = mutableListOf(), val deleted: Boolean = false, var counter: Long = 0, - val folders: MutableMap = mutableMapOf(), + var folders: MutableMap = mutableMapOf(), val files: MutableMap = mutableMapOf(), @Transient var metadataKey: ByteArray = EncryptionUtils.generateKey() diff --git a/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java b/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java index f71e52cf43..e4a8d506f7 100644 --- a/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java @@ -62,11 +62,9 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager; import static com.owncloud.android.datamodel.OCFile.PATH_SEPARATOR; /** - * Remote operation performing the synchronization of the list of files contained in a folder identified with its - * remote path. - * Fetches the list and properties of the files contained in the given folder, including their properties, and updates - * the local database with them. - * Does NOT enter in the child folders to synchronize their contents also. + * Remote operation performing the synchronization of the list of files contained in a folder identified with its remote + * path. Fetches the list and properties of the files contained in the given folder, including their properties, and + * updates the local database with them. Does NOT enter in the child folders to synchronize their contents also. */ @SuppressWarnings("PMD.AvoidDuplicateLiterals") public class RefreshFolderOperation extends RemoteOperation { @@ -74,37 +72,53 @@ public class RefreshFolderOperation extends RemoteOperation { private static final String TAG = RefreshFolderOperation.class.getSimpleName(); public static final String EVENT_SINGLE_FOLDER_CONTENTS_SYNCED = - RefreshFolderOperation.class.getName() + ".EVENT_SINGLE_FOLDER_CONTENTS_SYNCED"; + RefreshFolderOperation.class.getName() + ".EVENT_SINGLE_FOLDER_CONTENTS_SYNCED"; public static final String EVENT_SINGLE_FOLDER_SHARES_SYNCED = - RefreshFolderOperation.class.getName() + ".EVENT_SINGLE_FOLDER_SHARES_SYNCED"; + RefreshFolderOperation.class.getName() + ".EVENT_SINGLE_FOLDER_SHARES_SYNCED"; - /** Time stamp for the synchronization process in progress */ + /** + * Time stamp for the synchronization process in progress + */ private long mCurrentSyncTime; - /** Remote folder to synchronize */ + /** + * Remote folder to synchronize + */ private OCFile mLocalFolder; - /** Access to the local database */ + /** + * Access to the local database + */ private FileDataStorageManager mStorageManager; - /** Account where the file to synchronize belongs */ + /** + * Account where the file to synchronize belongs + */ private User user; - /** Android context; necessary to send requests to the download service */ + /** + * Android context; necessary to send requests to the download service + */ private Context mContext; - /** Files and folders contained in the synchronized folder after a successful operation */ + /** + * Files and folders contained in the synchronized folder after a successful operation + */ private List mChildren; - /** Counter of conflicts found between local and remote files */ + /** + * Counter of conflicts found between local and remote files + */ private int mConflictsFound; - /** Counter of failed operations in synchronization of kept-in-sync files */ + /** + * Counter of failed operations in synchronization of kept-in-sync files + */ private int mFailsInKeptInSyncFound; /** - * Map of remote and local paths to files that where locally stored in a location - * out of the ownCloud folder and couldn't be copied automatically into it + * Map of remote and local paths to files that where locally stored in a location out of the ownCloud folder and + * couldn't be copied automatically into it **/ private Map mForgottenLocalFiles; @@ -113,10 +127,14 @@ public class RefreshFolderOperation extends RemoteOperation { */ private boolean mSyncFullAccount; - /** 'True' means that the remote folder changed and should be fetched */ + /** + * 'True' means that the remote folder changed and should be fetched + */ private boolean mRemoteFolderChanged; - /** 'True' means that Etag will be ignored */ + /** + * 'True' means that Etag will be ignored + */ private boolean mIgnoreETag; /** @@ -131,16 +149,14 @@ public class RefreshFolderOperation extends RemoteOperation { /** * Creates a new instance of {@link RefreshFolderOperation}. * - * @param folder Folder to synchronize. - * @param currentSyncTime Time stamp for the synchronization process in progress. - * @param syncFullAccount 'True' means that this operation is part of a full account - * synchronization. - * @param ignoreETag 'True' means that the content of the remote folder should - * be fetched and updated even though the 'eTag' did not - * change. - * @param dataStorageManager Interface with the local database. - * @param user ownCloud account where the folder is located. - * @param context Application context. + * @param folder Folder to synchronize. + * @param currentSyncTime Time stamp for the synchronization process in progress. + * @param syncFullAccount 'True' means that this operation is part of a full account synchronization. + * @param ignoreETag 'True' means that the content of the remote folder should be fetched and updated even + * though the 'eTag' did not change. + * @param dataStorageManager Interface with the local database. + * @param user ownCloud account where the folder is located. + * @param context Application context. */ public RefreshFolderOperation(OCFile folder, long currentSyncTime, @@ -196,8 +212,8 @@ public class RefreshFolderOperation extends RemoteOperation { } /** - * Returns the list of files and folders contained in the synchronized folder, - * if called after synchronization is complete. + * Returns the list of files and folders contained in the synchronized folder, if called after synchronization is + * complete. * * @return List of files and folders contained in the synchronized folder. */ @@ -207,7 +223,7 @@ public class RefreshFolderOperation extends RemoteOperation { /** * Performs the synchronization. - * + *

* {@inheritDoc} */ @Override @@ -246,7 +262,7 @@ public class RefreshFolderOperation extends RemoteOperation { if (!mSyncFullAccount && mRemoteFolderChanged) { sendLocalBroadcast( EVENT_SINGLE_FOLDER_CONTENTS_SYNCED, mLocalFolder.getRemotePath(), result - ); + ); } if (result.isSuccess() && !mSyncFullAccount && !mOnlyFileMetadata) { @@ -256,7 +272,7 @@ public class RefreshFolderOperation extends RemoteOperation { if (!mSyncFullAccount) { sendLocalBroadcast( EVENT_SINGLE_FOLDER_SHARES_SYNCED, mLocalFolder.getRemotePath(), result - ); + ); } return result; @@ -371,7 +387,7 @@ public class RefreshFolderOperation extends RemoteOperation { result = new RemoteOperationResult(ResultCode.OK); Log_OC.i(TAG, "Checked " + user.getAccountName() + remotePath + " : " + - (mRemoteFolderChanged ? "changed" : "not changed")); + (mRemoteFolderChanged ? "changed" : "not changed")); } else { // check failed @@ -380,10 +396,10 @@ public class RefreshFolderOperation extends RemoteOperation { } if (result.isException()) { Log_OC.e(TAG, "Checked " + user.getAccountName() + remotePath + " : " + - result.getLogMessage(), result.getException()); + result.getLogMessage(), result.getException()); } else { Log_OC.e(TAG, "Checked " + user.getAccountName() + remotePath + " : " + - result.getLogMessage()); + result.getLogMessage()); } } @@ -425,9 +441,9 @@ public class RefreshFolderOperation extends RemoteOperation { /** - * Synchronizes the data retrieved from the server about the contents of the target folder - * with the current data in the local database. - * + * Synchronizes the data retrieved from the server about the contents of the target folder with the current data in + * the local database. + *

* Grants that mChildren is updated with fresh data after execution. * * @param folderAndFiles Remote folder and children files in Folder @@ -641,7 +657,6 @@ public class RefreshFolderOperation extends RemoteOperation { } } - // TODO write test for decryptedRemotePath existence... public static void updateFileNameForEncryptedFile(FileDataStorageManager storageManager, @NonNull DecryptedFolderMetadataFile metadata, OCFile updatedFile) { @@ -663,7 +678,6 @@ public class RefreshFolderOperation extends RemoteOperation { mimetype = decryptedFile.getMimetype(); } - OCFile parentFile = storageManager.getFileById(updatedFile.getParentId()); if (parentFile == null) { @@ -701,8 +715,8 @@ public class RefreshFolderOperation extends RemoteOperation { updatedFile.setFileId(localFile.getFileId()); updatedFile.setLastSyncDateForData(localFile.getLastSyncDateForData()); updatedFile.setModificationTimestampAtLastSyncForData( - localFile.getModificationTimestampAtLastSyncForData() - ); + localFile.getModificationTimestampAtLastSyncForData() + ); if (localFile.isEncrypted()) { if (mLocalFolder.getStoragePath() == null) { updatedFile.setStoragePath(FileStorageUtils.getDefaultSavePathFor(user.getAccountName(), mLocalFolder) + @@ -718,7 +732,7 @@ public class RefreshFolderOperation extends RemoteOperation { // eTag will not be updated unless file CONTENTS are synchronized if (!updatedFile.isFolder() && localFile.isDown() && - !updatedFile.getEtag().equals(localFile.getEtag())) { + !updatedFile.getEtag().equals(localFile.getEtag())) { updatedFile.setEtagInConflict(updatedFile.getEtag()); } @@ -728,8 +742,8 @@ public class RefreshFolderOperation extends RemoteOperation { updatedFile.setFileLength(remoteFile.getFileLength()); updatedFile.setMountType(remoteFile.getMountType()); } else if (remoteFolderChanged && MimeTypeUtil.isImage(remoteFile) && - remoteFile.getModificationTimestamp() != - localFile.getModificationTimestamp()) { + remoteFile.getModificationTimestamp() != + localFile.getModificationTimestamp()) { updatedFile.setUpdateThumbnailNeeded(true); Log_OC.d(TAG, "Image " + remoteFile.getFileName() + " updated on the server"); } @@ -764,13 +778,12 @@ public class RefreshFolderOperation extends RemoteOperation { } /** - * Performs a list of synchronization operations, determining if a download or upload is needed - * or if exists conflict due to changes both in local and remote contents of the each file. + * Performs a list of synchronization operations, determining if a download or upload is needed or if exists + * conflict due to changes both in local and remote contents of the each file. + *

+ * If download or upload is needed, request the operation to the corresponding service and goes on. * - * If download or upload is needed, request the operation to the corresponding service and goes - * on. - * - * @param filesToSyncContents Synchronization operations to execute. + * @param filesToSyncContents Synchronization operations to execute. */ private void startContentSynchronizations(List filesToSyncContents) { RemoteOperationResult contentsResult; @@ -783,10 +796,10 @@ public class RefreshFolderOperation extends RemoteOperation { mFailsInKeptInSyncFound++; if (contentsResult.getException() != null) { Log_OC.e(TAG, "Error while synchronizing favourites : " - + contentsResult.getLogMessage(), contentsResult.getException()); + + contentsResult.getLogMessage(), contentsResult.getException()); } else { Log_OC.e(TAG, "Error while synchronizing favourites : " - + contentsResult.getLogMessage()); + + contentsResult.getLogMessage()); } } } // won't let these fails break the synchronization process @@ -796,9 +809,9 @@ public class RefreshFolderOperation extends RemoteOperation { /** * Syncs the Share resources for the files contained in the folder refreshed (children, not deeper descendants). * - * @param client Handler of a session with an OC server. - * @return The result of the remote operation retrieving the Share resources in the folder refreshed by - * the operation. + * @param client Handler of a session with an OC server. + * @return The result of the remote operation retrieving the Share resources in the folder refreshed by the + * operation. */ private RemoteOperationResult refreshSharesForFolder(OwnCloudClient client) { RemoteOperationResult result; @@ -826,12 +839,10 @@ public class RefreshFolderOperation extends RemoteOperation { } /** - * Sends a message to any application component interested in the progress - * of the synchronization. + * Sends a message to any application component interested in the progress of the synchronization. * * @param event broadcast event (Intent Action) - * @param dirRemotePath Remote path of a folder that was just synchronized - * (with or without success) + * @param dirRemotePath Remote path of a folder that was just synchronized (with or without success) * @param result remote operation result */ private void sendLocalBroadcast(String event, String dirRemotePath, RemoteOperationResult result) { From f55d61c05a6b5022e6bcab6eeaea110a44df0e57 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 10 Apr 2024 16:30:27 +0200 Subject: [PATCH 28/74] Add tests Signed-off-by: alperozturk --- .../java/com/owncloud/android/EncryptionIT.kt | 32 +++++++++++++ .../android/utils/EncryptionUtilsIT.kt | 30 +++++++++++- .../android/utils/EncryptionUtilsV2IT.kt | 48 ++++--------------- .../e2e/v2/decrypted/DecryptedMetadata.kt | 2 +- .../operations/RefreshFolderOperation.java | 1 - 5 files changed, 71 insertions(+), 42 deletions(-) create mode 100644 app/src/androidTest/java/com/owncloud/android/EncryptionIT.kt diff --git a/app/src/androidTest/java/com/owncloud/android/EncryptionIT.kt b/app/src/androidTest/java/com/owncloud/android/EncryptionIT.kt new file mode 100644 index 0000000000..ed119a2a9d --- /dev/null +++ b/app/src/androidTest/java/com/owncloud/android/EncryptionIT.kt @@ -0,0 +1,32 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Your Name + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android + +import com.owncloud.android.datamodel.OCFile +import java.security.SecureRandom + +open class EncryptionIT : AbstractIT() { + + fun testFolder(): OCFile { + val rootPath = "/" + val folderPath = "/TestFolder/" + + OCFile(rootPath).apply { + storageManager.saveFile(this) + } + + return OCFile(folderPath).apply { + decryptedRemotePath = folderPath + isEncrypted = true + fileLength = SecureRandom().nextLong() + setFolder() + parentId = storageManager.getFileByDecryptedRemotePath(rootPath)!!.fileId + storageManager.saveFile(this) + } + } +} diff --git a/app/src/androidTest/java/com/owncloud/android/utils/EncryptionUtilsIT.kt b/app/src/androidTest/java/com/owncloud/android/utils/EncryptionUtilsIT.kt index 55ab2f472b..e5b2cf9d0b 100644 --- a/app/src/androidTest/java/com/owncloud/android/utils/EncryptionUtilsIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/utils/EncryptionUtilsIT.kt @@ -7,13 +7,18 @@ */ package com.owncloud.android.utils -import com.owncloud.android.AbstractIT +import com.owncloud.android.EncryptionIT import com.owncloud.android.datamodel.ArbitraryDataProviderImpl +import com.owncloud.android.datamodel.e2e.v1.decrypted.Data +import com.owncloud.android.datamodel.e2e.v1.decrypted.DecryptedFile +import com.owncloud.android.datamodel.e2e.v1.decrypted.DecryptedFolderMetadataFileV1 +import com.owncloud.android.datamodel.e2e.v1.decrypted.DecryptedMetadata import com.owncloud.android.lib.resources.e2ee.CsrHelper +import com.owncloud.android.operations.RefreshFolderOperation import org.junit.Assert.assertEquals import org.junit.Test -class EncryptionUtilsIT : AbstractIT() { +class EncryptionUtilsIT : EncryptionIT() { @Throws( java.security.NoSuchAlgorithmException::class, java.io.IOException::class, @@ -30,4 +35,25 @@ class EncryptionUtilsIT : AbstractIT() { assertEquals(key, EncryptionUtils.getPublicKey(user, e2eUser, arbitraryDataProvider)) } + + @Test + @Throws(Exception::class) + fun testUpdateFileNameForEncryptedFileV1() { + val folder = testFolder() + + val decryptedFilename = "image.png" + val mockEncryptedFilename = "encrypted_file_name.png" + + val decryptedMetadata = DecryptedMetadata() + val filesData = DecryptedFile().apply { + encrypted = Data().apply { + filename = decryptedFilename + } + } + val files = mapOf(mockEncryptedFilename to filesData) + val metadata = DecryptedFolderMetadataFileV1(decryptedMetadata, files) + + RefreshFolderOperation.updateFileNameForEncryptedFileV1(storageManager, metadata, folder) + assertEquals(folder.decryptedRemotePath.contains("null"), false) + } } diff --git a/app/src/androidTest/java/com/owncloud/android/utils/EncryptionUtilsV2IT.kt b/app/src/androidTest/java/com/owncloud/android/utils/EncryptionUtilsV2IT.kt index 96cf6f04d3..286700cf0c 100644 --- a/app/src/androidTest/java/com/owncloud/android/utils/EncryptionUtilsV2IT.kt +++ b/app/src/androidTest/java/com/owncloud/android/utils/EncryptionUtilsV2IT.kt @@ -10,7 +10,7 @@ package com.owncloud.android.utils import com.google.gson.reflect.TypeToken import com.nextcloud.client.account.MockUser import com.nextcloud.common.User -import com.owncloud.android.AbstractIT +import com.owncloud.android.EncryptionIT import com.owncloud.android.datamodel.OCFile import com.owncloud.android.datamodel.e2e.v1.decrypted.Data import com.owncloud.android.datamodel.e2e.v1.decrypted.DecryptedFolderMetadataFileV1 @@ -27,9 +27,8 @@ import junit.framework.TestCase.assertEquals import junit.framework.TestCase.assertTrue import org.junit.Assert.assertNotEquals import org.junit.Test -import java.security.SecureRandom -class EncryptionUtilsV2IT : AbstractIT() { +class EncryptionUtilsV2IT : EncryptionIT() { private val encryptionTestUtils = EncryptionTestUtils() private val encryptionUtilsV2 = EncryptionUtilsV2() @@ -785,44 +784,17 @@ class EncryptionUtilsV2IT : AbstractIT() { @Test @Throws(Exception::class) - fun testUpdateFileNameForEncryptedFileWhenEncryptedFileUploadRemotePathShouldSetAsEncrypted() { - val rootPath = "/" - val folderPath = "/TestFolder/" + fun testUpdateFileNameForEncryptedFile() { + val folder = testFolder() - OCFile(rootPath).apply { - storageManager.saveFile(this) - } + val metadata = EncryptionTestUtils().generateFolderMetadataV2( + client.userId, + EncryptionTestIT.publicKey + ) - OCFile(folderPath).apply { - decryptedRemotePath = folderPath - isEncrypted = true - fileLength = SecureRandom().nextLong() - setFolder() - parentId = storageManager.getFileByDecryptedRemotePath(rootPath)!!.fileId - storageManager.saveFile(this) - } + RefreshFolderOperation.updateFileNameForEncryptedFile(storageManager, metadata, folder) - val decryptedFilename = "image.png" - val mockEncryptedFilename = "encrypted_file_name.png" - - val imageFile = OCFile(folderPath + decryptedFilename).apply { - mimeType = "image/png" - fileName = mockEncryptedFilename - isEncrypted = true - fileLength = 1024000 - modificationTimestamp = 1188206955000 - parentId = storageManager.getFileByEncryptedRemotePath(folderPath).fileId - storageManager.saveFile(this) - } - - val decryptedMetadata = DecryptedMetadata().apply { - folders = mutableMapOf(mockEncryptedFilename to decryptedFilename) - } - val metadata = DecryptedFolderMetadataFile(decryptedMetadata) - - RefreshFolderOperation.updateFileNameForEncryptedFile(storageManager, metadata, imageFile) - - assertNotEquals(decryptedFilename, imageFile.fileName) + assertEquals(folder.decryptedRemotePath.contains("null"), false) } /** diff --git a/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/decrypted/DecryptedMetadata.kt b/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/decrypted/DecryptedMetadata.kt index f27cd440bb..88099b6702 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/decrypted/DecryptedMetadata.kt +++ b/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/decrypted/DecryptedMetadata.kt @@ -13,7 +13,7 @@ data class DecryptedMetadata( val keyChecksums: MutableList = mutableListOf(), val deleted: Boolean = false, var counter: Long = 0, - var folders: MutableMap = mutableMapOf(), + val folders: MutableMap = mutableMapOf(), val files: MutableMap = mutableMapOf(), @Transient var metadataKey: ByteArray = EncryptionUtils.generateKey() diff --git a/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java b/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java index e4a8d506f7..82f1790f18 100644 --- a/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java @@ -601,7 +601,6 @@ public class RefreshFolderOperation extends RemoteOperation { return metadata; } - // TODO write test for decryptedRemotePath existence... public static void updateFileNameForEncryptedFileV1(FileDataStorageManager storageManager, @NonNull DecryptedFolderMetadataFileV1 metadata, OCFile updatedFile) { From 501c6ee2a30730dbb0daf0f0cb08fa9b66b5426d Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 10 Apr 2024 16:37:07 +0200 Subject: [PATCH 29/74] Extract common logic for mimetype and decrypted remote path Signed-off-by: alperozturk --- .../operations/RefreshFolderOperation.java | 87 +++++++------------ 1 file changed, 32 insertions(+), 55 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java b/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java index 82f1790f18..b428efc8fe 100644 --- a/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java @@ -601,6 +601,36 @@ public class RefreshFolderOperation extends RemoteOperation { return metadata; } + private static void setMimeTypeAndDecryptedRemotePath(OCFile updatedFile, FileDataStorageManager storageManager, String decryptedFileName, String mimetype) { + OCFile parentFile = storageManager.getFileById(updatedFile.getParentId()); + + if (parentFile == null) { + throw new NullPointerException("parentFile cannot be null"); + } + + String decryptedRemotePath; + if (decryptedFileName != null) { + decryptedRemotePath = parentFile.getDecryptedRemotePath() + decryptedFileName; + } else { + decryptedRemotePath = parentFile.getRemotePath() + updatedFile.getFileName(); + } + + if (updatedFile.isFolder()) { + decryptedRemotePath += "/"; + } + updatedFile.setDecryptedRemotePath(decryptedRemotePath); + + if (mimetype == null || mimetype.isEmpty()) { + if (updatedFile.isFolder()) { + updatedFile.setMimeType(MimeType.DIRECTORY); + } else { + updatedFile.setMimeType("application/octet-stream"); + } + } else { + updatedFile.setMimeType(mimetype); + } + } + public static void updateFileNameForEncryptedFileV1(FileDataStorageManager storageManager, @NonNull DecryptedFolderMetadataFileV1 metadata, OCFile updatedFile) { @@ -623,34 +653,7 @@ public class RefreshFolderOperation extends RemoteOperation { mimetype = decryptedFile.getEncrypted().getMimetype(); } - - OCFile parentFile = storageManager.getFileById(updatedFile.getParentId()); - - if (parentFile == null) { - throw new NullPointerException("parentFile cannot be null"); - } - - String decryptedRemotePath; - if (decryptedFileName != null) { - decryptedRemotePath = parentFile.getDecryptedRemotePath() + decryptedFileName; - } else { - decryptedRemotePath = parentFile.getRemotePath() + updatedFile.getFileName(); - } - - if (updatedFile.isFolder()) { - decryptedRemotePath += "/"; - } - updatedFile.setDecryptedRemotePath(decryptedRemotePath); - - if (mimetype == null || mimetype.isEmpty()) { - if (updatedFile.isFolder()) { - updatedFile.setMimeType(MimeType.DIRECTORY); - } else { - updatedFile.setMimeType("application/octet-stream"); - } - } else { - updatedFile.setMimeType(mimetype); - } + setMimeTypeAndDecryptedRemotePath(updatedFile, storageManager, decryptedFileName, mimetype); } catch (NullPointerException e) { Log_OC.e(TAG, "DecryptedMetadata for file " + updatedFile.getFileId() + " not found!"); } @@ -677,33 +680,7 @@ public class RefreshFolderOperation extends RemoteOperation { mimetype = decryptedFile.getMimetype(); } - OCFile parentFile = storageManager.getFileById(updatedFile.getParentId()); - - if (parentFile == null) { - throw new NullPointerException("parentFile cannot be null"); - } - - String decryptedRemotePath; - if (decryptedFileName != null) { - decryptedRemotePath = parentFile.getDecryptedRemotePath() + decryptedFileName; - } else { - decryptedRemotePath = parentFile.getRemotePath() + updatedFile.getFileName(); - } - - if (updatedFile.isFolder()) { - decryptedRemotePath += "/"; - } - updatedFile.setDecryptedRemotePath(decryptedRemotePath); - - if (mimetype.isEmpty()) { - if (updatedFile.isFolder()) { - updatedFile.setMimeType(MimeType.DIRECTORY); - } else { - updatedFile.setMimeType("application/octet-stream"); - } - } else { - updatedFile.setMimeType(mimetype); - } + setMimeTypeAndDecryptedRemotePath(updatedFile, storageManager, decryptedFileName, mimetype); } catch (NullPointerException e) { Log_OC.e(TAG, "DecryptedMetadata for file " + updatedFile.getFileId() + " not found!"); } From 55aed575dfb58e8935b4b414b305910a64556b7a Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 10 Apr 2024 16:48:08 +0200 Subject: [PATCH 30/74] Extract common logic retrieving remote path Signed-off-by: alperozturk --- .../android/datamodel/FileDataStorageManager.java | 13 +++++++++++++ .../android/ui/activity/ConflictsResolveActivity.kt | 12 ++---------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java b/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java index 6026f30d6a..bcdf36f2f9 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java +++ b/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java @@ -2302,6 +2302,19 @@ public class FileDataStorageManager { return "/" + path.split("/")[1] + "/"; } + public String retrieveRemotePathConsideringEncryption(OCFile file) { + if (file == null) { + throw new NullPointerException("file cannot be null"); + } + + String remotePath = file.getRemotePath(); + if (file.isEncrypted()) { + remotePath = getEncryptedRemotePath(file.getRemotePath()); + } + + return remotePath; + } + public String getEncryptedRemotePath(String decryptedRemotePath) { String folderName = getFolderName(decryptedRemotePath); diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.kt index 10315d624c..a83e5c6dd0 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.kt @@ -164,11 +164,7 @@ class ConflictsResolveActivity : FileActivity(), OnConflictDecisionMadeListener return } if (existingFile == null) { - var remotePath = newFile!!.remotePath - if (newFile?.isEncrypted == true) { - remotePath = fileStorageManager?.getEncryptedRemotePath(newFile!!.remotePath) - } - + val remotePath = fileStorageManager?.retrieveRemotePathConsideringEncryption(newFile) ?: return val operation = ReadFileRemoteOperation(remotePath) @Suppress("TooGenericExceptionCaught") @@ -189,11 +185,7 @@ class ConflictsResolveActivity : FileActivity(), OnConflictDecisionMadeListener } }.start() } else { - var remotePath = existingFile!!.remotePath - if (newFile?.isEncrypted == true) { - remotePath = fileStorageManager?.getEncryptedRemotePath(existingFile!!.remotePath) - } - + val remotePath = fileStorageManager?.retrieveRemotePathConsideringEncryption(existingFile) ?: return startDialog(remotePath) } } From 221e6dddb87c9ae823d7525adddeb0cffe98c6fb Mon Sep 17 00:00:00 2001 From: alperozturk Date: Thu, 11 Apr 2024 09:36:39 +0200 Subject: [PATCH 31/74] Suppress test class Signed-off-by: alperozturk --- .../java/com/owncloud/android/utils/EncryptionUtilsV2IT.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/androidTest/java/com/owncloud/android/utils/EncryptionUtilsV2IT.kt b/app/src/androidTest/java/com/owncloud/android/utils/EncryptionUtilsV2IT.kt index 286700cf0c..4d72bbda3c 100644 --- a/app/src/androidTest/java/com/owncloud/android/utils/EncryptionUtilsV2IT.kt +++ b/app/src/androidTest/java/com/owncloud/android/utils/EncryptionUtilsV2IT.kt @@ -28,6 +28,7 @@ import junit.framework.TestCase.assertTrue import org.junit.Assert.assertNotEquals import org.junit.Test +@Suppress("TooManyFunctions", "LargeClass") class EncryptionUtilsV2IT : EncryptionIT() { private val encryptionTestUtils = EncryptionTestUtils() private val encryptionUtilsV2 = EncryptionUtilsV2() From 2a816c7d1932dbf23a8b6df636e549773c6af2e6 Mon Sep 17 00:00:00 2001 From: Jonas Mayer Date: Tue, 26 Mar 2024 14:35:03 +0100 Subject: [PATCH 32/74] Proof of concept for 10x faster check for changed files Signed-off-by: Jonas Mayer --- .../nextcloud/client/jobs/FilesSyncWork.kt | 14 +++--- .../datamodel/FilesystemDataProvider.java | 3 +- .../android/utils/FilesSyncHelper.java | 43 ++++++++++++++++++- 3 files changed, 51 insertions(+), 9 deletions(-) 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 651c65aa02..1ab6f8ce46 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/FilesSyncWork.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/FilesSyncWork.kt @@ -70,7 +70,7 @@ class FilesSyncWork( @Suppress("MagicNumber") private fun updateForegroundWorker(progressPercent: Int, useForegroundWorker: Boolean) { - if (useForegroundWorker) { + if (!useForegroundWorker) { return } @@ -95,6 +95,7 @@ class FilesSyncWork( @Suppress("MagicNumber") override fun doWork(): Result { backgroundJobManager.logStartOfWorker(BackgroundJobManagerImpl.formatClassTag(this::class)) + Log_OC.d(TAG,"FILESYNC WORKER STARTED") val overridePowerSaving = inputData.getBoolean(OVERRIDE_POWER_SAVING, false) // If we are in power save mode, better to postpone upload @@ -114,7 +115,9 @@ class FilesSyncWork( // Get changed files from ContentObserverWork (only images and videos) or by scanning filesystem val changedFiles = inputData.getStringArray(CHANGED_FILES) + Log_OC.d(TAG,"FILESYNC WORKER CHANGED FILES: "+changedFiles.contentToString()) collectChangedFiles(changedFiles) + Log_OC.d(TAG,"FILESYNC WORKER CHECKED CHANGED FILES") // Create all the providers we'll need val filesystemDataProvider = FilesystemDataProvider(contentResolver) @@ -129,11 +132,7 @@ class FilesSyncWork( (50 + (index.toDouble() / syncedFolders.size.toDouble()) * 50).toInt(), changedFiles.isNullOrEmpty() ) - if (syncedFolder.isEnabled && ( - changedFiles.isNullOrEmpty() || - MediaFolderType.CUSTOM != syncedFolder.type - ) - ) { + if (syncedFolder.isEnabled) { syncFolder( context, resources, @@ -145,6 +144,7 @@ class FilesSyncWork( ) } } + Log_OC.d(TAG,"FILESYNC WORKER ENDED") val result = Result.success() backgroundJobManager.logEndOfWorker(BackgroundJobManagerImpl.formatClassTag(this::class), result) return result @@ -222,7 +222,7 @@ class FilesSyncWork( needsWifi = syncedFolder.isWifiOnly uploadAction = syncedFolder.uploadAction } - + Log_OC.d(TAG,"FILESYNC SCHEDULE UPLOAD OF FILE") FileUploadHelper.instance().uploadNewFiles( user, localPaths, 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 6e088e7211..aec6fda1bc 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/FilesystemDataProvider.java +++ b/app/src/main/java/com/owncloud/android/datamodel/FilesystemDataProvider.java @@ -107,6 +107,7 @@ public class FilesystemDataProvider { public void storeOrUpdateFileValue(String localPath, long modifiedAt, boolean isFolder, SyncedFolder syncedFolder) { + // takes multiple milliseconds to query data from database (around 75% of execution time) (6ms) FileSystemDataSet data = getFilesystemDataSet(localPath, syncedFolder); int isFolderValue = 0; @@ -145,7 +146,7 @@ public class FilesystemDataProvider { } } - + // updating data takes multiple milliseconds (around 25% of exec time) (2 ms) int result = contentResolver.update( ProviderMeta.ProviderTableMeta.CONTENT_URI_FILESYSTEM, cv, diff --git a/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java b/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java index c68c54329c..387595d19f 100644 --- a/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java +++ b/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java @@ -20,7 +20,6 @@ import com.nextcloud.client.device.BatteryStatus; import com.nextcloud.client.device.PowerManagementService; import com.nextcloud.client.jobs.BackgroundJobManager; import com.nextcloud.client.jobs.upload.FileUploadHelper; -import com.nextcloud.client.jobs.upload.FileUploadWorker; import com.nextcloud.client.network.ConnectivityService; import com.owncloud.android.MainApp; import com.owncloud.android.datamodel.FilesystemDataProvider; @@ -40,6 +39,7 @@ import org.lukhnos.nnio.file.attribute.BasicFileAttributes; import java.io.File; import java.io.IOException; +import java.util.ArrayList; import static com.owncloud.android.datamodel.OCFile.PATH_SEPARATOR; @@ -78,6 +78,40 @@ public final class FilesSyncHelper { FilesystemDataProvider filesystemDataProvider = new FilesystemDataProvider(contentResolver); Path path = Paths.get(syncedFolder.getLocalPath()); + long startTime = System.nanoTime(); + // chick check for changes + long lastCheck = System.currentTimeMillis(); + ArrayList changedFiles = new ArrayList<>(); + FileUtil.walkFileTree(path, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) { + File file = path.toFile(); + if (syncedFolder.isExcludeHidden() && file.isHidden()) { + // exclude hidden file or folder + return FileVisitResult.CONTINUE; + } + if (file.lastModified() >= lastCheck){ + changedFiles.add(file); + } + + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + if (syncedFolder.isExcludeHidden() && dir.compareTo(Paths.get(syncedFolder.getLocalPath())) != 0 && dir.toFile().isHidden()) { + return null; + } + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFileFailed(Path file, IOException exc) { + return FileVisitResult.CONTINUE; + } + }); + Log_OC.d(TAG,"FILESYNC FINISHED QUICK CHECK FILE "+path+" "+(System.nanoTime() - startTime)); + startTime = System.nanoTime(); FileUtil.walkFileTree(path, new SimpleFileVisitor() { @Override public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) { @@ -87,6 +121,8 @@ public final class FilesSyncHelper { return FileVisitResult.CONTINUE; } if (syncedFolder.isExisting() || attrs.lastModifiedTime().toMillis() >= enabledTimestampMs) { + // storeOrUpdateFileValue takes a few millisec + // -> Rest of this file check takes not even 1 millisec. filesystemDataProvider.storeOrUpdateFileValue(path.toAbsolutePath().toString(), attrs.lastModifiedTime().toMillis(), file.isDirectory(), syncedFolder); @@ -108,6 +144,8 @@ public final class FilesSyncHelper { return FileVisitResult.CONTINUE; } }); + Log_OC.d(TAG,"FILESYNC FINISHED LONG CHECK FILE "+path+" "+(System.nanoTime() - startTime)); + } catch (IOException e) { Log_OC.e(TAG, "Something went wrong while indexing files for auto upload", e); } @@ -117,6 +155,7 @@ public final class FilesSyncHelper { public static void insertAllDBEntries(SyncedFolderProvider syncedFolderProvider) { for (SyncedFolder syncedFolder : syncedFolderProvider.getSyncedFolders()) { + Log_OC.d(TAG,"FILESYNC CHECK FOLDER "+syncedFolder.getLocalPath()); if (syncedFolder.isEnabled()) { insertAllDBEntriesForSyncedFolder(syncedFolder); } @@ -135,6 +174,7 @@ public final class FilesSyncHelper { filesystemDataProvider.storeOrUpdateFileValue(changedFile, file.lastModified(),file.isDirectory(), syncedFolder); + Log_OC.d(TAG,"FILESYNC ADDED UPLOAD TO DB"); break; } } @@ -191,6 +231,7 @@ public final class FilesSyncHelper { column_index_date_modified = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATE_MODIFIED); while (cursor.moveToNext()) { contentPath = cursor.getString(column_index_data); + Log_OC.d(TAG,"FILESYNC CHECK File "+contentPath); isFolder = new File(contentPath).isDirectory(); if (syncedFolder.isExisting() || cursor.getLong(column_index_date_modified) >= enabledTimestampMs / 1000.0) { filesystemDataProvider.storeOrUpdateFileValue(contentPath, From 8f3b5b106c4507204f1169acb7043b35b2e639f3 Mon Sep 17 00:00:00 2001 From: Jonas Mayer Date: Wed, 3 Apr 2024 18:04:54 +0200 Subject: [PATCH 33/74] draft implement faster auto upload file changed scan Signed-off-by: Jonas Mayer --- .../android/utils/SyncedFolderUtilsTest.kt | 6 +- .../database/entity/SyncedFolderEntity.kt | 6 +- .../android/datamodel/SyncedFolder.java | 16 +- .../datamodel/SyncedFolderDisplayItem.java | 12 +- .../datamodel/SyncedFolderProvider.java | 11 +- .../com/owncloud/android/db/ProviderMeta.java | 3 +- .../ui/activity/SyncedFoldersActivity.kt | 15 +- .../android/utils/FilesSyncHelper.java | 154 +++++++++--------- 8 files changed, 125 insertions(+), 98 deletions(-) diff --git a/app/src/androidTest/java/com/owncloud/android/utils/SyncedFolderUtilsTest.kt b/app/src/androidTest/java/com/owncloud/android/utils/SyncedFolderUtilsTest.kt index aba890bd06..5e31285307 100644 --- a/app/src/androidTest/java/com/owncloud/android/utils/SyncedFolderUtilsTest.kt +++ b/app/src/androidTest/java/com/owncloud/android/utils/SyncedFolderUtilsTest.kt @@ -174,7 +174,8 @@ class SyncedFolderUtilsTest : AbstractIT() { MediaFolderType.IMAGE, false, SubFolderRule.YEAR_MONTH, - false + false, + SyncedFolder.NOT_SCANNED_YET ) Assert.assertFalse(SyncedFolderUtils.isQualifyingMediaFolder(folder)) } @@ -198,7 +199,8 @@ class SyncedFolderUtilsTest : AbstractIT() { MediaFolderType.IMAGE, false, SubFolderRule.YEAR_MONTH, - false + false, + SyncedFolder.NOT_SCANNED_YET ) Assert.assertFalse(SyncedFolderUtils.isQualifyingMediaFolder(folder)) } diff --git a/app/src/main/java/com/nextcloud/client/database/entity/SyncedFolderEntity.kt b/app/src/main/java/com/nextcloud/client/database/entity/SyncedFolderEntity.kt index 72e57bc5de..8a4298cef6 100644 --- a/app/src/main/java/com/nextcloud/client/database/entity/SyncedFolderEntity.kt +++ b/app/src/main/java/com/nextcloud/client/database/entity/SyncedFolderEntity.kt @@ -45,6 +45,8 @@ data class SyncedFolderEntity( val hidden: Int?, @ColumnInfo(name = ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_RULE) val subFolderRule: Int?, - @ColumnInfo(name = ProviderTableMeta.SYNCED_EXCLUDE_HIDDEN) - val excludeHidden: Int? + @ColumnInfo(name = ProviderTableMeta.SYNCED_FOLDER_EXCLUDE_HIDDEN) + val excludeHidden: Int?, + @ColumnInfo(name = ProviderTableMeta.SYNCED_FOLDER_LAST_SCAN_TIMESTAMP_MS) + val lastScanTimestampMs: Long? ) diff --git a/app/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java b/app/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java index f787a550ad..3e5b8faad5 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java +++ b/app/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java @@ -21,6 +21,7 @@ import java.io.Serializable; public class SyncedFolder implements Serializable, Cloneable { public static final long UNPERSISTED_ID = Long.MIN_VALUE; public static final long EMPTY_ENABLED_TIMESTAMP_MS = -1; + public static final long NOT_SCANNED_YET = -1; private static final long serialVersionUID = -793476118299906429L; @@ -41,6 +42,7 @@ public class SyncedFolder implements Serializable, Cloneable { private boolean hidden; private SubFolderRule subfolderRule; private boolean excludeHidden; + private long lastScanTimestampMs; /** * constructor for new, to be persisted entity. @@ -75,7 +77,8 @@ public class SyncedFolder implements Serializable, Cloneable { MediaFolderType type, boolean hidden, SubFolderRule subFolderRule, - boolean excludeHidden) { + boolean excludeHidden, + long lastScanTimestampMs) { this(UNPERSISTED_ID, localPath, remotePath, @@ -91,7 +94,8 @@ public class SyncedFolder implements Serializable, Cloneable { type, hidden, subFolderRule, - excludeHidden); + excludeHidden, + lastScanTimestampMs); } /** @@ -114,7 +118,8 @@ public class SyncedFolder implements Serializable, Cloneable { MediaFolderType type, boolean hidden, SubFolderRule subFolderRule, - boolean excludeHidden) { + boolean excludeHidden, + long lastScanTimestampMs) { this.id = id; this.localPath = localPath; this.remotePath = remotePath; @@ -130,6 +135,7 @@ public class SyncedFolder implements Serializable, Cloneable { this.hidden = hidden; this.subfolderRule = subFolderRule; this.excludeHidden = excludeHidden; + this.lastScanTimestampMs = lastScanTimestampMs; } /** @@ -271,4 +277,8 @@ public class SyncedFolder implements Serializable, Cloneable { public boolean containsFile(String filePath){ return filePath.contains(localPath); } + + public long getLastScanTimestampMs() { return lastScanTimestampMs; } + + public void setLastScanTimestampMs(long lastScanTimestampMs) { this.lastScanTimestampMs = lastScanTimestampMs; } } diff --git a/app/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java b/app/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java index 5b156f5709..2bef272cf9 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java +++ b/app/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java @@ -60,7 +60,8 @@ public class SyncedFolderDisplayItem extends SyncedFolder { MediaFolderType type, boolean hidden, SubFolderRule subFolderRule, - boolean excludeHidden) { + boolean excludeHidden, + long lastScanTimestampMs) { super(id, localPath, remotePath, @@ -76,7 +77,8 @@ public class SyncedFolderDisplayItem extends SyncedFolder { type, hidden, subFolderRule, - excludeHidden); + excludeHidden, + lastScanTimestampMs); this.filePaths = filePaths; this.folderName = folderName; this.numberOfFiles = numberOfFiles; @@ -98,7 +100,8 @@ public class SyncedFolderDisplayItem extends SyncedFolder { MediaFolderType type, boolean hidden, SubFolderRule subFolderRule, - boolean excludeHidden) { + boolean excludeHidden, + long lastScanTimestampMs) { super(id, localPath, remotePath, @@ -114,7 +117,8 @@ public class SyncedFolderDisplayItem extends SyncedFolder { type, hidden, subFolderRule, - excludeHidden); + excludeHidden, + lastScanTimestampMs); this.folderName = folderName; } diff --git a/app/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java b/app/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java index d867684829..5147889264 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java +++ b/app/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java @@ -360,7 +360,9 @@ public class SyncedFolderProvider extends Observable { SubFolderRule subFolderRule = SubFolderRule.values()[cursor.getInt( cursor.getColumnIndexOrThrow(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_RULE))]; boolean excludeHidden = cursor.getInt(cursor.getColumnIndexOrThrow( - ProviderMeta.ProviderTableMeta.SYNCED_EXCLUDE_HIDDEN)) == 1; + ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_EXCLUDE_HIDDEN)) == 1; + long lastScanTimestampMs = cursor.getLong(cursor.getColumnIndexOrThrow( + ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_LAST_SCAN_TIMESTAMP_MS)); syncedFolder = new SyncedFolder(id, @@ -378,7 +380,8 @@ public class SyncedFolderProvider extends Observable { type, hidden, subFolderRule, - excludeHidden); + excludeHidden, + lastScanTimestampMs); } return syncedFolder; } @@ -407,8 +410,8 @@ public class SyncedFolderProvider extends Observable { cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_TYPE, syncedFolder.getType().id); cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_HIDDEN, syncedFolder.isHidden()); cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_RULE, syncedFolder.getSubfolderRule().ordinal()); - cv.put(ProviderMeta.ProviderTableMeta.SYNCED_EXCLUDE_HIDDEN, syncedFolder.isExcludeHidden()); - + cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_EXCLUDE_HIDDEN, syncedFolder.isExcludeHidden()); + cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_LAST_SCAN_TIMESTAMP_MS, syncedFolder.getLastScanTimestampMs()); return cv; } diff --git a/app/src/main/java/com/owncloud/android/db/ProviderMeta.java b/app/src/main/java/com/owncloud/android/db/ProviderMeta.java index 5c948f39ca..ee61c1dd2c 100644 --- a/app/src/main/java/com/owncloud/android/db/ProviderMeta.java +++ b/app/src/main/java/com/owncloud/android/db/ProviderMeta.java @@ -293,7 +293,8 @@ public class ProviderMeta { public static final String SYNCED_FOLDER_NAME_COLLISION_POLICY = "name_collision_policy"; public static final String SYNCED_FOLDER_HIDDEN = "hidden"; public static final String SYNCED_FOLDER_SUBFOLDER_RULE = "sub_folder_rule"; - public static final String SYNCED_EXCLUDE_HIDDEN = "exclude_hidden"; + public static final String SYNCED_FOLDER_EXCLUDE_HIDDEN = "exclude_hidden"; + public static final String SYNCED_FOLDER_LAST_SCAN_TIMESTAMP_MS = "last_scan_timestamp_ms"; // Columns of external links table public static final String EXTERNAL_LINKS_ICON_URL = "icon_url"; diff --git a/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt index bbdd2870f1..53fa9c3e60 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt @@ -387,7 +387,8 @@ class SyncedFoldersActivity : syncedFolder.type, syncedFolder.isHidden, syncedFolder.subfolderRule, - syncedFolder.isExcludeHidden + syncedFolder.isExcludeHidden, + syncedFolder.lastScanTimestampMs ) } @@ -418,7 +419,8 @@ class SyncedFoldersActivity : mediaFolder.type, syncedFolder.isHidden, syncedFolder.subfolderRule, - syncedFolder.isExcludeHidden + syncedFolder.isExcludeHidden, + syncedFolder.lastScanTimestampMs ) } @@ -448,7 +450,8 @@ class SyncedFoldersActivity : mediaFolder.type, false, SubFolderRule.YEAR_MONTH, - false + false, + SyncedFolder.NOT_SCANNED_YET ) } @@ -541,7 +544,8 @@ class SyncedFoldersActivity : MediaFolderType.CUSTOM, false, SubFolderRule.YEAR_MONTH, - false + false, + SyncedFolder.NOT_SCANNED_YET ) onSyncFolderSettingsClick(0, emptyCustomFolder) } else { @@ -658,7 +662,8 @@ class SyncedFoldersActivity : syncedFolder.type, syncedFolder.isHidden, syncedFolder.subFolderRule, - syncedFolder.isExcludeHidden + syncedFolder.isExcludeHidden, + SyncedFolder.NOT_SCANNED_YET ) saveOrUpdateSyncedFolder(newCustomFolder) adapter.addSyncFolderItem(newCustomFolder) diff --git a/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java b/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java index 387595d19f..4b597c1d2b 100644 --- a/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java +++ b/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java @@ -55,6 +55,59 @@ public final class FilesSyncHelper { // utility class -> private constructor } + private static void insertCustomFolderIntoDB(Path path, + SyncedFolder syncedFolder, + FilesystemDataProvider filesystemDataProvider, + long lastCheck, + long thisCheck) { + + final long enabledTimestampMs = syncedFolder.getEnabledTimestampMs(); + + try { + FileUtil.walkFileTree(path, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) { + File file = path.toFile(); + if (syncedFolder.isExcludeHidden() && file.isHidden()) { + // exclude hidden file or folder + return FileVisitResult.CONTINUE; + } + + if (lastCheck != SyncedFolder.NOT_SCANNED_YET && attrs.lastModifiedTime().toMillis() < lastCheck) { + // skip files that were already checked + return FileVisitResult.CONTINUE; + } + + if (syncedFolder.isExisting() || attrs.lastModifiedTime().toMillis() >= enabledTimestampMs) { + // storeOrUpdateFileValue takes a few ms + // -> Rest of this file check takes not even 1 ms. + filesystemDataProvider.storeOrUpdateFileValue(path.toAbsolutePath().toString(), + attrs.lastModifiedTime().toMillis(), + file.isDirectory(), syncedFolder); + } + + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + if (syncedFolder.isExcludeHidden() && dir.compareTo(Paths.get(syncedFolder.getLocalPath())) != 0 && dir.toFile().isHidden()) { + return null; + } + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFileFailed(Path file, IOException exc) { + return FileVisitResult.CONTINUE; + } + }); + syncedFolder.setLastScanTimestampMs(thisCheck); + } catch (IOException e) { + Log_OC.e(TAG, "Something went wrong while indexing files for auto upload", e); + } + } + private static void insertAllDBEntriesForSyncedFolder(SyncedFolder syncedFolder) { final Context context = MainApp.getAppContext(); final ContentResolver contentResolver = context.getContentResolver(); @@ -63,92 +116,31 @@ public final class FilesSyncHelper { if (syncedFolder.isEnabled() && (syncedFolder.isExisting() || enabledTimestampMs >= 0)) { MediaFolderType mediaType = syncedFolder.getType(); + final long lastCheck = syncedFolder.getLastScanTimestampMs(); + final long thisCheck = System.currentTimeMillis(); + if (mediaType == MediaFolderType.IMAGE) { - FilesSyncHelper.insertContentIntoDB(MediaStore.Images.Media.INTERNAL_CONTENT_URI - , syncedFolder); + FilesSyncHelper.insertContentIntoDB(MediaStore.Images.Media.INTERNAL_CONTENT_URI, + syncedFolder, + lastCheck, thisCheck); FilesSyncHelper.insertContentIntoDB(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, - syncedFolder); + syncedFolder, + lastCheck, thisCheck); } else if (mediaType == MediaFolderType.VIDEO) { FilesSyncHelper.insertContentIntoDB(MediaStore.Video.Media.INTERNAL_CONTENT_URI, - syncedFolder); + syncedFolder, + lastCheck, thisCheck); FilesSyncHelper.insertContentIntoDB(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, - syncedFolder); + syncedFolder, + lastCheck, thisCheck); } else { - try { FilesystemDataProvider filesystemDataProvider = new FilesystemDataProvider(contentResolver); Path path = Paths.get(syncedFolder.getLocalPath()); long startTime = System.nanoTime(); - // chick check for changes - long lastCheck = System.currentTimeMillis(); - ArrayList changedFiles = new ArrayList<>(); - FileUtil.walkFileTree(path, new SimpleFileVisitor() { - @Override - public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) { - File file = path.toFile(); - if (syncedFolder.isExcludeHidden() && file.isHidden()) { - // exclude hidden file or folder - return FileVisitResult.CONTINUE; - } - if (file.lastModified() >= lastCheck){ - changedFiles.add(file); - } + FilesSyncHelper.insertCustomFolderIntoDB(path, syncedFolder, filesystemDataProvider, lastCheck, thisCheck); + Log_OC.d(TAG,"FILESYNC FINISHED LONG CHECK FOLDER "+path+" "+(System.nanoTime() - startTime)); - return FileVisitResult.CONTINUE; - } - - @Override - public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { - if (syncedFolder.isExcludeHidden() && dir.compareTo(Paths.get(syncedFolder.getLocalPath())) != 0 && dir.toFile().isHidden()) { - return null; - } - return FileVisitResult.CONTINUE; - } - - @Override - public FileVisitResult visitFileFailed(Path file, IOException exc) { - return FileVisitResult.CONTINUE; - } - }); - Log_OC.d(TAG,"FILESYNC FINISHED QUICK CHECK FILE "+path+" "+(System.nanoTime() - startTime)); - startTime = System.nanoTime(); - FileUtil.walkFileTree(path, new SimpleFileVisitor() { - @Override - public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) { - File file = path.toFile(); - if (syncedFolder.isExcludeHidden() && file.isHidden()) { - // exclude hidden file or folder - return FileVisitResult.CONTINUE; - } - if (syncedFolder.isExisting() || attrs.lastModifiedTime().toMillis() >= enabledTimestampMs) { - // storeOrUpdateFileValue takes a few millisec - // -> Rest of this file check takes not even 1 millisec. - filesystemDataProvider.storeOrUpdateFileValue(path.toAbsolutePath().toString(), - attrs.lastModifiedTime().toMillis(), - file.isDirectory(), syncedFolder); - } - - return FileVisitResult.CONTINUE; - } - - @Override - public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { - if (syncedFolder.isExcludeHidden() && dir.compareTo(Paths.get(syncedFolder.getLocalPath())) != 0 && dir.toFile().isHidden()) { - return null; - } - return FileVisitResult.CONTINUE; - } - - @Override - public FileVisitResult visitFileFailed(Path file, IOException exc) { - return FileVisitResult.CONTINUE; - } - }); - Log_OC.d(TAG,"FILESYNC FINISHED LONG CHECK FILE "+path+" "+(System.nanoTime() - startTime)); - - } catch (IOException e) { - Log_OC.e(TAG, "Something went wrong while indexing files for auto upload", e); - } } } } @@ -200,7 +192,7 @@ public final class FilesSyncHelper { return filePath; } - private static void insertContentIntoDB(Uri uri, SyncedFolder syncedFolder) { + private static void insertContentIntoDB(Uri uri, SyncedFolder syncedFolder, long lastCheckMs, long thisCheckMs) { final Context context = MainApp.getAppContext(); final ContentResolver contentResolver = context.getContentResolver(); @@ -231,15 +223,23 @@ public final class FilesSyncHelper { column_index_date_modified = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATE_MODIFIED); while (cursor.moveToNext()) { contentPath = cursor.getString(column_index_data); - Log_OC.d(TAG,"FILESYNC CHECK File "+contentPath); isFolder = new File(contentPath).isDirectory(); + + if (syncedFolder.getLastScanTimestampMs() != SyncedFolder.NOT_SCANNED_YET && + cursor.getLong(column_index_date_modified) < (lastCheckMs / 1000.0)) { + continue; + } + if (syncedFolder.isExisting() || cursor.getLong(column_index_date_modified) >= enabledTimestampMs / 1000.0) { + // storeOrUpdateFileValue takes a few ms + // -> Rest of this file check takes not even 1 ms. filesystemDataProvider.storeOrUpdateFileValue(contentPath, cursor.getLong(column_index_date_modified), isFolder, syncedFolder); } } cursor.close(); + syncedFolder.setLastScanTimestampMs(thisCheckMs); } } From 757306d1c4355bffa2370e459627ec7711f2d9af Mon Sep 17 00:00:00 2001 From: Jonas Mayer Date: Mon, 8 Apr 2024 18:05:59 +0200 Subject: [PATCH 34/74] add new database version Signed-off-by: Jonas Mayer --- .../81.json | 1209 +++++++++++++++++ .../client/database/NextcloudDatabase.kt | 3 +- .../com/owncloud/android/db/ProviderMeta.java | 2 +- 3 files changed, 1212 insertions(+), 2 deletions(-) create mode 100644 app/schemas/com.nextcloud.client.database.NextcloudDatabase/81.json diff --git a/app/schemas/com.nextcloud.client.database.NextcloudDatabase/81.json b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/81.json new file mode 100644 index 0000000000..10d076c10d --- /dev/null +++ b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/81.json @@ -0,0 +1,1209 @@ +{ + "formatVersion": 1, + "database": { + "version": 81, + "identityHash": "082a63031678a67879428f688f02d3b5", + "entities": [ + { + "tableName": "arbitrary_data", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `cloud_id` TEXT, `key` TEXT, `value` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "cloudId", + "columnName": "cloud_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "capabilities", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `assistant` INTEGER, `account` TEXT, `version_mayor` INTEGER, `version_minor` INTEGER, `version_micro` INTEGER, `version_string` TEXT, `version_edition` TEXT, `extended_support` INTEGER, `core_pollinterval` INTEGER, `sharing_api_enabled` INTEGER, `sharing_public_enabled` INTEGER, `sharing_public_password_enforced` INTEGER, `sharing_public_expire_date_enabled` INTEGER, `sharing_public_expire_date_days` INTEGER, `sharing_public_expire_date_enforced` INTEGER, `sharing_public_send_mail` INTEGER, `sharing_public_upload` INTEGER, `sharing_user_send_mail` INTEGER, `sharing_resharing` INTEGER, `sharing_federation_outgoing` INTEGER, `sharing_federation_incoming` INTEGER, `files_bigfilechunking` INTEGER, `files_undelete` INTEGER, `files_versioning` INTEGER, `external_links` INTEGER, `server_name` TEXT, `server_color` TEXT, `server_text_color` TEXT, `server_element_color` TEXT, `server_slogan` TEXT, `server_logo` TEXT, `background_url` TEXT, `end_to_end_encryption` INTEGER, `end_to_end_encryption_keys_exist` INTEGER, `end_to_end_encryption_api_version` TEXT, `activity` INTEGER, `background_default` INTEGER, `background_plain` INTEGER, `richdocument` INTEGER, `richdocument_mimetype_list` TEXT, `richdocument_direct_editing` INTEGER, `richdocument_direct_templates` INTEGER, `richdocument_optional_mimetype_list` TEXT, `sharing_public_ask_for_optional_password` INTEGER, `richdocument_product_name` TEXT, `direct_editing_etag` TEXT, `user_status` INTEGER, `user_status_supports_emoji` INTEGER, `etag` TEXT, `files_locking_version` TEXT, `groupfolders` INTEGER, `drop_account` INTEGER, `security_guard` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "assistant", + "columnName": "assistant", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "accountName", + "columnName": "account", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "versionMajor", + "columnName": "version_mayor", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionMinor", + "columnName": "version_minor", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionMicro", + "columnName": "version_micro", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionString", + "columnName": "version_string", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "versionEditor", + "columnName": "version_edition", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "extendedSupport", + "columnName": "extended_support", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "corePollinterval", + "columnName": "core_pollinterval", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingApiEnabled", + "columnName": "sharing_api_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicEnabled", + "columnName": "sharing_public_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicPasswordEnforced", + "columnName": "sharing_public_password_enforced", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateEnabled", + "columnName": "sharing_public_expire_date_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateDays", + "columnName": "sharing_public_expire_date_days", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateEnforced", + "columnName": "sharing_public_expire_date_enforced", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicSendMail", + "columnName": "sharing_public_send_mail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicUpload", + "columnName": "sharing_public_upload", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingUserSendMail", + "columnName": "sharing_user_send_mail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingResharing", + "columnName": "sharing_resharing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingFederationOutgoing", + "columnName": "sharing_federation_outgoing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingFederationIncoming", + "columnName": "sharing_federation_incoming", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesBigfilechunking", + "columnName": "files_bigfilechunking", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesUndelete", + "columnName": "files_undelete", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesVersioning", + "columnName": "files_versioning", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "externalLinks", + "columnName": "external_links", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverName", + "columnName": "server_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverColor", + "columnName": "server_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverTextColor", + "columnName": "server_text_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverElementColor", + "columnName": "server_element_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverSlogan", + "columnName": "server_slogan", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverLogo", + "columnName": "server_logo", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverBackgroundUrl", + "columnName": "background_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "endToEndEncryption", + "columnName": "end_to_end_encryption", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "endToEndEncryptionKeysExist", + "columnName": "end_to_end_encryption_keys_exist", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "endToEndEncryptionApiVersion", + "columnName": "end_to_end_encryption_api_version", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "activity", + "columnName": "activity", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverBackgroundDefault", + "columnName": "background_default", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverBackgroundPlain", + "columnName": "background_plain", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocument", + "columnName": "richdocument", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentMimetypeList", + "columnName": "richdocument_mimetype_list", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "richdocumentDirectEditing", + "columnName": "richdocument_direct_editing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentTemplates", + "columnName": "richdocument_direct_templates", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentOptionalMimetypeList", + "columnName": "richdocument_optional_mimetype_list", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharingPublicAskForOptionalPassword", + "columnName": "sharing_public_ask_for_optional_password", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentProductName", + "columnName": "richdocument_product_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "directEditingEtag", + "columnName": "direct_editing_etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userStatus", + "columnName": "user_status", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userStatusSupportsEmoji", + "columnName": "user_status_supports_emoji", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "filesLockingVersion", + "columnName": "files_locking_version", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "groupfolders", + "columnName": "groupfolders", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "dropAccount", + "columnName": "drop_account", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "securityGuard", + "columnName": "security_guard", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "external_links", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `icon_url` TEXT, `language` TEXT, `type` INTEGER, `name` TEXT, `url` TEXT, `redirect` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "iconUrl", + "columnName": "icon_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "language", + "columnName": "language", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "redirect", + "columnName": "redirect", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "filelist", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `filename` TEXT, `encrypted_filename` TEXT, `path` TEXT, `path_decrypted` TEXT, `parent` INTEGER, `created` INTEGER, `modified` INTEGER, `content_type` TEXT, `content_length` INTEGER, `media_path` TEXT, `file_owner` TEXT, `last_sync_date` INTEGER, `last_sync_date_for_data` INTEGER, `modified_at_last_sync_for_data` INTEGER, `etag` TEXT, `etag_on_server` TEXT, `share_by_link` INTEGER, `permissions` TEXT, `remote_id` TEXT, `local_id` INTEGER NOT NULL DEFAULT -1, `update_thumbnail` INTEGER, `is_downloading` INTEGER, `favorite` INTEGER, `hidden` INTEGER, `is_encrypted` INTEGER, `etag_in_conflict` TEXT, `shared_via_users` INTEGER, `mount_type` INTEGER, `has_preview` INTEGER, `unread_comments_count` INTEGER, `owner_id` TEXT, `owner_display_name` TEXT, `note` TEXT, `sharees` TEXT, `rich_workspace` TEXT, `metadata_size` TEXT, `metadata_live_photo` TEXT, `locked` INTEGER, `lock_type` INTEGER, `lock_owner` TEXT, `lock_owner_display_name` TEXT, `lock_owner_editor` TEXT, `lock_timestamp` INTEGER, `lock_timeout` INTEGER, `lock_token` TEXT, `tags` TEXT, `metadata_gps` TEXT, `e2e_counter` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "filename", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "encryptedName", + "columnName": "encrypted_filename", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pathDecrypted", + "columnName": "path_decrypted", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "parent", + "columnName": "parent", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "creation", + "columnName": "created", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "modified", + "columnName": "modified", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "contentType", + "columnName": "content_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "contentLength", + "columnName": "content_length", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "storagePath", + "columnName": "media_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "accountOwner", + "columnName": "file_owner", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lastSyncDate", + "columnName": "last_sync_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastSyncDateForData", + "columnName": "last_sync_date_for_data", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "modifiedAtLastSyncForData", + "columnName": "modified_at_last_sync_for_data", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "etagOnServer", + "columnName": "etag_on_server", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharedViaLink", + "columnName": "share_by_link", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remoteId", + "columnName": "remote_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "localId", + "columnName": "local_id", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "updateThumbnail", + "columnName": "update_thumbnail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isDownloading", + "columnName": "is_downloading", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "favorite", + "columnName": "favorite", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isEncrypted", + "columnName": "is_encrypted", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etagInConflict", + "columnName": "etag_in_conflict", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharedWithSharee", + "columnName": "shared_via_users", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "mountType", + "columnName": "mount_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hasPreview", + "columnName": "has_preview", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "unreadCommentsCount", + "columnName": "unread_comments_count", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "ownerId", + "columnName": "owner_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ownerDisplayName", + "columnName": "owner_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharees", + "columnName": "sharees", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "richWorkspace", + "columnName": "rich_workspace", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataSize", + "columnName": "metadata_size", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataLivePhoto", + "columnName": "metadata_live_photo", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "locked", + "columnName": "locked", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockType", + "columnName": "lock_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockOwner", + "columnName": "lock_owner", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockOwnerDisplayName", + "columnName": "lock_owner_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockOwnerEditor", + "columnName": "lock_owner_editor", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockTimestamp", + "columnName": "lock_timestamp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockTimeout", + "columnName": "lock_timeout", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockToken", + "columnName": "lock_token", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "tags", + "columnName": "tags", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataGPS", + "columnName": "metadata_gps", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "e2eCounter", + "columnName": "e2e_counter", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "filesystem", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `is_folder` INTEGER, `found_at` INTEGER, `upload_triggered` INTEGER, `syncedfolder_id` TEXT, `crc32` TEXT, `modified_at` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileIsFolder", + "columnName": "is_folder", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileFoundRecently", + "columnName": "found_at", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileSentForUpload", + "columnName": "upload_triggered", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "syncedFolderId", + "columnName": "syncedfolder_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "crc32", + "columnName": "crc32", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileModified", + "columnName": "modified_at", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ocshares", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `file_source` INTEGER, `item_source` INTEGER, `share_type` INTEGER, `shate_with` TEXT, `path` TEXT, `permissions` INTEGER, `shared_date` INTEGER, `expiration_date` INTEGER, `token` TEXT, `shared_with_display_name` TEXT, `is_directory` INTEGER, `user_id` TEXT, `id_remote_shared` INTEGER, `owner_share` TEXT, `is_password_protected` INTEGER, `note` TEXT, `hide_download` INTEGER, `share_link` TEXT, `share_label` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileSource", + "columnName": "file_source", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "itemSource", + "columnName": "item_source", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareType", + "columnName": "share_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareWith", + "columnName": "shate_with", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharedDate", + "columnName": "shared_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "expirationDate", + "columnName": "expiration_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "token", + "columnName": "token", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "shareWithDisplayName", + "columnName": "shared_with_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isDirectory", + "columnName": "is_directory", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userId", + "columnName": "user_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "idRemoteShared", + "columnName": "id_remote_shared", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "accountOwner", + "columnName": "owner_share", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isPasswordProtected", + "columnName": "is_password_protected", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "hideDownload", + "columnName": "hide_download", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareLink", + "columnName": "share_link", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "shareLabel", + "columnName": "share_label", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "synced_folders", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `wifi_only` INTEGER, `charging_only` INTEGER, `existing` INTEGER, `enabled` INTEGER, `enabled_timestamp_ms` INTEGER, `subfolder_by_date` INTEGER, `account` TEXT, `upload_option` INTEGER, `name_collision_policy` INTEGER, `type` INTEGER, `hidden` INTEGER, `sub_folder_rule` INTEGER, `exclude_hidden` INTEGER, `last_scan_timestamp_ms` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remotePath", + "columnName": "remote_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "wifiOnly", + "columnName": "wifi_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "chargingOnly", + "columnName": "charging_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "existing", + "columnName": "existing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enabled", + "columnName": "enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enabledTimestampMs", + "columnName": "enabled_timestamp_ms", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "subfolderByDate", + "columnName": "subfolder_by_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "account", + "columnName": "account", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uploadAction", + "columnName": "upload_option", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "nameCollisionPolicy", + "columnName": "name_collision_policy", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "subFolderRule", + "columnName": "sub_folder_rule", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "excludeHidden", + "columnName": "exclude_hidden", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastScanTimestampMs", + "columnName": "last_scan_timestamp_ms", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "list_of_uploads", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `account_name` TEXT, `file_size` INTEGER, `status` INTEGER, `local_behaviour` INTEGER, `upload_time` INTEGER, `name_collision_policy` INTEGER, `is_create_remote_folder` INTEGER, `upload_end_timestamp` INTEGER, `last_result` INTEGER, `is_while_charging_only` INTEGER, `is_wifi_only` INTEGER, `created_by` INTEGER, `folder_unlock_token` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remotePath", + "columnName": "remote_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "accountName", + "columnName": "account_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileSize", + "columnName": "file_size", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localBehaviour", + "columnName": "local_behaviour", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "uploadTime", + "columnName": "upload_time", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "nameCollisionPolicy", + "columnName": "name_collision_policy", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isCreateRemoteFolder", + "columnName": "is_create_remote_folder", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "uploadEndTimestamp", + "columnName": "upload_end_timestamp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastResult", + "columnName": "last_result", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isWhileChargingOnly", + "columnName": "is_while_charging_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isWifiOnly", + "columnName": "is_wifi_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "createdBy", + "columnName": "created_by", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "folderUnlockToken", + "columnName": "folder_unlock_token", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "virtual", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `type` TEXT, `ocfile_id` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ocFileId", + "columnName": "ocfile_id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '082a63031678a67879428f688f02d3b5')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt b/app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt index f9386dc30f..db98e777db 100644 --- a/app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt +++ b/app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt @@ -58,7 +58,8 @@ import com.owncloud.android.db.ProviderMeta AutoMigration(from = 76, to = 77), AutoMigration(from = 77, to = 78), AutoMigration(from = 78, to = 79, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class), - AutoMigration(from = 79, to = 80) + AutoMigration(from = 79, to = 80), + AutoMigration(from = 80, to = 81) ], exportSchema = true ) diff --git a/app/src/main/java/com/owncloud/android/db/ProviderMeta.java b/app/src/main/java/com/owncloud/android/db/ProviderMeta.java index ee61c1dd2c..a22d5d8400 100644 --- a/app/src/main/java/com/owncloud/android/db/ProviderMeta.java +++ b/app/src/main/java/com/owncloud/android/db/ProviderMeta.java @@ -25,7 +25,7 @@ import java.util.List; */ public class ProviderMeta { public static final String DB_NAME = "filelist"; - public static final int DB_VERSION = 80; + public static final int DB_VERSION = 81; private ProviderMeta() { // No instance From 883a9912646613019c5d8a8ecf1b3e7fb3fca303 Mon Sep 17 00:00:00 2001 From: Jonas Mayer Date: Mon, 8 Apr 2024 18:14:55 +0200 Subject: [PATCH 35/74] remove not used var Signed-off-by: Jonas Mayer --- app/src/main/java/com/nextcloud/client/jobs/FilesSyncWork.kt | 1 - 1 file changed, 1 deletion(-) 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 1ab6f8ce46..2dfe8babd4 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/FilesSyncWork.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/FilesSyncWork.kt @@ -62,7 +62,6 @@ class FilesSyncWork( companion object { const val TAG = "FilesSyncJob" - const val SKIP_CUSTOM = "skipCustom" const val OVERRIDE_POWER_SAVING = "overridePowerSaving" const val CHANGED_FILES = "changedFiles" const val FOREGROUND_SERVICE_ID = 414 From 36f7b50f0ed23206780dc94aa08ee8b6f53f5f35 Mon Sep 17 00:00:00 2001 From: Jonas Mayer Date: Wed, 10 Apr 2024 13:11:49 +0200 Subject: [PATCH 36/74] Potentially broken database Signed-off-by: Jonas Mayer --- .../81.json | 1209 ----------------- .../client/SyncedFoldersActivityIT.java | 4 +- .../client/jobs/ContentObserverWork.kt | 2 + 3 files changed, 5 insertions(+), 1210 deletions(-) delete mode 100644 app/schemas/com.nextcloud.client.database.NextcloudDatabase/81.json diff --git a/app/schemas/com.nextcloud.client.database.NextcloudDatabase/81.json b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/81.json deleted file mode 100644 index 10d076c10d..0000000000 --- a/app/schemas/com.nextcloud.client.database.NextcloudDatabase/81.json +++ /dev/null @@ -1,1209 +0,0 @@ -{ - "formatVersion": 1, - "database": { - "version": 81, - "identityHash": "082a63031678a67879428f688f02d3b5", - "entities": [ - { - "tableName": "arbitrary_data", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `cloud_id` TEXT, `key` TEXT, `value` TEXT)", - "fields": [ - { - "fieldPath": "id", - "columnName": "_id", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "cloudId", - "columnName": "cloud_id", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "key", - "columnName": "key", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "value", - "columnName": "value", - "affinity": "TEXT", - "notNull": false - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "_id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "capabilities", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `assistant` INTEGER, `account` TEXT, `version_mayor` INTEGER, `version_minor` INTEGER, `version_micro` INTEGER, `version_string` TEXT, `version_edition` TEXT, `extended_support` INTEGER, `core_pollinterval` INTEGER, `sharing_api_enabled` INTEGER, `sharing_public_enabled` INTEGER, `sharing_public_password_enforced` INTEGER, `sharing_public_expire_date_enabled` INTEGER, `sharing_public_expire_date_days` INTEGER, `sharing_public_expire_date_enforced` INTEGER, `sharing_public_send_mail` INTEGER, `sharing_public_upload` INTEGER, `sharing_user_send_mail` INTEGER, `sharing_resharing` INTEGER, `sharing_federation_outgoing` INTEGER, `sharing_federation_incoming` INTEGER, `files_bigfilechunking` INTEGER, `files_undelete` INTEGER, `files_versioning` INTEGER, `external_links` INTEGER, `server_name` TEXT, `server_color` TEXT, `server_text_color` TEXT, `server_element_color` TEXT, `server_slogan` TEXT, `server_logo` TEXT, `background_url` TEXT, `end_to_end_encryption` INTEGER, `end_to_end_encryption_keys_exist` INTEGER, `end_to_end_encryption_api_version` TEXT, `activity` INTEGER, `background_default` INTEGER, `background_plain` INTEGER, `richdocument` INTEGER, `richdocument_mimetype_list` TEXT, `richdocument_direct_editing` INTEGER, `richdocument_direct_templates` INTEGER, `richdocument_optional_mimetype_list` TEXT, `sharing_public_ask_for_optional_password` INTEGER, `richdocument_product_name` TEXT, `direct_editing_etag` TEXT, `user_status` INTEGER, `user_status_supports_emoji` INTEGER, `etag` TEXT, `files_locking_version` TEXT, `groupfolders` INTEGER, `drop_account` INTEGER, `security_guard` INTEGER)", - "fields": [ - { - "fieldPath": "id", - "columnName": "_id", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "assistant", - "columnName": "assistant", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "accountName", - "columnName": "account", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "versionMajor", - "columnName": "version_mayor", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "versionMinor", - "columnName": "version_minor", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "versionMicro", - "columnName": "version_micro", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "versionString", - "columnName": "version_string", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "versionEditor", - "columnName": "version_edition", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "extendedSupport", - "columnName": "extended_support", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "corePollinterval", - "columnName": "core_pollinterval", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "sharingApiEnabled", - "columnName": "sharing_api_enabled", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "sharingPublicEnabled", - "columnName": "sharing_public_enabled", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "sharingPublicPasswordEnforced", - "columnName": "sharing_public_password_enforced", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "sharingPublicExpireDateEnabled", - "columnName": "sharing_public_expire_date_enabled", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "sharingPublicExpireDateDays", - "columnName": "sharing_public_expire_date_days", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "sharingPublicExpireDateEnforced", - "columnName": "sharing_public_expire_date_enforced", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "sharingPublicSendMail", - "columnName": "sharing_public_send_mail", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "sharingPublicUpload", - "columnName": "sharing_public_upload", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "sharingUserSendMail", - "columnName": "sharing_user_send_mail", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "sharingResharing", - "columnName": "sharing_resharing", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "sharingFederationOutgoing", - "columnName": "sharing_federation_outgoing", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "sharingFederationIncoming", - "columnName": "sharing_federation_incoming", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "filesBigfilechunking", - "columnName": "files_bigfilechunking", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "filesUndelete", - "columnName": "files_undelete", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "filesVersioning", - "columnName": "files_versioning", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "externalLinks", - "columnName": "external_links", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "serverName", - "columnName": "server_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "serverColor", - "columnName": "server_color", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "serverTextColor", - "columnName": "server_text_color", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "serverElementColor", - "columnName": "server_element_color", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "serverSlogan", - "columnName": "server_slogan", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "serverLogo", - "columnName": "server_logo", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "serverBackgroundUrl", - "columnName": "background_url", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "endToEndEncryption", - "columnName": "end_to_end_encryption", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "endToEndEncryptionKeysExist", - "columnName": "end_to_end_encryption_keys_exist", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "endToEndEncryptionApiVersion", - "columnName": "end_to_end_encryption_api_version", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "activity", - "columnName": "activity", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "serverBackgroundDefault", - "columnName": "background_default", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "serverBackgroundPlain", - "columnName": "background_plain", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "richdocument", - "columnName": "richdocument", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "richdocumentMimetypeList", - "columnName": "richdocument_mimetype_list", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "richdocumentDirectEditing", - "columnName": "richdocument_direct_editing", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "richdocumentTemplates", - "columnName": "richdocument_direct_templates", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "richdocumentOptionalMimetypeList", - "columnName": "richdocument_optional_mimetype_list", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "sharingPublicAskForOptionalPassword", - "columnName": "sharing_public_ask_for_optional_password", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "richdocumentProductName", - "columnName": "richdocument_product_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "directEditingEtag", - "columnName": "direct_editing_etag", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "userStatus", - "columnName": "user_status", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "userStatusSupportsEmoji", - "columnName": "user_status_supports_emoji", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "etag", - "columnName": "etag", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "filesLockingVersion", - "columnName": "files_locking_version", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "groupfolders", - "columnName": "groupfolders", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "dropAccount", - "columnName": "drop_account", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "securityGuard", - "columnName": "security_guard", - "affinity": "INTEGER", - "notNull": false - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "_id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "external_links", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `icon_url` TEXT, `language` TEXT, `type` INTEGER, `name` TEXT, `url` TEXT, `redirect` INTEGER)", - "fields": [ - { - "fieldPath": "id", - "columnName": "_id", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "iconUrl", - "columnName": "icon_url", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "language", - "columnName": "language", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "url", - "columnName": "url", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "redirect", - "columnName": "redirect", - "affinity": "INTEGER", - "notNull": false - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "_id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "filelist", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `filename` TEXT, `encrypted_filename` TEXT, `path` TEXT, `path_decrypted` TEXT, `parent` INTEGER, `created` INTEGER, `modified` INTEGER, `content_type` TEXT, `content_length` INTEGER, `media_path` TEXT, `file_owner` TEXT, `last_sync_date` INTEGER, `last_sync_date_for_data` INTEGER, `modified_at_last_sync_for_data` INTEGER, `etag` TEXT, `etag_on_server` TEXT, `share_by_link` INTEGER, `permissions` TEXT, `remote_id` TEXT, `local_id` INTEGER NOT NULL DEFAULT -1, `update_thumbnail` INTEGER, `is_downloading` INTEGER, `favorite` INTEGER, `hidden` INTEGER, `is_encrypted` INTEGER, `etag_in_conflict` TEXT, `shared_via_users` INTEGER, `mount_type` INTEGER, `has_preview` INTEGER, `unread_comments_count` INTEGER, `owner_id` TEXT, `owner_display_name` TEXT, `note` TEXT, `sharees` TEXT, `rich_workspace` TEXT, `metadata_size` TEXT, `metadata_live_photo` TEXT, `locked` INTEGER, `lock_type` INTEGER, `lock_owner` TEXT, `lock_owner_display_name` TEXT, `lock_owner_editor` TEXT, `lock_timestamp` INTEGER, `lock_timeout` INTEGER, `lock_token` TEXT, `tags` TEXT, `metadata_gps` TEXT, `e2e_counter` INTEGER)", - "fields": [ - { - "fieldPath": "id", - "columnName": "_id", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "name", - "columnName": "filename", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "encryptedName", - "columnName": "encrypted_filename", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "path", - "columnName": "path", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "pathDecrypted", - "columnName": "path_decrypted", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "parent", - "columnName": "parent", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "creation", - "columnName": "created", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "modified", - "columnName": "modified", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "contentType", - "columnName": "content_type", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "contentLength", - "columnName": "content_length", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "storagePath", - "columnName": "media_path", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "accountOwner", - "columnName": "file_owner", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "lastSyncDate", - "columnName": "last_sync_date", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "lastSyncDateForData", - "columnName": "last_sync_date_for_data", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "modifiedAtLastSyncForData", - "columnName": "modified_at_last_sync_for_data", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "etag", - "columnName": "etag", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "etagOnServer", - "columnName": "etag_on_server", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "sharedViaLink", - "columnName": "share_by_link", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "permissions", - "columnName": "permissions", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "remoteId", - "columnName": "remote_id", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "localId", - "columnName": "local_id", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "-1" - }, - { - "fieldPath": "updateThumbnail", - "columnName": "update_thumbnail", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "isDownloading", - "columnName": "is_downloading", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "favorite", - "columnName": "favorite", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "hidden", - "columnName": "hidden", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "isEncrypted", - "columnName": "is_encrypted", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "etagInConflict", - "columnName": "etag_in_conflict", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "sharedWithSharee", - "columnName": "shared_via_users", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "mountType", - "columnName": "mount_type", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "hasPreview", - "columnName": "has_preview", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "unreadCommentsCount", - "columnName": "unread_comments_count", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "ownerId", - "columnName": "owner_id", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "ownerDisplayName", - "columnName": "owner_display_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "note", - "columnName": "note", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "sharees", - "columnName": "sharees", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "richWorkspace", - "columnName": "rich_workspace", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "metadataSize", - "columnName": "metadata_size", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "metadataLivePhoto", - "columnName": "metadata_live_photo", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "locked", - "columnName": "locked", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "lockType", - "columnName": "lock_type", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "lockOwner", - "columnName": "lock_owner", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "lockOwnerDisplayName", - "columnName": "lock_owner_display_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "lockOwnerEditor", - "columnName": "lock_owner_editor", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "lockTimestamp", - "columnName": "lock_timestamp", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "lockTimeout", - "columnName": "lock_timeout", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "lockToken", - "columnName": "lock_token", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "tags", - "columnName": "tags", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "metadataGPS", - "columnName": "metadata_gps", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "e2eCounter", - "columnName": "e2e_counter", - "affinity": "INTEGER", - "notNull": false - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "_id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "filesystem", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `is_folder` INTEGER, `found_at` INTEGER, `upload_triggered` INTEGER, `syncedfolder_id` TEXT, `crc32` TEXT, `modified_at` INTEGER)", - "fields": [ - { - "fieldPath": "id", - "columnName": "_id", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "localPath", - "columnName": "local_path", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "fileIsFolder", - "columnName": "is_folder", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "fileFoundRecently", - "columnName": "found_at", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "fileSentForUpload", - "columnName": "upload_triggered", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "syncedFolderId", - "columnName": "syncedfolder_id", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "crc32", - "columnName": "crc32", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "fileModified", - "columnName": "modified_at", - "affinity": "INTEGER", - "notNull": false - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "_id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "ocshares", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `file_source` INTEGER, `item_source` INTEGER, `share_type` INTEGER, `shate_with` TEXT, `path` TEXT, `permissions` INTEGER, `shared_date` INTEGER, `expiration_date` INTEGER, `token` TEXT, `shared_with_display_name` TEXT, `is_directory` INTEGER, `user_id` TEXT, `id_remote_shared` INTEGER, `owner_share` TEXT, `is_password_protected` INTEGER, `note` TEXT, `hide_download` INTEGER, `share_link` TEXT, `share_label` TEXT)", - "fields": [ - { - "fieldPath": "id", - "columnName": "_id", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "fileSource", - "columnName": "file_source", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "itemSource", - "columnName": "item_source", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "shareType", - "columnName": "share_type", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "shareWith", - "columnName": "shate_with", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "path", - "columnName": "path", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "permissions", - "columnName": "permissions", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "sharedDate", - "columnName": "shared_date", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "expirationDate", - "columnName": "expiration_date", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "token", - "columnName": "token", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "shareWithDisplayName", - "columnName": "shared_with_display_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "isDirectory", - "columnName": "is_directory", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "userId", - "columnName": "user_id", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "idRemoteShared", - "columnName": "id_remote_shared", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "accountOwner", - "columnName": "owner_share", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "isPasswordProtected", - "columnName": "is_password_protected", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "note", - "columnName": "note", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "hideDownload", - "columnName": "hide_download", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "shareLink", - "columnName": "share_link", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "shareLabel", - "columnName": "share_label", - "affinity": "TEXT", - "notNull": false - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "_id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "synced_folders", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `wifi_only` INTEGER, `charging_only` INTEGER, `existing` INTEGER, `enabled` INTEGER, `enabled_timestamp_ms` INTEGER, `subfolder_by_date` INTEGER, `account` TEXT, `upload_option` INTEGER, `name_collision_policy` INTEGER, `type` INTEGER, `hidden` INTEGER, `sub_folder_rule` INTEGER, `exclude_hidden` INTEGER, `last_scan_timestamp_ms` INTEGER)", - "fields": [ - { - "fieldPath": "id", - "columnName": "_id", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "localPath", - "columnName": "local_path", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "remotePath", - "columnName": "remote_path", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "wifiOnly", - "columnName": "wifi_only", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "chargingOnly", - "columnName": "charging_only", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "existing", - "columnName": "existing", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "enabled", - "columnName": "enabled", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "enabledTimestampMs", - "columnName": "enabled_timestamp_ms", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "subfolderByDate", - "columnName": "subfolder_by_date", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "account", - "columnName": "account", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "uploadAction", - "columnName": "upload_option", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "nameCollisionPolicy", - "columnName": "name_collision_policy", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "hidden", - "columnName": "hidden", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "subFolderRule", - "columnName": "sub_folder_rule", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "excludeHidden", - "columnName": "exclude_hidden", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "lastScanTimestampMs", - "columnName": "last_scan_timestamp_ms", - "affinity": "INTEGER", - "notNull": false - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "_id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "list_of_uploads", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `account_name` TEXT, `file_size` INTEGER, `status` INTEGER, `local_behaviour` INTEGER, `upload_time` INTEGER, `name_collision_policy` INTEGER, `is_create_remote_folder` INTEGER, `upload_end_timestamp` INTEGER, `last_result` INTEGER, `is_while_charging_only` INTEGER, `is_wifi_only` INTEGER, `created_by` INTEGER, `folder_unlock_token` TEXT)", - "fields": [ - { - "fieldPath": "id", - "columnName": "_id", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "localPath", - "columnName": "local_path", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "remotePath", - "columnName": "remote_path", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "accountName", - "columnName": "account_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "fileSize", - "columnName": "file_size", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "status", - "columnName": "status", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "localBehaviour", - "columnName": "local_behaviour", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "uploadTime", - "columnName": "upload_time", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "nameCollisionPolicy", - "columnName": "name_collision_policy", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "isCreateRemoteFolder", - "columnName": "is_create_remote_folder", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "uploadEndTimestamp", - "columnName": "upload_end_timestamp", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "lastResult", - "columnName": "last_result", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "isWhileChargingOnly", - "columnName": "is_while_charging_only", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "isWifiOnly", - "columnName": "is_wifi_only", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "createdBy", - "columnName": "created_by", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "folderUnlockToken", - "columnName": "folder_unlock_token", - "affinity": "TEXT", - "notNull": false - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "_id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "virtual", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `type` TEXT, `ocfile_id` INTEGER)", - "fields": [ - { - "fieldPath": "id", - "columnName": "_id", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "ocFileId", - "columnName": "ocfile_id", - "affinity": "INTEGER", - "notNull": false - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "_id" - ] - }, - "indices": [], - "foreignKeys": [] - } - ], - "views": [], - "setupQueries": [ - "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '082a63031678a67879428f688f02d3b5')" - ] - } -} \ No newline at end of file diff --git a/app/src/androidTest/java/com/nextcloud/client/SyncedFoldersActivityIT.java b/app/src/androidTest/java/com/nextcloud/client/SyncedFoldersActivityIT.java index 57e97d8ccd..ba43a43af8 100644 --- a/app/src/androidTest/java/com/nextcloud/client/SyncedFoldersActivityIT.java +++ b/app/src/androidTest/java/com/nextcloud/client/SyncedFoldersActivityIT.java @@ -14,6 +14,7 @@ import com.nextcloud.client.preferences.SubFolderRule; import com.owncloud.android.AbstractIT; import com.owncloud.android.databinding.SyncedFoldersLayoutBinding; import com.owncloud.android.datamodel.MediaFolderType; +import com.owncloud.android.datamodel.SyncedFolder; import com.owncloud.android.datamodel.SyncedFolderDisplayItem; import com.owncloud.android.ui.activity.SyncedFoldersActivity; import com.owncloud.android.ui.dialog.SyncedFolderPreferencesDialogFragment; @@ -63,7 +64,8 @@ public class SyncedFoldersActivityIT extends AbstractIT { MediaFolderType.IMAGE, false, SubFolderRule.YEAR_MONTH, - false); + false, + SyncedFolder.NOT_SCANNED_YET); SyncedFolderPreferencesDialogFragment sut = SyncedFolderPreferencesDialogFragment.newInstance(item, 0); Intent intent = new Intent(targetContext, SyncedFoldersActivity.class); diff --git a/app/src/main/java/com/nextcloud/client/jobs/ContentObserverWork.kt b/app/src/main/java/com/nextcloud/client/jobs/ContentObserverWork.kt index caac611f7b..abc768e600 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/ContentObserverWork.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/ContentObserverWork.kt @@ -11,6 +11,7 @@ import androidx.work.Worker import androidx.work.WorkerParameters import com.nextcloud.client.device.PowerManagementService import com.owncloud.android.datamodel.SyncedFolderProvider +import com.owncloud.android.lib.common.utils.Log_OC /** * This work is triggered when OS detects change in media folders. @@ -31,6 +32,7 @@ class ContentObserverWork( backgroundJobManager.logStartOfWorker(BackgroundJobManagerImpl.formatClassTag(this::class)) if (params.triggeredContentUris.size > 0) { + Log_OC.d(TAG,"FILESYNC Content Observer detected files change") checkAndStartFileSyncJob() backgroundJobManager.startMediaFoldersDetectionJob() } From b801796aeb43aa482d15234a06b4def7be2db4ea Mon Sep 17 00:00:00 2001 From: Jonas Mayer Date: Wed, 10 Apr 2024 15:04:44 +0200 Subject: [PATCH 37/74] add 81.json again Signed-off-by: Jonas Mayer --- .../81.json | 1209 +++++++++++++++++ 1 file changed, 1209 insertions(+) create mode 100644 app/schemas/com.nextcloud.client.database.NextcloudDatabase/81.json diff --git a/app/schemas/com.nextcloud.client.database.NextcloudDatabase/81.json b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/81.json new file mode 100644 index 0000000000..10d076c10d --- /dev/null +++ b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/81.json @@ -0,0 +1,1209 @@ +{ + "formatVersion": 1, + "database": { + "version": 81, + "identityHash": "082a63031678a67879428f688f02d3b5", + "entities": [ + { + "tableName": "arbitrary_data", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `cloud_id` TEXT, `key` TEXT, `value` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "cloudId", + "columnName": "cloud_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "capabilities", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `assistant` INTEGER, `account` TEXT, `version_mayor` INTEGER, `version_minor` INTEGER, `version_micro` INTEGER, `version_string` TEXT, `version_edition` TEXT, `extended_support` INTEGER, `core_pollinterval` INTEGER, `sharing_api_enabled` INTEGER, `sharing_public_enabled` INTEGER, `sharing_public_password_enforced` INTEGER, `sharing_public_expire_date_enabled` INTEGER, `sharing_public_expire_date_days` INTEGER, `sharing_public_expire_date_enforced` INTEGER, `sharing_public_send_mail` INTEGER, `sharing_public_upload` INTEGER, `sharing_user_send_mail` INTEGER, `sharing_resharing` INTEGER, `sharing_federation_outgoing` INTEGER, `sharing_federation_incoming` INTEGER, `files_bigfilechunking` INTEGER, `files_undelete` INTEGER, `files_versioning` INTEGER, `external_links` INTEGER, `server_name` TEXT, `server_color` TEXT, `server_text_color` TEXT, `server_element_color` TEXT, `server_slogan` TEXT, `server_logo` TEXT, `background_url` TEXT, `end_to_end_encryption` INTEGER, `end_to_end_encryption_keys_exist` INTEGER, `end_to_end_encryption_api_version` TEXT, `activity` INTEGER, `background_default` INTEGER, `background_plain` INTEGER, `richdocument` INTEGER, `richdocument_mimetype_list` TEXT, `richdocument_direct_editing` INTEGER, `richdocument_direct_templates` INTEGER, `richdocument_optional_mimetype_list` TEXT, `sharing_public_ask_for_optional_password` INTEGER, `richdocument_product_name` TEXT, `direct_editing_etag` TEXT, `user_status` INTEGER, `user_status_supports_emoji` INTEGER, `etag` TEXT, `files_locking_version` TEXT, `groupfolders` INTEGER, `drop_account` INTEGER, `security_guard` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "assistant", + "columnName": "assistant", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "accountName", + "columnName": "account", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "versionMajor", + "columnName": "version_mayor", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionMinor", + "columnName": "version_minor", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionMicro", + "columnName": "version_micro", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionString", + "columnName": "version_string", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "versionEditor", + "columnName": "version_edition", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "extendedSupport", + "columnName": "extended_support", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "corePollinterval", + "columnName": "core_pollinterval", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingApiEnabled", + "columnName": "sharing_api_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicEnabled", + "columnName": "sharing_public_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicPasswordEnforced", + "columnName": "sharing_public_password_enforced", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateEnabled", + "columnName": "sharing_public_expire_date_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateDays", + "columnName": "sharing_public_expire_date_days", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateEnforced", + "columnName": "sharing_public_expire_date_enforced", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicSendMail", + "columnName": "sharing_public_send_mail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicUpload", + "columnName": "sharing_public_upload", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingUserSendMail", + "columnName": "sharing_user_send_mail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingResharing", + "columnName": "sharing_resharing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingFederationOutgoing", + "columnName": "sharing_federation_outgoing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingFederationIncoming", + "columnName": "sharing_federation_incoming", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesBigfilechunking", + "columnName": "files_bigfilechunking", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesUndelete", + "columnName": "files_undelete", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesVersioning", + "columnName": "files_versioning", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "externalLinks", + "columnName": "external_links", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverName", + "columnName": "server_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverColor", + "columnName": "server_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverTextColor", + "columnName": "server_text_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverElementColor", + "columnName": "server_element_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverSlogan", + "columnName": "server_slogan", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverLogo", + "columnName": "server_logo", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverBackgroundUrl", + "columnName": "background_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "endToEndEncryption", + "columnName": "end_to_end_encryption", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "endToEndEncryptionKeysExist", + "columnName": "end_to_end_encryption_keys_exist", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "endToEndEncryptionApiVersion", + "columnName": "end_to_end_encryption_api_version", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "activity", + "columnName": "activity", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverBackgroundDefault", + "columnName": "background_default", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverBackgroundPlain", + "columnName": "background_plain", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocument", + "columnName": "richdocument", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentMimetypeList", + "columnName": "richdocument_mimetype_list", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "richdocumentDirectEditing", + "columnName": "richdocument_direct_editing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentTemplates", + "columnName": "richdocument_direct_templates", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentOptionalMimetypeList", + "columnName": "richdocument_optional_mimetype_list", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharingPublicAskForOptionalPassword", + "columnName": "sharing_public_ask_for_optional_password", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentProductName", + "columnName": "richdocument_product_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "directEditingEtag", + "columnName": "direct_editing_etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userStatus", + "columnName": "user_status", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userStatusSupportsEmoji", + "columnName": "user_status_supports_emoji", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "filesLockingVersion", + "columnName": "files_locking_version", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "groupfolders", + "columnName": "groupfolders", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "dropAccount", + "columnName": "drop_account", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "securityGuard", + "columnName": "security_guard", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "external_links", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `icon_url` TEXT, `language` TEXT, `type` INTEGER, `name` TEXT, `url` TEXT, `redirect` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "iconUrl", + "columnName": "icon_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "language", + "columnName": "language", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "redirect", + "columnName": "redirect", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "filelist", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `filename` TEXT, `encrypted_filename` TEXT, `path` TEXT, `path_decrypted` TEXT, `parent` INTEGER, `created` INTEGER, `modified` INTEGER, `content_type` TEXT, `content_length` INTEGER, `media_path` TEXT, `file_owner` TEXT, `last_sync_date` INTEGER, `last_sync_date_for_data` INTEGER, `modified_at_last_sync_for_data` INTEGER, `etag` TEXT, `etag_on_server` TEXT, `share_by_link` INTEGER, `permissions` TEXT, `remote_id` TEXT, `local_id` INTEGER NOT NULL DEFAULT -1, `update_thumbnail` INTEGER, `is_downloading` INTEGER, `favorite` INTEGER, `hidden` INTEGER, `is_encrypted` INTEGER, `etag_in_conflict` TEXT, `shared_via_users` INTEGER, `mount_type` INTEGER, `has_preview` INTEGER, `unread_comments_count` INTEGER, `owner_id` TEXT, `owner_display_name` TEXT, `note` TEXT, `sharees` TEXT, `rich_workspace` TEXT, `metadata_size` TEXT, `metadata_live_photo` TEXT, `locked` INTEGER, `lock_type` INTEGER, `lock_owner` TEXT, `lock_owner_display_name` TEXT, `lock_owner_editor` TEXT, `lock_timestamp` INTEGER, `lock_timeout` INTEGER, `lock_token` TEXT, `tags` TEXT, `metadata_gps` TEXT, `e2e_counter` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "filename", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "encryptedName", + "columnName": "encrypted_filename", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pathDecrypted", + "columnName": "path_decrypted", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "parent", + "columnName": "parent", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "creation", + "columnName": "created", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "modified", + "columnName": "modified", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "contentType", + "columnName": "content_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "contentLength", + "columnName": "content_length", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "storagePath", + "columnName": "media_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "accountOwner", + "columnName": "file_owner", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lastSyncDate", + "columnName": "last_sync_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastSyncDateForData", + "columnName": "last_sync_date_for_data", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "modifiedAtLastSyncForData", + "columnName": "modified_at_last_sync_for_data", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "etagOnServer", + "columnName": "etag_on_server", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharedViaLink", + "columnName": "share_by_link", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remoteId", + "columnName": "remote_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "localId", + "columnName": "local_id", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "updateThumbnail", + "columnName": "update_thumbnail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isDownloading", + "columnName": "is_downloading", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "favorite", + "columnName": "favorite", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isEncrypted", + "columnName": "is_encrypted", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etagInConflict", + "columnName": "etag_in_conflict", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharedWithSharee", + "columnName": "shared_via_users", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "mountType", + "columnName": "mount_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hasPreview", + "columnName": "has_preview", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "unreadCommentsCount", + "columnName": "unread_comments_count", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "ownerId", + "columnName": "owner_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ownerDisplayName", + "columnName": "owner_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharees", + "columnName": "sharees", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "richWorkspace", + "columnName": "rich_workspace", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataSize", + "columnName": "metadata_size", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataLivePhoto", + "columnName": "metadata_live_photo", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "locked", + "columnName": "locked", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockType", + "columnName": "lock_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockOwner", + "columnName": "lock_owner", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockOwnerDisplayName", + "columnName": "lock_owner_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockOwnerEditor", + "columnName": "lock_owner_editor", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockTimestamp", + "columnName": "lock_timestamp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockTimeout", + "columnName": "lock_timeout", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockToken", + "columnName": "lock_token", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "tags", + "columnName": "tags", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataGPS", + "columnName": "metadata_gps", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "e2eCounter", + "columnName": "e2e_counter", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "filesystem", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `is_folder` INTEGER, `found_at` INTEGER, `upload_triggered` INTEGER, `syncedfolder_id` TEXT, `crc32` TEXT, `modified_at` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileIsFolder", + "columnName": "is_folder", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileFoundRecently", + "columnName": "found_at", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileSentForUpload", + "columnName": "upload_triggered", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "syncedFolderId", + "columnName": "syncedfolder_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "crc32", + "columnName": "crc32", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileModified", + "columnName": "modified_at", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ocshares", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `file_source` INTEGER, `item_source` INTEGER, `share_type` INTEGER, `shate_with` TEXT, `path` TEXT, `permissions` INTEGER, `shared_date` INTEGER, `expiration_date` INTEGER, `token` TEXT, `shared_with_display_name` TEXT, `is_directory` INTEGER, `user_id` TEXT, `id_remote_shared` INTEGER, `owner_share` TEXT, `is_password_protected` INTEGER, `note` TEXT, `hide_download` INTEGER, `share_link` TEXT, `share_label` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileSource", + "columnName": "file_source", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "itemSource", + "columnName": "item_source", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareType", + "columnName": "share_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareWith", + "columnName": "shate_with", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharedDate", + "columnName": "shared_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "expirationDate", + "columnName": "expiration_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "token", + "columnName": "token", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "shareWithDisplayName", + "columnName": "shared_with_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isDirectory", + "columnName": "is_directory", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userId", + "columnName": "user_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "idRemoteShared", + "columnName": "id_remote_shared", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "accountOwner", + "columnName": "owner_share", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isPasswordProtected", + "columnName": "is_password_protected", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "hideDownload", + "columnName": "hide_download", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareLink", + "columnName": "share_link", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "shareLabel", + "columnName": "share_label", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "synced_folders", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `wifi_only` INTEGER, `charging_only` INTEGER, `existing` INTEGER, `enabled` INTEGER, `enabled_timestamp_ms` INTEGER, `subfolder_by_date` INTEGER, `account` TEXT, `upload_option` INTEGER, `name_collision_policy` INTEGER, `type` INTEGER, `hidden` INTEGER, `sub_folder_rule` INTEGER, `exclude_hidden` INTEGER, `last_scan_timestamp_ms` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remotePath", + "columnName": "remote_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "wifiOnly", + "columnName": "wifi_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "chargingOnly", + "columnName": "charging_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "existing", + "columnName": "existing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enabled", + "columnName": "enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enabledTimestampMs", + "columnName": "enabled_timestamp_ms", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "subfolderByDate", + "columnName": "subfolder_by_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "account", + "columnName": "account", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uploadAction", + "columnName": "upload_option", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "nameCollisionPolicy", + "columnName": "name_collision_policy", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "subFolderRule", + "columnName": "sub_folder_rule", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "excludeHidden", + "columnName": "exclude_hidden", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastScanTimestampMs", + "columnName": "last_scan_timestamp_ms", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "list_of_uploads", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `account_name` TEXT, `file_size` INTEGER, `status` INTEGER, `local_behaviour` INTEGER, `upload_time` INTEGER, `name_collision_policy` INTEGER, `is_create_remote_folder` INTEGER, `upload_end_timestamp` INTEGER, `last_result` INTEGER, `is_while_charging_only` INTEGER, `is_wifi_only` INTEGER, `created_by` INTEGER, `folder_unlock_token` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remotePath", + "columnName": "remote_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "accountName", + "columnName": "account_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileSize", + "columnName": "file_size", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localBehaviour", + "columnName": "local_behaviour", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "uploadTime", + "columnName": "upload_time", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "nameCollisionPolicy", + "columnName": "name_collision_policy", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isCreateRemoteFolder", + "columnName": "is_create_remote_folder", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "uploadEndTimestamp", + "columnName": "upload_end_timestamp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastResult", + "columnName": "last_result", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isWhileChargingOnly", + "columnName": "is_while_charging_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isWifiOnly", + "columnName": "is_wifi_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "createdBy", + "columnName": "created_by", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "folderUnlockToken", + "columnName": "folder_unlock_token", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "virtual", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `type` TEXT, `ocfile_id` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ocFileId", + "columnName": "ocfile_id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '082a63031678a67879428f688f02d3b5')" + ] + } +} \ No newline at end of file From 488113a458f064c34868f7118daec8dfc91a900b Mon Sep 17 00:00:00 2001 From: Jonas Mayer Date: Wed, 10 Apr 2024 17:00:26 +0200 Subject: [PATCH 38/74] Fix infinite content observer issue Signed-off-by: Jonas Mayer --- .../java/com/nextcloud/client/jobs/BackgroundJobManagerTest.kt | 2 +- .../java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/androidTest/java/com/nextcloud/client/jobs/BackgroundJobManagerTest.kt b/app/src/androidTest/java/com/nextcloud/client/jobs/BackgroundJobManagerTest.kt index 21be19bbc2..e90b81ca73 100644 --- a/app/src/androidTest/java/com/nextcloud/client/jobs/BackgroundJobManagerTest.kt +++ b/app/src/androidTest/java/com/nextcloud/client/jobs/BackgroundJobManagerTest.kt @@ -201,7 +201,7 @@ class BackgroundJobManagerTest { fun job_is_unique_and_replaces_previous_job() { verify(workManager).enqueueUniqueWork( eq(BackgroundJobManagerImpl.JOB_CONTENT_OBSERVER), - eq(ExistingWorkPolicy.APPEND), + eq(ExistingWorkPolicy.REPLACE), argThat(IsOneTimeWorkRequest()) ) } 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 945e14ded7..0e3fd88386 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt @@ -264,7 +264,7 @@ internal class BackgroundJobManagerImpl( .setConstraints(constrains) .build() - workManager.enqueueUniqueWork(JOB_CONTENT_OBSERVER, ExistingWorkPolicy.APPEND, request) + workManager.enqueueUniqueWork(JOB_CONTENT_OBSERVER, ExistingWorkPolicy.REPLACE, request) } override fun schedulePeriodicContactsBackup(user: User) { From 914be8e85c0b70ba7f9dbe54d0c587aaaecd1aed Mon Sep 17 00:00:00 2001 From: Jonas Mayer Date: Wed, 10 Apr 2024 17:40:57 +0200 Subject: [PATCH 39/74] fix spotless Signed-off-by: Jonas Mayer --- .../com/nextcloud/client/jobs/ContentObserverWork.kt | 2 +- .../java/com/nextcloud/client/jobs/FilesSyncWork.kt | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/jobs/ContentObserverWork.kt b/app/src/main/java/com/nextcloud/client/jobs/ContentObserverWork.kt index abc768e600..a29674ef9c 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/ContentObserverWork.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/ContentObserverWork.kt @@ -32,7 +32,7 @@ class ContentObserverWork( backgroundJobManager.logStartOfWorker(BackgroundJobManagerImpl.formatClassTag(this::class)) if (params.triggeredContentUris.size > 0) { - Log_OC.d(TAG,"FILESYNC Content Observer detected files change") + Log_OC.d(TAG, "FILESYNC Content Observer detected files change") checkAndStartFileSyncJob() backgroundJobManager.startMediaFoldersDetectionJob() } 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 2dfe8babd4..c95aabe174 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/FilesSyncWork.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/FilesSyncWork.kt @@ -94,7 +94,7 @@ class FilesSyncWork( @Suppress("MagicNumber") override fun doWork(): Result { backgroundJobManager.logStartOfWorker(BackgroundJobManagerImpl.formatClassTag(this::class)) - Log_OC.d(TAG,"FILESYNC WORKER STARTED") + Log_OC.d(TAG, "FILESYNC WORKER STARTED") val overridePowerSaving = inputData.getBoolean(OVERRIDE_POWER_SAVING, false) // If we are in power save mode, better to postpone upload @@ -114,9 +114,9 @@ class FilesSyncWork( // Get changed files from ContentObserverWork (only images and videos) or by scanning filesystem val changedFiles = inputData.getStringArray(CHANGED_FILES) - Log_OC.d(TAG,"FILESYNC WORKER CHANGED FILES: "+changedFiles.contentToString()) + Log_OC.d(TAG, "FILESYNC WORKER CHANGED FILES: " + changedFiles.contentToString()) collectChangedFiles(changedFiles) - Log_OC.d(TAG,"FILESYNC WORKER CHECKED CHANGED FILES") + Log_OC.d(TAG, "FILESYNC WORKER CHECKED CHANGED FILES") // Create all the providers we'll need val filesystemDataProvider = FilesystemDataProvider(contentResolver) @@ -143,7 +143,7 @@ class FilesSyncWork( ) } } - Log_OC.d(TAG,"FILESYNC WORKER ENDED") + Log_OC.d(TAG, "FILESYNC WORKER ENDED") val result = Result.success() backgroundJobManager.logEndOfWorker(BackgroundJobManagerImpl.formatClassTag(this::class), result) return result @@ -221,7 +221,7 @@ class FilesSyncWork( needsWifi = syncedFolder.isWifiOnly uploadAction = syncedFolder.uploadAction } - Log_OC.d(TAG,"FILESYNC SCHEDULE UPLOAD OF FILE") + Log_OC.d(TAG, "FILESYNC SCHEDULE UPLOAD OF FILE") FileUploadHelper.instance().uploadNewFiles( user, localPaths, From 5c4eaac0c294429f2fc589bf67c2ef6c5f4e9850 Mon Sep 17 00:00:00 2001 From: Jonas Mayer Date: Thu, 11 Apr 2024 09:44:42 +0200 Subject: [PATCH 40/74] remove unnecessary log Signed-off-by: Jonas Mayer --- app/src/main/java/com/nextcloud/client/jobs/FilesSyncWork.kt | 1 - 1 file changed, 1 deletion(-) 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 c95aabe174..f45c3542ba 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/FilesSyncWork.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/FilesSyncWork.kt @@ -221,7 +221,6 @@ class FilesSyncWork( needsWifi = syncedFolder.isWifiOnly uploadAction = syncedFolder.uploadAction } - Log_OC.d(TAG, "FILESYNC SCHEDULE UPLOAD OF FILE") FileUploadHelper.instance().uploadNewFiles( user, localPaths, From 4dedc25a06093075eca89506dadaa73d96c9d230 Mon Sep 17 00:00:00 2001 From: Jonas Mayer Date: Tue, 16 Apr 2024 14:58:21 +0200 Subject: [PATCH 41/74] improve logs Signed-off-by: Jonas Mayer --- .../com/nextcloud/client/jobs/ContentObserverWork.kt | 2 +- .../java/com/nextcloud/client/jobs/FilesSyncWork.kt | 8 ++++---- .../com/owncloud/android/utils/FilesSyncHelper.java | 11 +++++------ 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/jobs/ContentObserverWork.kt b/app/src/main/java/com/nextcloud/client/jobs/ContentObserverWork.kt index a29674ef9c..718e527277 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/ContentObserverWork.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/ContentObserverWork.kt @@ -32,7 +32,7 @@ class ContentObserverWork( backgroundJobManager.logStartOfWorker(BackgroundJobManagerImpl.formatClassTag(this::class)) if (params.triggeredContentUris.size > 0) { - Log_OC.d(TAG, "FILESYNC Content Observer detected files change") + Log_OC.d(TAG, "File-sync Content Observer detected files change") checkAndStartFileSyncJob() backgroundJobManager.startMediaFoldersDetectionJob() } 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 f45c3542ba..58b57284ee 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/FilesSyncWork.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/FilesSyncWork.kt @@ -94,7 +94,7 @@ class FilesSyncWork( @Suppress("MagicNumber") override fun doWork(): Result { backgroundJobManager.logStartOfWorker(BackgroundJobManagerImpl.formatClassTag(this::class)) - Log_OC.d(TAG, "FILESYNC WORKER STARTED") + Log_OC.d(TAG, "File-sync worker started") val overridePowerSaving = inputData.getBoolean(OVERRIDE_POWER_SAVING, false) // If we are in power save mode, better to postpone upload @@ -114,9 +114,9 @@ class FilesSyncWork( // Get changed files from ContentObserverWork (only images and videos) or by scanning filesystem val changedFiles = inputData.getStringArray(CHANGED_FILES) - Log_OC.d(TAG, "FILESYNC WORKER CHANGED FILES: " + changedFiles.contentToString()) + Log_OC.d(TAG, "File-sync worker changed files from observer: " + changedFiles.contentToString()) collectChangedFiles(changedFiles) - Log_OC.d(TAG, "FILESYNC WORKER CHECKED CHANGED FILES") + Log_OC.d(TAG, "File-sync worker finished checking files.") // Create all the providers we'll need val filesystemDataProvider = FilesystemDataProvider(contentResolver) @@ -143,7 +143,7 @@ class FilesSyncWork( ) } } - Log_OC.d(TAG, "FILESYNC WORKER ENDED") + Log_OC.d(TAG, "File-sync worker finished") val result = Result.success() backgroundJobManager.logEndOfWorker(BackgroundJobManagerImpl.formatClassTag(this::class), result) return result diff --git a/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java b/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java index 4b597c1d2b..230dbfb775 100644 --- a/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java +++ b/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java @@ -119,6 +119,9 @@ public final class FilesSyncHelper { final long lastCheck = syncedFolder.getLastScanTimestampMs(); final long thisCheck = System.currentTimeMillis(); + Log_OC.d(TAG,"File-sync start check folder "+syncedFolder.getLocalPath()); + long startTime = System.nanoTime(); + if (mediaType == MediaFolderType.IMAGE) { FilesSyncHelper.insertContentIntoDB(MediaStore.Images.Media.INTERNAL_CONTENT_URI, syncedFolder, @@ -136,18 +139,15 @@ public final class FilesSyncHelper { } else { FilesystemDataProvider filesystemDataProvider = new FilesystemDataProvider(contentResolver); Path path = Paths.get(syncedFolder.getLocalPath()); - - long startTime = System.nanoTime(); FilesSyncHelper.insertCustomFolderIntoDB(path, syncedFolder, filesystemDataProvider, lastCheck, thisCheck); - Log_OC.d(TAG,"FILESYNC FINISHED LONG CHECK FOLDER "+path+" "+(System.nanoTime() - startTime)); - } + + Log_OC.d(TAG,"File-sync finished full check for custom folder "+syncedFolder.getLocalPath()+" within "+(System.nanoTime() - startTime)+ "ns"); } } public static void insertAllDBEntries(SyncedFolderProvider syncedFolderProvider) { for (SyncedFolder syncedFolder : syncedFolderProvider.getSyncedFolders()) { - Log_OC.d(TAG,"FILESYNC CHECK FOLDER "+syncedFolder.getLocalPath()); if (syncedFolder.isEnabled()) { insertAllDBEntriesForSyncedFolder(syncedFolder); } @@ -166,7 +166,6 @@ public final class FilesSyncHelper { filesystemDataProvider.storeOrUpdateFileValue(changedFile, file.lastModified(),file.isDirectory(), syncedFolder); - Log_OC.d(TAG,"FILESYNC ADDED UPLOAD TO DB"); break; } } From adba5bfb0e52ffb508fc9a9a14a0e451f7d650d6 Mon Sep 17 00:00:00 2001 From: Jonas Mayer <43114340+JonasMayerDev@users.noreply.github.com> Date: Tue, 16 Apr 2024 15:21:52 +0200 Subject: [PATCH 42/74] Update app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java Co-authored-by: Tom <70907959+ZetaTom@users.noreply.github.com> Signed-off-by: Jonas Mayer <43114340+JonasMayerDev@users.noreply.github.com> --- .../main/java/com/owncloud/android/utils/FilesSyncHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java b/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java index 230dbfb775..970ef1e3cb 100644 --- a/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java +++ b/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java @@ -73,7 +73,7 @@ public final class FilesSyncHelper { return FileVisitResult.CONTINUE; } - if (lastCheck != SyncedFolder.NOT_SCANNED_YET && attrs.lastModifiedTime().toMillis() < lastCheck) { + if (attrs.lastModifiedTime().toMillis() < lastCheck) { // skip files that were already checked return FileVisitResult.CONTINUE; } From a7e9ca1c896dbfab4ff91c2fc9057cafd2b7af29 Mon Sep 17 00:00:00 2001 From: Jonas Mayer <43114340+JonasMayerDev@users.noreply.github.com> Date: Tue, 16 Apr 2024 15:24:08 +0200 Subject: [PATCH 43/74] Update app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java Co-authored-by: Tom <70907959+ZetaTom@users.noreply.github.com> Signed-off-by: Jonas Mayer <43114340+JonasMayerDev@users.noreply.github.com> --- .../main/java/com/owncloud/android/utils/FilesSyncHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java b/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java index 970ef1e3cb..9cc3f9230c 100644 --- a/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java +++ b/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java @@ -64,7 +64,7 @@ public final class FilesSyncHelper { final long enabledTimestampMs = syncedFolder.getEnabledTimestampMs(); try { - FileUtil.walkFileTree(path, new SimpleFileVisitor() { + FileUtil.walkFileTree(path, new SimpleFileVisitor<>() { @Override public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) { File file = path.toFile(); From 350e59d395493016e7f058fa092ecd071ade64d8 Mon Sep 17 00:00:00 2001 From: Jonas Mayer <43114340+JonasMayerDev@users.noreply.github.com> Date: Tue, 16 Apr 2024 15:24:32 +0200 Subject: [PATCH 44/74] Update app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java Co-authored-by: Tom <70907959+ZetaTom@users.noreply.github.com> Signed-off-by: Jonas Mayer <43114340+JonasMayerDev@users.noreply.github.com> --- .../main/java/com/owncloud/android/utils/FilesSyncHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java b/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java index 9cc3f9230c..ef7e5dde5e 100644 --- a/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java +++ b/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java @@ -90,7 +90,7 @@ public final class FilesSyncHelper { } @Override - public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) { if (syncedFolder.isExcludeHidden() && dir.compareTo(Paths.get(syncedFolder.getLocalPath())) != 0 && dir.toFile().isHidden()) { return null; } From be7d997dc3f8a996a58525f3dee0912c0c4b5142 Mon Sep 17 00:00:00 2001 From: Jonas Mayer <43114340+JonasMayerDev@users.noreply.github.com> Date: Tue, 16 Apr 2024 15:30:22 +0200 Subject: [PATCH 45/74] Update app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java Co-authored-by: Tom <70907959+ZetaTom@users.noreply.github.com> Signed-off-by: Jonas Mayer <43114340+JonasMayerDev@users.noreply.github.com> --- .../main/java/com/owncloud/android/utils/FilesSyncHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java b/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java index ef7e5dde5e..6d8bdad14d 100644 --- a/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java +++ b/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java @@ -229,7 +229,7 @@ public final class FilesSyncHelper { continue; } - if (syncedFolder.isExisting() || cursor.getLong(column_index_date_modified) >= enabledTimestampMs / 1000.0) { + if (syncedFolder.isExisting() || cursor.getLong(column_index_date_modified) >= enabledTimestampMs / 1000) { // storeOrUpdateFileValue takes a few ms // -> Rest of this file check takes not even 1 ms. filesystemDataProvider.storeOrUpdateFileValue(contentPath, From 7312db10688542de6088323d70eba77bdca01ecf Mon Sep 17 00:00:00 2001 From: Jonas Mayer <43114340+JonasMayerDev@users.noreply.github.com> Date: Tue, 16 Apr 2024 15:30:29 +0200 Subject: [PATCH 46/74] Update app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java Co-authored-by: Tom <70907959+ZetaTom@users.noreply.github.com> Signed-off-by: Jonas Mayer <43114340+JonasMayerDev@users.noreply.github.com> --- .../main/java/com/owncloud/android/utils/FilesSyncHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java b/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java index 6d8bdad14d..1f4b6783a5 100644 --- a/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java +++ b/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java @@ -225,7 +225,7 @@ public final class FilesSyncHelper { isFolder = new File(contentPath).isDirectory(); if (syncedFolder.getLastScanTimestampMs() != SyncedFolder.NOT_SCANNED_YET && - cursor.getLong(column_index_date_modified) < (lastCheckMs / 1000.0)) { + cursor.getLong(column_index_date_modified) < (lastCheckMs / 1000)) { continue; } From 684e842f081692d62197d04804cb5e1a7c4dfb71 Mon Sep 17 00:00:00 2001 From: Jonas Mayer Date: Tue, 16 Apr 2024 15:36:02 +0200 Subject: [PATCH 47/74] rename some vars Signed-off-by: Jonas Mayer --- .../android/utils/FilesSyncHelper.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java b/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java index 1f4b6783a5..ddc8a80776 100644 --- a/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java +++ b/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java @@ -39,7 +39,6 @@ import org.lukhnos.nnio.file.attribute.BasicFileAttributes; import java.io.File; import java.io.IOException; -import java.util.ArrayList; import static com.owncloud.android.datamodel.OCFile.PATH_SEPARATOR; @@ -116,8 +115,8 @@ public final class FilesSyncHelper { if (syncedFolder.isEnabled() && (syncedFolder.isExisting() || enabledTimestampMs >= 0)) { MediaFolderType mediaType = syncedFolder.getType(); - final long lastCheck = syncedFolder.getLastScanTimestampMs(); - final long thisCheck = System.currentTimeMillis(); + final long lastCheckTimestampMs = syncedFolder.getLastScanTimestampMs(); + final long thisCheckTimestampMs = System.currentTimeMillis(); Log_OC.d(TAG,"File-sync start check folder "+syncedFolder.getLocalPath()); long startTime = System.nanoTime(); @@ -125,21 +124,21 @@ public final class FilesSyncHelper { if (mediaType == MediaFolderType.IMAGE) { FilesSyncHelper.insertContentIntoDB(MediaStore.Images.Media.INTERNAL_CONTENT_URI, syncedFolder, - lastCheck, thisCheck); + lastCheckTimestampMs, thisCheckTimestampMs); FilesSyncHelper.insertContentIntoDB(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, syncedFolder, - lastCheck, thisCheck); + lastCheckTimestampMs, thisCheckTimestampMs); } else if (mediaType == MediaFolderType.VIDEO) { FilesSyncHelper.insertContentIntoDB(MediaStore.Video.Media.INTERNAL_CONTENT_URI, syncedFolder, - lastCheck, thisCheck); + lastCheckTimestampMs, thisCheckTimestampMs); FilesSyncHelper.insertContentIntoDB(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, syncedFolder, - lastCheck, thisCheck); + lastCheckTimestampMs, thisCheckTimestampMs); } else { FilesystemDataProvider filesystemDataProvider = new FilesystemDataProvider(contentResolver); Path path = Paths.get(syncedFolder.getLocalPath()); - FilesSyncHelper.insertCustomFolderIntoDB(path, syncedFolder, filesystemDataProvider, lastCheck, thisCheck); + FilesSyncHelper.insertCustomFolderIntoDB(path, syncedFolder, filesystemDataProvider, lastCheckTimestampMs, thisCheckTimestampMs); } Log_OC.d(TAG,"File-sync finished full check for custom folder "+syncedFolder.getLocalPath()+" within "+(System.nanoTime() - startTime)+ "ns"); @@ -191,7 +190,8 @@ public final class FilesSyncHelper { return filePath; } - private static void insertContentIntoDB(Uri uri, SyncedFolder syncedFolder, long lastCheckMs, long thisCheckMs) { + private static void insertContentIntoDB(Uri uri, SyncedFolder syncedFolder, + long lastCheckTimestampMs, long thisCheckTimestampMs) { final Context context = MainApp.getAppContext(); final ContentResolver contentResolver = context.getContentResolver(); @@ -225,7 +225,7 @@ public final class FilesSyncHelper { isFolder = new File(contentPath).isDirectory(); if (syncedFolder.getLastScanTimestampMs() != SyncedFolder.NOT_SCANNED_YET && - cursor.getLong(column_index_date_modified) < (lastCheckMs / 1000)) { + cursor.getLong(column_index_date_modified) < (lastCheckTimestampMs / 1000)) { continue; } @@ -238,7 +238,7 @@ public final class FilesSyncHelper { } } cursor.close(); - syncedFolder.setLastScanTimestampMs(thisCheckMs); + syncedFolder.setLastScanTimestampMs(thisCheckTimestampMs); } } From fef1458a060179815b739fac105b7f0970ee03fc Mon Sep 17 00:00:00 2001 From: Jonas Mayer Date: Tue, 16 Apr 2024 16:24:15 +0200 Subject: [PATCH 48/74] fix unit test Signed-off-by: Jonas Mayer --- .../android/ui/activity/SyncedFoldersActivityTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/test/java/com/owncloud/android/ui/activity/SyncedFoldersActivityTest.java b/app/src/test/java/com/owncloud/android/ui/activity/SyncedFoldersActivityTest.java index 13981df525..49f1b5e5e8 100644 --- a/app/src/test/java/com/owncloud/android/ui/activity/SyncedFoldersActivityTest.java +++ b/app/src/test/java/com/owncloud/android/ui/activity/SyncedFoldersActivityTest.java @@ -10,6 +10,7 @@ package com.owncloud.android.ui.activity; import com.nextcloud.client.jobs.upload.FileUploadWorker; import com.nextcloud.client.preferences.SubFolderRule; import com.owncloud.android.datamodel.MediaFolderType; +import com.owncloud.android.datamodel.SyncedFolder; import com.owncloud.android.datamodel.SyncedFolderDisplayItem; import com.owncloud.android.files.services.NameCollisionPolicy; @@ -164,6 +165,7 @@ public class SyncedFoldersActivityTest { MediaFolderType.IMAGE, false, SubFolderRule.YEAR_MONTH, - true); + true, + SyncedFolder.NOT_SCANNED_YET); } } From afeb87ff783beeeb0fe39394a4fa67d1c13e0ebc Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 15 Apr 2024 14:16:20 +0200 Subject: [PATCH 49/74] Remove assistant from navbar if banner is visible Signed-off-by: alperozturk --- .../android/ui/activity/DrawerActivity.java | 88 ++++++++++--------- .../android/utils/DrawerMenuUtil.java | 18 ++-- 2 files changed, 57 insertions(+), 49 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index a8a0ff1c4e..a217021eee 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -284,6 +284,8 @@ public abstract class DrawerActivity extends ToolbarActivity viewThemeUtils.material.colorProgressBar(mQuotaProgressBar); } + private boolean showTopBanner = false; + public void updateHeader() { int primaryColor = themeColorUtils.unchangedPrimaryColor(getAccount(), this); @@ -340,46 +342,54 @@ public abstract class DrawerActivity extends ToolbarActivity // hide ecosystem apps according to user preference or in branded client LinearLayout ecosystemApps = mNavigationViewHeader.findViewById(R.id.drawer_ecosystem_apps); - if (getResources().getBoolean(R.bool.is_branded_client) || !preferences.isShowEcosystemApps()) { - ecosystemApps.setVisibility(View.GONE); + showTopBanner = !getResources().getBoolean(R.bool.is_branded_client) && preferences.isShowEcosystemApps(); + + if (showTopBanner) { + showBanner(ecosystemApps, primaryColor); } else { - LinearLayout notesView = ecosystemApps.findViewById(R.id.drawer_ecosystem_notes); - LinearLayout talkView = ecosystemApps.findViewById(R.id.drawer_ecosystem_talk); - LinearLayout moreView = ecosystemApps.findViewById(R.id.drawer_ecosystem_more); - LinearLayout assistantView = ecosystemApps.findViewById(R.id.drawer_ecosystem_assistant); - - notesView.setOnClickListener(v -> openAppOrStore("it.niedermann.owncloud.notes")); - talkView.setOnClickListener(v -> openAppOrStore("com.nextcloud.talk2")); - moreView.setOnClickListener(v -> openAppStore("Nextcloud", true)); - assistantView.setOnClickListener(v -> startComposeActivity(ComposeDestination.AssistantScreen, R.string.assistant_screen_top_bar_title, -1)); - if (getCapabilities() != null && getCapabilities().getAssistant().isTrue()) { - assistantView.setVisibility(View.VISIBLE); - } else { - assistantView.setVisibility(View.GONE); - } - - List views = Arrays.asList(notesView, talkView, moreView, assistantView); - - int iconColor; - if (Hct.fromInt(primaryColor).getTone() < 80.0) { - iconColor = Color.WHITE; - } else { - iconColor = getColor(R.color.grey_800_transparent); - } - - for (LinearLayout view : views) { - ImageView imageView = (ImageView) view.getChildAt(0); - imageView.setImageTintList(ColorStateList.valueOf(iconColor)); - GradientDrawable background = (GradientDrawable) imageView.getBackground(); - background.setStroke(DisplayUtils.convertDpToPixel(1, this), iconColor); - TextView textView = (TextView) view.getChildAt(1); - textView.setTextColor(iconColor); - } - - ecosystemApps.setVisibility(View.VISIBLE); + MenuItem assistanMenuItem = findViewById(R.id.nav_assistant); + assistanMenuItem.setVisible(false); + ecosystemApps.setVisibility(View.GONE); } } + private void showBanner(LinearLayout banner, int primaryColor) { + LinearLayout notesView = banner.findViewById(R.id.drawer_ecosystem_notes); + LinearLayout talkView = banner.findViewById(R.id.drawer_ecosystem_talk); + LinearLayout moreView = banner.findViewById(R.id.drawer_ecosystem_more); + LinearLayout assistantView = banner.findViewById(R.id.drawer_ecosystem_assistant); + + notesView.setOnClickListener(v -> openAppOrStore("it.niedermann.owncloud.notes")); + talkView.setOnClickListener(v -> openAppOrStore("com.nextcloud.talk2")); + moreView.setOnClickListener(v -> openAppStore("Nextcloud", true)); + assistantView.setOnClickListener(v -> startComposeActivity(ComposeDestination.AssistantScreen, R.string.assistant_screen_top_bar_title, -1)); + if (getCapabilities() != null && getCapabilities().getAssistant().isTrue()) { + assistantView.setVisibility(View.VISIBLE); + } else { + assistantView.setVisibility(View.GONE); + } + + List views = Arrays.asList(notesView, talkView, moreView, assistantView); + + int iconColor; + if (Hct.fromInt(primaryColor).getTone() < 80.0) { + iconColor = Color.WHITE; + } else { + iconColor = getColor(R.color.grey_800_transparent); + } + + for (LinearLayout view : views) { + ImageView imageView = (ImageView) view.getChildAt(0); + imageView.setImageTintList(ColorStateList.valueOf(iconColor)); + GradientDrawable background = (GradientDrawable) imageView.getBackground(); + background.setStroke(DisplayUtils.convertDpToPixel(1, this), iconColor); + TextView textView = (TextView) view.getChildAt(1); + textView.setTextColor(iconColor); + } + + banner.setVisibility(View.VISIBLE); + } + /** * Open specified app and, if not installed redirect to corresponding download. * @@ -461,11 +471,9 @@ public abstract class DrawerActivity extends ToolbarActivity DrawerMenuUtil.filterTrashbinMenuItem(menu, capability); DrawerMenuUtil.filterActivityMenuItem(menu, capability); DrawerMenuUtil.filterGroupfoldersMenuItem(menu, capability); - DrawerMenuUtil.filterAssistantMenuItem(menu, capability, getResources()); + DrawerMenuUtil.filterAssistantMenuItem(menu, capability, getResources(), showTopBanner); DrawerMenuUtil.setupHomeMenuItem(menu, getResources()); - - DrawerMenuUtil.removeMenuItem(menu, R.id.nav_community, - !getResources().getBoolean(R.bool.participate_enabled)); + DrawerMenuUtil.removeMenuItem(menu, R.id.nav_community, !getResources().getBoolean(R.bool.participate_enabled)); DrawerMenuUtil.removeMenuItem(menu, R.id.nav_shared, !getResources().getBoolean(R.bool.shared_enabled)); DrawerMenuUtil.removeMenuItem(menu, R.id.nav_logout, !getResources().getBoolean(R.bool.show_drawer_logout)); } diff --git a/app/src/main/java/com/owncloud/android/utils/DrawerMenuUtil.java b/app/src/main/java/com/owncloud/android/utils/DrawerMenuUtil.java index d2d942f942..22c59548f0 100644 --- a/app/src/main/java/com/owncloud/android/utils/DrawerMenuUtil.java +++ b/app/src/main/java/com/owncloud/android/utils/DrawerMenuUtil.java @@ -27,7 +27,7 @@ public final class DrawerMenuUtil { User user, Resources resources) { if (user.isAnonymous()) { - filterMenuItems(menu, R.id.nav_gallery, R.id.nav_favorites); + removeMenuItem(menu, R.id.nav_gallery, R.id.nav_favorites); } if (!resources.getBoolean(R.bool.recently_modified_enabled)) { @@ -38,26 +38,26 @@ public final class DrawerMenuUtil { public static void filterTrashbinMenuItem(Menu menu, @Nullable OCCapability capability) { if (capability != null && capability.getFilesUndelete().isFalse() || capability != null && capability.getFilesUndelete().isUnknown()) { - filterMenuItems(menu, R.id.nav_trashbin); + removeMenuItem(menu, R.id.nav_trashbin); } } public static void filterActivityMenuItem(Menu menu, @Nullable OCCapability capability) { if (capability != null && capability.getActivity().isFalse()) { - filterMenuItems(menu, R.id.nav_activity); + removeMenuItem(menu, R.id.nav_activity); } } - public static void filterAssistantMenuItem(Menu menu, @Nullable OCCapability capability, Resources resources) { - boolean showCondition = capability != null && capability.getAssistant().isTrue() && !resources.getBoolean(R.bool.is_branded_client); - if (!showCondition) { - filterMenuItems(menu, R.id.nav_assistant); + public static void filterAssistantMenuItem(Menu menu, @Nullable OCCapability capability, Resources resources, boolean showTopBanner) { + boolean showCondition = (capability != null && capability.getAssistant().isTrue() && !resources.getBoolean(R.bool.is_branded_client)); + if (!showCondition || showTopBanner) { + removeMenuItem(menu, R.id.nav_assistant); } } public static void filterGroupfoldersMenuItem(Menu menu, @Nullable OCCapability capability) { if (capability != null && !capability.getGroupfolders().isTrue()) { - filterMenuItems(menu, R.id.nav_groupfolders); + removeMenuItem(menu, R.id.nav_groupfolders); } } @@ -74,7 +74,7 @@ public final class DrawerMenuUtil { } } - private static void filterMenuItems(Menu menu, int... menuIds) { + private static void removeMenuItem(Menu menu, int... menuIds) { if (menuIds != null) { for (int menuId : menuIds) { menu.removeItem(menuId); From 0200e51eb75ec0a48238fb3470c63db554bb0892 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 15 Apr 2024 14:55:00 +0200 Subject: [PATCH 50/74] Toggle drawer from Assistant screen via menu button Signed-off-by: alperozturk --- .../ui/composeActivity/ComposeActivity.kt | 11 +++--- .../android/ui/activity/DrawerActivity.java | 10 ++++++ .../android/ui/activity/ToolbarActivity.java | 18 +++++++++- app/src/main/res/layout/toolbar_standard.xml | 36 +++++++++++++++++++ 4 files changed, 70 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt index c57b39a203..cfedceeabf 100644 --- a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt +++ b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt @@ -53,11 +53,14 @@ class ComposeActivity : DrawerActivity() { val titleId = intent.getIntExtra(TITLE, R.string.empty) menuItemId = intent.getIntExtra(MENU_ITEM, -1) - setupToolbar() - updateActionBarTitleAndHomeButtonByString(getString(titleId)) - if (menuItemId != -1) { setupDrawer(menuItemId!!) + } else { + setupDrawer() + } + + setupToolbarShowOnlyMenuButtonAndTitle(getString(titleId)) { + toggleDrawer() } binding.composeView.setContent { @@ -80,7 +83,7 @@ class ComposeActivity : DrawerActivity() { override fun onOptionsItemSelected(item: MenuItem): Boolean { return when (item.itemId) { android.R.id.home -> { - if (isDrawerOpen) closeDrawer() else openDrawer() + toggleDrawer() true } else -> super.onOptionsItemSelected(item) diff --git a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index a217021eee..dd34c36d00 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -506,6 +506,8 @@ public abstract class DrawerActivity extends ToolbarActivity intent.putExtra(FileDisplayActivity.DRAWER_MENU_ID, menuItem.getItemId()); startActivity(intent); } + + closeDrawer(); } else if (itemId == R.id.nav_favorites) { handleSearchEvents(new SearchEvent("", SearchRemoteOperation.SearchType.FAVORITE_SEARCH), menuItem.getItemId()); @@ -681,6 +683,14 @@ public abstract class DrawerActivity extends ToolbarActivity return mDrawerLayout != null && mDrawerLayout.isDrawerOpen(GravityCompat.START); } + public void toggleDrawer() { + if (isDrawerOpen()) { + closeDrawer(); + } else { + openDrawer(); + } + } + /** * closes the drawer. */ diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java index 5e6dec41f0..b018647ed0 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java @@ -104,6 +104,22 @@ public abstract class ToolbarActivity extends BaseActivity implements Injectable viewThemeUtils.material.colorMaterialTextButton(mSwitchAccountButton); } + public void setupToolbarShowOnlyMenuButtonAndTitle(String title, View.OnClickListener toggleDrawer) { + setupToolbar(false, false); + + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + actionBar.setDisplayShowTitleEnabled(false); + } + + LinearLayout toolbar = findViewById(R.id.toolbar_linear_layout); + MaterialButton menuButton = findViewById(R.id.toolbar_menu_button); + MaterialTextView titleTextView = findViewById(R.id.toolbar_title); + titleTextView.setText(title); + toolbar.setVisibility(View.VISIBLE); + menuButton.setOnClickListener(toggleDrawer); + } + public void setupToolbar() { setupToolbar(false, false); } @@ -278,7 +294,7 @@ public abstract class ToolbarActivity extends BaseActivity implements Injectable public void clearToolbarSubtitle() { ActionBar actionBar = getSupportActionBar(); - if(actionBar != null){ + if (actionBar != null) { actionBar.setSubtitle(null); } } diff --git a/app/src/main/res/layout/toolbar_standard.xml b/app/src/main/res/layout/toolbar_standard.xml index 41b3049813..c499ef9a97 100644 --- a/app/src/main/res/layout/toolbar_standard.xml +++ b/app/src/main/res/layout/toolbar_standard.xml @@ -114,6 +114,42 @@ + + + + + + + + + Date: Mon, 15 Apr 2024 15:48:31 +0200 Subject: [PATCH 51/74] Use all files as default menuItem Signed-off-by: alperozturk --- .../ui/composeActivity/ComposeActivity.kt | 14 ++++---------- .../android/ui/activity/DrawerActivity.java | 4 ++++ 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt index cfedceeabf..6934dabcd0 100644 --- a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt +++ b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt @@ -36,7 +36,7 @@ import java.lang.ref.WeakReference class ComposeActivity : DrawerActivity() { lateinit var binding: ActivityComposeBinding - private var menuItemId: Int? = null + private var menuItemId: Int = R.id.nav_all_files companion object { const val DESTINATION = "DESTINATION" @@ -51,13 +51,9 @@ class ComposeActivity : DrawerActivity() { val destination = intent.getSerializableArgument(DESTINATION, ComposeDestination::class.java) val titleId = intent.getIntExtra(TITLE, R.string.empty) - menuItemId = intent.getIntExtra(MENU_ITEM, -1) + menuItemId = intent.getIntExtra(MENU_ITEM, R.id.nav_all_files) - if (menuItemId != -1) { - setupDrawer(menuItemId!!) - } else { - setupDrawer() - } + setupDrawer(menuItemId) setupToolbarShowOnlyMenuButtonAndTitle(getString(titleId)) { toggleDrawer() @@ -75,9 +71,7 @@ class ComposeActivity : DrawerActivity() { override fun onResume() { super.onResume() - if (menuItemId != -1) { - setDrawerMenuItemChecked(R.id.nav_assistant) - } + setDrawerMenuItemChecked(menuItemId) } override fun onOptionsItemSelected(item: MenuItem): Boolean { diff --git a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index dd34c36d00..8929f4144e 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -29,8 +29,10 @@ import android.graphics.drawable.LayerDrawable; import android.net.Uri; import android.os.Bundle; import android.os.Handler; +import android.os.Looper; import android.os.SystemClock; import android.text.TextUtils; +import android.view.Gravity; import android.view.Menu; import android.view.MenuItem; import android.view.View; @@ -484,6 +486,8 @@ public abstract class DrawerActivity extends ToolbarActivity } private void onNavigationItemClicked(final MenuItem menuItem) { + closeDrawer(); + setDrawerMenuItemChecked(menuItem.getItemId()); int itemId = menuItem.getItemId(); From 8e82b5e16cb6c4021297eaa5dc85476449fc0dc2 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 15 Apr 2024 16:28:15 +0200 Subject: [PATCH 52/74] Use chunks for bigger task output Signed-off-by: alperozturk --- .../client/assistant/component/TaskView.kt | 58 +++++++++---------- .../repository/AssistantMockRepository.kt | 37 ++++++++---- .../utils/extensions/StringExtensions.kt | 28 +++++++++ .../android/ui/activity/DrawerActivity.java | 1 - 4 files changed, 81 insertions(+), 43 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/utils/extensions/StringExtensions.kt diff --git a/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt b/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt index c09e98716c..92106131d2 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt @@ -28,6 +28,7 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue @@ -43,6 +44,8 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.nextcloud.client.assistant.extensions.statusData import com.nextcloud.ui.composeComponents.bottomSheet.MoreActionsBottomSheet +import com.nextcloud.utils.extensions.getRandomString +import com.nextcloud.utils.extensions.splitIntoChunks import com.owncloud.android.R import com.owncloud.android.lib.resources.assistant.model.Task @@ -53,7 +56,8 @@ fun TaskView( task: Task, showDeleteTaskAlertDialog: (Long) -> Unit ) { - var expanded by remember { mutableStateOf(false) } + var loadedChunkSize by remember { mutableIntStateOf(1) } + val taskOutputChunks = task.output?.splitIntoChunks(100) var showMoreActionsBottomSheet by remember { mutableStateOf(false) } Column( @@ -62,7 +66,11 @@ fun TaskView( .clip(RoundedCornerShape(16.dp)) .background(MaterialTheme.colorScheme.primary) .combinedClickable(onClick = { - expanded = !expanded + if (taskOutputChunks?.size != loadedChunkSize) { + loadedChunkSize += 1 + } else { + loadedChunkSize = 1 + } }, onLongClick = { showMoreActionsBottomSheet = true }) @@ -80,21 +88,23 @@ fun TaskView( Spacer(modifier = Modifier.height(16.dp)) - task.output?.let { - HorizontalDivider(modifier = Modifier.padding(horizontal = 4.dp, vertical = 8.dp)) + taskOutputChunks?.take(loadedChunkSize).let { + it?.joinToString("")?.let { output -> + HorizontalDivider(modifier = Modifier.padding(horizontal = 4.dp, vertical = 8.dp)) - Text( - text = if (expanded) it else it.take(100) + "...", - fontSize = 12.sp, - color = Color.White, - modifier = Modifier - .animateContentSize( - animationSpec = spring( - dampingRatio = Spring.DampingRatioLowBouncy, - stiffness = Spring.StiffnessLow + Text( + text = output, + fontSize = 12.sp, + color = Color.White, + modifier = Modifier + .animateContentSize( + animationSpec = spring( + dampingRatio = Spring.DampingRatioLowBouncy, + stiffness = Spring.StiffnessLow + ) ) - ) - ) + ) + } } Row( @@ -121,7 +131,7 @@ fun TaskView( if ((task.output?.length ?: 0) >= 100) { Image( painter = painterResource( - id = if (!expanded) R.drawable.ic_expand_more else R.drawable.ic_expand_less + id = if (loadedChunkSize != taskOutputChunks?.size) R.drawable.ic_expand_more else R.drawable.ic_expand_less ), contentDescription = "expand content icon", colorFilter = ColorFilter.tint(Color.White) @@ -153,21 +163,7 @@ fun TaskView( @Preview @Composable private fun TaskViewPreview() { - val output = - "Lorem Ipsum is simply dummy text of the printing and " + - "typesetting industry. Lorem Ipsum has been the " + - "industry's standard dummy text ever since the 1500s, " + - "when an unknown printer took a galley of type and " + - "scrambled it to make a type specimen book. " + - "It has survived not only five centuries, but also " + - "the leap into electronic typesetting, remaining" + - " essentially unchanged. It wLorem Ipsum is simply dummy" + - " text of the printing and typesetting industry. " + - "Lorem Ipsum has been the industry's standard dummy " + - "text ever since the 1500s, when an unknown printer took a" + - " galley of type and scrambled it to make a type specimen book. " + - "It has survived not only five centuries, but also the leap " + - "into electronic typesetting, remaining essentially unchanged." + val output = "Lorem".getRandomString(100) TaskView( task = Task( diff --git a/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantMockRepository.kt b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantMockRepository.kt index 754a4cb5f1..c160e8ec8d 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantMockRepository.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantMockRepository.kt @@ -7,6 +7,7 @@ */ package com.nextcloud.client.assistant.repository +import com.nextcloud.utils.extensions.getRandomString import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.resources.assistant.model.Task import com.owncloud.android.lib.resources.assistant.model.TaskList @@ -41,15 +42,18 @@ class AssistantMockRepository(private val giveEmptyTasks: Boolean = false) : Ass null, "12", "", - "Give me some text", - "Lorem Ipsum is simply dummy text of the printing and typesetting industry. " + - "Lorem Ipsum has been the industry's standard dummy text ever since the 1500s," + - " when an unknown printer took a galley of type and scrambled it to make a type" + - " specimen book. It has survived not only five centuries, " + - "but also the leap into electronic typesetting, remaining essentially unchanged." + - " It was popularised in the 1960s with the release of Letraset sheets containing " + - "Lorem Ipsum passages, and more recently with desktop publishing software like Aldus" + - " PageMaker including versions of Lorem Ipsum", + "Give me some long text", + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc consectetur condimentum augue, sit amet maximus nibh pharetra sodales. Donec blandit nulla vitae diam aliquam, vel viverra sem faucibus. Duis vitae pretium sapien. Curabitur nec libero erat. Nunc pretium eleifend mi non congue. Sed efficitur ullamcorper mi, ac faucibus mi mollis sed. Donec vestibulum enim vel massa sodales facilisis. Integer in consequat nibh. Sed id nisi erat. Donec sollicitudin pharetra massa, id sodales arcu mollis luctus. Donec sed ullamcorper nisl, non euismod metus. Proin eget sollicitudin purus.\n" + + "\n" + + "Etiam sit amet nisl pretium, facilisis ligula vel, luctus arcu. Nunc bibendum hendrerit ultricies. Integer scelerisque sem arcu, eget fermentum neque pretium in. Curabitur facilisis neque vel leo blandit tincidunt. Nullam at sodales nisl, eu porta leo. Nulla facilisi. Fusce cursus, turpis eget auctor iaculis, eros lectus posuere magna, a tristique purus nunc id quam.\n" + + "\n" + + "Nullam faucibus mauris eget magna vehicula auctor. Aliquam molestie purus quis magna mattis, sed commodo dolor condimentum. Mauris hendrerit libero ut tellus rutrum, et sagittis diam luctus. Nunc non semper neque, eget scelerisque tortor. Donec hendrerit faucibus dolor, at congue orci dignissim nec. Duis vel interdum elit, maximus elementum orci. Phasellus ut ligula id sapien dictum euismod. Vestibulum tincidunt vitae orci a congue. Nunc nec commodo urna, quis vulputate orci. Suspendisse euismod urna orci. Phasellus in metus lobortis, auctor mauris vel, venenatis nulla.\n" + + "\n" + + "Quisque lectus felis, placerat eget consequat quis, mattis et nisi. In bibendum in orci fermentum rhoncus. Nam eu nibh ex. Cras vel ligula eu quam pharetra ullamcorper. Integer a ultricies eros, at rutrum ligula. Nam laoreet convallis velit sit amet vulputate. In eleifend interdum risus, pulvinar dictum tellus. Fusce in posuere mauris, sed commodo urna. Etiam a ante id felis viverra commodo vel sed elit. Maecenas in libero turpis. Donec non elit feugiat, ullamcorper massa sit amet, lobortis turpis. Fusce mollis felis eu elementum ornare. Nulla facilisi.\n" + + "\n" + + "Curabitur sed erat vel urna luctus consequat sit amet et lacus. Donec eu tempus sapien. Morbi commodo finibus sapien, in consequat dui. Etiam ac odio magna. Cras ut nisl scelerisque, consectetur ante et, laoreet diam. Morbi efficitur, nibh ac volutpat rhoncus, urna eros cursus ipsum, non vulputate magna ipsum sit amet risus. Donec at arcu ullamcorper, pretium augue porttitor, dapibus arcu. Ut mollis velit sed tristique maximus. Duis iaculis porta ligula iaculis congue. Quisque laoreet ligula euismod faucibus consectetur. Nunc sit amet quam venenatis, dignissim nisl id, commodo augue. Nulla faucibus dui nec tortor viverra, at posuere orci sollicitudin. Sed fringilla porta lectus, id pretium tortor mattis eget. Cras suscipit mi pharetra, eleifend nisl quis, ultrices eros. Maecenas a nulla in dui blandit pellentesque non et mi.\n" + + "\n" + + "Curabitur purus mauris, scelerisque vitae metus vitae, pellentesque suscipit eros. Morbi tincidunt consequat felis at sagittis. Aenean purus elit, porta eu fringilla vitae, tincidunt ac leo. Quisque non neque et eros pretium sagittis vel sit amet sem. Fusce nec finibus elit. Proin scelerisque libero a mi blandit, quis accumsan dolor interdum. Praesent aliquet nisi in vehicula viverra. Nunc vitae accumsan neque. Donec.", "", "" ), @@ -60,10 +64,21 @@ class AssistantMockRepository(private val giveEmptyTasks: Boolean = false) : Ass "12", "", "Give me some text 2", - "Lorem Ipsum is simply dummy text of the printing and typesetting industry.", + "Lorem".getRandomString(100), "", "" - ) + ), + Task( + 3, + "FreePrompt", + null, + "12", + "", + "Give me some text", + "Lorem".getRandomString(300), + "", + "" + ), ) ) } diff --git a/app/src/main/java/com/nextcloud/utils/extensions/StringExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/StringExtensions.kt new file mode 100644 index 0000000000..25855676d7 --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/extensions/StringExtensions.kt @@ -0,0 +1,28 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Your Name + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils.extensions + +fun String.getRandomString(length: Int) : String { + val allowedChars = ('A'..'Z') + ('a'..'z') + ('0'..'9') + val result = (1..length) + .map { allowedChars.random() } + .joinToString("") + + return this + result +} + +fun String.splitIntoChunks(chunkSize: Int): List { + val chunks = mutableListOf() + var startIndex = 0 + while (startIndex < length) { + val endIndex = kotlin.math.min(startIndex + chunkSize, length) + chunks.add(substring(startIndex, endIndex)) + startIndex = endIndex + } + return chunks +} diff --git a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index 8929f4144e..d9a9e5ffbf 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -32,7 +32,6 @@ import android.os.Handler; import android.os.Looper; import android.os.SystemClock; import android.text.TextUtils; -import android.view.Gravity; import android.view.Menu; import android.view.MenuItem; import android.view.View; From ac863b3778f10b396bc86b48b7e2fbd140603437 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 15 Apr 2024 16:39:57 +0200 Subject: [PATCH 53/74] Use scrollable text view instead chunks Signed-off-by: alperozturk --- .../client/assistant/AsssistantScreen.kt | 1 + .../client/assistant/component/TaskView.kt | 57 +++++++------------ .../repository/AssistantMockRepository.kt | 48 +++++++++++++++- .../utils/extensions/StringExtensions.kt | 11 ---- 4 files changed, 66 insertions(+), 51 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt index d7b09d4f00..205fc677c3 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt @@ -18,6 +18,7 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.rememberScrollState import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Add import androidx.compose.material3.ExperimentalMaterial3Api diff --git a/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt b/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt index 92106131d2..10aff5b861 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt @@ -22,13 +22,14 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue @@ -45,7 +46,6 @@ import androidx.compose.ui.unit.sp import com.nextcloud.client.assistant.extensions.statusData import com.nextcloud.ui.composeComponents.bottomSheet.MoreActionsBottomSheet import com.nextcloud.utils.extensions.getRandomString -import com.nextcloud.utils.extensions.splitIntoChunks import com.owncloud.android.R import com.owncloud.android.lib.resources.assistant.model.Task @@ -56,8 +56,7 @@ fun TaskView( task: Task, showDeleteTaskAlertDialog: (Long) -> Unit ) { - var loadedChunkSize by remember { mutableIntStateOf(1) } - val taskOutputChunks = task.output?.splitIntoChunks(100) + val verticalScrollState = rememberScrollState(0) var showMoreActionsBottomSheet by remember { mutableStateOf(false) } Column( @@ -66,11 +65,7 @@ fun TaskView( .clip(RoundedCornerShape(16.dp)) .background(MaterialTheme.colorScheme.primary) .combinedClickable(onClick = { - if (taskOutputChunks?.size != loadedChunkSize) { - loadedChunkSize += 1 - } else { - loadedChunkSize = 1 - } + }, onLongClick = { showMoreActionsBottomSheet = true }) @@ -88,23 +83,23 @@ fun TaskView( Spacer(modifier = Modifier.height(16.dp)) - taskOutputChunks?.take(loadedChunkSize).let { - it?.joinToString("")?.let { output -> - HorizontalDivider(modifier = Modifier.padding(horizontal = 4.dp, vertical = 8.dp)) + task.output?.let { + HorizontalDivider(modifier = Modifier.padding(horizontal = 4.dp, vertical = 8.dp)) - Text( - text = output, - fontSize = 12.sp, - color = Color.White, - modifier = Modifier - .animateContentSize( - animationSpec = spring( - dampingRatio = Spring.DampingRatioLowBouncy, - stiffness = Spring.StiffnessLow - ) + Text( + text = it, + fontSize = 12.sp, + color = Color.White, + modifier = Modifier + .height(100.dp) + .verticalScroll(verticalScrollState) + .animateContentSize( + animationSpec = spring( + dampingRatio = Spring.DampingRatioLowBouncy, + stiffness = Spring.StiffnessLow ) - ) - } + ) + ) } Row( @@ -125,20 +120,6 @@ fun TaskView( Spacer(modifier = Modifier.width(6.dp)) Text(text = stringResource(id = descriptionId), color = Color.White) - - Spacer(modifier = Modifier.weight(1f)) - - if ((task.output?.length ?: 0) >= 100) { - Image( - painter = painterResource( - id = if (loadedChunkSize != taskOutputChunks?.size) R.drawable.ic_expand_more else R.drawable.ic_expand_less - ), - contentDescription = "expand content icon", - colorFilter = ColorFilter.tint(Color.White) - ) - } - - Spacer(modifier = Modifier.width(8.dp)) } if (showMoreActionsBottomSheet) { diff --git a/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantMockRepository.kt b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantMockRepository.kt index c160e8ec8d..6a47406ba7 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantMockRepository.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantMockRepository.kt @@ -42,7 +42,7 @@ class AssistantMockRepository(private val giveEmptyTasks: Boolean = false) : Ass null, "12", "", - "Give me some long text", + "Give me some long text 1", "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc consectetur condimentum augue, sit amet maximus nibh pharetra sodales. Donec blandit nulla vitae diam aliquam, vel viverra sem faucibus. Duis vitae pretium sapien. Curabitur nec libero erat. Nunc pretium eleifend mi non congue. Sed efficitur ullamcorper mi, ac faucibus mi mollis sed. Donec vestibulum enim vel massa sodales facilisis. Integer in consequat nibh. Sed id nisi erat. Donec sollicitudin pharetra massa, id sodales arcu mollis luctus. Donec sed ullamcorper nisl, non euismod metus. Proin eget sollicitudin purus.\n" + "\n" + "Etiam sit amet nisl pretium, facilisis ligula vel, luctus arcu. Nunc bibendum hendrerit ultricies. Integer scelerisque sem arcu, eget fermentum neque pretium in. Curabitur facilisis neque vel leo blandit tincidunt. Nullam at sodales nisl, eu porta leo. Nulla facilisi. Fusce cursus, turpis eget auctor iaculis, eros lectus posuere magna, a tristique purus nunc id quam.\n" + @@ -74,7 +74,51 @@ class AssistantMockRepository(private val giveEmptyTasks: Boolean = false) : Ass null, "12", "", - "Give me some text", + "Give me some text 3", + "Lorem".getRandomString(300), + "", + "" + ), + Task( + 4, + "FreePrompt", + null, + "12", + "", + "Give me some text 4", + "Lorem".getRandomString(300), + "", + "" + ), + Task( + 5, + "FreePrompt", + null, + "12", + "", + "Give me some text 5", + "Lorem".getRandomString(300), + "", + "" + ), + Task( + 6, + "FreePrompt", + null, + "12", + "", + "Give me some text 6", + "Lorem".getRandomString(300), + "", + "" + ), + Task( + 7, + "FreePrompt", + null, + "12", + "", + "Give me some text 7", "Lorem".getRandomString(300), "", "" diff --git a/app/src/main/java/com/nextcloud/utils/extensions/StringExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/StringExtensions.kt index 25855676d7..8d3bba7568 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/StringExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/StringExtensions.kt @@ -15,14 +15,3 @@ fun String.getRandomString(length: Int) : String { return this + result } - -fun String.splitIntoChunks(chunkSize: Int): List { - val chunks = mutableListOf() - var startIndex = 0 - while (startIndex < length) { - val endIndex = kotlin.math.min(startIndex + chunkSize, length) - chunks.add(substring(startIndex, endIndex)) - startIndex = endIndex - } - return chunks -} From 035ae37cd312905fda2cc920b2d0dd2fff655751 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 15 Apr 2024 16:40:10 +0200 Subject: [PATCH 54/74] Use scrollable text view instead chunks Signed-off-by: alperozturk --- .../main/java/com/nextcloud/client/assistant/AsssistantScreen.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt index 205fc677c3..d7b09d4f00 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt @@ -18,7 +18,6 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items -import androidx.compose.foundation.rememberScrollState import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Add import androidx.compose.material3.ExperimentalMaterial3Api From 1e3e3ae86a83601bbf0850847d8f6537ceac264b Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 16 Apr 2024 10:12:12 +0200 Subject: [PATCH 55/74] Fix code analytics Signed-off-by: alperozturk --- .../client/assistant/component/TaskView.kt | 1 + .../repository/AssistantMockRepository.kt | 33 ++++++++++++------- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt b/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt index 10aff5b861..48e45a9c5b 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt @@ -141,6 +141,7 @@ fun TaskView( } } +@Suppress("MagicNumber") @Preview @Composable private fun TaskViewPreview() { diff --git a/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantMockRepository.kt b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantMockRepository.kt index 6a47406ba7..020865814d 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantMockRepository.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantMockRepository.kt @@ -14,6 +14,7 @@ import com.owncloud.android.lib.resources.assistant.model.TaskList import com.owncloud.android.lib.resources.assistant.model.TaskType import com.owncloud.android.lib.resources.assistant.model.TaskTypes +@Suppress("MagicNumber") class AssistantMockRepository(private val giveEmptyTasks: Boolean = false) : AssistantRepositoryType { override fun getTaskTypes(): RemoteOperationResult { return RemoteOperationResult(RemoteOperationResult.ResultCode.OK).apply { @@ -43,18 +44,26 @@ class AssistantMockRepository(private val giveEmptyTasks: Boolean = false) : Ass "12", "", "Give me some long text 1", - "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc consectetur condimentum augue, sit amet maximus nibh pharetra sodales. Donec blandit nulla vitae diam aliquam, vel viverra sem faucibus. Duis vitae pretium sapien. Curabitur nec libero erat. Nunc pretium eleifend mi non congue. Sed efficitur ullamcorper mi, ac faucibus mi mollis sed. Donec vestibulum enim vel massa sodales facilisis. Integer in consequat nibh. Sed id nisi erat. Donec sollicitudin pharetra massa, id sodales arcu mollis luctus. Donec sed ullamcorper nisl, non euismod metus. Proin eget sollicitudin purus.\n" + - "\n" + - "Etiam sit amet nisl pretium, facilisis ligula vel, luctus arcu. Nunc bibendum hendrerit ultricies. Integer scelerisque sem arcu, eget fermentum neque pretium in. Curabitur facilisis neque vel leo blandit tincidunt. Nullam at sodales nisl, eu porta leo. Nulla facilisi. Fusce cursus, turpis eget auctor iaculis, eros lectus posuere magna, a tristique purus nunc id quam.\n" + - "\n" + - "Nullam faucibus mauris eget magna vehicula auctor. Aliquam molestie purus quis magna mattis, sed commodo dolor condimentum. Mauris hendrerit libero ut tellus rutrum, et sagittis diam luctus. Nunc non semper neque, eget scelerisque tortor. Donec hendrerit faucibus dolor, at congue orci dignissim nec. Duis vel interdum elit, maximus elementum orci. Phasellus ut ligula id sapien dictum euismod. Vestibulum tincidunt vitae orci a congue. Nunc nec commodo urna, quis vulputate orci. Suspendisse euismod urna orci. Phasellus in metus lobortis, auctor mauris vel, venenatis nulla.\n" + - "\n" + - "Quisque lectus felis, placerat eget consequat quis, mattis et nisi. In bibendum in orci fermentum rhoncus. Nam eu nibh ex. Cras vel ligula eu quam pharetra ullamcorper. Integer a ultricies eros, at rutrum ligula. Nam laoreet convallis velit sit amet vulputate. In eleifend interdum risus, pulvinar dictum tellus. Fusce in posuere mauris, sed commodo urna. Etiam a ante id felis viverra commodo vel sed elit. Maecenas in libero turpis. Donec non elit feugiat, ullamcorper massa sit amet, lobortis turpis. Fusce mollis felis eu elementum ornare. Nulla facilisi.\n" + - "\n" + - "Curabitur sed erat vel urna luctus consequat sit amet et lacus. Donec eu tempus sapien. Morbi commodo finibus sapien, in consequat dui. Etiam ac odio magna. Cras ut nisl scelerisque, consectetur ante et, laoreet diam. Morbi efficitur, nibh ac volutpat rhoncus, urna eros cursus ipsum, non vulputate magna ipsum sit amet risus. Donec at arcu ullamcorper, pretium augue porttitor, dapibus arcu. Ut mollis velit sed tristique maximus. Duis iaculis porta ligula iaculis congue. Quisque laoreet ligula euismod faucibus consectetur. Nunc sit amet quam venenatis, dignissim nisl id, commodo augue. Nulla faucibus dui nec tortor viverra, at posuere orci sollicitudin. Sed fringilla porta lectus, id pretium tortor mattis eget. Cras suscipit mi pharetra, eleifend nisl quis, ultrices eros. Maecenas a nulla in dui blandit pellentesque non et mi.\n" + - "\n" + - "Curabitur purus mauris, scelerisque vitae metus vitae, pellentesque suscipit eros. Morbi tincidunt consequat felis at sagittis. Aenean purus elit, porta eu fringilla vitae, tincidunt ac leo. Quisque non neque et eros pretium sagittis vel sit amet sem. Fusce nec finibus elit. Proin scelerisque libero a mi blandit, quis accumsan dolor interdum. Praesent aliquet nisi in vehicula viverra. Nunc vitae accumsan neque. Donec.", - "", + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc consectetur " + + "condimentum augue, sit amet maximus nibh pharetra sodales. Donec blandit nulla vitae " + + "diam aliquam, vel viverra sem faucibus. Duis vitae pretium sapien. Curabitur nec libero " + + "erat. Nunc pretium eleifend mi non congue. Sed efficitur ullamcorper mi, ac faucibus mi" + + " mollis sed. Donec vestibulum enim vel massa sodales facilisis. Integer in consequat nibh." + + " Sed id nisi erat. Donec sollicitudin pharetra massa, id sodales arcu mollis luctus. " + + "Donec sed ullamcorper nisl, non euismod metus. Proin eget sollicitudin purus.\n" + "\n" + + "Etiam sit amet nisl pretium, facilisis ligula vel, luctus arcu. Nunc bibendum " + + "hendrerit ultricies. Integer scelerisque sem arcu, eget fermentum neque pretium in." + + " Curabitur facilisis neque vel leo blandit tincidunt. Nullam at sodales nisl, " + + "eu porta leo. Nulla facilisi. Fusce cursus, turpis eget auctor iaculis, eros " + + "lectus posuere magna, a tristique purus nunc id quam.\n" + "\n" + "Nullam faucibus " + + "mauris eget magna vehicula auctor. Aliquam molestie purus quis magna mattis, sed " + + "commodo dolor condimentum. Mauris hendrerit libero ut tellus rutrum, et sagittis diam " + + "luctus. Nunc non semper neque, eget scelerisque tortor. Donec hendrerit faucibus dolor, " + + "at congue orci dignissim nec. Duis vel interdum elit, maximus elementum orci. " + + "Phasellus ut ligula id sapien dictum euismod. Vestibulum tincidunt vitae orci a congue. " + + "unc nec commodo urna, quis vulputate orci. Suspendisse euismod urna orci. Phasellus in metus lobortis, auctor " + + "mauris vel, venenatis nulla.\n" + "\n" + "Quisque lectus felis, placerat eget consequat quis, mattis et nisi. " + + "In bibendum in orci fermentum rhoncus. Nam eu nibh ex. Cras vel ligula eu quam pharetra ullamcorper. ", "" ), Task( From 6d219c5b8c695abd754a4c96324f3e0e4b952408 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 16 Apr 2024 10:29:14 +0200 Subject: [PATCH 56/74] Fix code analytics Signed-off-by: alperozturk --- .../repository/AssistantMockRepository.kt | 23 ++----------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantMockRepository.kt b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantMockRepository.kt index 020865814d..04efe9828b 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantMockRepository.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantMockRepository.kt @@ -44,26 +44,7 @@ class AssistantMockRepository(private val giveEmptyTasks: Boolean = false) : Ass "12", "", "Give me some long text 1", - "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc consectetur " + - "condimentum augue, sit amet maximus nibh pharetra sodales. Donec blandit nulla vitae " + - "diam aliquam, vel viverra sem faucibus. Duis vitae pretium sapien. Curabitur nec libero " + - "erat. Nunc pretium eleifend mi non congue. Sed efficitur ullamcorper mi, ac faucibus mi" + - " mollis sed. Donec vestibulum enim vel massa sodales facilisis. Integer in consequat nibh." + - " Sed id nisi erat. Donec sollicitudin pharetra massa, id sodales arcu mollis luctus. " + - "Donec sed ullamcorper nisl, non euismod metus. Proin eget sollicitudin purus.\n" + "\n" + - "Etiam sit amet nisl pretium, facilisis ligula vel, luctus arcu. Nunc bibendum " + - "hendrerit ultricies. Integer scelerisque sem arcu, eget fermentum neque pretium in." + - " Curabitur facilisis neque vel leo blandit tincidunt. Nullam at sodales nisl, " + - "eu porta leo. Nulla facilisi. Fusce cursus, turpis eget auctor iaculis, eros " + - "lectus posuere magna, a tristique purus nunc id quam.\n" + "\n" + "Nullam faucibus " + - "mauris eget magna vehicula auctor. Aliquam molestie purus quis magna mattis, sed " + - "commodo dolor condimentum. Mauris hendrerit libero ut tellus rutrum, et sagittis diam " + - "luctus. Nunc non semper neque, eget scelerisque tortor. Donec hendrerit faucibus dolor, " + - "at congue orci dignissim nec. Duis vel interdum elit, maximus elementum orci. " + - "Phasellus ut ligula id sapien dictum euismod. Vestibulum tincidunt vitae orci a congue. " + - "unc nec commodo urna, quis vulputate orci. Suspendisse euismod urna orci. Phasellus in metus lobortis, auctor " + - "mauris vel, venenatis nulla.\n" + "\n" + "Quisque lectus felis, placerat eget consequat quis, mattis et nisi. " + - "In bibendum in orci fermentum rhoncus. Nam eu nibh ex. Cras vel ligula eu quam pharetra ullamcorper. ", + "Lorem ipsum".getRandomString(100), "" ), Task( @@ -131,7 +112,7 @@ class AssistantMockRepository(private val giveEmptyTasks: Boolean = false) : Ass "Lorem".getRandomString(300), "", "" - ), + ) ) ) } From 05b141ef643607ddfaa4304beb9988be7f4ce5f9 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 16 Apr 2024 10:29:23 +0200 Subject: [PATCH 57/74] Fix code analytics Signed-off-by: alperozturk --- .../java/com/nextcloud/client/assistant/component/TaskView.kt | 1 - .../java/com/nextcloud/utils/extensions/StringExtensions.kt | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt b/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt index 48e45a9c5b..441f703320 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt @@ -65,7 +65,6 @@ fun TaskView( .clip(RoundedCornerShape(16.dp)) .background(MaterialTheme.colorScheme.primary) .combinedClickable(onClick = { - }, onLongClick = { showMoreActionsBottomSheet = true }) diff --git a/app/src/main/java/com/nextcloud/utils/extensions/StringExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/StringExtensions.kt index 8d3bba7568..9888e951de 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/StringExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/StringExtensions.kt @@ -7,7 +7,7 @@ package com.nextcloud.utils.extensions -fun String.getRandomString(length: Int) : String { +fun String.getRandomString(length: Int): String { val allowedChars = ('A'..'Z') + ('a'..'z') + ('0'..'9') val result = (1..length) .map { allowedChars.random() } From 7e97a02c440b893c8ddef0d34dc77ee8346b8a8f Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 16 Apr 2024 11:07:55 +0200 Subject: [PATCH 58/74] Fix code analytics Signed-off-by: alperozturk --- .../com/owncloud/android/ui/activity/DrawerActivity.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index d9a9e5ffbf..97253333ee 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -533,7 +533,8 @@ public abstract class DrawerActivity extends ToolbarActivity startActivity(CommunityActivity.class); } else if (itemId == R.id.nav_logout) { mCheckedMenuItem = -1; - menuItem.setChecked(false); + MenuItem isNewMenuItemChecked = menuItem.setChecked(false); + Log_OC.d(TAG,"onNavigationItemClicked nav_logout setChecked " + isNewMenuItemChecked); final Optional optionalUser = getUser(); if (optionalUser.isPresent()) { UserInfoActivity.openAccountRemovalDialog(optionalUser.get(), getSupportFragmentManager()); @@ -1143,7 +1144,8 @@ public abstract class DrawerActivity extends ToolbarActivity @Override public void avatarGenerated(Drawable avatarDrawable, Object callContext) { if (callContext instanceof MenuItem menuItem) { - menuItem.setIcon(avatarDrawable); + MenuItem newIcon = menuItem.setIcon(avatarDrawable); + Log_OC.d(TAG,"avatarGenerated new icon: " + newIcon); } else if (callContext instanceof ImageView imageView) { imageView.setImageDrawable(avatarDrawable); } else if (callContext instanceof MaterialButton materialButton) { From 3d03ea09b085cd8b23cfff7e687fc75e0d50b977 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 16 Apr 2024 11:48:51 +0200 Subject: [PATCH 59/74] Fix filterAssistantMenuItem Signed-off-by: alperozturk --- .../android/ui/activity/DrawerActivity.java | 35 +++++++++++-------- .../android/utils/DrawerMenuUtil.java | 9 +++-- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index 97253333ee..3de5d66aba 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -29,7 +29,6 @@ import android.graphics.drawable.LayerDrawable; import android.net.Uri; import android.os.Bundle; import android.os.Handler; -import android.os.Looper; import android.os.SystemClock; import android.text.TextUtils; import android.view.Menu; @@ -285,14 +284,12 @@ public abstract class DrawerActivity extends ToolbarActivity viewThemeUtils.material.colorProgressBar(mQuotaProgressBar); } - private boolean showTopBanner = false; - public void updateHeader() { int primaryColor = themeColorUtils.unchangedPrimaryColor(getAccount(), this); + boolean isClientBranded = getResources().getBoolean(R.bool.is_branded_client); if (getAccount() != null && - getCapabilities().getServerBackground() != null && - !getResources().getBoolean(R.bool.is_branded_client)) { + getCapabilities().getServerBackground() != null && !isClientBranded) { OCCapability capability = getCapabilities(); String logo = capability.getServerLogo(); @@ -342,19 +339,29 @@ public abstract class DrawerActivity extends ToolbarActivity } // hide ecosystem apps according to user preference or in branded client - LinearLayout ecosystemApps = mNavigationViewHeader.findViewById(R.id.drawer_ecosystem_apps); - showTopBanner = !getResources().getBoolean(R.bool.is_branded_client) && preferences.isShowEcosystemApps(); + LinearLayout banner = mNavigationViewHeader.findViewById(R.id.drawer_ecosystem_apps); + boolean shouldHideTopBanner = isClientBranded || !preferences.isShowEcosystemApps(); - if (showTopBanner) { - showBanner(ecosystemApps, primaryColor); + if (shouldHideTopBanner) { + hideTopBanner(banner); } else { - MenuItem assistanMenuItem = findViewById(R.id.nav_assistant); - assistanMenuItem.setVisible(false); - ecosystemApps.setVisibility(View.GONE); + showTopBanner(banner, primaryColor); } } - private void showBanner(LinearLayout banner, int primaryColor) { + private void hideTopBanner(LinearLayout banner) { + banner.setVisibility(View.GONE); + } + + private void hideAssistantMenuItem() { + MenuItem assistantMenuItem = findViewById(R.id.nav_assistant); + + if (assistantMenuItem != null) { + assistantMenuItem.setVisible(false); + } + } + + private void showTopBanner(LinearLayout banner, int primaryColor) { LinearLayout notesView = banner.findViewById(R.id.drawer_ecosystem_notes); LinearLayout talkView = banner.findViewById(R.id.drawer_ecosystem_talk); LinearLayout moreView = banner.findViewById(R.id.drawer_ecosystem_more); @@ -472,7 +479,7 @@ public abstract class DrawerActivity extends ToolbarActivity DrawerMenuUtil.filterTrashbinMenuItem(menu, capability); DrawerMenuUtil.filterActivityMenuItem(menu, capability); DrawerMenuUtil.filterGroupfoldersMenuItem(menu, capability); - DrawerMenuUtil.filterAssistantMenuItem(menu, capability, getResources(), showTopBanner); + DrawerMenuUtil.filterAssistantMenuItem(menu, capability, getResources()); DrawerMenuUtil.setupHomeMenuItem(menu, getResources()); DrawerMenuUtil.removeMenuItem(menu, R.id.nav_community, !getResources().getBoolean(R.bool.participate_enabled)); DrawerMenuUtil.removeMenuItem(menu, R.id.nav_shared, !getResources().getBoolean(R.bool.shared_enabled)); diff --git a/app/src/main/java/com/owncloud/android/utils/DrawerMenuUtil.java b/app/src/main/java/com/owncloud/android/utils/DrawerMenuUtil.java index 22c59548f0..0f39645319 100644 --- a/app/src/main/java/com/owncloud/android/utils/DrawerMenuUtil.java +++ b/app/src/main/java/com/owncloud/android/utils/DrawerMenuUtil.java @@ -48,9 +48,12 @@ public final class DrawerMenuUtil { } } - public static void filterAssistantMenuItem(Menu menu, @Nullable OCCapability capability, Resources resources, boolean showTopBanner) { - boolean showCondition = (capability != null && capability.getAssistant().isTrue() && !resources.getBoolean(R.bool.is_branded_client)); - if (!showCondition || showTopBanner) { + public static void filterAssistantMenuItem(Menu menu, @Nullable OCCapability capability, Resources resources) { + if (resources.getBoolean(R.bool.is_branded_client)) { + if (capability != null && capability.getAssistant().isFalse()) { + removeMenuItem(menu, R.id.nav_assistant); + } + } else { removeMenuItem(menu, R.id.nav_assistant); } } From d3f3d3461b58cd3fccd0873cc6fe3a463926f3ae Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 16 Apr 2024 11:53:26 +0200 Subject: [PATCH 60/74] Remove unused codes Signed-off-by: alperozturk --- .../com/owncloud/android/ui/activity/DrawerActivity.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index 3de5d66aba..35b8b8b45a 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -353,14 +353,6 @@ public abstract class DrawerActivity extends ToolbarActivity banner.setVisibility(View.GONE); } - private void hideAssistantMenuItem() { - MenuItem assistantMenuItem = findViewById(R.id.nav_assistant); - - if (assistantMenuItem != null) { - assistantMenuItem.setVisible(false); - } - } - private void showTopBanner(LinearLayout banner, int primaryColor) { LinearLayout notesView = banner.findViewById(R.id.drawer_ecosystem_notes); LinearLayout talkView = banner.findViewById(R.id.drawer_ecosystem_talk); From ca51c206b38c9b3e4599b991716a200623aa5913 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 16 Apr 2024 12:50:28 +0200 Subject: [PATCH 61/74] Remove unused xml Signed-off-by: alperozturk --- app/src/main/res/drawable/ic_expand_less.xml | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 app/src/main/res/drawable/ic_expand_less.xml diff --git a/app/src/main/res/drawable/ic_expand_less.xml b/app/src/main/res/drawable/ic_expand_less.xml deleted file mode 100644 index a625653901..0000000000 --- a/app/src/main/res/drawable/ic_expand_less.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - From a3785336d8f31529eb767e27b600f3759323d433 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 16 Apr 2024 16:32:04 +0200 Subject: [PATCH 62/74] Add task detail screen Signed-off-by: alperozturk --- .../client/assistant/component/TaskView.kt | 20 ++- .../assistant/taskDetail/TaskDetailScreen.kt | 146 ++++++++++++++++++ app/src/main/res/values/colors.xml | 1 + app/src/main/res/values/strings.xml | 3 + 4 files changed, 165 insertions(+), 5 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/client/assistant/taskDetail/TaskDetailScreen.kt diff --git a/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt b/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt index 441f703320..41ccd46793 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt @@ -22,9 +22,7 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width -import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.verticalScroll import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text @@ -44,6 +42,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.nextcloud.client.assistant.extensions.statusData +import com.nextcloud.client.assistant.taskDetail.TaskDetailScreen import com.nextcloud.ui.composeComponents.bottomSheet.MoreActionsBottomSheet import com.nextcloud.utils.extensions.getRandomString import com.owncloud.android.R @@ -56,7 +55,7 @@ fun TaskView( task: Task, showDeleteTaskAlertDialog: (Long) -> Unit ) { - val verticalScrollState = rememberScrollState(0) + var showTaskDetailBottomSheet by remember { mutableStateOf(false) } var showMoreActionsBottomSheet by remember { mutableStateOf(false) } Column( @@ -65,6 +64,7 @@ fun TaskView( .clip(RoundedCornerShape(16.dp)) .background(MaterialTheme.colorScheme.primary) .combinedClickable(onClick = { + showTaskDetailBottomSheet = true }, onLongClick = { showMoreActionsBottomSheet = true }) @@ -86,12 +86,11 @@ fun TaskView( HorizontalDivider(modifier = Modifier.padding(horizontal = 4.dp, vertical = 8.dp)) Text( - text = it, + text = it.take(100), fontSize = 12.sp, color = Color.White, modifier = Modifier .height(100.dp) - .verticalScroll(verticalScrollState) .animateContentSize( animationSpec = spring( dampingRatio = Spring.DampingRatioLowBouncy, @@ -137,6 +136,16 @@ fun TaskView( dismiss = { showMoreActionsBottomSheet = false } ) } + + if (showTaskDetailBottomSheet) { + task.input?.let { input -> + task.output?.let { output -> + TaskDetailScreen(input, output) { + showTaskDetailBottomSheet = false + } + } + } + } } } @@ -159,5 +168,6 @@ private fun TaskViewPreview() { "" ) ) { + } } diff --git a/app/src/main/java/com/nextcloud/client/assistant/taskDetail/TaskDetailScreen.kt b/app/src/main/java/com/nextcloud/client/assistant/taskDetail/TaskDetailScreen.kt new file mode 100644 index 0000000000..cb94ec7473 --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/assistant/taskDetail/TaskDetailScreen.kt @@ -0,0 +1,146 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Your Name + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.client.assistant.taskDetail + +import androidx.compose.animation.animateContentSize +import androidx.compose.animation.core.Spring +import androidx.compose.animation.core.spring +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.widthIn +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.Text +import androidx.compose.material3.rememberModalBottomSheetState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.colorResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.nextcloud.utils.extensions.getRandomString +import com.owncloud.android.R + +@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class) +@Composable +fun TaskDetailScreen(input: String, output: String, dismiss: () -> Unit) { + var showInput by remember { mutableStateOf(true) } + val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) + + ModalBottomSheet( + modifier = Modifier.padding(top = 32.dp), + containerColor = Color.White, + onDismissRequest = { + dismiss() + }, + sheetState = sheetState + ) { + LazyColumn( + modifier = Modifier + .fillMaxSize() + .padding(16.dp) + ) { + + stickyHeader { + Row( + modifier = Modifier + .fillMaxWidth() + .background(color = colorResource(id = R.color.light_grey), shape = RoundedCornerShape(8.dp)) + ) { + TextInputSelectButton( + Modifier.weight(1f), + R.string.assistant_task_detail_screen_input_button_title, + showInput, + onClick = { + showInput = true + } + ) + + TextInputSelectButton( + Modifier.weight(1f), + R.string.assistant_task_detail_screen_output_button_title, + !showInput, + onClick = { + showInput = false + } + ) + } + } + + item { + Spacer(modifier = Modifier.height(16.dp)) + + Column(modifier = Modifier + .fillMaxSize() + .background(color = colorResource(id = R.color.light_grey), shape = RoundedCornerShape(8.dp)) + .padding(16.dp)) { + Text( + text = if (showInput) { + input + } else { + output + }, + fontSize = 12.sp, + color = Color.Black, + modifier = Modifier + .animateContentSize( + animationSpec = spring( + dampingRatio = Spring.DampingRatioLowBouncy, + stiffness = Spring.StiffnessLow + ) + ) + ) + } + + Spacer(modifier = Modifier.height(32.dp)) + } + } + } +} + +@Composable +private fun TextInputSelectButton(modifier: Modifier, titleId: Int, highlightCondition: Boolean, onClick: () -> Unit) { + Button( + onClick = onClick, + shape = RoundedCornerShape(8.dp), + colors = if (highlightCondition) { + ButtonDefaults.buttonColors(containerColor = Color.White) + } else { + ButtonDefaults.buttonColors(containerColor = colorResource(id = R.color.light_grey)) + }, + modifier = modifier + .widthIn(min = 0.dp, max = 200.dp) + .padding(horizontal = 4.dp) + ) { + Text(text = stringResource(id = titleId), color = Color.Black) + } +} + +@Suppress("MagicNumber") +@Preview +@Composable +private fun TaskDetailScreenPreview() { + TaskDetailScreen(input = "some input".getRandomString(20), output = "some output".getRandomString(3000)) { } +} diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index d28fe79467..9a721eb3e3 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -15,6 +15,7 @@ #ffffff #B3FFFFFF #333333 + #F5F5F5 #303034 #E9E8EB @color/secondary_text_color diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a2777a201e..f99a6fd7ff 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -52,6 +52,9 @@ Failed In Progress + Input + Output + All Assistant From 7df114dda0195f42535a393d369d104809578ad5 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 16 Apr 2024 17:09:43 +0200 Subject: [PATCH 63/74] Fix code analytics Signed-off-by: alperozturk --- .../nextcloud/client/assistant/component/TaskView.kt | 1 - .../client/assistant/taskDetail/TaskDetailScreen.kt | 12 +++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt b/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt index 41ccd46793..28775341cb 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt @@ -168,6 +168,5 @@ private fun TaskViewPreview() { "" ) ) { - } } diff --git a/app/src/main/java/com/nextcloud/client/assistant/taskDetail/TaskDetailScreen.kt b/app/src/main/java/com/nextcloud/client/assistant/taskDetail/TaskDetailScreen.kt index cb94ec7473..c6a8cdc64f 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/taskDetail/TaskDetailScreen.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/taskDetail/TaskDetailScreen.kt @@ -43,6 +43,7 @@ import androidx.compose.ui.unit.sp import com.nextcloud.utils.extensions.getRandomString import com.owncloud.android.R +@Suppress("LongMethod") @OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class) @Composable fun TaskDetailScreen(input: String, output: String, dismiss: () -> Unit) { @@ -62,7 +63,6 @@ fun TaskDetailScreen(input: String, output: String, dismiss: () -> Unit) { .fillMaxSize() .padding(16.dp) ) { - stickyHeader { Row( modifier = Modifier @@ -92,10 +92,12 @@ fun TaskDetailScreen(input: String, output: String, dismiss: () -> Unit) { item { Spacer(modifier = Modifier.height(16.dp)) - Column(modifier = Modifier - .fillMaxSize() - .background(color = colorResource(id = R.color.light_grey), shape = RoundedCornerShape(8.dp)) - .padding(16.dp)) { + Column( + modifier = Modifier + .fillMaxSize() + .background(color = colorResource(id = R.color.light_grey), shape = RoundedCornerShape(8.dp)) + .padding(16.dp) + ) { Text( text = if (showInput) { input From c5808fafe8854714dfc8ec88fa11a2df0edfb96d Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 16 Apr 2024 20:08:15 +0200 Subject: [PATCH 64/74] Add completion time Signed-off-by: alperozturk --- .../client/assistant/AsssistantScreen.kt | 4 +- .../assistant/extensions/TaskExtensions.kt | 5 ++ .../client/assistant/task/TaskStatus.kt | 57 +++++++++++++++++++ .../assistant/{component => task}/TaskView.kt | 44 ++------------ ...tailScreen.kt => TaskDetailBottomSheet.kt} | 24 ++++++-- .../{component => taskTypes}/TaskTypesRow.kt | 2 +- 6 files changed, 91 insertions(+), 45 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/client/assistant/task/TaskStatus.kt rename app/src/main/java/com/nextcloud/client/assistant/{component => task}/TaskView.kt (72%) rename app/src/main/java/com/nextcloud/client/assistant/taskDetail/{TaskDetailScreen.kt => TaskDetailBottomSheet.kt} (90%) rename app/src/main/java/com/nextcloud/client/assistant/{component => taskTypes}/TaskTypesRow.kt (97%) diff --git a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt index d7b09d4f00..4dd567c4fd 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt @@ -42,8 +42,8 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.nextcloud.client.assistant.component.AddTaskAlertDialog import com.nextcloud.client.assistant.component.CenterText -import com.nextcloud.client.assistant.component.TaskTypesRow -import com.nextcloud.client.assistant.component.TaskView +import com.nextcloud.client.assistant.taskTypes.TaskTypesRow +import com.nextcloud.client.assistant.task.TaskView import com.nextcloud.client.assistant.repository.AssistantMockRepository import com.nextcloud.ui.composeActivity.ComposeActivity import com.nextcloud.ui.composeComponents.alertDialog.SimpleAlertDialog diff --git a/app/src/main/java/com/nextcloud/client/assistant/extensions/TaskExtensions.kt b/app/src/main/java/com/nextcloud/client/assistant/extensions/TaskExtensions.kt index fb7eeb0faa..f2ff257800 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/extensions/TaskExtensions.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/extensions/TaskExtensions.kt @@ -35,3 +35,8 @@ fun Task.statusData(): Pair { } } } + +// TODO add +fun Task.completionDateRepresentation(): String { + return completionExpectedAt ?: "TODO IMPLEMENT IT" +} diff --git a/app/src/main/java/com/nextcloud/client/assistant/task/TaskStatus.kt b/app/src/main/java/com/nextcloud/client/assistant/task/TaskStatus.kt new file mode 100644 index 0000000000..e011852d12 --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/assistant/task/TaskStatus.kt @@ -0,0 +1,57 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Your Name + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.client.assistant.task + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import com.nextcloud.client.assistant.extensions.completionDateRepresentation +import com.nextcloud.client.assistant.extensions.statusData +import com.owncloud.android.lib.resources.assistant.model.Task + +@Composable +fun TaskStatus(task: Task, foregroundColor: Color) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + val (iconId, descriptionId) = task.statusData() + + Image( + painter = painterResource(id = iconId), + modifier = Modifier.size(16.dp), + colorFilter = ColorFilter.tint(foregroundColor), + contentDescription = "status icon" + ) + + Spacer(modifier = Modifier.width(6.dp)) + + Text(text = stringResource(id = descriptionId), color = foregroundColor) + + Spacer(modifier = Modifier.weight(1f)) + + Text(text = task.completionDateRepresentation(), color = foregroundColor) + + Spacer(modifier = Modifier.width(6.dp)) + } +} diff --git a/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt b/app/src/main/java/com/nextcloud/client/assistant/task/TaskView.kt similarity index 72% rename from app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt rename to app/src/main/java/com/nextcloud/client/assistant/task/TaskView.kt index 28775341cb..865f41bf4c 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/task/TaskView.kt @@ -1,27 +1,22 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk - * SPDX-FileCopyrightText: 2024 Nextcloud GmbH + * SPDX-FileCopyrightText: 2024 Your Name * SPDX-License-Identifier: AGPL-3.0-or-later */ -package com.nextcloud.client.assistant.component +package com.nextcloud.client.assistant.task import androidx.compose.animation.animateContentSize import androidx.compose.animation.core.Spring import androidx.compose.animation.core.spring import androidx.compose.foundation.ExperimentalFoundationApi -import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.MaterialTheme @@ -31,18 +26,13 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.nextcloud.client.assistant.extensions.statusData -import com.nextcloud.client.assistant.taskDetail.TaskDetailScreen +import com.nextcloud.client.assistant.taskDetail.TaskDetailBottomSheet import com.nextcloud.ui.composeComponents.bottomSheet.MoreActionsBottomSheet import com.nextcloud.utils.extensions.getRandomString import com.owncloud.android.R @@ -100,25 +90,7 @@ fun TaskView( ) } - Row( - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 16.dp), - verticalAlignment = Alignment.CenterVertically - ) { - val (iconId, descriptionId) = task.statusData() - - Image( - painter = painterResource(id = iconId), - modifier = Modifier.size(16.dp), - colorFilter = ColorFilter.tint(Color.White), - contentDescription = "status icon" - ) - - Spacer(modifier = Modifier.width(6.dp)) - - Text(text = stringResource(id = descriptionId), color = Color.White) - } + TaskStatus(task, foregroundColor = Color.White) if (showMoreActionsBottomSheet) { val bottomSheetAction = listOf( @@ -138,12 +110,8 @@ fun TaskView( } if (showTaskDetailBottomSheet) { - task.input?.let { input -> - task.output?.let { output -> - TaskDetailScreen(input, output) { - showTaskDetailBottomSheet = false - } - } + TaskDetailBottomSheet(task) { + showTaskDetailBottomSheet = false } } } diff --git a/app/src/main/java/com/nextcloud/client/assistant/taskDetail/TaskDetailScreen.kt b/app/src/main/java/com/nextcloud/client/assistant/taskDetail/TaskDetailBottomSheet.kt similarity index 90% rename from app/src/main/java/com/nextcloud/client/assistant/taskDetail/TaskDetailScreen.kt rename to app/src/main/java/com/nextcloud/client/assistant/taskDetail/TaskDetailBottomSheet.kt index c6a8cdc64f..70833cef0d 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/taskDetail/TaskDetailScreen.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/taskDetail/TaskDetailBottomSheet.kt @@ -40,13 +40,15 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import com.nextcloud.client.assistant.task.TaskStatus import com.nextcloud.utils.extensions.getRandomString import com.owncloud.android.R +import com.owncloud.android.lib.resources.assistant.model.Task @Suppress("LongMethod") @OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class) @Composable -fun TaskDetailScreen(input: String, output: String, dismiss: () -> Unit) { +fun TaskDetailBottomSheet(task: Task, dismiss: () -> Unit) { var showInput by remember { mutableStateOf(true) } val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) @@ -100,9 +102,9 @@ fun TaskDetailScreen(input: String, output: String, dismiss: () -> Unit) { ) { Text( text = if (showInput) { - input + task.input ?: "" } else { - output + task.output ?: "" }, fontSize = 12.sp, color = Color.Black, @@ -116,6 +118,8 @@ fun TaskDetailScreen(input: String, output: String, dismiss: () -> Unit) { ) } + TaskStatus(task, foregroundColor = Color.Black) + Spacer(modifier = Modifier.height(32.dp)) } } @@ -144,5 +148,17 @@ private fun TextInputSelectButton(modifier: Modifier, titleId: Int, highlightCon @Preview @Composable private fun TaskDetailScreenPreview() { - TaskDetailScreen(input = "some input".getRandomString(20), output = "some output".getRandomString(3000)) { } + TaskDetailBottomSheet(task = Task( + 1, + "Free Prompt", + 0, + "1", + "1", + "Give me text".getRandomString(100), + "output".getRandomString(300), + "", + "" + )) { + + } } diff --git a/app/src/main/java/com/nextcloud/client/assistant/component/TaskTypesRow.kt b/app/src/main/java/com/nextcloud/client/assistant/taskTypes/TaskTypesRow.kt similarity index 97% rename from app/src/main/java/com/nextcloud/client/assistant/component/TaskTypesRow.kt rename to app/src/main/java/com/nextcloud/client/assistant/taskTypes/TaskTypesRow.kt index 6a4690b01e..eab26875c2 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/component/TaskTypesRow.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/taskTypes/TaskTypesRow.kt @@ -5,7 +5,7 @@ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later */ -package com.nextcloud.client.assistant.component +package com.nextcloud.client.assistant.taskTypes import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.Row From 1e88d0df163340b1e2dd6f3c38d6d9d43e69d54b Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 16 Apr 2024 21:52:53 +0200 Subject: [PATCH 65/74] Fix drawer close Signed-off-by: alperozturk --- .../ui/composeActivity/ComposeActivity.kt | 2 +- .../android/ui/activity/DrawerActivity.java | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt index 6934dabcd0..f0de1e0888 100644 --- a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt +++ b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt @@ -56,7 +56,7 @@ class ComposeActivity : DrawerActivity() { setupDrawer(menuItemId) setupToolbarShowOnlyMenuButtonAndTitle(getString(titleId)) { - toggleDrawer() + openDrawer() } binding.composeView.setContent { diff --git a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index 35b8b8b45a..98d03372eb 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -484,8 +484,6 @@ public abstract class DrawerActivity extends ToolbarActivity } private void onNavigationItemClicked(final MenuItem menuItem) { - closeDrawer(); - setDrawerMenuItemChecked(menuItem.getItemId()); int itemId = menuItem.getItemId(); @@ -504,6 +502,11 @@ public abstract class DrawerActivity extends ToolbarActivity MainApp.showOnlyPersonalFiles(itemId == R.id.nav_personal_files); Intent intent = new Intent(getApplicationContext(), FileDisplayActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + + if (this instanceof ComposeActivity) { + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + } + intent.setAction(FileDisplayActivity.ALL_FILES); intent.putExtra(FileDisplayActivity.DRAWER_MENU_ID, menuItem.getItemId()); startActivity(intent); @@ -640,6 +643,11 @@ public abstract class DrawerActivity extends ToolbarActivity private void launchActivityForSearch(SearchEvent searchEvent, int menuItemId) { Intent intent = new Intent(getApplicationContext(), FileDisplayActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + + if (this instanceof ComposeActivity) { + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + } + intent.setAction(Intent.ACTION_SEARCH); intent.putExtra(OCFileListFragment.SEARCH_EVENT, searchEvent); intent.putExtra(FileDisplayActivity.DRAWER_MENU_ID, menuItemId); @@ -1136,6 +1144,11 @@ public abstract class DrawerActivity extends ToolbarActivity MainApp.showOnlyPersonalFiles(onlyPersonalFiles); Intent fileDisplayActivity = new Intent(getApplicationContext(), FileDisplayActivity.class); fileDisplayActivity.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + + if (this instanceof ComposeActivity) { + fileDisplayActivity.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + } + fileDisplayActivity.setAction(FileDisplayActivity.ALL_FILES); startActivity(fileDisplayActivity); } From 330d0ac83708747b826a5c8b931cbaac519f4060 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 17 Apr 2024 08:09:06 +0200 Subject: [PATCH 66/74] ADD TODO Signed-off-by: alperozturk --- .../java/com/nextcloud/client/assistant/task/TaskStatus.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/nextcloud/client/assistant/task/TaskStatus.kt b/app/src/main/java/com/nextcloud/client/assistant/task/TaskStatus.kt index e011852d12..c9179881c7 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/task/TaskStatus.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/task/TaskStatus.kt @@ -23,7 +23,6 @@ import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import com.nextcloud.client.assistant.extensions.completionDateRepresentation import com.nextcloud.client.assistant.extensions.statusData import com.owncloud.android.lib.resources.assistant.model.Task @@ -48,10 +47,12 @@ fun TaskStatus(task: Task, foregroundColor: Color) { Text(text = stringResource(id = descriptionId), color = foregroundColor) + /* Spacer(modifier = Modifier.weight(1f)) Text(text = task.completionDateRepresentation(), color = foregroundColor) Spacer(modifier = Modifier.width(6.dp)) + */ } } From 0609caadb3b68954b8f9b02e573319073230a307 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 17 Apr 2024 08:09:43 +0200 Subject: [PATCH 67/74] Fix code analytics Signed-off-by: alperozturk --- .../taskDetail/TaskDetailBottomSheet.kt | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/assistant/taskDetail/TaskDetailBottomSheet.kt b/app/src/main/java/com/nextcloud/client/assistant/taskDetail/TaskDetailBottomSheet.kt index 70833cef0d..e7148de43d 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/taskDetail/TaskDetailBottomSheet.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/taskDetail/TaskDetailBottomSheet.kt @@ -148,17 +148,18 @@ private fun TextInputSelectButton(modifier: Modifier, titleId: Int, highlightCon @Preview @Composable private fun TaskDetailScreenPreview() { - TaskDetailBottomSheet(task = Task( - 1, - "Free Prompt", - 0, - "1", - "1", - "Give me text".getRandomString(100), - "output".getRandomString(300), - "", - "" - )) { - + TaskDetailBottomSheet( + task = Task( + 1, + "Free Prompt", + 0, + "1", + "1", + "Give me text".getRandomString(100), + "output".getRandomString(300), + "", + "" + ) + ) { } } From 8d73fd74011657444e116dd86488539e0f91a809 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 17 Apr 2024 07:16:27 +0000 Subject: [PATCH 68/74] Update ubuntu:jammy Docker digest to 1b8d8ff Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .devcontainer/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index b6f52d89c6..8f068afcad 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:jammy@sha256:77906da86b60585ce12215807090eb327e7386c8fafb5402369e421f44eff17e +FROM ubuntu:jammy@sha256:1b8d8ff4777f36f19bfe73ee4df61e3a0b789caeff29caa019539ec7c9a57f95 ARG DEBIAN_FRONTEND=noninteractive ENV ANDROID_HOME=/usr/lib/android-sdk From 58ec17a63f1bb8701f65bda81b4d5683a585640f Mon Sep 17 00:00:00 2001 From: tobiasKaminsky Date: Wed, 17 Apr 2024 10:10:33 +0200 Subject: [PATCH 69/74] suppress spotbugs Signed-off-by: tobiasKaminsky --- .../com/owncloud/android/datamodel/FileDataStorageManager.java | 1 + .../com/owncloud/android/operations/RefreshFolderOperation.java | 2 ++ 2 files changed, 3 insertions(+) diff --git a/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java b/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java index bcdf36f2f9..d0d9761ef0 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java +++ b/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java @@ -2337,6 +2337,7 @@ public class FileDataStorageManager { return null; } + @SuppressFBWarnings("OCP") private List> getDecryptedFileNamesAndEncryptedRemotePaths(List fileList) { List> result = new ArrayList<>(); diff --git a/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java b/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java index b428efc8fe..33b31c44df 100644 --- a/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java @@ -58,6 +58,7 @@ import java.util.Vector; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.localbroadcastmanager.content.LocalBroadcastManager; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import static com.owncloud.android.datamodel.OCFile.PATH_SEPARATOR; @@ -601,6 +602,7 @@ public class RefreshFolderOperation extends RemoteOperation { return metadata; } + @SuppressFBWarnings("CE") private static void setMimeTypeAndDecryptedRemotePath(OCFile updatedFile, FileDataStorageManager storageManager, String decryptedFileName, String mimetype) { OCFile parentFile = storageManager.getFileById(updatedFile.getParentId()); From c2530c8cfc7914471ba634e52f091376336f28e1 Mon Sep 17 00:00:00 2001 From: tobiasKaminsky Date: Wed, 17 Apr 2024 10:42:22 +0200 Subject: [PATCH 70/74] suppress spotbugs Signed-off-by: tobiasKaminsky --- .../com/owncloud/android/datamodel/FileDataStorageManager.java | 1 + .../com/owncloud/android/operations/RefreshFolderOperation.java | 1 + 2 files changed, 2 insertions(+) diff --git a/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java b/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java index d0d9761ef0..214c9820a7 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java +++ b/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java @@ -1705,6 +1705,7 @@ public class FileDataStorageManager { } } + @SuppressFBWarnings("PSC") public void saveConflict(OCFile ocFile, String etagInConflict) { ContentValues cv = new ContentValues(); if (!ocFile.isDown()) { diff --git a/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java b/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java index 33b31c44df..7da4f99517 100644 --- a/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java @@ -738,6 +738,7 @@ public class RefreshFolderOperation extends RemoteOperation { } @NonNull + @SuppressFBWarnings("OCP") public static Map prefillLocalFilesMap(Object metadata, List localFiles) { Map localFilesMap = Maps.newHashMapWithExpectedSize(localFiles.size()); From 6c7a9211e51f46b7dd5579c9678128d368867067 Mon Sep 17 00:00:00 2001 From: tobiasKaminsky Date: Wed, 17 Apr 2024 12:00:45 +0200 Subject: [PATCH 71/74] outdated version is now NC26 Signed-off-by: tobiasKaminsky --- app/src/main/java/com/owncloud/android/MainApp.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/owncloud/android/MainApp.java b/app/src/main/java/com/owncloud/android/MainApp.java index ae7e03f736..f6d754b402 100644 --- a/app/src/main/java/com/owncloud/android/MainApp.java +++ b/app/src/main/java/com/owncloud/android/MainApp.java @@ -124,7 +124,7 @@ import static com.owncloud.android.ui.activity.ContactsPreferenceActivity.PREFER * Contains methods to build the "static" strings. These strings were before constants in different classes. */ public class MainApp extends MultiDexApplication implements HasAndroidInjector { - public static final OwnCloudVersion OUTDATED_SERVER_VERSION = NextcloudVersion.nextcloud_23; + public static final OwnCloudVersion OUTDATED_SERVER_VERSION = NextcloudVersion.nextcloud_26; public static final OwnCloudVersion MINIMUM_SUPPORTED_SERVER_VERSION = OwnCloudVersion.nextcloud_16; private static final String TAG = MainApp.class.getSimpleName(); From 260b4ac08f8059a0b015e9f383e321c0bf0f9851 Mon Sep 17 00:00:00 2001 From: tobiasKaminsky Date: Wed, 17 Apr 2024 12:14:00 +0200 Subject: [PATCH 72/74] Bump master to 3.30 alpha Signed-off-by: tobiasKaminsky --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 261956d666..e912466a66 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -77,7 +77,7 @@ configurations.configureEach { // semantic versioning for version code def versionMajor = 3 -def versionMinor = 29 +def versionMinor = 30 def versionPatch = 0 def versionBuild = 0 // 0-50=Alpha / 51-98=RC / 90-99=stable From eda6fdaa04a556a1ee7479ea0f8f6d55a9651fbf Mon Sep 17 00:00:00 2001 From: Nextcloud bot Date: Wed, 17 Apr 2024 10:14:16 +0000 Subject: [PATCH 73/74] Fix(l10n): Update translations from Transifex Signed-off-by: Nextcloud bot --- app/src/main/res/values-ar/strings.xml | 3 +- app/src/main/res/values-b+en+001/strings.xml | 4 +- app/src/main/res/values-bg-rBG/strings.xml | 2 - app/src/main/res/values-br/strings.xml | 2 - app/src/main/res/values-ca/strings.xml | 2 - app/src/main/res/values-cs-rCZ/strings.xml | 3 +- app/src/main/res/values-da/strings.xml | 2 - app/src/main/res/values-de/strings.xml | 4 +- app/src/main/res/values-el/strings.xml | 2 - app/src/main/res/values-eo/strings.xml | 1 - app/src/main/res/values-es-rAR/strings.xml | 2 - app/src/main/res/values-es-rCL/strings.xml | 1 - app/src/main/res/values-es-rCO/strings.xml | 1 - app/src/main/res/values-es-rCR/strings.xml | 1 - app/src/main/res/values-es-rDO/strings.xml | 1 - app/src/main/res/values-es-rEC/strings.xml | 2 - app/src/main/res/values-es-rGT/strings.xml | 1 - app/src/main/res/values-es-rMX/strings.xml | 2 - app/src/main/res/values-es-rSV/strings.xml | 1 - app/src/main/res/values-es/strings.xml | 4 +- app/src/main/res/values-et-rEE/strings.xml | 2 - app/src/main/res/values-eu/strings.xml | 4 +- app/src/main/res/values-fa/strings.xml | 2 - app/src/main/res/values-fi-rFI/strings.xml | 2 - app/src/main/res/values-fr/strings.xml | 4 +- app/src/main/res/values-gd/strings.xml | 2 - app/src/main/res/values-gl/strings.xml | 3 +- app/src/main/res/values-hr/strings.xml | 2 - app/src/main/res/values-hu-rHU/strings.xml | 2 - app/src/main/res/values-in/strings.xml | 2 - app/src/main/res/values-is/strings.xml | 2 - app/src/main/res/values-it/strings.xml | 2 - app/src/main/res/values-iw/strings.xml | 2 - app/src/main/res/values-ja-rJP/strings.xml | 2 - app/src/main/res/values-ka-rGE/strings.xml | 506 ------------------- app/src/main/res/values-ka/strings.xml | 2 - app/src/main/res/values-ko/strings.xml | 2 - app/src/main/res/values-lo/strings.xml | 2 - app/src/main/res/values-lt-rLT/strings.xml | 2 - app/src/main/res/values-lv/strings.xml | 2 - app/src/main/res/values-mk/strings.xml | 2 - app/src/main/res/values-nb-rNO/strings.xml | 4 +- app/src/main/res/values-nl/strings.xml | 2 - app/src/main/res/values-pl/strings.xml | 2 - app/src/main/res/values-pt-rBR/strings.xml | 4 +- app/src/main/res/values-pt-rPT/strings.xml | 2 - app/src/main/res/values-ro/strings.xml | 2 - app/src/main/res/values-ru/strings.xml | 2 - app/src/main/res/values-sc/strings.xml | 2 - app/src/main/res/values-sk-rSK/strings.xml | 2 - app/src/main/res/values-sl/strings.xml | 2 - app/src/main/res/values-sq/strings.xml | 2 - app/src/main/res/values-sr-rSP/strings.xml | 1 - app/src/main/res/values-sr/strings.xml | 4 +- app/src/main/res/values-sv/strings.xml | 3 +- app/src/main/res/values-th-rTH/strings.xml | 2 - app/src/main/res/values-tk/strings.xml | 2 - app/src/main/res/values-tr/strings.xml | 4 +- app/src/main/res/values-uk/strings.xml | 2 - app/src/main/res/values-vi/strings.xml | 2 - app/src/main/res/values-zh-rCN/strings.xml | 3 +- app/src/main/res/values-zh-rHK/strings.xml | 4 +- app/src/main/res/values-zh-rTW/strings.xml | 4 +- 63 files changed, 27 insertions(+), 622 deletions(-) delete mode 100644 app/src/main/res/values-ka-rGE/strings.xml diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 67b371d125..f4420e1201 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -7,7 +7,6 @@ فشل إنشاء الحساب أيقونة الحساب الحساب غير موجود - مسح الملفات التي فشل رفعها تعديل إزالة جميع التنبيهات تفريغ سلة المهملات @@ -57,6 +56,7 @@ تعذّر جلب أنواع المهام. قم رجاءً بالتحقُّق من اتصالك بالإنترنت. المُساعِد غير معروف + المُخرَجَات الحساب المرتبط غير موجود! فشل الوصول لـ: %1$s هذا الحساب لم تتم إضافته في هذا الجهاز بعد @@ -171,7 +171,6 @@ هل توَدُّ حقاً حذف %1$s وما يحتويه؟ هل توَدُّ حقاً حذف العناصر المختارة وما يحتوّه؟ محلياً فقط - خطأ في إنشاء حوار التعارض! ملف متضارب %1$s ملف محلي إذا قمت باختيار كلا الاصدارين, الملف المحلي سيحتوي على رقم ملحق باسم الملف. diff --git a/app/src/main/res/values-b+en+001/strings.xml b/app/src/main/res/values-b+en+001/strings.xml index cb05124dde..34545c31d1 100644 --- a/app/src/main/res/values-b+en+001/strings.xml +++ b/app/src/main/res/values-b+en+001/strings.xml @@ -7,7 +7,6 @@ Account creation failed Account icon Account not found! - Clear failed uploads Edit Clear all notifications Empty trash bin @@ -57,6 +56,8 @@ Unable to fetch task types, please check your internet connection. Assistant Unknown + Input + Output Associated account not found! Access failed: %1$s The account is not added on this device yet @@ -171,7 +172,6 @@ Do you really want to delete %1$s and its content? Do you really want to delete the selected items and their contents? Local only - Error creating conflict dialogue! Conflicting file %1$s Local file If you select both versions, the local file will have a number appended to its name. diff --git a/app/src/main/res/values-bg-rBG/strings.xml b/app/src/main/res/values-bg-rBG/strings.xml index 33f8be278b..ed82112939 100644 --- a/app/src/main/res/values-bg-rBG/strings.xml +++ b/app/src/main/res/values-bg-rBG/strings.xml @@ -7,7 +7,6 @@ Създаването на профила се провали Профил Няма намерен профил! - Изчисти провалените качвания Променяне Премахнете известията Изпразване на кошчето @@ -153,7 +152,6 @@ Наистина ли желаете %1$s и съдържанието ѝ да бъдат изтрито? Наистина ли желаете избраните елементи и съдържанието им да бъдат премахнати? Само локално - Грешка при създаване на диалог за конфликт! Несъвместим файл %1$s Локален файл Ако изберете и двете версии, ще бъде добавен номер към името на локалния файл. diff --git a/app/src/main/res/values-br/strings.xml b/app/src/main/res/values-br/strings.xml index f2b9de3335..ddafa90666 100644 --- a/app/src/main/res/values-br/strings.xml +++ b/app/src/main/res/values-br/strings.xml @@ -7,7 +7,6 @@ C\'hwitet eo bet ar c\'houiñ eus anr c\'hont Ikonenn kont N\'o ket bet kavet ar c\'hont - Lemmel ar pellgasoù chwitet Cheñch Lemmel toud ar c\'hemenadennoù Kas/Rannan @@ -136,7 +135,6 @@ Sur oc\'h lemel %1$s ha pep tra a zo barzh ? Sur oc\'h lemel an traoù choazet ha pep tra a zo barzh ? E-berzh nemetken - Ur fazi en deus krouet divizoù diemglev ! Ma choazit an daou stumm, ar restr diabarzh en do un niver ouzhpennet war e anv. Skeudenn implijer evit ar roll darempredoù Oaetre ebet roet, netra a zo bet emporzhiet. diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 3f595ad529..2347b3dd86 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -7,7 +7,6 @@ Ha fallat la creació del compte Icona del compte No s\'ha trobat el compte! - Esborra les pujades amb errors Edita Neteja totes les notificacions Buida la paperera @@ -152,7 +151,6 @@ Esteu segur que voleu suprimir %1$s i els seus continguts? Esteu segur que voleu suprimir els elements seleccionats i el seu contingut? Només local - Error creant diàleg de conflicte! Fitxer en conflicte %1$s Fitxer local Si seleccioneu ambdues versions, s\'afegirà un numero al nom del fitxer local. diff --git a/app/src/main/res/values-cs-rCZ/strings.xml b/app/src/main/res/values-cs-rCZ/strings.xml index 9ede6b4bdd..68c1ac6925 100644 --- a/app/src/main/res/values-cs-rCZ/strings.xml +++ b/app/src/main/res/values-cs-rCZ/strings.xml @@ -7,7 +7,6 @@ Vytvoření účtu se nezdařilo Ikona účtu Účet nenalezen! - Vyčistit nezdařená nahrávání Upravit Vyčistit všechna upozornění Vyprázdnit koš @@ -43,6 +42,7 @@ Dokončeno Úloha úspěšně smazána Neznámé + Výstup Související účet nenalezen! Přístup se nezdařil: %1$s Účet zatím není na tomto zařízení přidán @@ -157,7 +157,6 @@ Opravdu chcete %1$s a jeho obsah odstranit? Opravdu chcete vybrané položky a jejich obsah odstranit? Pouze místní - Chyba při vytváření dialogu ke konfliktu! Kolidující soubor %1$s Místní soubor Pokud zvolíte obě verze, k názvu místního souboru bude připojeno číslo. diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index f71abf8e69..3acb2da042 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -7,7 +7,6 @@ Fejl ved oprettelse af konto Kontoikon Konto blev ikke fundet. - Fjern fejlede uploads Redigér Ryd alle notifikationer Tøm papirkurv @@ -165,7 +164,6 @@ Er du sikker på at du vil slette %1$s med indhold? Er du sikker på at du vil slette de valgte artikler med indhold? Kun lokal - Fejl ved skabelse af konfliktdialog Fil i konflikt %1$s Lokal fil Hvis du vælger begge versioner, vil den lokale fil få tilføjet et nummer til sit navn. diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index bff7d365d3..91c7eab694 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -7,7 +7,6 @@ Kontoerstellung fehlgeschlagen Konto-Icon Kein Konto gefunden - Fehlgeschlagene Uploads entfernen Bearbeiten Alle Benachrichtigungen löschen Papierkorb leeren @@ -57,6 +56,8 @@ Die Aufgabentypen können nicht abgerufen werden. Bitte überprüfen Sie Ihre Internetverbindung. Assistent Unbekannt + Eingabe + Ausgabe Verknüpftes Konto nicht gefunden! Zugriffsfehler: %1$s Das Konto ist bislang auf dem Gerät nicht vorhanden @@ -171,7 +172,6 @@ Wollen Sie %1$s und deren Inhalte wirklich löschen? Möchten Sie die ausgewählten Elemente und deren inhalt wirklich löschen? Nur lokal - Fehler beim Erstellen des Konfliktdialoges Konflikt-Datei %1$s Lokale Datei Falls beide Versionen gewählt werden, wird bei der lokalen Datei eine Zahl am Ende des Dateinamens hinzugefügt. diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 16962a8c98..f1be75ab8a 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -7,7 +7,6 @@ Αποτυχία δημιουργίας λογαριασμού Εικονίδιο λογαριασμού Δεν βρέθηκε λογαριασμός! - Εκκαθάριση αποτυχημένων μεταφορτώσεων Επεξεργασία Εκκαθάριση ειδοποιήσεων Άδειασμα απορριμάτων @@ -151,7 +150,6 @@ Θέλετε σίγουρα να διαγράψετε το %1$s και τα περιεχόμενά του; Θέλετε να διαγράψετε τα επιλεγμένα αντικείμενα και τα περιεχόμενά τους; Μόνο τοπικά - Σφάλμα δημιουργίας αναφοράς διένεξης! Αρχείο σε αντίφαση %1$s Τοπικό αρχείο Εάν επιλέξετε και τις δύο εκδόσεις, στο όνομα του τοπικού αρχείου θα προστεθεί ένας αριθμός. diff --git a/app/src/main/res/values-eo/strings.xml b/app/src/main/res/values-eo/strings.xml index fd216e897d..dc5eb4c77d 100644 --- a/app/src/main/res/values-eo/strings.xml +++ b/app/src/main/res/values-eo/strings.xml @@ -7,7 +7,6 @@ Kreado de konto malsukcesis Konta piktogramo Konto ne trovita! - Vakigi malsukcesajn alŝutojn Modifi Forviŝi ĉiujn sciigojn Malpleni rubujon diff --git a/app/src/main/res/values-es-rAR/strings.xml b/app/src/main/res/values-es-rAR/strings.xml index 743254e123..7e55c9bf6e 100644 --- a/app/src/main/res/values-es-rAR/strings.xml +++ b/app/src/main/res/values-es-rAR/strings.xml @@ -7,7 +7,6 @@ Falló la creación de la carpeta Icono de cuenta Cuenta no encontrada! - Borrar subidas fallidas Editar Borrar todas las notificaciones Vaciar la papelera @@ -150,7 +149,6 @@ ¿Realmente quieres eliminar %1$s y su contenido? ¿Realmente desea eliminar los elementos seleccionados y sus contenidos? Sólo local - ¡Error al crear el diálogo de conflicto! Archivo en conflicto %1$s Archivo local Si selecciona ambas versiones, se le agregará un número al nombre del archivo copiado. diff --git a/app/src/main/res/values-es-rCL/strings.xml b/app/src/main/res/values-es-rCL/strings.xml index e156f7a255..65ce9cf03e 100644 --- a/app/src/main/res/values-es-rCL/strings.xml +++ b/app/src/main/res/values-es-rCL/strings.xml @@ -5,7 +5,6 @@ versión %1$s Ícono de la cuenta ¡No se encontró la cuenta! - Borrar cargas fallidas Editar Enviar/Compartir Vista de cuadrícula diff --git a/app/src/main/res/values-es-rCO/strings.xml b/app/src/main/res/values-es-rCO/strings.xml index db582242d5..0d6222e019 100644 --- a/app/src/main/res/values-es-rCO/strings.xml +++ b/app/src/main/res/values-es-rCO/strings.xml @@ -6,7 +6,6 @@ Fallo en la creación de la cuenta Ícono de la cuenta ¡No se encontró la cuenta! - Borrar cargas fallidas Editar Borrar todas las notificaciones Vacíar la papelera diff --git a/app/src/main/res/values-es-rCR/strings.xml b/app/src/main/res/values-es-rCR/strings.xml index b1315dd71b..d62040810d 100644 --- a/app/src/main/res/values-es-rCR/strings.xml +++ b/app/src/main/res/values-es-rCR/strings.xml @@ -5,7 +5,6 @@ versión %1$s Ícono de la cuenta ¡No se encontró la cuenta! - Borrar cargas fallidas Editar Enviar/Compartir Vista de cuadrícula diff --git a/app/src/main/res/values-es-rDO/strings.xml b/app/src/main/res/values-es-rDO/strings.xml index 80762f0262..2df7ebef15 100644 --- a/app/src/main/res/values-es-rDO/strings.xml +++ b/app/src/main/res/values-es-rDO/strings.xml @@ -7,7 +7,6 @@ Error al crear la cuenta Ícono de la cuenta ¡No se encontró la cuenta! - Borrar cargas fallidas Editar Borrar todas las notificaciones Enviar/Compartir diff --git a/app/src/main/res/values-es-rEC/strings.xml b/app/src/main/res/values-es-rEC/strings.xml index 5e4079a394..d47f677e77 100644 --- a/app/src/main/res/values-es-rEC/strings.xml +++ b/app/src/main/res/values-es-rEC/strings.xml @@ -7,7 +7,6 @@ Creación de cuenta fallida Ícono de la cuenta ¡No se encontró la cuenta! - Borrar cargas fallidas Editar Borrar todas las notificaciones Eliminar papelera de reciclaje @@ -153,7 +152,6 @@ ¿Realmente quieres eliminar %1$s y sus contenidos? ¿Reamente deseas eliminar los elementos seleccionados y sus contenidos? Sólo local - ¡Error al crear el diálogo de conflicto! Archivo en conflicto %1$s Archivo local Si selecciona ambas versiones, el archivo local tendrá un número agregado a su nombre. diff --git a/app/src/main/res/values-es-rGT/strings.xml b/app/src/main/res/values-es-rGT/strings.xml index db65d670f3..fff2dea4f3 100644 --- a/app/src/main/res/values-es-rGT/strings.xml +++ b/app/src/main/res/values-es-rGT/strings.xml @@ -5,7 +5,6 @@ versión %1$s Ícono de la cuenta ¡No se encontró la cuenta! - Borrar cargas fallidas Editar Enviar/Compartir Vista de cuadrícula diff --git a/app/src/main/res/values-es-rMX/strings.xml b/app/src/main/res/values-es-rMX/strings.xml index 2854e8c326..607f89aa83 100644 --- a/app/src/main/res/values-es-rMX/strings.xml +++ b/app/src/main/res/values-es-rMX/strings.xml @@ -7,7 +7,6 @@ Falló la creación de la cuenta Ícono de la cuenta ¡No se encontró la cuenta! - Borrar cargas fallidas Editar Limpiar todas las notificaciones Vaciar la papelera de reciclaje @@ -171,7 +170,6 @@ ¿Realmente quieres eliminar %1$s y sus contenidos? ¿Reamente deseas eliminar los elementos seleccionados y sus contenidos? Sólo local - ¡Se presentó un error al crear el diálogo de conflictos! Archivo conflictivo %1$s Archivo local Si seleccionas ambas versiones, el archivo local tendrá un número al final del nombre. diff --git a/app/src/main/res/values-es-rSV/strings.xml b/app/src/main/res/values-es-rSV/strings.xml index b1315dd71b..d62040810d 100644 --- a/app/src/main/res/values-es-rSV/strings.xml +++ b/app/src/main/res/values-es-rSV/strings.xml @@ -5,7 +5,6 @@ versión %1$s Ícono de la cuenta ¡No se encontró la cuenta! - Borrar cargas fallidas Editar Enviar/Compartir Vista de cuadrícula diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 10f3ec42ef..55ad1cdc18 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -7,7 +7,6 @@ Ha fallado la creación de la cuenta Icono de la cuenta ¡Cuenta no encontrada! - Borrar subidas fallidas Editar Limpiar todas las notificaciones Vaciar papelera @@ -47,6 +46,8 @@ ¡La tarea fue eliminada exitósamente! Eliminar tarea Desconocido + Entrada + Salida ¡Cuenta asociada no encontrada! Acceso fallido: %1$s La cuenta no se ha añadido aún en este dispositivo @@ -160,7 +161,6 @@ ¿Realmente deseas eliminar %1$s y todo su contenido? ¿Estás seguro de que quieres eliminar los elementos seleccionados y sus contenidos? Solo local - ¡Error al crear el diálogo de conflicto! Conflicto en archivo %1$s Archivo local Si seleccionas ambas versiones, el archivo local tendrá un número añadido a su nombre. diff --git a/app/src/main/res/values-et-rEE/strings.xml b/app/src/main/res/values-et-rEE/strings.xml index 3fd7190e64..bd34acccb1 100644 --- a/app/src/main/res/values-et-rEE/strings.xml +++ b/app/src/main/res/values-et-rEE/strings.xml @@ -7,7 +7,6 @@ Konto loomine ebaõnnestus Konto ikoon Kontot ei leitud! - Puhasta ebaõnnestunud üleslaadimised Redigeeri Kustuta kõik teavitused Tühjenda prügikast @@ -136,7 +135,6 @@ Oled sa kindel, et soovid %1$s ja selle sisu kustutada? Oled sa kindel, et soovid valitud objektid ja nende sisud kustutada? Ainult kohalik - Konfliktidialoogi loomine ebaõnnestus! Kohalik fail Kui valid mõlemad versioonid, siis lisatakse kohaliku faili nimele number. Serveri fail diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index 4fdfacf562..732363ee66 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -7,7 +7,6 @@ Kontuaren sorrerak huts egin du Kontuaren ikonoa Ez da kontua aurkitu! - Garbitu huts egin duten igoerak Aldatu Garbitu jakinarazpen guztiak Hustu zakarrontzia @@ -47,6 +46,8 @@ Ezabatu zeregina Morroia Ezezaguna + Sarrera + Irteera Ez da aurkitu lotutako konturik Huts egin du atzitzean: %1$s Kontua ez da gailu honetan gehitu oraindik @@ -161,7 +162,6 @@ Ziur zaude %1$s eta bere edukia ezabatu nahi duzula? Ziur zaude hautatutako elementuak eta beren edukiak ezabatu nahi dituzula? Lokala bakarrik - Errorea gatazkaren elkarrizketa-koadroa sortzean! %1$sfitxategi gatazkatsua Fitxategi lokala Bi bertsioak hautatzen badituzu, fitxategi lokalaren izenari zenbaki bat gehituko zaio. diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index ffa200e713..a11913415a 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -7,7 +7,6 @@ ایجاد حساب ناموفق بود آیکون حساب حساب پیدا نشد! - پاک کردن آپلودهای ناموفق ویرایش پاک کردن تمام اعلانها خالی کردن سطل زباله @@ -158,7 +157,6 @@ آیا واقعا می خواهید %1$s و محتویات آن را حذف کنید؟ آیا واقعاً می‌خواهید موارد انتخاب شده و محتوای آنها حذف شود؟ فقط محلی - هنگام ساختن دیالوگ مغایرت‌ها خطایی رخ داده است! فایل متناقض %1$s پروندهٔ محلّی اگر هردو نسخه را انتخاب کنید، یک شماره به نام فایل محلی اضافه خواهد شد. diff --git a/app/src/main/res/values-fi-rFI/strings.xml b/app/src/main/res/values-fi-rFI/strings.xml index 0acc4e060d..e68acdc7ad 100644 --- a/app/src/main/res/values-fi-rFI/strings.xml +++ b/app/src/main/res/values-fi-rFI/strings.xml @@ -7,7 +7,6 @@ Tilin luonti epäonnistui Tilikuvake Tiliä ei löydy! - Tyhjennä epäonnistuneet lähetykset Muokkaa Hylkää kaikki ilmoitukset Tyhjennä roskakori @@ -158,7 +157,6 @@ Haluatko varmasti poistaa kohteen %1$s ja sen sisällön? Haluatko varmasti poistaa valitut kohteet ja niiden sisällön? Vain paikallisen - Virhe konfliktitietojen näyttämisessä! Ristiriitainen kohde %1$s Paikallinen tiedosto Jos valitset molemmat versiot, paikallisen tiedoston nimeen lisätään numero. diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 363fa1ee4d..1370acfc6a 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -7,7 +7,6 @@ Échec de création du compte Icône du compte Compte introuvable ! - Effacer les téléversements échoués Modifier Effacer toutes les notifications Vider la corbeille @@ -57,6 +56,8 @@ Impossible de récupérer les types des tâches, veuillez vérifier votre connexion Internet. Assistant Inconnu + Entrée + Sortie Compte associé introuvable ! L\'accès a échoué: %1$s Le compte n\'est pas encore ajouté sur cet appareil @@ -171,7 +172,6 @@ Voulez-vous vraiment supprimer %1$s et ses contenus ? Souhaitez-vous vraiment supprimer les éléments sélectionnés ainsi que leurs contenus ? Local seulement - Une erreur qui crée un dialogue conflictuel ! Fichier %1$s en conflit fichier local Si vous sélectionnez les deux versions, le fichier local aura un numéro ajouté à son nom. diff --git a/app/src/main/res/values-gd/strings.xml b/app/src/main/res/values-gd/strings.xml index cafc2e6030..0782068f8d 100644 --- a/app/src/main/res/values-gd/strings.xml +++ b/app/src/main/res/values-gd/strings.xml @@ -7,7 +7,6 @@ Dh’fhàillig cruthachadh a’ chunntais Ìomhaigheag a’ chunntais Cha deach an cunntas a lorg! - Falamhaich na luchdaidhean suas a dh’fhàillig Deasaich Falamhaich a h-uile brath Falamhaich an sgudal @@ -138,7 +137,6 @@ A bheil thu cinnteach gu bheil thu airson %1$s ’s a shusbaint a sguabadh às? A bheil thu cinnteach gu bheil thu airson na nithean a thagh thu ’s an susbaint a sguabadh às? Ionadail a-mhàin - Mearachd le cruthachadh còmhradh na còmhstri! Faidhle %1$s ann an còmhstri Ma thaghas tu an dà thionndadh, thèid àireamh a chur ri ainm an fhaidhle ionadail. Ìomhaigheag a’ chleachdaiche air liosta an luchd-aithne diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index 78bc6ce05b..372563b198 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -7,7 +7,6 @@ Produciuse un fallo ao crear a conta Icona da conta Non se atopou a conta! - Limpar os envíos fallados Editar Limpar todas as notificacións Baleirar o lixo @@ -43,6 +42,7 @@ Completado A tarefa foi eliminada satisfactoriamente Descoñecido + Entrada Non se atopou unha conta asociada! Acceso fallado: %1$s A conta aínda non existe no dispositivo @@ -155,7 +155,6 @@ Confirma que quere eliminar %1$s e todo o seu contido? Confirma que quere eliminar os elementos seleccionados e o seu contido? Só local - Produciuse un erro ao crear o diálogo de conflitos! Ficheiro en conflito %1$s Ficheiro local Se selecciona ambas versións, o ficheiro local terá un número engadido ao nome. diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 1ceb37ce30..c4dad0a47f 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -7,7 +7,6 @@ Kreiranje korisničkog računa neuspjelo Ikona korisničkog računa Korisnički račun nije pronađen! - Izbriši neuspješne otpreme Uredi Izbriši sve obavijesti Isprazni kantu za smeće @@ -145,7 +144,6 @@ Želite li zaista izbrisati %1$s i pripadajući sadržaj? Želite li zaista izbrisati odabrane stavke i pripadajući sadržaj? Samo lokalno - Pogreška pri stvaranju dijaloškog okvira o nepodudaranju! Nepodudarna datoteka %1$s Lokalna datoteka Ako odaberete obje inačice, lokalna će datoteka uz naziv imati i broj. diff --git a/app/src/main/res/values-hu-rHU/strings.xml b/app/src/main/res/values-hu-rHU/strings.xml index f78a9c10e7..3e2123ce57 100644 --- a/app/src/main/res/values-hu-rHU/strings.xml +++ b/app/src/main/res/values-hu-rHU/strings.xml @@ -7,7 +7,6 @@ A fiók létrehozása sikertelen Fiók ikon A fiók nem található! - Sikertelen feltöltések eltávolítása Szerkesztés Összes értesítés törlése Kuka ürítése @@ -155,7 +154,6 @@ Biztos, hogy törli ezt: %1$s és a tartalmát? Biztos, hogy törli a kiválasztott elemeket és tartalmukat? Csak a helyi példány - Hiba az ütközési párbeszédablak létrehozásakor! Ütköző fájl: %1$s Helyi fájl Amennyiben mindkét verziót kiválasztja, a helyi fájl nevéhez egy szám lesz hozzáfűzve. diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index bd58c42484..2fbc92625e 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -7,7 +7,6 @@ Pembuatan akun gagal Ikon akun Akun tidak ditemukan! - Bersihkan unggahan gagal. Sunting Hapus semua notifikasi Kosongkan tempat sampah @@ -147,7 +146,6 @@ Otomatis unggah hanya bekerja dengan baik apabila Anda mengeluarkan aplikasi ini Apakah anda yakin ingin menghapus %1$s beserta isinya? Apa anda yakin ingin menghapus item yang terpilih beserta isinya? Lokal saja - Galat saat membuat dialog konflik! File konflik %1$s File lokal Jika Anda memilih kedua versi, nama dari berkas lokal akan ditambahi angka. diff --git a/app/src/main/res/values-is/strings.xml b/app/src/main/res/values-is/strings.xml index 8c96737ff0..74c49d144c 100644 --- a/app/src/main/res/values-is/strings.xml +++ b/app/src/main/res/values-is/strings.xml @@ -7,7 +7,6 @@ Gerð notandaaðgangs mistókst Táknmynd fyrir notandaaðgang Notandaaðgangur fannst ekki! - Hreinsa lista yfir innsendingar sem mistókust Breyta Hreinsa allar tilkynningar Tæma ruslið @@ -150,7 +149,6 @@ Ertu viss um að þú viljir eyða %1$s og innihaldi þess? Ertu viss um að þú viljir eyða völdum atriðum og innihaldi þeirra? Einungis staðvært - Villa við að búa til árekstraglugga! Skrá á tölvunni Ef þú velur báðar útgáfur, þá mun verða bætt tölustaf aftan við heiti afrituðu skrárinnar. Skrá á netþjóni diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index cb8efdebde..bdcc76abb7 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -7,7 +7,6 @@ Creazione dell\'account non riuscita Icona dell\'account Account non trovato - Cancella caricamenti non riusciti Modifica Cancella tutte le notifiche Svuota cestino @@ -153,7 +152,6 @@ Vuoi davvero rimuovere %1$s e il relativo contenuto? Vuoi davvero eliminare gli elementi selezionati e il loro contenuto? Solo localmente - Errore di creazione della finestra di conflitto! File %1$s in conflitto File locale Se selezioni entrambe le versioni, il file locale ha un numero aggiunto al suo nome. diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index db059895ba..5def87a8ee 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -7,7 +7,6 @@ יצירת החשבון נכשלה סמל חשבון החשבון לא נמצא! - פינוי העלאות שנכשלו עריכה פינוי כל ההתראות פינוי סל האשפה @@ -144,7 +143,6 @@ למחוק את %1$s על תוכנו? למחוק את הפריטים הנבחרים ואת תוכנם? מקומי בלבד - שגיאה ביצירת תיבת דו־שיח סתירה! קובץ מקומי בבחירה של שתי הגרסאות, יתווסף מספר בסוף שמו של הקובץ המקומי. קובץ בשרת diff --git a/app/src/main/res/values-ja-rJP/strings.xml b/app/src/main/res/values-ja-rJP/strings.xml index fe5368775e..30c83eff3f 100644 --- a/app/src/main/res/values-ja-rJP/strings.xml +++ b/app/src/main/res/values-ja-rJP/strings.xml @@ -7,7 +7,6 @@ アカウントの作成に失敗しました アカウントアイコン アカウントが見つかりません! - 失敗したアップロードのクリア 編集 全ての通知を削除 ゴミ箱を空にする @@ -155,7 +154,6 @@ 本当に %1$s を中身も一緒に削除しますか? 本当に選択したアイテムとその内容を削除しますか? ローカルのみ - コンフリクトダイアログの作成に失敗しました %1$sはすでに存在します ローカルファイル 両方のバージョンを選択した場合、ローカルファイルはファイル名に数字が追加されます。 diff --git a/app/src/main/res/values-ka-rGE/strings.xml b/app/src/main/res/values-ka-rGE/strings.xml deleted file mode 100644 index 01b6281dcc..0000000000 --- a/app/src/main/res/values-ka-rGE/strings.xml +++ /dev/null @@ -1,506 +0,0 @@ - - - %1$s Android აპლიკაცია - ჩვენს შესახებ - ვერსია %1$s - ანგარიშის პიქტოგრამა - ანგარიში ვერ იქნა ნაპოვნი! - წარუმატებლად დასრულებული ატვირთების გასუფთავება - ცვლილება - გაგზავნა/გაზიარება - ბადისებური ხედი - ჩამონათვლისებური ხედი - ახალი დირექტორია - გახსნა - ძიება - დეტალები - გაგზავნა - პარამეტრები - განლაგება - აქტიური მომხმარებელი - აქტოვობა ჯერ არაა - გაგზავნა - გააგზავნეთ ბმული… - აქტივობა - ხელახალი გაზიარების დაშვება - ყველა - ანგარიში ამ მოწყობილობაზე ჯერ არაა დამატებული - მოწყობილობაზე ანგარიში ამ მომხმარებლით და სერვერით უკვე არსებობს - შეყვანილი მომხმარებელი არ ემთხვევა ამ ანგარიშის მომხმარებელს - სერვერის ამოუცნობი ვერსია - კავშირი დამყარდა - სერვერის მისამართი https://... - სერვერისთვის არასწორი მისამსამართის ფორმატი - სერვერი ვერ მოიძებნა - ქსელთან კავშირი არ არის - დაცული კავშირი არაა ხელმისაწვდომი. - არასწორი სერვერის კონფიგურაცია - წარუმატებელი ავტორიზაცია - წვდომა ავტორიზაციის სერვერის მიერ აიკრძალა - დაცული კავშირი გადამისამართდა დაუცველი მარშრუტით. - დაცული კავშირი დამყარდა - SSL ინიციალიზაცია ვერ მოხერხდა - SSL სერვერის იდენტობა ვერ დამოწმდა - კავშირის შემოწმება - სერვერმა პასუხი დააგვიანია - არასწორი მომხმარებელი ან პაროლი - წარმოიშვა უცნობი შეცდომა! - ჰოსტი ვერ მოიძებნა - %1$s არ უჭერს მხარს მრავალი ანგარიშის არსებობას - კავშირი ვერ დამყარდა - ატვირთვა მხოლოდ შეუზღუდავ Wi-Fi-ზე - /ავტომატური_ატვირთვა - შექმენით ახალი სპეციფიური დირექტორია - დააყენეთ სპეციფიური დირექტორია - ავატარი - დახურვა - გამორთვა - კალენდარი - სერტიპიკატის ჩატვირთვასთან პრობლემაა. - ჩექბოქსი - ტექსტი %1$s-იდან დაკოპირდა - ბუფერში გადასატანად ტექსტი მიღებული არაა - ბუფერში გადატანისას წარმოშვა მოულოდნელი შეცდომა - უკან - გაუქმება - სინქრონიზაციის შეჩერება - აირჩიეთ ანგარიში - დადასტურება - კოპირება - გაუქმება - შეცდომა - არასაკმარისი მეხსიერება - უცნობი შეცდომა - იტვირთება… - შემდეგი - არა - კარგი - მოლოდინშია - გაუქმება - გადარქმევა - შენახვა - გაგზავნა - გაზიარება - გამოტოვება - კი - დევ. ვერსიის შემოწმება - იმყოფება სისხლდენის ზღვარზე და მოიცავს ყველა დამდეგ ფუნქციას. შეიძლება გამოჩნდეს შეცდომები, ასეთი შემთხვევისას გთხოვთ გვამცნობოთ რეპორტით. - ფურუმი - დაეხმარეთ სხვებს - აქტიურად შეიტანეთ წვლილი - აპლიკაცია - გადათარგმნეთ - დეველოპმენტ რელიზის პირდაპირი ჩამოტვირთვა - მიიღეთ დეველოპმენტ რელიზი F-Droid აპლიკაციიდან - მიიღეთ რელიზ კანდიდატი F-Droid აპლიკაციიდან - მიიღეთ რელიზ კანდიდატი Google Play-დან - რელიზის კანდიდატი - ეს რელიზის კანდიდატი (რკ) დამდეგი რელიზის კადრია და მოსალოდნელია მისი სტაბილურობა. ამის დამოწმებაში დაგვეხმარება ინდივიდუალური მოწყობილობის შემოწმება. შემოწმებისთვის დარეგისტრირდით Play-ზე ან გადახედეთ \"ვერსიის\" სექციას F-Droid-ში. - იპოვეთ შეცდომა? უცნაურობა? - დაგვეხმარეთ შემოწმებით - დაამატეთ მოხსენიება GitHub-ზე - ნამდვილად გსურთ %1$s-ის გაუქმება? - დარწმუნებული ხართ, რომ გსურთ არჩეული ჩანაწერების გაუქმება? - ნამდვილად გსურთ %1$s-ის და შემცველობის გაუქმება? - დარწმუნებული ხართ, რომ გსურთ არჩეული ჩანაწერებისა და მათი შემცველობის გაუქმება? - მხოლოდ ლოკალური - მომხმარებლის პიქტოგრამა კონტაქტების სიისთვის - უფლებები არაა მოცემული, არაფერი არ იქნა იმპორტირებული. - კონტაქტები - ახალი ბექაფი - ბექაფი დაინიშნა და მალე დაიწყება - იმპორტი დაინიშნა და მალე დაიწყება - ფაილი ვერ იქნა ნაპოვნი - თქვენი ბოლო ბექაფი ვერ იქნა ნაპოვნი! - კოპირებულია კლიპბორდში - ამ ფაილისა თუ დირექტორიის კოპირებისას წარმოიშვა შეცდომა - დირექტორიის კოპირება მასში არსებულ დირექტორიაში შეუძლებელია - ფაილი დანიშნულ დირექტორიაში უკვე არსებობს - ბმულის კოპირება - ამჟამად კოპირება/გადატანა დაშიფრულ დირექტორიაში არაა მხარდაჭერილი. - შექმნა - დირექტორია ვერ შეიქმნა - ანგარიშის გაუქმება - ყველას წაშლა - ხელმისაწვდომია ახალი ვერსია - ახალი ვერსია არაა ხელმისაწვდომი. - დახურვა - თქვენს ტელეფონს ამ ალგორითმის დაიჯესტის მხარდაჭერა არ აქვს. - გამორთვა - დათხოვნა - დასრულებულია - ვერ ჩამოიტვირთა %1$s - ჩამოტვირთვა ვერ მოხერხდა, ახლიდან გაიარეთ ავტორიზაცია - ჩამოტვირთვა ვერ განხორციელდა - ფაილი სერვერზე ხელმისაწვდომი აღარაა - %1$d%% ჩამოტვირთვა %2$s - %1$s ჩამოტვირთლია - ჩამოტვირთულია - ჯერ ვერ ჩამოიტვირთა - მხაზველი დასათაურების ფონური სურათი - აქტივობები - ყველა ფაილი - რჩეულები - სახლი - შეტყობინებები - მოწყობილობაზე - ახლად შეცვლილი - გაზიარებული - გაუქმებული ფაილები - ატვირთვები - გასვლა - გამოყენებულია %1$s სულ %2$s-იდან  - ავტო-ატვირთვა - უფრო მეტი - ჩანაწერები - საუბარი - დააყენეთ როგორც დაშიფრული - შიფრაციის დაყენება - დახურვა - პირადი გასაღების დეშიფრაციისთვის გთხოვთ შეიყვანოთ პაროლი. - ეს დირექტორია არაა ცარიელი - 12 სიტყვა ერთად წარმოქმნის ძალიან ძლიერ პაროლს, იძლევა თქვენი დაშიფრული ფაილების ჩვენებისა და მოხმარების უფლებას. გთხოვთ ჩაიწეროთ და შეინახოთ დაცულ ადგილას. - შენიშნეთ თქვენი 12 სიტყვიანი შიფრაციის პაროლი - გასაღებების შენახვა - შიფრაციის დაყენება - გასაღებების შენახვა ვერ მოხერხდა, გთხოვთ სცადოთ ახლიდან. - შეცდომა დეშიფრაციისას. არასწორი პაროლი? - %1$s ვერ კოპირდება %2$s ლოკალურ დირექტორიაში - კრიტიკული შეცდომა: ოპერაციების განხორციელება შეუძლებელია - ანგარიშები - შექმნილია - მომხმარებელი - ფონური საქმეები - ჩამოტვირთვა - ატვირთვა - რჩეულებში დამატება - რჩეული - წაშლა - ფაილი - ატვირთეთ რამე ან გაუწიეთ თქვნს მოწყობილობას სინქრონიზაცია. - რჩეულებში ჯერ არაფერი დამატებულია - აქ ფაილები არაა - ამ დირექტორიაში შედეგები არაა - შედეგები არაა - აქ არაფერია. შეგიძლიათ დაამატოთ დირექტორია. - გასული 7 დღის განმავლობაში შეცვლილი ფაილ(ებ)ი ვერ იქნა ნაპოვნი - იქნებ სხვა დირექტორიაშია? - ჯერ არაფერი გაზიარებულა - დირექტორია - იტვირთება… - ამ ფაილთან სამუშაოდ არც ერთი აპლიკაცია არაა დაყანებული. - წამის წინ - მონაცემების დირექტორია უკვე არსებობს. აირჩიეთ ერთ-ერთი შემდეგიდან: - Nextcloud დირექტორია უკვე არსებობს - საჭიროა მეტი სივრცე - წყარო-ფაილის წაკითხვა ვერ მოხერხდა - სამიზნე ფაილში ჩაწერა ვერ მოხერხდა - მიგრაციისას წარმოიქმნა შეცდომა - ინდექსის განახლება ვერ მოხერხდა - დასრულებულია - ჩანაცვლება - წყარო-დირექტორია ვერ იკითხება! - მოხმარება - ფაილი ვერ იქნა ნაპოვნი - ფაილის სინქ ვერ მოხერხდა. ნაჩვენებია ბოლო ხელმისაწვდომი ვარიანტი. - სახელის შეცვლა - დეტალები - ჩამოტვირთვა - ექსპორტი - ატვირთვისას ფაილს სახელი შეეცვალა %1$s-ზე - სინქ - ფაილი არაა არჩეული - ფაილის სახელი ვერ იქნება ცარიელი - აკრძალული ნიშნები: / \\ < > : \" | ? * - ფაილის სახელი შეიცავს მინ. 1 არასწორ ნიშანს - ფაილის სახელი - შექმნა - აქ დირექტორიები არაა - აირჩიეთ - გადატანა - თქვენ არ გაქვთ უფლება, %s - რომ დააკოპიროთ ეს ფაილი - შექმნათ ეს ფაილი - გააუქმოთ ეს ფაილი - რომ გადაიტანოთ ეს ფაილი - შეუცვალოთ ამ ფაილს სახელი - ფაილების ატვირთვა... - გარკვეული ფაილების გადატანა ვერ მოხერხდა - ლოკალური: %1$s - ყველას გადატანა - დისტანციური: %1$s - ყველა ფაილი გადატანილია - წინ - 4 საათი - სახელი - პაროლი - ატვირთვა მხოლოდ დატენვისას - /მყისიერი_ატვირთვა - უჩინარი - ბმული - ატვირთვისა და ცვლილების უფლებების მინიჭება - ფაილის ჩაგდება (მხოლოდ ატვირთვა) - ჩამოწერილი მაკეტი - ამ დირექტორიაში ფაილები არაა. - ლოკალურ ფაილ-სისტემაში ფაილი ვერ მოიძებნა - მომდევნო დირექტორიები არაა. - %1$s Android აპლიკაციის ლოგები. - ლოგინი - განახლება - იტვირთება… - ლოგები - მონაცემების გასუფთავება -  %1$s-ის მონაცემებიდან პარამეტრები, მონაცემთა ბაზა და სერტიფიკატები სამუდამოდ წაიშლება.\n\nგადმოწერილი ფაილები დარჩება ხელუხლებელი.\n\nეს პროცესი გასტანს გარკვეულ დროს. - მოცულობის მენეჯმენტი - მედია ფაილის წაკითხვა ვერ მოხერხდა - მედია ფაილს გააჩნია არასწორი კოდირება - ფაილის დაკვრის მცდელობის დრო ამოიწურა - ჩაშენებული მედია დამკვრელი ვერ უკრავს მედია ფაილს - მედია კოდეკი მხარდაუჭერელია - სწრაფი გადახვევის ღილაკი - %1$s მუსიკის დამკვრელი - დაკვრის ან პაუზის ღილაკი - გადახვევის ღილაკი - %1$s (იკვრება) - ჯერ ახალი - ჯერ ძველი - ა - ჰ - ჰ - ა - ჯერ დიდი - ჯერ პატარა - მეტი - ამ ფაილისა თუ დირექტორიის გადატანისას წარმოიშვა შეცდომა - დირექტორიის გადატანა მასში არსებულ დირექტორიაში შეუძლებელია - ფაილი დანიშნულ დირექტორიაში უკვე არსებობს - სერვერთან დაკავშირებისას წარმოიქმნა შეცდომა - ფოტო - აჩვენებს გადმოწერის პროგრესს - ჩამოტვირთვები - აჩვენებს ფაილების სინქ. პროგრესს და შედეგებს - ფაილების სინქ. - მუსიკის დამკვრელის პროგრესი - მედია დამკვრელი - აჩვენებს ატვირთვის პროგრესს - ატვირთვები - შეტყობინებები არაა - გთხოვთ დაბრუნდეთ მოგვინაებით. - 1 საათი - შეიყვანეთ თქვენი პასკოდი - პასკოდი მოთხოვნილ იქნება აპლიკაციის ყოველი გაშვებისას - გთხოვთ შეიყვანოთ თქვენი პასკოდი - პასკოდები ერთმანეთს არ ემთხვევა - გთხოვთ თქვენი პასკოდი შეიყვანოთ ახლიდან - თქვენი პასკოდის გაუქმება - პასკოდი გაუქმდა - პასკოდი შენახულია - არასწორი პასკოდი - ფაილების ატვირთვისა და გადმოწერისათვის საჭიროა დამატებითი უფლებები. - სურათის დასაყენებლად ვერც ერთი აპლიკაცია ვერ იქნა ნაპოვნი - 389 კბ - ადგილი.txt - 12:23:45 - ეს არის ადგილი - 18/05/2012 12:23 - გაუქმებულია - დარჩა ორიგინალ დირექტორიაში - გადატანილია აპლიკაციის დირექტორიაში - ანგარიშის დამატება - არც Google Play-ა დაყენებული, არც F-Droid-ი - ჩვენს შესახებ - დეტალები - დევ. - ზოგადი - მეტი - კონტაქტების ყოველდღიური ბექაფი - GNU ზოგადი ღია ლიცენზია, ვერსია 2 - დახმარება - ბეჭედი - ქვე-დირექტორიების მოხმარება - ლიცენზია - არც ერთი - ანგარიშების მართვა - დამალული ფაილების ჩვენება - მიიღეთ კოდი - ლოკალური დირექტორია - დისტანციური დირექტორია - ვიზუალური თემა - მუქი - ღია - სურათის წინასწარი ჩვენება - წინასწარი ჩვენებისთვის ლოკალური ფაილი არაა - სურათის ჩვენება შეუძლებელია - ბოდიში - კონფიდენციალურობა - Google Play სერვისების მესაკუთრეობრივი დამოკიდებულებების გამო, ფუშ შეტყობინებები გათიშულია. - ვადაგასული აუტენტიფიკაციის გამო ფუშ შეტყობინებები არაა. გთხოვთ გაითვალისწინოთ თქვენი ანგარიშის თავიდან დამატება. - ფუშ შეტყობინებები ამჟამად ხელმიუწვდომელია. - სცდეთ %1$s თქვენს მოწყობილობაზე! - გიწვევთ გამოიყენოთ %1$s თქვენს მოწყობილობაზე.\nჩამოტვირთეთ აქედან: %2$s - %1$s ან %2$s - გადატვირთვა - გაუქმება ვერ მოხერხდა - გაუქმება - გაუქმდა - შეიყვანეთ ახალი სახელი - ლოკალური ასლის სახელის გადარქმევა ვერ მოხერხდა, სცადეთ სხვა სახელი - გადარქმევა შეუძლებელია, სახელი უკვე დაკავებულია - ხელახალი გაზიარება არაა დაშვებული - ხელახალი გაზიარება არაა ნებადართული - ზომა-შეცვლილი სურათი არაა ხელმისაწვდომი. გადმოვწეროთ მთლიანი სურათი? - ყველას მონიშვნა - გაგზავნა - დააყენეთ როგორც - გამოიყენეთ სურათი როგორც - გაზიარება - გაზიარება - %1$s-ის გაზიარება - %1$s (ჯგუფი) - ბმულის გაზიარება - უნდა შეიყვანოთ პაროლი - გააზიაროთ ეს ფაილი - შეიყვანეთ პაროლი - მიუთითეთ ვადის გასვლის დრო - პაროლის დაყენება - შეუძლია შეცვალოს - %1$s (დისტანციური) - პარამეტრები - ბმულის გაზიარება - გაზიარებული - დაასორტირე - დამალვა - დეტალები - სერვერის იდენტურობა ვერ დამოწმდა - ქვეყანა: - ზოგადი სახელი: - ადგილმდებარეობა: - ორგანიზაცია: - ორგანიზაციის განყოფილება: - შტატი: - თითის ანაბეჭდი: - გაცემულია მიერ: - ხელმოწერა: - ალგორითმი: - გაცემულია: - მოქმედების ვადა: - ვისგან: - ვის: - - ამ შეცდომაზე ინფორმაცია არაა - სერტიფიკატის შენახვა ვერ მოხერხდა - სერტიფიკატის ჩვენება შეუძლებელია. - გსურთ მაინც ენდოთ ამ სერტიფიკატს? - - სერვერის სერტიფიკატი ვადაგასულია - - სერვერის სერტიფიკატი არ არის სანდო - - სერვერის სერტიფიკატის თარიღები მომავალშია - - URL არ ემთხვევა სერტიფიკატში არსებულ ჰოსტნეიმს - საწყისი - ჩამოტვირთვები - გაგიზიარდათ \"%1$s\" - %1$s-სგან გაგიზიარდათ \"%2$s\" - წარმოიშვა კონფლიქტი - დირექტორია %1$s აღარ არსებობს - %1$s სინქ ვერ მოხერხდა - %1$s რესურსის არასწორი პაროლი - Kept-in-sync ფაილების შეცდომა - სინქრონიზაცია დასრუდლა წარუმატებლად - სინქ ვერ მოხერხდა, ახლიდან გაიარეთ ავტორიზაცია - ფაილების შემცველობა სინქრონიზირებულია - როდესაც ერთი ფაილი სინქრონიზირებულია რამოდენიმე ანგარიშთან, 1.3.16 ვერსიაში მონაცემების დაკარგვისგან თავის ასარიდებლად, ამ მოწყობილობით ატვირთული ფაილ(ებ)ი კოპირდება %1$s დირექტორიაში.\n\nამ ცვლილების გამო, აპლიკაციის ძველი ვერსიით ატვირთული ყველა ფაილი დაკოპირდა %2$s დირექტორიაში. თუმცა, შეცდომის გამო სინქრონიზაციისას მოხდა ამ ოპერაციის შეჩერება. შეგიძლიათ ფაილ(ებ)ი დატოვოთ ისე როგორც არის და წაშალოთ ბმული %3$s-სთან, ან გადაიტანოთ ფაილ(ებ)ი %1$s დირექტორიაში და შეინარჩუნოთ ბმული %4$s-ისთვის.\n\nქვემოთ ჩამოწერილია ლოკალური და %5$s-ში არსებული დისტანციური ფაილ(ებ)ი. - რამოდენიმე ლოკალური ფაილი გამორჩენილია - ამოღებულია ფაილის უახლესი ვერსია. - სინქ. სტატუსის ღილაკი - ფაილები - პარამეტრების ღილაკი - დირექტორიების კონფიგურაცია - მყისიერი ატვირთვა სრულიად გამოსწორდა. ავტო-ატვირთვას რეკონფიგურაცია გაუწიეთ მთავარი მენიუდან.\n\nისიამოვნეთ ახალი და გაუმჯობესებული ავტო-ატვირთვით. - %1$s-ისთვის - სახეობა - ტეგები - სერვერთან კავშირის შემოწმება - 30 წუთი - ამ კვირაში - მინიატურა - დღეს - გაუქმებული ფაილები - წაშლილი ფაილები ვერ მოიძებნა - სამუდამოდ წაშლა - შიფრაციის გაუქმება - შეწყვიტოთ ამ ფაილის გაზიარება - წვდომა არასანდო დომენით. დამატებითი ინფორმაციისთვის გთხოვთ იხილოთ დოკუმენტაცია. - განაახლოთ ეს გაზიარება - წარუმატებლად დასრულებული ატვირთების გასუფთავება - წარუმატებლად დასრულებული ატვირთების ხელახლა გაშვება - ფაილის სახელი - ფაილის სახეობა - Google Maps იარლიყის ფაილი(%s) - ინტერნეტ იარლიყის ფაილი(%s) - ტექსტ ფაილის ჩამონაჭერი(.txt) - ატვირთვისთვის შეიყვანეთ ფაილის სახელი და სახეობა - ფაილების ატვირთვა - ელემენტის ატვირთვის ქმედების ღილაკი - წაშლა - არც ერთი ატვირთვა არაა ხელმისაწვდომი. - ატვირთეთ რაიმე ან გაააქტიურეთ ავტო-ატვირთვა. - არასაკმარისი ადგილი აფერხებს არჩეული ფაილების კოპირებას %1$s დირექტორიაში. გსურთ სანაცვლოდ მათი გადატანა? - უცნობი შეცდომა - აირჩიეთ - ატვირთვა - მიღებული მონაცემები არ მოიცავდნენ სწორ ფაილს. - %1$s მომხმარებელს არ აქვს უფლება წაიკითხოს ფაილი. - ფაილის ასლი დროებით დირექტორიაში ვერ შეიქმნა. სცადეთ მისი ახლიდან გაგზავნა. - ატვირთვისთვის არჩეული ფაილი ვერ იქნა ნაპოვნი. გთხოვთ დაამოწმოთ ფაილის არსებობა. - ფაილი ატვირთვისთვის არაა - დირექტორიის სახელი - აირჩიეთ ატვირთვის დირექტორია - %1$s ვერ აიტვირთა - ატვირთვა ვერ მოხერხდა, ახლიდან გაიარეთ ავტორიზაცია - ატვირთვა ვერ განხორციელდა - ატვირთვის ოპციონი: - ფაილის საწყის დირექტორიაში დატოვება - ფაილის საწყისი დირექტორიიდან გაუქმება - ატვირთოთ ამ დირექტორიაში - %1$d%% ატვირთვა %2$s - იტვირთება... - ატვირთულია %1$s - გამოსვლა - დაყენება - ანგარიშები %1$s თქვენს მოწყობილობაზე არაა. გთხოვთ დააყენოთ ანგარიში. - ანგარიში ვერ მოიძებნა - მიმდინარე - ვერ მოხერხებული/მიმდინარე რესტარტი - ატვირთულია - უარყოფილია - ველოდებით ატვირთვას - ატვირთვები - უარყოფილია - კონფლიქტი - კავშირის პრობლემა - შეცდომა უფლებამოსილებებში - ფაილის შეცდომა - დირექტორიის შეცდომა - ლოკალური ფაილი ვერ იქნა ნაპოვნი - შეცდომა უფლებებში - სერვერის არასანდო სერტიფიკატი - აპლიკაცია შეჩერდა - დასრულებულია - უცნობი შეცდომა - ველოდებით ეკონომიური რეჟიმიდან გამოსვლას - გადახდის მოლოდინშია - მომხმარებელი - მისამართი - ელ-ფოსტა - ტელეფონის ნომერი - Twitter-ი - ვებ-საიტი - მოხმარებლის ინფორმაციის მიღებისას წარმოიშვა შეცდომა - პირადი ინფორმაციის კომპლექტი არაა - დაამატეთ სახელი, ფოტო და საკონტაქტო დეტალები თქვენს პროფილის გვერდზე. - მომხმარებლის სახელი - ჩამოტვირთვა - მოწმდება შენახული უფლებამოსილებანი - ფაილის კოპირება პირადი საცავიდან - განახლება - რა არის ახალი სურათი - გამოტოვება - ახალი %1$s-ში - ელ-წერილის გაგზავნა - diff --git a/app/src/main/res/values-ka/strings.xml b/app/src/main/res/values-ka/strings.xml index ed840d8569..7f19cbf06a 100644 --- a/app/src/main/res/values-ka/strings.xml +++ b/app/src/main/res/values-ka/strings.xml @@ -7,7 +7,6 @@ Account creation failed Account icon Account not found! - Clear failed uploads Edit Clear all notifications Empty trash bin @@ -152,7 +151,6 @@ Do you really want to delete %1$s and the contents thereof? Do you really want to delete the selected items and their contents? Local only - Error creating conflict dialog! Conflicting file %1$s Local file If you select both versions, the local file will have a number appended to its name. diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 7c7d534b45..30781f7118 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -7,7 +7,6 @@ 계정 생성에 실패했습니다. 계정 아이콘 계정을 찾을 수 없습니다! - 실패한 업로드 삭제 편집 모든 알림 지우기 휴지통 비우기 @@ -154,7 +153,6 @@ %1$s 및 포함된 모든 내용을 삭제하시겠습니까? 선택한 항목과 포함된 내용을 삭제하시겠습니까? 로컬만 - 다이얼로그의 충돌로 인한 오류 충돌하는 파일 %1$s 로컬 파일 두 버전을 모두 선택하면 기존 파일 이름에 번호가 추가됩니다. diff --git a/app/src/main/res/values-lo/strings.xml b/app/src/main/res/values-lo/strings.xml index 11b73771ac..39cda0df62 100644 --- a/app/src/main/res/values-lo/strings.xml +++ b/app/src/main/res/values-lo/strings.xml @@ -7,7 +7,6 @@ ການສ້າງບັນຊີບໍ່ສຳເລັດ ໄອຄອນບັນຊີ ບໍ່ພົບບັນຊີ! - ການອັບໂຫຼດບໍ່ສຳເລັດ ແກ້ໄຂ ລ້າງການແຈ້ງເຕືອນທັງຫມົດ ລ້າງຖັງຂີ້ເຫຍື່ອ @@ -145,7 +144,6 @@ ທ່ານຕ້ອງການ ລຶບ%1$s ແລະ ເນື້ອຫາບໍ? ທ່ານຕ້ອງການລຶບລາຍການທີ່ເລືອກ ແລະ ເນື້ອຫາແທ້ບໍ? ຊ່ອງເກັບຢ່າງດຽວ - ຄວາມຜິດພາດໃນການສ້າງການສົນທະນາຜິດພາດ! ຟາຍຜິດພາດ%1$s ຖ້າທ່ານເລືອກເອົາທັງສອງເວີຊັ້ນ, ບ່ອນເກັບຟາຍຈະມີຈໍານວນສະສົມ ລາຍການໄອຄອນຜູ້ຕິດຕໍ່ diff --git a/app/src/main/res/values-lt-rLT/strings.xml b/app/src/main/res/values-lt-rLT/strings.xml index eccaac995f..24ddd93adb 100644 --- a/app/src/main/res/values-lt-rLT/strings.xml +++ b/app/src/main/res/values-lt-rLT/strings.xml @@ -7,7 +7,6 @@ Paskyros sukūrimas nepavyko Paskyros piktograma Paskyra nerasta! - Išvalyti nepavykusius įkėlimus Taisyti Išvalyti visus pranešimus Išvalyti šiukšlinę @@ -147,7 +146,6 @@ Ar tikrai norite ištrinti %1$s ir jo turinį? Ar tikrai norite ištrinti pažymėtus elementus ir jų turinį? Tik vietiniai - Klaida kuriant konflikto dialogą! Nesuderinamas failas%1$s Vietinis failas Jei pasirinksite abi versijas, vietinis failas prie pavadinimo turės numerį. diff --git a/app/src/main/res/values-lv/strings.xml b/app/src/main/res/values-lv/strings.xml index f7debdce83..080f09398c 100644 --- a/app/src/main/res/values-lv/strings.xml +++ b/app/src/main/res/values-lv/strings.xml @@ -7,7 +7,6 @@ Konta izveidošana neizdevās Konta ikona Konts nav atrasts! - Notīrīt neizdevušās augšupielādes Rediģēt Notīrīt visus paziņojumus Izdzēst miskastes saturu @@ -123,7 +122,6 @@ Vai tiešām vēlaties izdzēst %1$s un tā saturu? Vai tiešām vēlies dzēst izvēlētos objektus un to saturu? Tikai lokālos - Kļūda veidojot konfliktu dialogu! Lietotāja ikona kontaktpersonu sarakstam Nav dota atļauja, importēšana neizdevās Kontakti diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index f9d63cdeaf..bea7664360 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -7,7 +7,6 @@ Неуспешно креирање на сметка Икона за сметката Сметката не е пројандена! - Исчисти ги неуспешните прикачувања Уреди Исчисти ги сите известувања Испразни ја корпата со отпадоци @@ -144,7 +143,6 @@ али си сигурен дека сакаш да ја избришеш %1$s и содржината во истата? Дали си сигурен дека сакаш да ја избришеш означената ставкаи содржината во неа? Само локално - Грешка при креирање конфликтен извештај! Датотеки со конфликт %1$s Локална датотека Ако ги одберете и двете верзии, локалната датотека ќе има број додаден на нејзиното име. diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml index 5274e2b330..a97cedbda0 100644 --- a/app/src/main/res/values-nb-rNO/strings.xml +++ b/app/src/main/res/values-nb-rNO/strings.xml @@ -7,7 +7,6 @@ Oppretting av konto mislyktes Kontoikon Konto ble ikke funnet! - Fjern feilede opplastinger Rediger Slett alle varsel Tøm papirkurv @@ -57,6 +56,8 @@ Kan ikke hente oppgavetyper, vennligst sjekk internettforbindelsen din. Assistent Ukjent + Inndata + Utdata Tilknyttet bruker ikke funnet! Tilgang mislyktes: %1$s Kontoen er ikke lagt til på denne enheten enda @@ -171,7 +172,6 @@ Ønsker du virkelig å slette %1$s og dets innhold? Vil du virkelig fjerne de valgte elementene og dets innhold? Kun lokalt - Feil ved oppretting av konfliktdialog! Konflikt med%1$s Lokal fil Hvis du velger begge versjonene vil den lokale filen få et tall lagt til på slutten av navnet. diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 7c997db67b..764a6aaa87 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -7,7 +7,6 @@ Account aanmaken mislukt Accountpictogram Account niet gevonden! - Wis mislukte uploads Bewerken Handel alle meldingen af Prullenbak legen @@ -167,7 +166,6 @@ Wil je %1$s en de inhoud ervan werkelijk verwijderen? Wil je de geselecteerde objecten en hun inhoud echt verwijderen? Alleen lokaal - Fout bij het maken van conflictdialoog! Conflicterend bestand%1$s Lokaal bestand Als je beide versies selecteert, zal het lokale bestand een nummer aan de naam toegevoegd krijgen. diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 417fdfa52d..78f1f38430 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -7,7 +7,6 @@ Błąd tworzenia konta Ikona konta Nie znaleziono konta! - Wyczyść nieudane wysyłanie Edytuj Wyczyść wszystkie powiadomienia Opróżnij kosz @@ -153,7 +152,6 @@ Czy na pewno chcesz usunąć %1$s wraz z zawartością? Czy na pewno chcesz usunąć wybrane pozycje i ich zawartość? Tylko lokalnie - Błąd podczas tworzenia dialogu konfliktu! Plik powodujący konflikt %1$s Plik lokalny Jeśli wybierzesz obie wersje, to do nazwy pliku lokalnego zostanie dodany numer. diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 9049bf3b68..1de92b5d71 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -7,7 +7,6 @@ Falha ao criar conta Ícone da conta Conta não encontrada! - Apagar envios com falha Editar Apagar todas as notificações Esvaziar lixeira @@ -57,6 +56,8 @@ Não foi possível buscar os tipos de tarefas. Verifique sua conexão com a Internet. Assistente Desconhecido + Entrada + Saída Conta associada não encontrada! O acesso falhou: %1$s A conta ainda não está adicionada neste dispositivo @@ -171,7 +172,6 @@ Quer realmente excluir %1$s e seu conteúdo? Quer realmente excluir os itens selecionados e seus conteúdos? Somente local - Erro ao criar diálogo de conflito! Arquivo conflitante %1$s Arquivo local Se você selecionar as duas versões, o arquivo local terá um número anexado ao seu nome. diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml index 93615fe7e6..0393ad0c6e 100644 --- a/app/src/main/res/values-pt-rPT/strings.xml +++ b/app/src/main/res/values-pt-rPT/strings.xml @@ -7,7 +7,6 @@ Criação de conta falhou Ícone de conta Conta não encontrada! - Limpar envios falhados Editar Limpar todas as notifcações Esvaziar a reciclagem @@ -150,7 +149,6 @@ Deseja realmente apagar %1$s e o seu conteúdo? Quer realmente apagar os itens seleccionados e os seus conteúdos? Apenas localmente - Erro ao criar a janela de conflito! Ficheiro em conflito %1$s Ficheiro local Se selecionou ambas as versões, o ficheiro local terá um número acrescentado ao seu nome. diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 57598577b2..a75583470f 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -7,7 +7,6 @@ Eroare la crearea contului Iconița contului Contul nu a fost găsit! - Elimină încărcările eșuate Editare Elimină toate notificările Golește coșul de gunoi @@ -150,7 +149,6 @@ Sigur vreți să eliminați %1$s și conținutul său? Doriți să ștergeți elementele selectate și conținutul lor? Doar local - Eroare la crearea dialogului pentru conflict! Conflict cu fișierul %1$s Fișier local Dacă selectezi ambele variante, atunci fișierul local va avea un număr adăugat la numele său. diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index fd87b685db..88ebecacc1 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -7,7 +7,6 @@ Ошибка создания учетной записи Значок учётной записи Учётная запись не найдена! - Убрать неудавшиеся загрузки Редактировать Удалить все уведомления Очистить корзину @@ -158,7 +157,6 @@ Действительно удалить «%1$s» и его содержимое? Действительно удалить выбранные объекты и их содержимое? Только локально - Не удалось создать диалоговое окно разрешения конфликта синхронизации. Конфликт версий файла «%1$s» Локальный файл Если вы выберете обе версии, локальный файл будет иметь номер, добавленный к его имени. diff --git a/app/src/main/res/values-sc/strings.xml b/app/src/main/res/values-sc/strings.xml index 8a6f92e903..6fc476d640 100644 --- a/app/src/main/res/values-sc/strings.xml +++ b/app/src/main/res/values-sc/strings.xml @@ -7,7 +7,6 @@ Errore in sa creatzione de su contu Icona de su contu Contu no agatadu! - Boga·nche is carrigamentos faddidos Modìfica Boga·nche totu is notìficas Bòida s\'àliga @@ -144,7 +143,6 @@ A beru boles cantzellare %1$s e is cuntenutos suos? A beru boles cantzellare is elementos seberados e is cuntenutos issoro? Locale isceti - Errore creende una bentana de cunflitu! Archìviu in cunflitu %1$s Si seletzionas ambas is versiones, s\'archìviu locale at a tènnere unu nùmeru in agiunta a su nùmene. Icona utente pro lista de cuntatos diff --git a/app/src/main/res/values-sk-rSK/strings.xml b/app/src/main/res/values-sk-rSK/strings.xml index e396c88bfc..2d9d9fcf03 100644 --- a/app/src/main/res/values-sk-rSK/strings.xml +++ b/app/src/main/res/values-sk-rSK/strings.xml @@ -7,7 +7,6 @@ Vytvorenie účtu sa nepodarilo Ikona účtu Účet sa nenašiel! - Odstrániť zlyhané nahrávania Upraviť Odstrániť všetky upozornenia Vyprázdniť kôš @@ -171,7 +170,6 @@ Naozaj chcete odstrániť %1$s a jeho obsah? Naozaj chcete odstrániť vybraté položky a ich obsah? Iba lokálne - Chyba pri vytváraní konfliktného dialógového okna! Konfliktný súbor %1$s Lokálny súbor Ak vyberiete obe verzie, miestny súbor bude mať k svojmu názvu pridané číslo. diff --git a/app/src/main/res/values-sl/strings.xml b/app/src/main/res/values-sl/strings.xml index 649b9df23b..5b72b578d4 100644 --- a/app/src/main/res/values-sl/strings.xml +++ b/app/src/main/res/values-sl/strings.xml @@ -7,7 +7,6 @@ Ustvarjanje računa je spodletelo ikona računa Računa ni mogoče najti! - Počisti dnevnik spodletelih pošiljanj Uredi Počisti vsa obvestila Izprazni smeti @@ -153,7 +152,6 @@ Ali res želite izbrisati %1$s skupaj z vsebino? Ali zares želite izbrisati izbrane predmete in vsebino? Le krajevno - Prišlo je do napake med ustvarjanjem pogovornega okna spora! Različice %1$s v sporu. Krajevna datoteka Če izberete obe različici, bo k imenu krajevne datoteke dodana številka. diff --git a/app/src/main/res/values-sq/strings.xml b/app/src/main/res/values-sq/strings.xml index 600a73fb3c..50f7c9c863 100644 --- a/app/src/main/res/values-sq/strings.xml +++ b/app/src/main/res/values-sq/strings.xml @@ -7,7 +7,6 @@ Krijimi i llogarisë dështoi Ikona e llogarisë Llogaria nuk u gjet! - Pastroni ngarkimet e dështuara Përpuno Pastro të gjithë njoftimet Dërgoje/Shpërndaje @@ -131,7 +130,6 @@ Doni vërtet të hiqet %1$s dhe përmbajtja e tij? Doni vërtet të hiqni artikujt e zgjedhur dhe përmbajtjen e tyre? Vetëm vendorja - Gabim në krijimin e dialogut për konflikte! Nëse zgjidhni të dy versionet, skedari lokal do të ketë një numër të bashkangjitur në emër Ikona e përdoruesit për listën e kontakteve Asnjë leje e dhënë, nuk u importua asgjë. diff --git a/app/src/main/res/values-sr-rSP/strings.xml b/app/src/main/res/values-sr-rSP/strings.xml index 6343160a1f..d7f12e426d 100644 --- a/app/src/main/res/values-sr-rSP/strings.xml +++ b/app/src/main/res/values-sr-rSP/strings.xml @@ -8,7 +8,6 @@ Pravljenje naloga nije uspelo Ikona naloga Nalog nije nađen! - Izbriši neuspela otpremanja Izmeni Očisti sva obaveštenja Slanje/Deljenje diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index 30d08f507a..6759393306 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -7,7 +7,6 @@ Прављење налога није успело Икона налога Налог није нађен! - Избриши неуспела отпремања Уреди Очисти сва обавештења Испразни корпу за отпатке @@ -57,6 +56,8 @@ Не могу да се преузму типови задатака, молимо вас да проверите везу са интернетом. Асистент Непознато + Унос + Излаз Придружени налог није нађен! Неуспешан приступ: %1$s Налог још није додат на овај уређај @@ -171,7 +172,6 @@ Заиста желите да обришете %1$s и сав њен садржај? Заиста желите да обришете означене ставке и њихов садржај? Само локално - Грешка при прављењу дијалога сукоба! Конфликтни фајл %1$s Локални фајл Ако изаберете обе верзије, локални фајл имаће број придодат свом називу. diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 703267b6e6..453b194c83 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -7,7 +7,6 @@ Skapande av konto misslyckades Kontoikon Kontot hittades inte! - Ta bort misslyckade uppladdningar Redigera Rensa alla aviseringar Töm papperskorgen @@ -57,6 +56,7 @@ Det går inte att hämta uppgiftstyper, kontrollera din internetanslutning. Assistent Okänd + Inmatning Associerat konto kunde inte hittas! Åtkomst misslyckades: %1$s Kontot har ännu inte lagts till på den här enheten @@ -171,7 +171,6 @@ Vill du verkligen ta bort %1$s och dess innehåll? Vill du verkligen radera de valda objekten och dess innehåll? Endast lokalt - Fel vid skapande av konfliktdialog! Konfliktande fil %1$s Lokal fil Om du väljer båda versionerna kommer den lokala filen få ett nummer tillagt i filnamnet. diff --git a/app/src/main/res/values-th-rTH/strings.xml b/app/src/main/res/values-th-rTH/strings.xml index 6b2168742e..1e3da07892 100644 --- a/app/src/main/res/values-th-rTH/strings.xml +++ b/app/src/main/res/values-th-rTH/strings.xml @@ -7,7 +7,6 @@ การสร้างบัญชีล้มเหลว ไอคอนบัญชี ไม่พบบัญชี! - ล้างการอัปโหลดที่ล้มเหลว แก้ไข ล้างการแจ้งเตือน ล้างข้อมูลในถังขยะ @@ -147,7 +146,6 @@ คุณต้องการลบ %1$s และเนื้อหาที่อยู่ในนั้นจริง ๆ หรือไม่? คุณต้องการลบรายการที่เลือกและเนื้อหาที่อยู่ในนั้นจริง ๆ หรือไม่? เฉพาะต้นทางเท่านั้น - ข้อผิดพลาดในการสร้างกล่องโต้ตอบข้อขัดแย้ง! ขัดขัดแย้งไฟล์ %1$s ไฟล์ต้นทาง ถ้าคุณเลือกทั้งสองรุ่น ชื่อของไฟล์ที่อยู่ในเครื่องจะมีตัวเลขเพิ่มเข้าไป diff --git a/app/src/main/res/values-tk/strings.xml b/app/src/main/res/values-tk/strings.xml index ba8ed96063..962c6b34d4 100644 --- a/app/src/main/res/values-tk/strings.xml +++ b/app/src/main/res/values-tk/strings.xml @@ -7,7 +7,6 @@ Akaunt döretmeklik şowsuz boldy Hasap nyşany Hasap tapylmady - Şowsuz ýüklemeleri arassalaň Redaktirläň ähli bildirişleri arassala Iber / Paýlaş @@ -137,7 +136,6 @@ Hakykatdan hem %1$sonuň mazmuny hem pozmak isleýärsiňizmi? Saýlanan elementleri we olaryň mazmunyny hakykatdanam pozmak isleýärsiňizmi? Diňe ýerli - Jedelli gepleşikleri döretmekdäki ýalňyşlyk! Jedelli faýl%1$s Eger siz iki wersiýasyny hem saýlasaňyz, ýerli faýlyň adyna san goşular. Aragatnaşyk sanawy üçin ulanyjy nyşany diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 43b1e0e786..e2c1d451f4 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -7,7 +7,6 @@ Hesap oluşturulamadı Hesap simgesi Hesap bulunamadı! - Tamamlanamayan yüklemeler silinsin Düzenle Tüm bildirimleri temizle Çöp kutusunu boşalt @@ -57,6 +56,8 @@ Görev türleri alınamadı. Lütfen İnternet bağlantınızı denetleyin. Yardımcı Bilinmiyor + Giriş + Çıktı İlişkili hesap bulunamadı! Erişilemedi: %1$s Aygıt üzerinde henüz bu hesap açılmamış @@ -171,7 +172,6 @@ %1$s klasörünü ve içindekileri silmek istediğinize emin misiniz? Seçilmiş ögeleri ve içindekileri silmek istediğinize emin misiniz? Yalnızca yerel - Çakışma penceresi oluşturulurken sorun çıktı! %1$s dosyası çakışıyor Yerel dosya İki sürümü de saklamayı seçerseniz, yerel dosyanın adına bir numara eklenecek. diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index b1974fff65..0e310d690b 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -7,7 +7,6 @@ Не вдалося створити обліковий запис Зображення облікового запису Обліковий запис не знайдено! - Очистити невдалі завантаження Редагувати Очистити всі сповіщення Очистити кошик @@ -171,7 +170,6 @@ Ви справді бажаєте вилучити %1$s та його вміст? Дійсно вилучити вибрані об\'єкти та їхній вміст? Лише на пристрої - Помилка у створенні діалогу щодо конфлікту! Конфліктний файл %1$s Файл на пристрої Якщо ви виберете обидві версії, то до назви локального файлу буде додано порядковий номер. diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index acf96f9d6f..968796c73b 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -7,7 +7,6 @@ Tạo tài khoản không thành công Biểu tượng tài khoản Không tìm thấy tài khoản! - Dọn dẹp các tải lên bị lỗi Chỉnh sửa Xoá tất cả thông báo Dọn sạch thùng rác @@ -147,7 +146,6 @@ Bạn có thực sự muốn xóa %1$s và nội dung của nó? Bạn có thực sự muốn xóa các mục đã chọn và nội dung của chúng không? Chỉ cục bộ - Lỗi khi tạo hộp thoại xung đột! Tập tin xung đột %1$s Tệp cục bộ Nếu bạn chọn cả hai phiên bản, tệp trên máy sẽ có một số được thêm vào tên của nó. diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 230e092186..ff8fee2903 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -7,7 +7,6 @@ 账号创建失败 账号图标 找不到账号! - 清除失败上传任务 编辑 清除所有通知 清空垃圾箱 @@ -47,6 +46,7 @@ 任务已创建 任务已删除 未知 + 输出结果 相关账号未找到! 访问已失败: %1$s 该账号尚未添加到此设备上 @@ -161,7 +161,6 @@ 确定删除%1$s及其相关内容? 确定删除选中项及其内容? 仅本地 - 创建冲突会话时出错! 冲突文件 %1$s 本地文件 如果您选择两个版本,则本地文件的名称后面将附加一个数字。 diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index 612b543117..032e3562df 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -7,7 +7,6 @@ 建立賬戶失敗 賬戶圖示 沒有找到任何賬戶 - 清除失敗的上傳 編輯 清除所有通知訊息 清空回收桶 @@ -57,6 +56,8 @@ 無法擷取任務類型,請檢查您的網際網路連線。 助手 不詳 + 輸入 + 輸出 無法找到連結賬戶 存取失敗:%1$s 此賬戶尚未加入到這個裝置 @@ -171,7 +172,6 @@ 你真的想要刪除 %1$s 與裡面的檔案? 您真的想要刪除所選項目和裡面的內容嗎? 只有近端 - 建立抵觸訊息時發生錯誤! 檔案 %1$s 抵觸 近端檔案 若您同時選擇兩個版本,近端的檔案會在檔名後附加編號。 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 84e5699237..5d2475f444 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -7,7 +7,6 @@ 帳號建立失敗 帳號圖示 找不到帳號! - 清除失敗的上傳 編輯 清除所有通知訊息 清空回收桶 @@ -57,6 +56,8 @@ 無法擷取工作項目類型,請檢查您的網際網路連線。 助理 未知 + 輸入 + 輸出 找不到相關的帳號! 存取失敗:%1$s 此帳號尚未加入到這個裝置 @@ -171,7 +172,6 @@ 你真的想要刪除 %1$s 與裡面的檔案? 您真的想要刪除所選項目和裡面的內容嗎? 僅本機 - 建立衝突對話框時發生錯誤! 檔案 %1$s 衝突 本機檔案 若您同時選擇兩個版本,本機的檔案會在檔名後附加編號。 From b22c037ab208fec634d59b76984a93f92ad363c7 Mon Sep 17 00:00:00 2001 From: nextcloud-android-bot Date: Wed, 17 Apr 2024 19:20:21 +0000 Subject: [PATCH 74/74] =?UTF-8?q?=F0=9F=94=84=20synced=20local=20'.github/?= =?UTF-8?q?workflows/'=20with=20remote=20'config/workflows/'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: nextcloud-android-bot --- .github/workflows/codeql.yml | 4 ++-- .github/workflows/scorecard.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index e235d5a23c..e2612483c1 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -39,7 +39,7 @@ jobs: with: swap-size-gb: 10 - name: Initialize CodeQL - uses: github/codeql-action/init@df5a14dc28094dc936e103b37d749c6628682b60 # v3.25.0 + uses: github/codeql-action/init@c7f9125735019aa87cfc361530512d50ea439c71 # v3.25.1 with: languages: ${{ matrix.language }} - name: Set up JDK 17 @@ -53,4 +53,4 @@ jobs: echo "org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError" > "$HOME/.gradle/gradle.properties" ./gradlew assembleDebug - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@df5a14dc28094dc936e103b37d749c6628682b60 # v3.25.0 + uses: github/codeql-action/analyze@c7f9125735019aa87cfc361530512d50ea439c71 # v3.25.1 diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index b14bc5d284..d8406c85c6 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -42,6 +42,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@df5a14dc28094dc936e103b37d749c6628682b60 # v3.25.0 + uses: github/codeql-action/upload-sarif@c7f9125735019aa87cfc361530512d50ea439c71 # v3.25.1 with: sarif_file: results.sarif