From 7c008036c41cc2dee11a7d9924bcc3477d9d6987 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Wed, 1 Apr 2026 16:17:59 +0200 Subject: [PATCH 1/6] Rename .java to .kt Signed-off-by: alperozturk96 --- .../{UploadsStorageManager.java => UploadsStorageManager.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/src/main/java/com/owncloud/android/datamodel/{UploadsStorageManager.java => UploadsStorageManager.kt} (100%) diff --git a/app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java b/app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.kt similarity index 100% rename from app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java rename to app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.kt From 32d0e2b872b4942e31a24dd88d39235f2c67b51b Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Wed, 1 Apr 2026 16:18:00 +0200 Subject: [PATCH 2/6] upload storage manager improvements Signed-off-by: alperozturk96 --- .../datamodel/UploadsStorageManager.kt | 868 ++++++++---------- 1 file changed, 381 insertions(+), 487 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.kt b/app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.kt index 9616641d9391..1466c8d6d6bd 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.kt +++ b/app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.kt @@ -13,632 +13,522 @@ * SPDX-FileCopyrightText: 2014 Luke Owncloud * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ -package com.owncloud.android.datamodel; - -import android.content.ContentResolver; -import android.content.ContentValues; -import android.database.Cursor; -import android.os.Handler; -import android.os.Looper; - -import com.nextcloud.client.account.CurrentAccountProvider; -import com.nextcloud.client.account.User; -import com.nextcloud.client.database.NextcloudDatabase; -import com.nextcloud.client.database.dao.UploadDao; -import com.nextcloud.client.database.entity.UploadEntity; -import com.nextcloud.client.database.entity.UploadEntityKt; -import com.nextcloud.client.jobs.upload.FileUploadHelper; -import com.nextcloud.client.jobs.upload.FileUploadWorker; -import com.nextcloud.utils.autoRename.AutoRename; -import com.nextcloud.utils.extensions.RemoteOperationResultExtensionsKt; -import com.owncloud.android.MainApp; -import com.owncloud.android.db.OCUpload; -import com.owncloud.android.db.ProviderMeta.ProviderTableMeta; -import com.owncloud.android.db.UploadResult; -import com.owncloud.android.files.services.NameCollisionPolicy; -import com.owncloud.android.lib.common.operations.RemoteOperationResult; -import com.owncloud.android.lib.common.utils.Log_OC; -import com.owncloud.android.lib.resources.status.OCCapability; -import com.owncloud.android.operations.UploadFileOperation; -import com.owncloud.android.utils.theme.CapabilityUtils; - -import java.io.File; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.List; -import java.util.Locale; -import java.util.Observable; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.VisibleForTesting; - -/** - * Database helper for storing list of files to be uploaded, including status information for each file. - */ -public class UploadsStorageManager extends Observable { - private static final String TAG = UploadsStorageManager.class.getSimpleName(); - - private static final String IS_EQUAL = "== ?"; - private static final String EQUAL = "=="; - private static final String OR = " OR "; - private static final String AND = " AND "; - private static final String ANGLE_BRACKETS = "<>"; - private static final int SINGLE_RESULT = 1; - - private static final long QUERY_PAGE_SIZE = 100; - - private final ContentResolver contentResolver; - private final CurrentAccountProvider currentAccountProvider; - private OCCapability capability; - public final UploadDao uploadDao = NextcloudDatabase.getInstance(MainApp.getAppContext()).uploadDao(); - - public UploadsStorageManager( - CurrentAccountProvider currentAccountProvider, - ContentResolver contentResolver - ) { - if (contentResolver == null) { - throw new IllegalArgumentException("Cannot create an instance with a NULL contentResolver"); - } - this.contentResolver = contentResolver; - this.currentAccountProvider = currentAccountProvider; - } - - private void initOCCapability() { +package com.owncloud.android.datamodel + +import android.content.ContentResolver +import android.content.ContentValues +import android.database.Cursor +import android.os.Handler +import android.os.Looper +import androidx.annotation.VisibleForTesting +import com.nextcloud.client.account.CurrentAccountProvider +import com.nextcloud.client.account.User +import com.nextcloud.client.database.NextcloudDatabase +import com.nextcloud.client.database.dao.UploadDao +import com.nextcloud.client.database.entity.UploadEntity +import com.nextcloud.client.database.entity.toOCUpload +import com.nextcloud.client.jobs.upload.FileUploadHelper +import com.nextcloud.client.jobs.upload.FileUploadWorker +import com.nextcloud.utils.autoRename.AutoRename +import com.nextcloud.utils.extensions.isConflict +import com.owncloud.android.MainApp +import com.owncloud.android.db.OCUpload +import com.owncloud.android.db.ProviderMeta.ProviderTableMeta +import com.owncloud.android.db.UploadResult +import com.owncloud.android.files.services.NameCollisionPolicy +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.lib.resources.status.OCCapability +import com.owncloud.android.operations.UploadFileOperation +import com.owncloud.android.utils.theme.CapabilityUtils +import java.io.File +import java.util.Calendar +import java.util.Locale +import java.util.Observable + +class UploadsStorageManager( + private val currentAccountProvider: CurrentAccountProvider, + private val contentResolver: ContentResolver +) : Observable() { + + private var capability: OCCapability? = null + + @JvmField + val uploadDao: UploadDao = NextcloudDatabase.instance().uploadDao() + + private fun initOCCapability() { try { - this.capability = CapabilityUtils.getCapability(MainApp.getAppContext()); - } catch (RuntimeException e) { - Log_OC.e(TAG,"Failed to set OCCapability: Dependencies are not yet ready."); + this.capability = CapabilityUtils.getCapability(MainApp.getAppContext()) + } catch (e: RuntimeException) { + Log_OC.e(TAG, "Failed to set OCCapability: Dependencies are not yet ready.") } } - /** - * Update an upload object in DB. - * - * @param ocUpload Upload object with state to update - * @return num of updated uploads. - */ - public synchronized int updateUpload(OCUpload ocUpload) { - Log_OC.v(TAG, "Updating " + ocUpload.getLocalPath() + " with status=" + ocUpload.getUploadStatus()); + @Synchronized + fun updateUpload(ocUpload: OCUpload): Int { + Log_OC.v(TAG, "Updating " + ocUpload.localPath + " with status=" + ocUpload.uploadStatus) - OCUpload existingUpload = getUploadById(ocUpload.getUploadId()); + val existingUpload = getUploadById(ocUpload.uploadId) if (existingUpload == null) { - Log_OC.e(TAG, "Upload not found for ID: " + ocUpload.getUploadId()); - return 0; + Log_OC.e(TAG, "Upload not found for ID: " + ocUpload.uploadId) + return 0 } - if (!existingUpload.getAccountName().equals(ocUpload.getAccountName())) { - Log_OC.e(TAG, "Account mismatch for upload ID " + ocUpload.getUploadId() + - ": expected " + existingUpload.getAccountName() + - ", got " + ocUpload.getAccountName()); - return 0; + if (existingUpload.accountName != ocUpload.accountName) { + Log_OC.e( + TAG, "Account mismatch for upload ID " + ocUpload.uploadId + + ": expected " + existingUpload.accountName + + ", got " + ocUpload.accountName + ) + return 0 } - ContentValues cv = new ContentValues(); - cv.put(ProviderTableMeta.UPLOADS_LOCAL_PATH, ocUpload.getLocalPath()); - cv.put(ProviderTableMeta.UPLOADS_REMOTE_PATH, ocUpload.getRemotePath()); - cv.put(ProviderTableMeta.UPLOADS_ACCOUNT_NAME, ocUpload.getAccountName()); - cv.put(ProviderTableMeta.UPLOADS_STATUS, ocUpload.getUploadStatus().value); - cv.put(ProviderTableMeta.UPLOADS_LAST_RESULT, ocUpload.getLastResult().getValue()); - - long uploadEndTimestamp = ocUpload.getUploadEndTimestamp(); - cv.put(ProviderTableMeta.UPLOADS_UPLOAD_END_TIMESTAMP_LONG, uploadEndTimestamp); - cv.put(ProviderTableMeta.UPLOADS_FILE_SIZE, ocUpload.getFileSize()); - cv.put(ProviderTableMeta.UPLOADS_FOLDER_UNLOCK_TOKEN, ocUpload.getFolderUnlockToken()); - - int result = getDB().update(ProviderTableMeta.CONTENT_URI_UPLOADS, - cv, - ProviderTableMeta._ID + "=? AND " + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "=?", - new String[]{String.valueOf(ocUpload.getUploadId()), ocUpload.getAccountName()} - ); - - Log_OC.d(TAG, "updateUpload returns with: " + result + " for file: " + ocUpload.getLocalPath()); + val cv = ContentValues().apply { + put(ProviderTableMeta.UPLOADS_LOCAL_PATH, ocUpload.localPath) + put(ProviderTableMeta.UPLOADS_REMOTE_PATH, ocUpload.remotePath) + put(ProviderTableMeta.UPLOADS_ACCOUNT_NAME, ocUpload.accountName) + put(ProviderTableMeta.UPLOADS_STATUS, ocUpload.uploadStatus.value) + put(ProviderTableMeta.UPLOADS_LAST_RESULT, ocUpload.lastResult.value) + + val uploadEndTimestamp = ocUpload.uploadEndTimestamp + put(ProviderTableMeta.UPLOADS_UPLOAD_END_TIMESTAMP_LONG, uploadEndTimestamp) + put(ProviderTableMeta.UPLOADS_FILE_SIZE, ocUpload.fileSize) + put(ProviderTableMeta.UPLOADS_FOLDER_UNLOCK_TOKEN, ocUpload.folderUnlockToken) + } + + val result: Int = contentResolver.update( + ProviderTableMeta.CONTENT_URI_UPLOADS, + cv, + ProviderTableMeta._ID + "=? AND " + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "=?", + arrayOf(ocUpload.uploadId.toString(), ocUpload.accountName) + ) + + Log_OC.d(TAG, "updateUpload returns with: " + result + " for file: " + ocUpload.localPath) if (result != SINGLE_RESULT) { - Log_OC.e(TAG, "Failed to update item " + ocUpload.getLocalPath() + " into upload db."); + Log_OC.e(TAG, "Failed to update item " + ocUpload.localPath + " into upload db.") } else { - notifyObserversNow(); + notifyObserversNow() } - return result; + return result } - private int updateUploadInternal(Cursor c, UploadStatus status, UploadResult result, String remotePath, - String localPath) { - - int r = 0; + private fun updateUploadInternal( + c: Cursor, status: UploadStatus?, result: UploadResult?, remotePath: String?, + localPath: String? + ): Int { + var r = 0 while (c.moveToNext()) { - // read upload object and update - OCUpload upload = createOCUploadFromCursor(c); + val upload = createOCUploadFromCursor(c) - String path = c.getString(c.getColumnIndexOrThrow(ProviderTableMeta.UPLOADS_LOCAL_PATH)); + val path = c.getString(c.getColumnIndexOrThrow(ProviderTableMeta.UPLOADS_LOCAL_PATH)) Log_OC.v( TAG, - "Updating " + path + " with status:" + status + " and result:" - + (result == null ? "null" : result.toString()) + " (old:" - + upload.toFormattedString() + ')'); + ("Updating " + path + " with status:" + status + " and result:" + + (result?.toString() ?: "null") + " (old:" + + upload.toFormattedString() + ')') + ) + + upload.setUploadStatus(status) + upload.lastResult = result + upload.remotePath = remotePath - upload.setUploadStatus(status); - upload.setLastResult(result); - upload.setRemotePath(remotePath); if (localPath != null) { - upload.setLocalPath(localPath); + upload.localPath = localPath } + if (status == UploadStatus.UPLOAD_SUCCEEDED) { - upload.setUploadEndTimestamp(Calendar.getInstance().getTimeInMillis()); + upload.uploadEndTimestamp = Calendar.getInstance().getTimeInMillis() } - // store update upload object to db - r = updateUpload(upload); - + r = updateUpload(upload) } - return r; + return r } - /** - * Update upload status of file uniquely referenced by id. - * - * @param id upload id. - * @param status new status. - * @param result new result of upload operation - * @param remotePath path of the file to upload in the ownCloud storage - * @param localPath path of the file to upload in the device storage - * @return 1 if file status was updated, else 0. - */ - private void updateUploadStatus(long id, UploadStatus status, UploadResult result, String remotePath, - String localPath) { - //Log_OC.v(TAG, "Updating "+filepath+" with uploadStatus="+status +" and result="+result); - - Cursor c = getDB().query( + private fun updateUploadStatus( + id: Long, status: UploadStatus?, result: UploadResult?, remotePath: String?, + localPath: String? + ) { + val c = contentResolver.query( ProviderTableMeta.CONTENT_URI_UPLOADS, null, ProviderTableMeta._ID + "=?", - new String[]{String.valueOf(id)}, + arrayOf(id.toString()), null - ); + ) if (c != null) { - if (c.getCount() != SINGLE_RESULT) { - Log_OC.e(TAG, c.getCount() + " items for id=" + id - + " available in UploadDb. Expected 1. Failed to update upload db."); + if (c.count != SINGLE_RESULT) { + Log_OC.e( + TAG, (c.count.toString() + " items for id=" + id + + " available in UploadDb. Expected 1. Failed to update upload db.") + ) } else { - updateUploadInternal(c, status, result, remotePath, localPath); + updateUploadInternal(c, status, result, remotePath, localPath) } - c.close(); + c.close() } else { - Log_OC.e(TAG, "Cursor is null"); + Log_OC.e(TAG, "Cursor is null") } - } - /** - * Should be called when some value of this DB was changed. All observers are informed. - */ - public void notifyObserversNow() { - Log_OC.d(TAG, "notifyObserversNow"); - new Handler(Looper.getMainLooper()).post(() -> { - setChanged(); - notifyObservers(); - }); + fun notifyObserversNow() { + Log_OC.d(TAG, "notifyObserversNow") + Handler(Looper.getMainLooper()).post { + setChanged() + notifyObservers() + } } - /** - * Remove an upload from the uploads list, known its target account and remote path. - * - * @param upload Upload instance to remove from persisted storage. - * @return true when the upload was stored and could be removed. - */ - public int removeUpload(@Nullable OCUpload upload) { - if (upload == null) { - return 0; - } else { - return removeUpload(upload.getUploadId()); - } + fun removeUpload(upload: OCUpload?): Int { + return if (upload == null) 0 else removeUpload(upload.uploadId) } - /** - * Remove an upload from the uploads list, known its target account and remote path. - * - * @param id to remove from persisted storage. - * @return true when the upload was stored and could be removed. - */ - public int removeUpload(long id) { - int result = getDB().delete( + fun removeUpload(id: Long): Int { + val result: Int = contentResolver.delete( ProviderTableMeta.CONTENT_URI_UPLOADS, ProviderTableMeta._ID + "=?", - new String[]{Long.toString(id)} - ); - Log_OC.d(TAG, "delete returns " + result + " for upload with id " + id); + arrayOf(id.toString()) + ) + Log_OC.d(TAG, "delete returns $result for upload with id $id") if (result > 0) { - notifyObserversNow(); + notifyObserversNow() } - return result; + return result } - /** - * Remove an upload from the uploads list, known its target account and remote path. - * - * @param accountName Name of the OC account target of the upload to remove. - * @param remotePath Absolute path in the OC account target of the upload to remove. - * @return true when one or more upload entries were removed - */ - public int removeUpload(String accountName, String remotePath) { - int result = getDB().delete( + fun removeUpload(accountName: String?, remotePath: String?): Int { + val result: Int = contentResolver.delete( ProviderTableMeta.CONTENT_URI_UPLOADS, ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "=? AND " + ProviderTableMeta.UPLOADS_REMOTE_PATH + "=?", - new String[]{accountName, remotePath} - ); - Log_OC.d(TAG, "delete returns " + result + " for file " + remotePath + " in " + accountName); + arrayOf(accountName, remotePath) + ) + Log_OC.d(TAG, "delete returns $result for file $remotePath in $accountName") if (result > 0) { - notifyObserversNow(); + notifyObserversNow() } - return result; + return result } - /** - * Remove all the uploads of a given account from the uploads list. - * - * @param accountName Name of the OC account target of the uploads to remove. - * @return true when one or more upload entries were removed - */ - public int removeUploads(String accountName) { - int result = getDB().delete( + fun removeUploads(accountName: String?): Int { + val result: Int = contentResolver.delete( ProviderTableMeta.CONTENT_URI_UPLOADS, ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "=?", - new String[]{accountName} - ); - Log_OC.d(TAG, "delete returns " + result + " for uploads in " + accountName); + arrayOf(accountName) + ) + Log_OC.d(TAG, "delete returns $result for uploads in $accountName") if (result > 0) { - notifyObserversNow(); + notifyObserversNow() } - return result; + return result } - public OCUpload[] getAllStoredUploads() { - return getUploads(null, (String[]) null); - } + fun getAllStoredUploads(): Array = getUploads(null, null) - public @Nullable - OCUpload getUploadById(long id) { - OCUpload result = null; - Cursor cursor = getDB().query( + fun getUploadById(id: Long): OCUpload? { + var result: OCUpload? = null + val cursor = contentResolver.query( ProviderTableMeta.CONTENT_URI_UPLOADS, null, ProviderTableMeta._ID + "=?", - new String[]{Long.toString(id)}, - "_id ASC"); + arrayOf(id.toString()), + "_id ASC" + ) if (cursor != null) { if (cursor.moveToFirst()) { - result = createOCUploadFromCursor(cursor); + result = createOCUploadFromCursor(cursor) } } - Log_OC.d(TAG, "Retrieve job " + result + " for id " + id); - return result; + Log_OC.d(TAG, "Retrieve job $result for id $id") + return result } - public List getUploadsByIds(long[] uploadIds, String accountName) { - final List result = new ArrayList<>(); - - final List entities = uploadDao.getUploadsByIds(uploadIds, accountName); - entities.forEach(uploadEntity -> { - OCUpload ocUpload = createOCUploadFromEntity(uploadEntity); - if (ocUpload != null) { - result.add(ocUpload); - } - }); - - return result; + fun getUploadsByIds(uploadIds: LongArray, accountName: String): List { + val result = ArrayList() + uploadDao.getUploadsByIds(uploadIds, accountName).forEach { entity -> + createOCUploadFromEntity(entity)?.let { result.add(it) } + } + return result } - private OCUpload[] getUploads(@Nullable String selection, @Nullable String... selectionArgs) { - final List uploads = new ArrayList<>(); - long page = 0; - long rowsRead; - long rowsTotal = 0; - long lastRowID = -1; + private fun getUploads(selection: String?, vararg selectionArgs: String?): Array { + val uploads = ArrayList() + var page: Long = 0 + var rowsRead: Long + var rowsTotal: Long = 0 + var lastRowID: Long = -1 do { - final List uploadsPage = getUploadPage(lastRowID, selection, selectionArgs); - rowsRead = uploadsPage.size(); - rowsTotal += rowsRead; - if (!uploadsPage.isEmpty()) { - lastRowID = uploadsPage.get(uploadsPage.size() - 1).getUploadId(); + val uploadsPage = getUploadPage(lastRowID, selection, *selectionArgs) + rowsRead = uploadsPage.size.toLong() + rowsTotal += rowsRead + if (uploadsPage.isNotEmpty()) { + lastRowID = uploadsPage.last().uploadId } - Log_OC.v(TAG, String.format(Locale.ENGLISH, - "getUploads() got %d rows from page %d, %d rows total so far, last ID %d", - rowsRead, - page, - rowsTotal, - lastRowID - )); - uploads.addAll(uploadsPage); - page++; - } while (rowsRead > 0); - - - Log_OC.v(TAG, String.format(Locale.ENGLISH, - "getUploads() returning %d (%d) rows after reading %d pages", - rowsTotal, - uploads.size(), - page - )); - - - return uploads.toArray(new OCUpload[0]); - } - - @NonNull - private List getUploadPage(final long afterId, @Nullable String selection, @Nullable String... selectionArgs) { - return getUploadPage(QUERY_PAGE_SIZE, afterId, true, selection, selectionArgs); + Log_OC.v( + TAG, String.format( + Locale.ENGLISH, + "getUploads() got %d rows from page %d, %d rows total so far, last ID %d", + rowsRead, + page, + rowsTotal, + lastRowID + ) + ) + uploads.addAll(uploadsPage) + page++ + } while (rowsRead > 0) + + Log_OC.v( + TAG, String.format( + Locale.ENGLISH, + "getUploads() returning %d (%d) rows after reading %d pages", + rowsTotal, + uploads.size, + page + ) + ) + + + return uploads.toTypedArray() } - @NonNull - private List getUploadPage(long limit, final long afterId, final boolean descending, @Nullable String selection, @Nullable String... selectionArgs) { - List uploads = new ArrayList<>(); - String pageSelection = selection; - String[] pageSelectionArgs = selectionArgs; - - String idComparator; - String sortDirection; - if (descending) { - sortDirection = "DESC"; - idComparator = "<"; - } else { - sortDirection = "ASC"; - idComparator = ">"; - } + private fun getUploadPage(afterId: Long, selection: String?, vararg selectionArgs: String?): List = + getUploadPage(QUERY_PAGE_SIZE, afterId, true, selection, *selectionArgs) + + private fun getUploadPage( + limit: Long, + afterId: Long, + descending: Boolean, + selection: String?, + vararg selectionArgs: String? + ): List { + val uploads = ArrayList() + val (sortDirection, idComparator) = if (descending) "DESC" to "<" else "ASC" to ">" + val pageSelection: String? + val pageSelectionArgs: Array if (afterId >= 0) { - if (selection != null) { - pageSelection = "(" + selection + ") AND _id " + idComparator + " ?"; - } else { - pageSelection = "_id " + idComparator + " ?"; + pageSelection = if (selection != null) "($selection) AND _id $idComparator ?" else "_id $idComparator ?" + pageSelectionArgs = arrayOfNulls(selectionArgs.size + 1).also { arr -> + selectionArgs.forEachIndexed { i, v -> arr[i] = v } + arr[selectionArgs.size] = afterId.toString() } - if (selectionArgs != null) { - pageSelectionArgs = Arrays.copyOf(selectionArgs, selectionArgs.length + 1); - } else { - pageSelectionArgs = new String[1]; - } - pageSelectionArgs[pageSelectionArgs.length - 1] = String.valueOf(afterId); - Log_OC.d(TAG, String.format(Locale.ENGLISH, "QUERY: %s ROWID: %d", pageSelection, afterId)); + Log_OC.d(TAG, String.format(Locale.ENGLISH, "QUERY: %s ROWID: %d", pageSelection, afterId)) } else { - Log_OC.d(TAG, String.format(Locale.ENGLISH, "QUERY: %s ROWID: %d", selection, afterId)); + pageSelection = selection + pageSelectionArgs = arrayOfNulls(selectionArgs.size).also { arr -> + selectionArgs.forEachIndexed { i, v -> arr[i] = v } + } + Log_OC.d(TAG, String.format(Locale.ENGLISH, "QUERY: %s ROWID: %d", selection, afterId)) } - String sortOrder; - if (limit > 0) { - sortOrder = String.format(Locale.ENGLISH, "_id " + sortDirection + " LIMIT %d", limit); + val sortOrder = if (limit > 0) { + String.format(Locale.ENGLISH, "_id $sortDirection LIMIT %d", limit) } else { - sortOrder = String.format(Locale.ENGLISH, "_id " + sortDirection); + "_id $sortDirection" } - Cursor c = getDB().query( + contentResolver.query( ProviderTableMeta.CONTENT_URI_UPLOADS, null, pageSelection, pageSelectionArgs, - sortOrder); - - if (c != null) { + sortOrder + )?.use { c -> if (c.moveToFirst()) { do { - OCUpload upload = createOCUploadFromCursor(c); - if (upload == null) { - Log_OC.e(TAG, "OCUpload could not be created from cursor"); - } else { - uploads.add(upload); - } - } while (c.moveToNext() && !c.isAfterLast()); + uploads.add(createOCUploadFromCursor(c)) + } while (c.moveToNext() && !c.isAfterLast) } - c.close(); } - return uploads; + + return uploads } - @Nullable - private OCUpload createOCUploadFromEntity(UploadEntity entity) { - if (entity == null) { - return null; - } - initOCCapability(); - return UploadEntityKt.toOCUpload(entity, capability); + private fun createOCUploadFromEntity(entity: UploadEntity?): OCUpload? { + if (entity == null) return null + initOCCapability() + return entity.toOCUpload(capability) } - private OCUpload createOCUploadFromCursor(Cursor c) { - initOCCapability(); + private fun createOCUploadFromCursor(c: Cursor): OCUpload { + initOCCapability() - OCUpload upload = null; - if (c != null) { - String localPath = c.getString(c.getColumnIndexOrThrow(ProviderTableMeta.UPLOADS_LOCAL_PATH)); + fun Cursor.str(col: String): String = getString(getColumnIndexOrThrow(col)) + fun Cursor.int(col: String): Int = getInt(getColumnIndexOrThrow(col)) + fun Cursor.long(col: String): Long = getLong(getColumnIndexOrThrow(col)) - String remotePath = c.getString(c.getColumnIndexOrThrow(ProviderTableMeta.UPLOADS_REMOTE_PATH)); - if (capability != null) { - remotePath = AutoRename.INSTANCE.rename(remotePath, capability); - } + var remotePath = c.str(ProviderTableMeta.UPLOADS_REMOTE_PATH) + if (capability != null) { + remotePath = AutoRename.rename(remotePath, capability!!) + } - String accountName = c.getString(c.getColumnIndexOrThrow(ProviderTableMeta.UPLOADS_ACCOUNT_NAME)); - upload = new OCUpload(localPath, remotePath, accountName); - - upload.setFileSize(c.getLong(c.getColumnIndexOrThrow(ProviderTableMeta.UPLOADS_FILE_SIZE))); - upload.setUploadId(c.getLong(c.getColumnIndexOrThrow(ProviderTableMeta._ID))); - upload.setUploadStatus( - UploadStatus.fromValue(c.getInt(c.getColumnIndexOrThrow(ProviderTableMeta.UPLOADS_STATUS))) - ); - upload.setLocalAction(c.getInt(c.getColumnIndexOrThrow(ProviderTableMeta.UPLOADS_LOCAL_BEHAVIOUR))); - upload.setNameCollisionPolicy(NameCollisionPolicy.deserialize(c.getInt( - c.getColumnIndexOrThrow(ProviderTableMeta.UPLOADS_NAME_COLLISION_POLICY)))); - upload.setCreateRemoteFolder(c.getInt( - c.getColumnIndexOrThrow(ProviderTableMeta.UPLOADS_IS_CREATE_REMOTE_FOLDER)) == 1); - - final var uploadEndTimestampColumnIndex= c.getColumnIndex(ProviderTableMeta.UPLOADS_UPLOAD_END_TIMESTAMP_LONG); - if (uploadEndTimestampColumnIndex > -1) { - final var uploadEndTimestamp = c.getLong(uploadEndTimestampColumnIndex); - if (uploadEndTimestamp > 0) { - upload.setUploadEndTimestamp(uploadEndTimestamp); - } + return OCUpload( + c.str(ProviderTableMeta.UPLOADS_LOCAL_PATH), + remotePath, + c.str(ProviderTableMeta.UPLOADS_ACCOUNT_NAME) + ).apply { + fileSize = c.long(ProviderTableMeta.UPLOADS_FILE_SIZE) + uploadId = c.long(ProviderTableMeta._ID) + setUploadStatus(UploadStatus.fromValue(c.int(ProviderTableMeta.UPLOADS_STATUS))) + localAction = c.int(ProviderTableMeta.UPLOADS_LOCAL_BEHAVIOUR) + nameCollisionPolicy = NameCollisionPolicy.deserialize(c.int(ProviderTableMeta.UPLOADS_NAME_COLLISION_POLICY)) + isCreateRemoteFolder = c.int(ProviderTableMeta.UPLOADS_IS_CREATE_REMOTE_FOLDER) == 1 + + val timestampIndex = c.getColumnIndex(ProviderTableMeta.UPLOADS_UPLOAD_END_TIMESTAMP_LONG) + if (timestampIndex > -1) { + val ts = c.getLong(timestampIndex) + if (ts > 0) uploadEndTimestamp = ts } - upload.setLastResult(UploadResult.fromValue( - c.getInt(c.getColumnIndexOrThrow(ProviderTableMeta.UPLOADS_LAST_RESULT)))); - upload.setCreatedBy(c.getInt(c.getColumnIndexOrThrow(ProviderTableMeta.UPLOADS_CREATED_BY))); - upload.setUseWifiOnly(c.getInt(c.getColumnIndexOrThrow(ProviderTableMeta.UPLOADS_IS_WIFI_ONLY)) == 1); - upload.setWhileChargingOnly(c.getInt(c.getColumnIndexOrThrow(ProviderTableMeta.UPLOADS_IS_WHILE_CHARGING_ONLY)) - == 1); - upload.setFolderUnlockToken(c.getString(c.getColumnIndexOrThrow(ProviderTableMeta.UPLOADS_FOLDER_UNLOCK_TOKEN))); + + lastResult = UploadResult.fromValue(c.int(ProviderTableMeta.UPLOADS_LAST_RESULT)) + createdBy = c.int(ProviderTableMeta.UPLOADS_CREATED_BY) + isUseWifiOnly = c.int(ProviderTableMeta.UPLOADS_IS_WIFI_ONLY) == 1 + isWhileChargingOnly = c.int(ProviderTableMeta.UPLOADS_IS_WHILE_CHARGING_ONLY) == 1 + folderUnlockToken = c.str(ProviderTableMeta.UPLOADS_FOLDER_UNLOCK_TOKEN) } - return upload; } - public long[] getCurrentUploadIds(final @NonNull String accountName) { - final var result = uploadDao.getAllIds(UploadStatus.UPLOAD_IN_PROGRESS.value, accountName); - return result.stream() - .mapToLong(Integer::longValue) - .toArray(); - } + fun getCurrentUploadIds(accountName: String): LongArray = + uploadDao.getAllIds(UploadStatus.UPLOAD_IN_PROGRESS.value, accountName) + .stream() + .mapToLong { it.toLong() } + .toArray() - public OCUpload[] getUploadsForAccount(final @NonNull String accountName) { - return getUploads(ProviderTableMeta.UPLOADS_ACCOUNT_NAME + IS_EQUAL, accountName); - } + fun getUploadsForAccount(accountName: String): Array = + getUploads(ProviderTableMeta.UPLOADS_ACCOUNT_NAME + IS_EQUAL, accountName) - private ContentResolver getDB() { - return contentResolver; - } - - public void clearFailedButNotDelayedUploads() { - User user = currentAccountProvider.getUser(); - final long deleted = getDB().delete( + fun clearFailedButNotDelayedUploads() { + val user = currentAccountProvider.user + val deleted = contentResolver.delete( ProviderTableMeta.CONTENT_URI_UPLOADS, ProviderTableMeta.UPLOADS_STATUS + EQUAL + UploadStatus.UPLOAD_FAILED.value + - AND + ProviderTableMeta.UPLOADS_LAST_RESULT + - ANGLE_BRACKETS + UploadResult.LOCK_FAILED.getValue() + - AND + ProviderTableMeta.UPLOADS_LAST_RESULT + - ANGLE_BRACKETS + UploadResult.DELAYED_FOR_WIFI.getValue() + - AND + ProviderTableMeta.UPLOADS_LAST_RESULT + - ANGLE_BRACKETS + UploadResult.DELAYED_FOR_CHARGING.getValue() + - AND + ProviderTableMeta.UPLOADS_LAST_RESULT + - ANGLE_BRACKETS + UploadResult.DELAYED_IN_POWER_SAVE_MODE.getValue() + + AND + ProviderTableMeta.UPLOADS_LAST_RESULT + ANGLE_BRACKETS + UploadResult.LOCK_FAILED.value + + AND + ProviderTableMeta.UPLOADS_LAST_RESULT + ANGLE_BRACKETS + UploadResult.DELAYED_FOR_WIFI.value + + AND + ProviderTableMeta.UPLOADS_LAST_RESULT + ANGLE_BRACKETS + UploadResult.DELAYED_FOR_CHARGING.value + + AND + ProviderTableMeta.UPLOADS_LAST_RESULT + ANGLE_BRACKETS + UploadResult.DELAYED_IN_POWER_SAVE_MODE.value + AND + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + IS_EQUAL, - new String[]{user.getAccountName()} - ); - Log_OC.d(TAG, "delete all failed uploads but those delayed for Wifi"); - if (deleted > 0) { - notifyObserversNow(); - } - } + arrayOf(user.accountName) + ) - public void clearCancelledUploadsForCurrentAccount() { - User user = currentAccountProvider.getUser(); - final long deleted = getDB().delete( - ProviderTableMeta.CONTENT_URI_UPLOADS, - ProviderTableMeta.UPLOADS_STATUS + EQUAL + UploadStatus.UPLOAD_CANCELLED.value + AND + - ProviderTableMeta.UPLOADS_ACCOUNT_NAME + IS_EQUAL, new String[]{user.getAccountName()} - ); + Log_OC.d(TAG, "delete all failed uploads but those delayed for Wifi") - Log_OC.d(TAG, "delete all cancelled uploads"); - if (deleted > 0) { - notifyObserversNow(); - } + if (deleted > 0) notifyObserversNow() } - public void clearSuccessfulUploads() { - User user = currentAccountProvider.getUser(); - final long deleted = getDB().delete( + fun clearCancelledUploadsForCurrentAccount() { + val user = currentAccountProvider.user + val deleted = contentResolver.delete( ProviderTableMeta.CONTENT_URI_UPLOADS, - ProviderTableMeta.UPLOADS_STATUS + EQUAL + UploadStatus.UPLOAD_SUCCEEDED.value + AND + - ProviderTableMeta.UPLOADS_ACCOUNT_NAME + IS_EQUAL, new String[]{user.getAccountName()} - ); + ProviderTableMeta.UPLOADS_STATUS + EQUAL + UploadStatus.UPLOAD_CANCELLED.value + + AND + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + IS_EQUAL, + arrayOf(user.accountName) + ) + Log_OC.d(TAG, "delete all cancelled uploads") + if (deleted > 0) notifyObserversNow() + } - Log_OC.d(TAG, "delete all successful uploads"); - if (deleted > 0) { - notifyObserversNow(); - } + fun clearSuccessfulUploads() { + val user = currentAccountProvider.user + val deleted = contentResolver.delete( + ProviderTableMeta.CONTENT_URI_UPLOADS, + ProviderTableMeta.UPLOADS_STATUS + EQUAL + UploadStatus.UPLOAD_SUCCEEDED.value + + AND + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + IS_EQUAL, + arrayOf(user.accountName) + ) + Log_OC.d(TAG, "delete all successful uploads") + if (deleted > 0) notifyObserversNow() } - public void updateDatabaseUploadResult(RemoteOperationResult uploadResult, UploadFileOperation upload) { - Log_OC.d(TAG, "updateDatabaseUploadResult uploadResult: " + uploadResult + " upload: " + upload); + fun updateDatabaseUploadResult(uploadResult: RemoteOperationResult<*>, upload: UploadFileOperation) { + Log_OC.d(TAG, "updateDatabaseUploadResult uploadResult: $uploadResult upload: $upload") - if (uploadResult.isCancelled()) { - Log_OC.w(TAG, "upload is cancelled, removing upload"); - removeUpload(upload.getUser().getAccountName(), upload.getRemotePath()); - return; + if (uploadResult.isCancelled) { + Log_OC.w(TAG, "upload is cancelled, removing upload") + removeUpload(upload.user.accountName, upload.remotePath) + return } - String localPath = (upload.getLocalBehaviour() == FileUploadWorker.LOCAL_BEHAVIOUR_MOVE) - ? upload.getStoragePath() : null; - + val localPath = + if (upload.localBehaviour == FileUploadWorker.LOCAL_BEHAVIOUR_MOVE) upload.storagePath else null - Log_OC.d(TAG, "local behaviour: " + upload.getLocalBehaviour()); - Log_OC.d(TAG, "local path of upload: " + localPath); + Log_OC.d(TAG, "local behaviour: " + upload.localBehaviour) + Log_OC.d(TAG, "local path of upload: $localPath") - UploadStatus status = UploadStatus.UPLOAD_FAILED; - UploadResult result = UploadResult.fromOperationResult(uploadResult); - RemoteOperationResult.ResultCode code = uploadResult.getCode(); + var status = UploadStatus.UPLOAD_FAILED + var result = UploadResult.fromOperationResult(uploadResult) + val code = uploadResult.code - if (uploadResult.isSuccess()) { - status = UploadStatus.UPLOAD_SUCCEEDED; - result = UploadResult.UPLOADED; - } else if (RemoteOperationResultExtensionsKt.isConflict(code)) { - boolean isSame = new FileUploadHelper().isSameFileOnRemote( - upload.getUser(), new File(upload.getStoragePath()), upload.getRemotePath(), upload.getContext()); + if (uploadResult.isSuccess) { + status = UploadStatus.UPLOAD_SUCCEEDED + result = UploadResult.UPLOADED + } else if (code.isConflict()) { + val isSame = FileUploadHelper().isSameFileOnRemote( + upload.user, File(upload.storagePath), upload.remotePath, upload.context + ) if (isSame) { - result = UploadResult.SAME_FILE_CONFLICT; - status = UploadStatus.UPLOAD_SUCCEEDED; + result = UploadResult.SAME_FILE_CONFLICT + status = UploadStatus.UPLOAD_SUCCEEDED } else { - result = UploadResult.SYNC_CONFLICT; + result = UploadResult.SYNC_CONFLICT } } else if (code == RemoteOperationResult.ResultCode.LOCAL_FILE_NOT_FOUND) { // upload status is SUCCEEDED because user cannot take action about it, it will always fail - status = UploadStatus.UPLOAD_SUCCEEDED; - result = UploadResult.FILE_NOT_FOUND; + status = UploadStatus.UPLOAD_SUCCEEDED + result = UploadResult.FILE_NOT_FOUND } - Log_OC.d(TAG, String.format( - "Upload Finished [%s] | RemoteCode: %s | internalResult: %s | FinalStatus: %s | Path: %s", - uploadResult.isSuccess() ? "✅" : "❌", - code, - result.name(), - status, - upload.getRemotePath())); - updateUploadStatus(upload.getOCUploadId(), status, result, upload.getRemotePath(), localPath); + Log_OC.d( + TAG, String.format( + "Upload Finished [%s] | RemoteCode: %s | internalResult: %s | FinalStatus: %s | Path: %s", + if (uploadResult.isSuccess) "✅" else "❌", + code, + result.name, + status, + upload.remotePath + ) + ) + updateUploadStatus(upload.ocUploadId, status, result, upload.remotePath, localPath) } - /** - * Updates the persistent upload database with an upload now in progress. - */ - public void updateDatabaseUploadStart(UploadFileOperation upload) { - String localPath = (FileUploadWorker.LOCAL_BEHAVIOUR_MOVE == upload.getLocalBehaviour()) - ? upload.getStoragePath() : null; + fun updateDatabaseUploadStart(upload: UploadFileOperation) { + val localPath = + if (FileUploadWorker.LOCAL_BEHAVIOUR_MOVE == upload.localBehaviour) upload.storagePath else null updateUploadStatus( - upload.getOCUploadId(), + upload.ocUploadId, UploadStatus.UPLOAD_IN_PROGRESS, UploadResult.UNKNOWN, - upload.getRemotePath(), + upload.remotePath, localPath - ); + ) } @VisibleForTesting - public void removeAllUploads() { - Log_OC.v(TAG, "Delete all uploads!"); - getDB().delete( + fun removeAllUploads() { + Log_OC.v(TAG, "Delete all uploads!") + contentResolver.delete( ProviderTableMeta.CONTENT_URI_UPLOADS, "", - new String[]{}); + arrayOf() + ) } - public int removeUserUploads(User user) { - Log_OC.v(TAG, "Delete all uploads for account " + user.getAccountName()); - return getDB().delete( + fun removeUserUploads(user: User): Int { + Log_OC.v(TAG, "Delete all uploads for account " + user.accountName) + return contentResolver.delete( ProviderTableMeta.CONTENT_URI_UPLOADS, ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "=?", - new String[]{user.getAccountName()}); + arrayOf(user.accountName) + ) } - public enum UploadStatus { - + enum class UploadStatus(val value: Int) { /** * Upload currently in progress or scheduled to be executed. */ @@ -659,25 +549,29 @@ public class UploadsStorageManager extends Observable { */ UPLOAD_CANCELLED(3); - private final int value; - - UploadStatus(int value) { - this.value = value; + companion object { + fun fromValue(value: Int): UploadStatus? { + return when (value) { + 0 -> UPLOAD_IN_PROGRESS + 1 -> UPLOAD_FAILED + 2 -> UPLOAD_SUCCEEDED + 3 -> UPLOAD_CANCELLED + else -> null + } + } } + } - public static UploadStatus fromValue(int value) { - return switch (value) { - case 0 -> UPLOAD_IN_PROGRESS; - case 1 -> UPLOAD_FAILED; - case 2 -> UPLOAD_SUCCEEDED; - case 3 -> UPLOAD_CANCELLED; - default -> null; - }; - } + companion object { + private val TAG: String = UploadsStorageManager::class.java.getSimpleName() - public int getValue() { - return value; - } + private const val IS_EQUAL = "== ?" + private const val EQUAL = "==" + private const val OR = " OR " + private const val AND = " AND " + private const val ANGLE_BRACKETS = "<>" + private const val SINGLE_RESULT = 1 + private const val QUERY_PAGE_SIZE: Long = 100 } } From a11199eecb17818fe80043c08c21b2341bbd6ebd Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Thu, 2 Apr 2026 08:39:00 +0200 Subject: [PATCH 3/6] upload storage manager improvements Signed-off-by: alperozturk96 --- .../datamodel/UploadsStorageManager.kt | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.kt b/app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.kt index 1466c8d6d6bd..c357137ae5cf 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.kt +++ b/app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.kt @@ -60,14 +60,12 @@ class UploadsStorageManager( try { this.capability = CapabilityUtils.getCapability(MainApp.getAppContext()) } catch (e: RuntimeException) { - Log_OC.e(TAG, "Failed to set OCCapability: Dependencies are not yet ready.") + Log_OC.e(TAG, "Failed to set OCCapability: Dependencies are not yet ready. $e") } } @Synchronized fun updateUpload(ocUpload: OCUpload): Int { - Log_OC.v(TAG, "Updating " + ocUpload.localPath + " with status=" + ocUpload.uploadStatus) - val existingUpload = getUploadById(ocUpload.uploadId) if (existingUpload == null) { Log_OC.e(TAG, "Upload not found for ID: " + ocUpload.uploadId) @@ -83,6 +81,8 @@ class UploadsStorageManager( return 0 } + Log_OC.v(TAG, "Updating " + ocUpload.localPath + " with status=" + ocUpload.uploadStatus) + val cv = ContentValues().apply { put(ProviderTableMeta.UPLOADS_LOCAL_PATH, ocUpload.localPath) put(ProviderTableMeta.UPLOADS_REMOTE_PATH, ocUpload.remotePath) @@ -96,7 +96,7 @@ class UploadsStorageManager( put(ProviderTableMeta.UPLOADS_FOLDER_UNLOCK_TOKEN, ocUpload.folderUnlockToken) } - val result: Int = contentResolver.update( + val result = contentResolver.update( ProviderTableMeta.CONTENT_URI_UPLOADS, cv, ProviderTableMeta._ID + "=? AND " + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "=?", @@ -104,6 +104,7 @@ class UploadsStorageManager( ) Log_OC.d(TAG, "updateUpload returns with: " + result + " for file: " + ocUpload.localPath) + if (result != SINGLE_RESULT) { Log_OC.e(TAG, "Failed to update item " + ocUpload.localPath + " into upload db.") } else { @@ -114,7 +115,10 @@ class UploadsStorageManager( } private fun updateUploadInternal( - c: Cursor, status: UploadStatus?, result: UploadResult?, remotePath: String?, + c: Cursor, + status: UploadStatus?, + result: UploadResult?, + remotePath: String?, localPath: String? ): Int { var r = 0 @@ -148,7 +152,10 @@ class UploadsStorageManager( } private fun updateUploadStatus( - id: Long, status: UploadStatus?, result: UploadResult?, remotePath: String?, + id: Long, + status: UploadStatus?, + result: UploadResult?, + remotePath: String?, localPath: String? ) { val c = contentResolver.query( @@ -175,7 +182,7 @@ class UploadsStorageManager( } fun notifyObserversNow() { - Log_OC.d(TAG, "notifyObserversNow") + Log_OC.d(TAG, "notifying upload storage manager observers") Handler(Looper.getMainLooper()).post { setChanged() notifyObservers() @@ -187,7 +194,7 @@ class UploadsStorageManager( } fun removeUpload(id: Long): Int { - val result: Int = contentResolver.delete( + val result = contentResolver.delete( ProviderTableMeta.CONTENT_URI_UPLOADS, ProviderTableMeta._ID + "=?", arrayOf(id.toString()) @@ -199,8 +206,8 @@ class UploadsStorageManager( return result } - fun removeUpload(accountName: String?, remotePath: String?): Int { - val result: Int = contentResolver.delete( + private fun removeUpload(accountName: String?, remotePath: String?): Int { + val result = contentResolver.delete( ProviderTableMeta.CONTENT_URI_UPLOADS, ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "=? AND " + ProviderTableMeta.UPLOADS_REMOTE_PATH + "=?", arrayOf(accountName, remotePath) @@ -213,7 +220,7 @@ class UploadsStorageManager( } fun removeUploads(accountName: String?): Int { - val result: Int = contentResolver.delete( + val result = contentResolver.delete( ProviderTableMeta.CONTENT_URI_UPLOADS, ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "=?", arrayOf(accountName) @@ -493,6 +500,7 @@ class UploadsStorageManager( upload.remotePath ) ) + updateUploadStatus(upload.ocUploadId, status, result, upload.remotePath, localPath) } From 9dd072e06562c74e87a4dd4ef1e59ad3e00e429a Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Thu, 2 Apr 2026 09:56:50 +0200 Subject: [PATCH 4/6] Rename .java to .kt Signed-off-by: alperozturk96 --- ...{UploadStorageManagerTest.java => UploadStorageManagerTest.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/src/androidTest/java/com/owncloud/android/datamodel/{UploadStorageManagerTest.java => UploadStorageManagerTest.kt} (100%) diff --git a/app/src/androidTest/java/com/owncloud/android/datamodel/UploadStorageManagerTest.java b/app/src/androidTest/java/com/owncloud/android/datamodel/UploadStorageManagerTest.kt similarity index 100% rename from app/src/androidTest/java/com/owncloud/android/datamodel/UploadStorageManagerTest.java rename to app/src/androidTest/java/com/owncloud/android/datamodel/UploadStorageManagerTest.kt From c7ab099a836c25138e070fc53497b56098c00def Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Thu, 2 Apr 2026 09:56:51 +0200 Subject: [PATCH 5/6] upload storage manager improvements Signed-off-by: alperozturk96 --- .../datamodel/UploadStorageManagerTest.kt | 332 +++++++++--------- .../datamodel/UploadsStorageManager.kt | 58 +-- 2 files changed, 205 insertions(+), 185 deletions(-) diff --git a/app/src/androidTest/java/com/owncloud/android/datamodel/UploadStorageManagerTest.kt b/app/src/androidTest/java/com/owncloud/android/datamodel/UploadStorageManagerTest.kt index ca4c416f26a9..ee659be1d4b0 100644 --- a/app/src/androidTest/java/com/owncloud/android/datamodel/UploadStorageManagerTest.kt +++ b/app/src/androidTest/java/com/owncloud/android/datamodel/UploadStorageManagerTest.kt @@ -2,228 +2,238 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2017 JARP * SPDX-FileCopyrightText: 2019 Tobias Kaminsky * SPDX-FileCopyrightText: 2021 Chris Narkiewicz * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ -package com.owncloud.android.datamodel; - -import android.accounts.Account; -import android.accounts.AccountManager; -import android.content.ActivityNotFoundException; -import android.content.ContentResolver; -import android.content.Context; - -import com.nextcloud.client.account.CurrentAccountProvider; -import com.nextcloud.client.account.User; -import com.nextcloud.client.account.UserAccountManager; -import com.nextcloud.client.account.UserAccountManagerImpl; -import com.nextcloud.client.database.entity.UploadEntityKt; -import com.nextcloud.test.RandomStringGenerator; -import com.owncloud.android.AbstractIT; -import com.owncloud.android.MainApp; -import com.owncloud.android.db.OCUpload; -import com.owncloud.android.db.UploadResult; -import com.owncloud.android.files.services.NameCollisionPolicy; -import com.owncloud.android.lib.common.accounts.AccountUtils; -import com.owncloud.android.operations.UploadFileOperation; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.io.File; -import java.util.ArrayList; -import java.util.Random; -import java.util.UUID; - -import androidx.test.core.app.ApplicationProvider; -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SmallTest; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +package com.owncloud.android.datamodel + +import android.accounts.Account +import android.accounts.AccountManager +import android.content.ActivityNotFoundException +import android.content.Context +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.nextcloud.client.account.CurrentAccountProvider +import com.nextcloud.client.account.User +import com.nextcloud.client.account.UserAccountManager +import com.nextcloud.client.account.UserAccountManagerImpl +import com.nextcloud.client.database.entity.toUploadEntity +import com.nextcloud.test.RandomStringGenerator.make +import com.owncloud.android.AbstractIT +import com.owncloud.android.MainApp +import com.owncloud.android.db.OCUpload +import com.owncloud.android.db.UploadResult +import com.owncloud.android.files.services.NameCollisionPolicy +import com.owncloud.android.lib.common.accounts.AccountUtils +import com.owncloud.android.operations.UploadFileOperation +import org.junit.After +import org.junit.Assert +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.MockitoAnnotations +import java.io.File +import java.util.Random +import java.util.UUID +import java.util.function.Supplier /** * Created by JARP on 6/7/17. */ -@RunWith(AndroidJUnit4.class) +@RunWith(AndroidJUnit4::class) @SmallTest -public class UploadStorageManagerTest extends AbstractIT { - private UploadsStorageManager uploadsStorageManager; - private CurrentAccountProvider currentAccountProvider = () -> null; - private UserAccountManager userAccountManager; - private User user2; +class UploadStorageManagerTest : AbstractIT() { + private lateinit var uploadsStorageManager: UploadsStorageManager + + @Mock + private lateinit var currentAccountProvider: CurrentAccountProvider + + private lateinit var userAccountManager: UserAccountManager + + private lateinit var user2: User @Before - public void setUp() { - Context instrumentationCtx = ApplicationProvider.getApplicationContext(); - ContentResolver contentResolver = instrumentationCtx.getContentResolver(); - uploadsStorageManager = new UploadsStorageManager(currentAccountProvider, contentResolver); - userAccountManager = UserAccountManagerImpl.fromContext(targetContext); + fun setUp() { + MockitoAnnotations.openMocks(this) - Account temp = new Account("test2@test.com", MainApp.getAccountType(targetContext)); + val instrumentationCtx = ApplicationProvider.getApplicationContext() + val contentResolver = instrumentationCtx.contentResolver + uploadsStorageManager = UploadsStorageManager(currentAccountProvider, contentResolver) + userAccountManager = UserAccountManagerImpl.fromContext(targetContext) + + val temp = Account("test2@test.com", MainApp.getAccountType(targetContext)) if (!userAccountManager.exists(temp)) { - AccountManager platformAccountManager = AccountManager.get(targetContext); - platformAccountManager.addAccountExplicitly(temp, "testPassword", null); - platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_OC_ACCOUNT_VERSION, - Integer.toString(UserAccountManager.ACCOUNT_VERSION)); - platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_OC_VERSION, "14.0.0.0"); - platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_OC_BASE_URL, "test.com"); - platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_USER_ID, "test"); // same as userId + val platformAccountManager = AccountManager.get(targetContext) + platformAccountManager.addAccountExplicitly(temp, "testPassword", null) + platformAccountManager.setUserData( + temp, + AccountUtils.Constants.KEY_OC_ACCOUNT_VERSION, + UserAccountManager.ACCOUNT_VERSION.toString() + ) + platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_OC_VERSION, "14.0.0.0") + platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_OC_BASE_URL, "test.com") + platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_USER_ID, "test") // same as userId } - final UserAccountManager userAccountManager = UserAccountManagerImpl.fromContext(targetContext); - user2 = userAccountManager.getUser("test2@test.com").orElseThrow(ActivityNotFoundException::new); + val userAccountManager: UserAccountManager = UserAccountManagerImpl.fromContext(targetContext) + user2 = userAccountManager.getUser("test2@test.com") + .orElseThrow(Supplier { ActivityNotFoundException() }) } @Test - public void testDeleteAllUploads() { + fun testDeleteAllUploads() { // Clean - for (User user : userAccountManager.getAllUsers()) { - uploadsStorageManager.removeUserUploads(user); + for (user in userAccountManager.getAllUsers()) { + uploadsStorageManager.removeUserUploads(user) } - int accountRowsA = 3; - int accountRowsB = 4; - insertUploads(account, accountRowsA); - insertUploads(user2.toPlatformAccount(), accountRowsB); - - assertEquals("Expected 4 removed uploads files", - 4, - uploadsStorageManager.removeUserUploads(user2)); + val accountRowsA = 3 + val accountRowsB = 4 + insertUploads(account, accountRowsA) + insertUploads(user2.toPlatformAccount(), accountRowsB) + + Assert.assertEquals( + "Expected 4 removed uploads files", + 4, + uploadsStorageManager.removeUserUploads(user2).toLong() + ) } @Test - public void largeTest() { - int size = 3000; - ArrayList uploads = new ArrayList<>(); + fun largeTest() { + val size = 3000 + val uploads = ArrayList() - deleteAllUploads(); - assertEquals(0, uploadsStorageManager.getAllStoredUploads().length); + deleteAllUploads() + Assert.assertEquals(0, uploadsStorageManager.getAllStoredUploads().size.toLong()) - for (int i = 0; i < size; i++) { - OCUpload upload = createUpload(account); + for (i in 0.. uploads, OCUpload storedUpload) { - for (int i = 0; i < uploads.size(); i++) { + private fun contains(uploads: ArrayList, storedUpload: OCUpload): Boolean { + for (i in uploads.indices) { if (storedUpload.isSame(uploads.get(i), true)) { - return true; + return true } } - return false; + return false } - @Test(expected = IllegalArgumentException.class) - public void corruptedUpload() { - OCUpload corruptUpload = new OCUpload(File.separator + "LocalPath", - OCFile.PATH_SEPARATOR + "RemotePath", - account.name); - - corruptUpload.setLocalPath(null); - uploadsStorageManager.uploadDao.insertOrReplace(UploadEntityKt.toUploadEntity(corruptUpload)); - uploadsStorageManager.getAllStoredUploads(); + @Test(expected = IllegalArgumentException::class) + fun corruptedUpload() { + val corruptUpload = OCUpload( + File.separator + "LocalPath", + OCFile.PATH_SEPARATOR + "RemotePath", + account.name + ) + + corruptUpload.localPath = null + uploadsStorageManager.uploadDao.insertOrReplace(corruptUpload.toUploadEntity()) + uploadsStorageManager.getAllStoredUploads() } @Test - public void getById() { - OCUpload upload = createUpload(account); - long id = uploadsStorageManager.uploadDao.insertOrReplace(UploadEntityKt.toUploadEntity(upload)); - OCUpload newUpload = uploadsStorageManager.getUploadById(id); - - assertNotNull(newUpload); - assertEquals(upload.getLocalAction(), newUpload.getLocalAction()); - assertEquals(upload.getFolderUnlockToken(), newUpload.getFolderUnlockToken()); + fun getById() { + val upload = createUpload(account) + val id = uploadsStorageManager.uploadDao.insertOrReplace(upload.toUploadEntity()) + val newUpload = uploadsStorageManager.getUploadById(id) + + Assert.assertNotNull(newUpload) + Assert.assertEquals(upload.localAction.toLong(), newUpload!!.localAction.toLong()) + Assert.assertEquals(upload.folderUnlockToken, newUpload.folderUnlockToken) } @Test - public void getByIdNull() { - OCUpload newUpload = uploadsStorageManager.getUploadById(-1); - - assertNull(newUpload); + fun getByIdNull() { + val newUpload = uploadsStorageManager.getUploadById(-1) + Assert.assertNull(newUpload) } - private void insertUploads(Account account, int rowsToInsert) { - for (int i = 0; i < rowsToInsert; i++) { - uploadsStorageManager.uploadDao.insertOrReplace(UploadEntityKt.toUploadEntity(createUpload(account))); + private fun insertUploads(account: Account, rowsToInsert: Int) { + for (i in 0.. 0) Log_OC.v( - TAG, String.format( + TAG, + String.format( Locale.ENGLISH, "getUploads() returning %d (%d) rows after reading %d pages", rowsTotal, @@ -299,7 +306,6 @@ class UploadsStorageManager( ) ) - return uploads.toTypedArray() } @@ -383,7 +389,8 @@ class UploadsStorageManager( uploadId = c.long(ProviderTableMeta._ID) setUploadStatus(UploadStatus.fromValue(c.int(ProviderTableMeta.UPLOADS_STATUS))) localAction = c.int(ProviderTableMeta.UPLOADS_LOCAL_BEHAVIOUR) - nameCollisionPolicy = NameCollisionPolicy.deserialize(c.int(ProviderTableMeta.UPLOADS_NAME_COLLISION_POLICY)) + nameCollisionPolicy = + NameCollisionPolicy.deserialize(c.int(ProviderTableMeta.UPLOADS_NAME_COLLISION_POLICY)) isCreateRemoteFolder = c.int(ProviderTableMeta.UPLOADS_IS_CREATE_REMOTE_FOLDER) == 1 val timestampIndex = c.getColumnIndex(ProviderTableMeta.UPLOADS_UPLOAD_END_TIMESTAMP_LONG) @@ -417,7 +424,8 @@ class UploadsStorageManager( AND + ProviderTableMeta.UPLOADS_LAST_RESULT + ANGLE_BRACKETS + UploadResult.LOCK_FAILED.value + AND + ProviderTableMeta.UPLOADS_LAST_RESULT + ANGLE_BRACKETS + UploadResult.DELAYED_FOR_WIFI.value + AND + ProviderTableMeta.UPLOADS_LAST_RESULT + ANGLE_BRACKETS + UploadResult.DELAYED_FOR_CHARGING.value + - AND + ProviderTableMeta.UPLOADS_LAST_RESULT + ANGLE_BRACKETS + UploadResult.DELAYED_IN_POWER_SAVE_MODE.value + + AND + ProviderTableMeta.UPLOADS_LAST_RESULT + ANGLE_BRACKETS + + UploadResult.DELAYED_IN_POWER_SAVE_MODE.value + AND + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + IS_EQUAL, arrayOf(user.accountName) ) @@ -475,7 +483,10 @@ class UploadsStorageManager( result = UploadResult.UPLOADED } else if (code.isConflict()) { val isSame = FileUploadHelper().isSameFileOnRemote( - upload.user, File(upload.storagePath), upload.remotePath, upload.context + upload.user, + File(upload.storagePath), + upload.remotePath, + upload.context ) if (isSame) { @@ -491,7 +502,8 @@ class UploadsStorageManager( } Log_OC.d( - TAG, String.format( + TAG, + String.format( "Upload Finished [%s] | RemoteCode: %s | internalResult: %s | FinalStatus: %s | Path: %s", if (uploadResult.isSuccess) "✅" else "❌", code, @@ -558,14 +570,12 @@ class UploadsStorageManager( UPLOAD_CANCELLED(3); companion object { - fun fromValue(value: Int): UploadStatus? { - return when (value) { - 0 -> UPLOAD_IN_PROGRESS - 1 -> UPLOAD_FAILED - 2 -> UPLOAD_SUCCEEDED - 3 -> UPLOAD_CANCELLED - else -> null - } + fun fromValue(value: Int): UploadStatus? = when (value) { + 0 -> UPLOAD_IN_PROGRESS + 1 -> UPLOAD_FAILED + 2 -> UPLOAD_SUCCEEDED + 3 -> UPLOAD_CANCELLED + else -> null } } } From fb0c84fa5cdf3c189b0c9f41deb72a94e08fcbfe Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Thu, 2 Apr 2026 10:08:18 +0200 Subject: [PATCH 6/6] fix(activities-fragment): tests, access binding Signed-off-by: alperozturk96 --- .../com/owncloud/android/datamodel/UploadStorageManagerTest.kt | 2 +- .../com/owncloud/android/datamodel/UploadsStorageManager.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/androidTest/java/com/owncloud/android/datamodel/UploadStorageManagerTest.kt b/app/src/androidTest/java/com/owncloud/android/datamodel/UploadStorageManagerTest.kt index ee659be1d4b0..dd1b256c86d6 100644 --- a/app/src/androidTest/java/com/owncloud/android/datamodel/UploadStorageManagerTest.kt +++ b/app/src/androidTest/java/com/owncloud/android/datamodel/UploadStorageManagerTest.kt @@ -154,7 +154,7 @@ class UploadStorageManagerTest : AbstractIT() { return false } - @Test(expected = IllegalArgumentException::class) + @Test(expected = NullPointerException::class) fun corruptedUpload() { val corruptUpload = OCUpload( File.separator + "LocalPath", diff --git a/app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.kt b/app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.kt index 14678bd10c14..fb6ce25cfe7f 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.kt +++ b/app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.kt @@ -237,7 +237,7 @@ class UploadsStorageManager( return result } - fun getAllStoredUploads(): Array = getUploads(null, null) + fun getAllStoredUploads(): Array = getUploads(null) fun getUploadById(id: Long): OCUpload? { var result: OCUpload? = null