From 9f65babb38512231624f50bd6334db648b866449 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Fri, 27 Feb 2026 12:23:44 +0100 Subject: [PATCH 1/6] fix(upload-list-adapter): invalid view holder adapter position Signed-off-by: alperozturk96 --- .../ui/activity/UploadListActivity.java | 4 +- .../android/ui/adapter/UploadListAdapter.java | 299 ++++-------------- 2 files changed, 62 insertions(+), 241 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java index dbd70b68b0f9..8ab7119b1511 100755 --- a/app/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java @@ -317,7 +317,9 @@ public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationRe private class UploadFinishReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - throttler.run("update_upload_list", () -> uploadListAdapter.loadUploadItemsFromDb()); + throttler.run("update_upload_list", () -> uploadListAdapter.loadUploadItemsFromDb(() -> { + + })); } } } 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 4d8e4164cbdf..806d71c3a39f 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 @@ -8,7 +8,6 @@ */ package com.owncloud.android.ui.adapter; -import android.annotation.SuppressLint; import android.app.NotificationManager; import android.content.ActivityNotFoundException; import android.content.Context; @@ -58,15 +57,10 @@ import com.owncloud.android.utils.theme.ViewThemeUtils; import java.io.File; +import java.util.ArrayList; import java.util.Arrays; -import java.util.HashSet; import java.util.List; import java.util.Optional; -import java.util.Set; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.RejectedExecutionHandler; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; import androidx.annotation.NonNull; import kotlin.Unit; @@ -77,30 +71,25 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter { private static final String TAG = UploadListAdapter.class.getSimpleName(); - private static class GroupConfig { - final Type type; - final int titleRes; - final UploadStatus status; - final NameCollisionPolicy nameCollisionPolicy; - - GroupConfig(Type type, int titleRes, UploadStatus status, NameCollisionPolicy nameCollisionPolicy) { - this.type = type; - this.titleRes = titleRes; - this.status = status; - this.nameCollisionPolicy = nameCollisionPolicy; - } - - public static List getConfigs() { - return List.of( - new GroupConfig(Type.CURRENT, R.string.uploads_view_group_current_uploads, UploadStatus.UPLOAD_IN_PROGRESS, null), - new GroupConfig(Type.FAILED, R.string.uploads_view_group_failed_uploads, UploadStatus.UPLOAD_FAILED, null), - new GroupConfig(Type.CANCELLED, R.string.uploads_view_group_manually_cancelled_uploads, UploadStatus.UPLOAD_CANCELLED, null), - new GroupConfig(Type.FINISHED, R.string.uploads_view_group_finished_uploads, UploadStatus.UPLOAD_SUCCEEDED, NameCollisionPolicy.ASK_USER), // ASK_USER default value - new GroupConfig(Type.SKIPPED, R.string.uploads_view_upload_status_skip, UploadStatus.UPLOAD_SUCCEEDED, NameCollisionPolicy.SKIP) - ); + private record Section( + Type type, + int titleRes, + UploadStatus status, + NameCollisionPolicy collisionPolicy, + OCUpload[] items + ) { + Section withItems(OCUpload[] newItems) { + return new Section(type, titleRes, status, collisionPolicy, newItems); } } + private final List
sections = new ArrayList<>(List.of( + new Section(Type.CURRENT, R.string.uploads_view_group_current_uploads, UploadStatus.UPLOAD_IN_PROGRESS, null, new OCUpload[0]), + new Section(Type.FAILED, R.string.uploads_view_group_failed_uploads, UploadStatus.UPLOAD_FAILED, null, new OCUpload[0]), + new Section(Type.CANCELLED, R.string.uploads_view_group_manually_cancelled_uploads, UploadStatus.UPLOAD_CANCELLED, null, new OCUpload[0]), + new Section(Type.FINISHED, R.string.uploads_view_group_finished_uploads, UploadStatus.UPLOAD_SUCCEEDED, NameCollisionPolicy.ASK_USER, new OCUpload[0]), + new Section(Type.SKIPPED, R.string.uploads_view_upload_status_skip, UploadStatus.UPLOAD_SUCCEEDED, NameCollisionPolicy.SKIP, new OCUpload[0]))); + private UploadProgressListener uploadProgressListener; private final FileActivity parentActivity; private final UploadsStorageManager uploadsStorageManager; @@ -109,17 +98,9 @@ public static List getConfigs() { private final PowerManagementService powerManagementService; private final UserAccountManager accountManager; private final Clock clock; - private final UploadGroup[] uploadGroups; private final boolean showUser; private final ViewThemeUtils viewThemeUtils; private NotificationManager mNotificationManager; - private final ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, - 0L, TimeUnit.MILLISECONDS, - new LinkedBlockingQueue<>(1), - new UploadGroupLoadPolicy()); - - private final List uploadGroupConfigs = GroupConfig.getConfigs(); - private final FileUploadHelper uploadHelper = FileUploadHelper.Companion.instance(); public UploadListAdapter(final FileActivity fileActivity, @@ -140,60 +121,30 @@ public UploadListAdapter(final FileActivity fileActivity, this.powerManagementService = powerManagementService; this.clock = clock; this.viewThemeUtils = viewThemeUtils; - - uploadGroups = new UploadGroup[uploadGroupConfigs.size()]; - shouldShowHeadersForEmptySections(false); - initUploadGroups(); showUser = accountManager.getAccounts().length > 1; } - private void initUploadGroups() { - final var optionalUser = parentActivity.getUser(); - if (optionalUser.isEmpty()) { - return; - } - - final var accountName = optionalUser.get().getAccountName(); - - for (int i = 0; i < uploadGroupConfigs.size(); i++) { - final var config = uploadGroupConfigs.get(i); - uploadGroups[i] = createUploadGroup(config, accountName); - } - } - - private UploadGroup createUploadGroup(GroupConfig config, String accountName) { - return new UploadGroup(config.type, parentActivity.getString(config.titleRes)) { - @Override - public void refresh(LoadCompleteListener listener) { - uploadHelper.getUploadsByStatus(accountName, config.status, config.nameCollisionPolicy,ocUploads -> { - fixAndSortItems(ocUploads); - listener.onComplete(); - return Unit.INSTANCE; - }); - } - }; - } - @Override public int getSectionCount() { - return uploadGroups.length; + return sections.size(); } @Override public int getItemCount(int section) { - return uploadGroups[section].getItems().length; + return sections.get(section).items().length; } @Override public void onBindHeaderViewHolder(SectionedViewHolder holder, int section, boolean expanded) { HeaderViewHolder headerViewHolder = (HeaderViewHolder) holder; - UploadGroup group = uploadGroups[section]; + Section group = sections.get(section); + String title = parentActivity.getString(group.titleRes()); + int count = group.items().length; headerViewHolder.binding.uploadListTitle.setText( - String.format(parentActivity.getString(R.string.uploads_view_group_header), - group.getGroupName(), group.getGroupItemCount())); + String.format(parentActivity.getString(R.string.uploads_view_group_header), title, count)); viewThemeUtils.platform.colorPrimaryTextViewElement(headerViewHolder.binding.uploadListTitle); headerViewHolder.binding.uploadListTitle.setOnClickListener(v -> { @@ -223,25 +174,17 @@ public void onBindHeaderViewHolder(SectionedViewHolder holder, int section, bool headerViewHolder.binding.uploadListAction.setOnClickListener(v -> { switch (group.type) { case CURRENT -> { - OCUpload ocUpload = group.getItem(0); - if (ocUpload == null) { - return; - } - - String accountName = ocUpload.getAccountName(); - if (accountName == null) { - return; - } + String accountName = group.items()[0].getAccountName(); for (OCUpload upload: group.items) { uploadHelper.updateUploadStatus(upload.getRemotePath(), accountName, UploadStatus.UPLOAD_CANCELLED); FileUploadWorker.Companion.cancelCurrentUpload(upload.getRemotePath(), accountName, () -> Unit.INSTANCE); } - loadUploadItemsFromDb(); + loadUploadItemsFromDb(() -> {}); } case FINISHED -> { uploadsStorageManager.clearSuccessfulUploads(); - loadUploadItemsFromDb(); + loadUploadItemsFromDb(() -> {}); } case FAILED -> showFailedPopupMenu(headerViewHolder); case CANCELLED -> showCancelledPopupMenu(headerViewHolder); @@ -261,14 +204,14 @@ private void showFailedPopupMenu(HeaderViewHolder headerViewHolder) { if (itemId == R.id.action_upload_list_failed_clear) { uploadsStorageManager.clearFailedButNotDelayedUploads(); clearTempEncryptedFolder(); - loadUploadItemsFromDb(); + loadUploadItemsFromDb(() -> {}); } else if (itemId == R.id.action_upload_list_failed_retry) { uploadHelper.retryFailedUploads( uploadsStorageManager, connectivityService, accountManager, powerManagementService); - loadUploadItemsFromDb(); + loadUploadItemsFromDb(() -> {}); } return true; @@ -286,7 +229,7 @@ private void showCancelledPopupMenu(HeaderViewHolder headerViewHolder) { if (itemId == R.id.action_upload_list_cancelled_clear) { uploadsStorageManager.clearCancelledUploadsForCurrentAccount(); - loadUploadItemsFromDb(); + loadUploadItemsFromDb(() -> {}); clearTempEncryptedFolder(); } else if (itemId == R.id.action_upload_list_cancelled_resume) { retryCancelledUploads(); @@ -311,7 +254,7 @@ private void retryCancelledUploads() { connectivityService, accountManager, powerManagementService); - loadUploadItemsFromDb(); + loadUploadItemsFromDb(() -> {}); parentActivity.runOnUiThread(() -> { if (showNotExistMessage) { showNotExistMessage(); @@ -331,16 +274,13 @@ public void onBindFooterViewHolder(SectionedViewHolder holder, int section) { @Override public void onBindViewHolder(SectionedViewHolder holder, int section, int relativePosition, int absolutePosition) { - if (uploadGroups.length == 0 || section < 0 || section >= uploadGroups.length) { + if (sections.isEmpty() || section < 0 || section >= sections.size()) { return; } - UploadGroup uploadGroup = uploadGroups[section]; - if (uploadGroup == null) { - return; - } + Section sectionData = sections.get(section); + OCUpload item = sectionData.items()[relativePosition]; - OCUpload item = uploadGroup.getItem(relativePosition); if (item == null) { return; } @@ -469,17 +409,14 @@ public void onBindViewHolder(SectionedViewHolder holder, int section, int relati itemViewHolder.binding.uploadRightButton.setOnClickListener(v -> { uploadHelper.updateUploadStatus(item.getRemotePath(), item.getAccountName(), UploadStatus.UPLOAD_CANCELLED); FileUploadWorker.Companion.cancelCurrentUpload(item.getRemotePath(), item.getAccountName(), () -> Unit.INSTANCE); - loadUploadItemsFromDb(); + loadUploadItemsFromDb(() -> {}); }); } else if (item.getUploadStatus() == UploadStatus.UPLOAD_FAILED) { if (item.getLastResult() == UploadResult.SYNC_CONFLICT) { itemViewHolder.binding.uploadRightButton.setImageResource(R.drawable.ic_dots_vertical); itemViewHolder.binding.uploadRightButton.setOnClickListener(view -> { - if (optionalUser.isPresent()) { - User user = optionalUser.get(); - showItemConflictPopup(user, itemViewHolder, item, status, view); - } + optionalUser.ifPresent(user -> showItemConflictPopup(user, itemViewHolder, item, status, view)); }); } else { // Delete @@ -519,12 +456,9 @@ public void onBindViewHolder(SectionedViewHolder holder, int section, int relati Optional user = accountManager.getUser(item.getAccountName()); if (file.exists() && user.isPresent()) { uploadHelper.retryUpload(item, user.get()); - loadUploadItemsFromDb(); + loadUploadItemsFromDb(() -> {}); } else { - DisplayUtils.showSnackMessage( - v.getRootView().findViewById(android.R.id.content), - R.string.local_file_not_found_message - ); + DisplayUtils.showSnackMessage(v.getRootView().findViewById(android.R.id.content), R.string.local_file_not_found_message); } }); } else if (item.getUploadStatus() == UploadStatus.UPLOAD_SUCCEEDED) { @@ -723,7 +657,7 @@ private void showItemConflictPopup(User user, public void removeUpload(OCUpload item) { uploadsStorageManager.removeUpload(item); cancelOldErrorNotification(item); - loadUploadItemsFromDb(); + loadUploadItemsFromDb(() -> {}); } private void refreshFolder( @@ -871,21 +805,28 @@ public SectionedViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int vie } } - /** - * Load upload items from {@link UploadsStorageManager}. - */ - public final void loadUploadItemsFromDb() { - loadUploadItemsFromDb(null); - } + public final void loadUploadItemsFromDb(Runnable onCompleted) { + parentActivity.getUser().ifPresent(user -> { + String accountName = user.getAccountName(); - /** - * Load upload items from {@link UploadsStorageManager}. - * @param loadCompleteListener load complete listener - */ - public final void loadUploadItemsFromDb(LoadCompleteListener loadCompleteListener) { - Log_OC.d(TAG, "loadUploadItemsFromDb"); - // Use thread pool to avoid repeated loading of data - executor.execute(new UploadGroupLoadRunnable(loadCompleteListener)); + for (int i = 0; i < sections.size(); i++) { + final int index = i; + Section sec = sections.get(index); + + uploadHelper.getUploadsByStatus(accountName, sec.status(), sec.collisionPolicy(), uploads -> { + for (OCUpload upload : uploads) { + upload.setDataFixed(uploadHelper); + } + Arrays.sort(uploads, new OCUploadComparator()); + + sections.set(index, sec.withItems(uploads)); + parentActivity.runOnUiThread(this::notifyDataSetChanged); + onCompleted.run(); + + return Unit.INSTANCE; + }); + } + }); } /** @@ -962,132 +903,10 @@ static class ItemViewHolder extends SectionedViewHolder { } } - interface Refresh { - void refresh(LoadCompleteListener listener); - } - - interface Apply { - void apply(); - } - enum Type { CURRENT, FINISHED, FAILED, CANCELLED, SKIPPED } - abstract class UploadGroup implements Refresh, Apply { - private final Type type; - private OCUpload[] items; - private OCUpload[] refreshItems; - private final String name; - - UploadGroup(Type type, String groupName) { - this.type = type; - this.name = groupName; - items = new OCUpload[0]; - } - - private String getGroupName() { - return name; - } - - public OCUpload[] getItems() { - return items; - } - - public OCUpload getItem(int position) { - if (items.length == 0 || position < 0 || position >= items.length) { - return null; - } - - return items[position]; - } - - public void setItems(OCUpload... items) { - this.items = items; - } - - public void setRefreshItems(OCUpload... items) { - this.refreshItems = items; - } - - void fixAndSortItems(OCUpload... array) { - for (OCUpload upload : array) { - upload.setDataFixed(uploadHelper); - } - Arrays.sort(array, new OCUploadComparator()); - setRefreshItems(array); - } - - private int getGroupItemCount() { - return items == null ? 0 : items.length; - } - - @Override - public void apply() { - setItems(this.refreshItems); - } - } - - public interface LoadCompleteListener { - void onComplete(); - } - - private class UploadGroupLoadRunnable implements Runnable { - - private final Set loadCompleteListenerSet = new HashSet<>(); - - public UploadGroupLoadRunnable(LoadCompleteListener loadCompleteListener) { - if (loadCompleteListener != null) { - loadCompleteListenerSet.add(loadCompleteListener); - } - } - - @SuppressLint("NotifyDataSetChanged") - @Override - public void run() { - final int groupCount = uploadGroups.length; - final int[] completedCount = {0}; - - for (UploadGroup group : uploadGroups) { - group.refresh(() -> { - synchronized (completedCount) { - group.apply(); - completedCount[0]++; - if (completedCount[0] == groupCount) { - - // All groups finished, update UI once - parentActivity.runOnUiThread(() -> { - notifyDataSetChanged(); - for (LoadCompleteListener loadCompleteListener : loadCompleteListenerSet) { - loadCompleteListener.onComplete(); - } - }); - } - } - }); - } - } - } - - private static class UploadGroupLoadPolicy implements RejectedExecutionHandler { - @Override - public void rejectedExecution(Runnable runnable, ThreadPoolExecutor executor) { - if (executor.isShutdown()) { - return; - } - - Runnable oldest = executor.getQueue().poll(); - - if (oldest instanceof UploadGroupLoadRunnable oldUploadTask && runnable instanceof UploadGroupLoadRunnable newUploadTask) { - if (!oldUploadTask.loadCompleteListenerSet.isEmpty()) { - newUploadTask.loadCompleteListenerSet.addAll(oldUploadTask.loadCompleteListenerSet); - } - } - - executor.execute(runnable); - } - } - public void cancelOldErrorNotification(OCUpload upload) { if (mNotificationManager == null) { mNotificationManager = (NotificationManager) parentActivity.getSystemService(Context.NOTIFICATION_SERVICE); From cd538c66bfa27c266ee24fed162e617fea3acb75 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Fri, 27 Feb 2026 12:48:10 +0100 Subject: [PATCH 2/6] fix(upload-list-adapter): do not refresh list after immediately async call Signed-off-by: alperozturk96 --- .../client/jobs/upload/FileUploadHelper.kt | 4 +- .../ui/activity/UploadListActivity.java | 4 +- .../android/ui/adapter/UploadListAdapter.java | 42 ++++++++++++++----- 3 files changed, 36 insertions(+), 14 deletions(-) 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 5bb40b30969d..d96bc2990885 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 @@ -264,9 +264,11 @@ class FileUploadHelper { uploadsStorageManager.uploadDao.deleteByRemotePathAndAccountName(remotePath, accountName) } - fun updateUploadStatus(remotePath: String, accountName: String, status: UploadStatus) { + @JvmOverloads + fun updateUploadStatus(remotePath: String, accountName: String, status: UploadStatus, onCompleted: () -> Unit = {}) { ioScope.launch { uploadsStorageManager.uploadDao.updateStatus(remotePath, accountName, status.value) + onCompleted() } } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java index 8ab7119b1511..b0f1f9e6f0db 100755 --- a/app/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java @@ -317,9 +317,7 @@ public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationRe private class UploadFinishReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - throttler.run("update_upload_list", () -> uploadListAdapter.loadUploadItemsFromDb(() -> { - - })); + throttler.run("update_upload_list", () -> uploadListAdapter.loadUploadItemsFromDb(() -> {})); } } } 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 806d71c3a39f..defb63cb7880 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 @@ -64,6 +64,7 @@ import androidx.annotation.NonNull; import kotlin.Unit; +import kotlin.jvm.functions.Function0; /** * This Adapter populates a ListView with following types of uploads: pending, active, completed. Filtering possible. @@ -176,11 +177,31 @@ public void onBindHeaderViewHolder(SectionedViewHolder holder, int section, bool case CURRENT -> { String accountName = group.items()[0].getAccountName(); - for (OCUpload upload: group.items) { - uploadHelper.updateUploadStatus(upload.getRemotePath(), accountName, UploadStatus.UPLOAD_CANCELLED); - FileUploadWorker.Companion.cancelCurrentUpload(upload.getRemotePath(), accountName, () -> Unit.INSTANCE); + final int totalUploads = group.items().length; + final int[] completedCount = {0}; + + for (int i=0; i() { + @Override + public Unit invoke() { + FileUploadWorker.Companion.cancelCurrentUpload(upload.getRemotePath(), accountName, new Function0() { + @Override + public Unit invoke() { + completedCount[0]++; + if (completedCount[0] == totalUploads) { + Log_OC.d(TAG, "refreshing upload items"); + + // All uploads finished, refresh UI once + loadUploadItemsFromDb(() -> {}); + } + return Unit.INSTANCE; + } + }); + return Unit.INSTANCE; + } + }); } - loadUploadItemsFromDb(() -> {}); } case FINISHED -> { uploadsStorageManager.clearSuccessfulUploads(); @@ -211,7 +232,6 @@ private void showFailedPopupMenu(HeaderViewHolder headerViewHolder) { connectivityService, accountManager, powerManagementService); - loadUploadItemsFromDb(() -> {}); } return true; @@ -254,7 +274,6 @@ private void retryCancelledUploads() { connectivityService, accountManager, powerManagementService); - loadUploadItemsFromDb(() -> {}); parentActivity.runOnUiThread(() -> { if (showNotExistMessage) { showNotExistMessage(); @@ -407,9 +426,13 @@ public void onBindViewHolder(SectionedViewHolder holder, int section, int relati itemViewHolder.binding.uploadRightButton.setImageResource(R.drawable.ic_action_cancel_grey); itemViewHolder.binding.uploadRightButton.setVisibility(View.VISIBLE); itemViewHolder.binding.uploadRightButton.setOnClickListener(v -> { - uploadHelper.updateUploadStatus(item.getRemotePath(), item.getAccountName(), UploadStatus.UPLOAD_CANCELLED); - FileUploadWorker.Companion.cancelCurrentUpload(item.getRemotePath(), item.getAccountName(), () -> Unit.INSTANCE); - loadUploadItemsFromDb(() -> {}); + uploadHelper.updateUploadStatus(item.getRemotePath(), item.getAccountName(), UploadStatus.UPLOAD_CANCELLED, () -> { + FileUploadWorker.Companion.cancelCurrentUpload(item.getRemotePath(), item.getAccountName(), () -> { + loadUploadItemsFromDb(() -> {}); + return Unit.INSTANCE; + }); + return Unit.INSTANCE; + }); }); } else if (item.getUploadStatus() == UploadStatus.UPLOAD_FAILED) { @@ -456,7 +479,6 @@ public void onBindViewHolder(SectionedViewHolder holder, int section, int relati Optional user = accountManager.getUser(item.getAccountName()); if (file.exists() && user.isPresent()) { uploadHelper.retryUpload(item, user.get()); - loadUploadItemsFromDb(() -> {}); } else { DisplayUtils.showSnackMessage(v.getRootView().findViewById(android.R.id.content), R.string.local_file_not_found_message); } From eb4234daf6ee896e03cd1d76514f7707148d6ab3 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Fri, 27 Feb 2026 14:17:11 +0100 Subject: [PATCH 3/6] fix codacy Signed-off-by: alperozturk96 --- .../com/nextcloud/client/jobs/upload/FileUploadHelper.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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 d96bc2990885..679fb75eac1b 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 @@ -265,7 +265,12 @@ class FileUploadHelper { } @JvmOverloads - fun updateUploadStatus(remotePath: String, accountName: String, status: UploadStatus, onCompleted: () -> Unit = {}) { + fun updateUploadStatus( + remotePath: String, + accountName: String, + status: UploadStatus, + onCompleted: () -> Unit = {} + ) { ioScope.launch { uploadsStorageManager.uploadDao.updateStatus(remotePath, accountName, status.value) onCompleted() From 5cf4e8fe168ea6569d3ec22f1920f4a4cdc85fa1 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Fri, 27 Feb 2026 15:00:39 +0100 Subject: [PATCH 4/6] use more suitable section title for FINISHED Signed-off-by: alperozturk96 --- .../java/com/owncloud/android/ui/adapter/UploadListAdapter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 defb63cb7880..996db1c4fa9a 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 @@ -88,7 +88,7 @@ Section withItems(OCUpload[] newItems) { new Section(Type.CURRENT, R.string.uploads_view_group_current_uploads, UploadStatus.UPLOAD_IN_PROGRESS, null, new OCUpload[0]), new Section(Type.FAILED, R.string.uploads_view_group_failed_uploads, UploadStatus.UPLOAD_FAILED, null, new OCUpload[0]), new Section(Type.CANCELLED, R.string.uploads_view_group_manually_cancelled_uploads, UploadStatus.UPLOAD_CANCELLED, null, new OCUpload[0]), - new Section(Type.FINISHED, R.string.uploads_view_group_finished_uploads, UploadStatus.UPLOAD_SUCCEEDED, NameCollisionPolicy.ASK_USER, new OCUpload[0]), + new Section(Type.FINISHED, R.string.uploads_view_upload_status_succeeded, UploadStatus.UPLOAD_SUCCEEDED, NameCollisionPolicy.ASK_USER, new OCUpload[0]), new Section(Type.SKIPPED, R.string.uploads_view_upload_status_skip, UploadStatus.UPLOAD_SUCCEEDED, NameCollisionPolicy.SKIP, new OCUpload[0]))); private UploadProgressListener uploadProgressListener; From 777daeb4425a1993d80ee5167a3f508be7e274c9 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Fri, 27 Feb 2026 15:19:21 +0100 Subject: [PATCH 5/6] fix lint Signed-off-by: alperozturk96 --- .../owncloud/android/ui/adapter/UploadListAdapter.java | 8 ++++---- app/src/main/res/values/strings.xml | 2 +- 2 files changed, 5 insertions(+), 5 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 996db1c4fa9a..b8080d1652e3 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 @@ -88,7 +88,7 @@ Section withItems(OCUpload[] newItems) { new Section(Type.CURRENT, R.string.uploads_view_group_current_uploads, UploadStatus.UPLOAD_IN_PROGRESS, null, new OCUpload[0]), new Section(Type.FAILED, R.string.uploads_view_group_failed_uploads, UploadStatus.UPLOAD_FAILED, null, new OCUpload[0]), new Section(Type.CANCELLED, R.string.uploads_view_group_manually_cancelled_uploads, UploadStatus.UPLOAD_CANCELLED, null, new OCUpload[0]), - new Section(Type.FINISHED, R.string.uploads_view_upload_status_succeeded, UploadStatus.UPLOAD_SUCCEEDED, NameCollisionPolicy.ASK_USER, new OCUpload[0]), + new Section(Type.COMPLETED, R.string.uploads_view_group_completed_uploads, UploadStatus.UPLOAD_SUCCEEDED, NameCollisionPolicy.ASK_USER, new OCUpload[0]), new Section(Type.SKIPPED, R.string.uploads_view_upload_status_skip, UploadStatus.UPLOAD_SUCCEEDED, NameCollisionPolicy.SKIP, new OCUpload[0]))); private UploadProgressListener uploadProgressListener; @@ -163,7 +163,7 @@ public void onBindHeaderViewHolder(SectionedViewHolder holder, int section, bool }}); switch (group.type) { - case CURRENT, FINISHED -> headerViewHolder.binding.uploadListAction.setImageResource(R.drawable.ic_close); + case CURRENT, COMPLETED -> headerViewHolder.binding.uploadListAction.setImageResource(R.drawable.ic_close); case CANCELLED, FAILED -> headerViewHolder.binding.uploadListAction.setImageResource(R.drawable.ic_dots_vertical); @@ -203,7 +203,7 @@ public Unit invoke() { }); } } - case FINISHED -> { + case COMPLETED -> { uploadsStorageManager.clearSuccessfulUploads(); loadUploadItemsFromDb(() -> {}); } @@ -926,7 +926,7 @@ static class ItemViewHolder extends SectionedViewHolder { } enum Type { - CURRENT, FINISHED, FAILED, CANCELLED, SKIPPED + CURRENT, COMPLETED, FAILED, CANCELLED, SKIPPED } public void cancelOldErrorNotification(OCUpload upload) { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3912bf80aca3..02355b4c69f4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -289,7 +289,7 @@ Current Failed/pending restart Cancelled - Uploaded + Completed Completed Skipped A file with the same name already exists. From 9487ef81187d7565d2ff757313d1cc30864e0d53 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Fri, 27 Feb 2026 15:23:59 +0100 Subject: [PATCH 6/6] call onCompleted on main thread Signed-off-by: alperozturk96 --- .../owncloud/android/ui/adapter/UploadListAdapter.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 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 b8080d1652e3..09edd56f45f5 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 @@ -8,6 +8,7 @@ */ package com.owncloud.android.ui.adapter; +import android.annotation.SuppressLint; import android.app.NotificationManager; import android.content.ActivityNotFoundException; import android.content.Context; @@ -827,6 +828,7 @@ public SectionedViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int vie } } + @SuppressLint("NotifyDataSetChanged") public final void loadUploadItemsFromDb(Runnable onCompleted) { parentActivity.getUser().ifPresent(user -> { String accountName = user.getAccountName(); @@ -842,9 +844,11 @@ public final void loadUploadItemsFromDb(Runnable onCompleted) { Arrays.sort(uploads, new OCUploadComparator()); sections.set(index, sec.withItems(uploads)); - parentActivity.runOnUiThread(this::notifyDataSetChanged); - onCompleted.run(); + parentActivity.runOnUiThread(() -> { + notifyDataSetChanged(); + onCompleted.run(); + }); return Unit.INSTANCE; }); }