diff --git a/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobFactory.kt b/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobFactory.kt index e445bf2231e4..03ece0215871 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobFactory.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobFactory.kt @@ -251,6 +251,7 @@ class BackgroundJobFactory @Inject constructor( viewThemeUtils.get(), localBroadcastManager.get(), backgroundJobManager.get(), + preferences, context, params ) 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 8c71a8b6677e..99c86cbe36b3 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 @@ -160,7 +160,7 @@ class FileUploadHelper { } } - private fun cancelAndRestartUploadJob(user: User) { + fun cancelAndRestartUploadJob(user: User) { backgroundJobManager.run { cancelFilesUploadJob(user) startFilesUploadJob(user) 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 b00d3a37354f..91b2586ef82b 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 @@ -32,6 +32,7 @@ import com.nextcloud.client.device.PowerManagementService import com.nextcloud.client.jobs.BackgroundJobManager import com.nextcloud.client.jobs.BackgroundJobManagerImpl import com.nextcloud.client.network.ConnectivityService +import com.nextcloud.client.preferences.AppPreferences import com.nextcloud.model.WorkerState import com.nextcloud.model.WorkerStateLiveData import com.owncloud.android.datamodel.FileDataStorageManager @@ -58,6 +59,7 @@ class FileUploadWorker( val viewThemeUtils: ViewThemeUtils, val localBroadcastManager: LocalBroadcastManager, private val backgroundJobManager: BackgroundJobManager, + val preferences: AppPreferences, val context: Context, params: WorkerParameters ) : Worker(context, params), OnDatatransferProgressListener { @@ -136,11 +138,20 @@ class FileUploadWorker( WorkerStateLiveData.instance().setWorkState(WorkerState.Idle) } + @Suppress("ReturnCount") private fun retrievePagesBySortingUploadsByID(): Result { val accountName = inputData.getString(ACCOUNT) ?: return Result.failure() var currentPage = uploadsStorageManager.getCurrentAndPendingUploadsForAccountPageAscById(-1, accountName) while (currentPage.isNotEmpty() && !isStopped) { + if (preferences.isGlobalUploadPaused) { + Log_OC.d(TAG, "Upload is paused, skip uploading files!") + notificationManager.notifyPaused( + intents.notificationStartIntent(null) + ) + return Result.success() + } + Log_OC.d(TAG, "Handling ${currentPage.size} uploads for account $accountName") val lastId = currentPage.last().uploadId uploadFiles(currentPage, accountName) diff --git a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploaderIntents.kt b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploaderIntents.kt index c068d7aa25cc..cb9e620a4084 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploaderIntents.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploaderIntents.kt @@ -97,10 +97,10 @@ class FileUploaderIntents(private val context: Context) { ) } - fun notificationStartIntent(operation: UploadFileOperation): PendingIntent { + fun notificationStartIntent(operation: UploadFileOperation?): PendingIntent { val intent = UploadListActivity.createIntent( - operation.file, - operation.user, + operation?.file, + operation?.user, Intent.FLAG_ACTIVITY_CLEAR_TOP, context ) 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 2c3a01f7f90b..76bbce3594bd 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 @@ -35,17 +35,14 @@ import com.owncloud.android.operations.UploadFileOperation import com.owncloud.android.ui.notifications.NotificationUtils import com.owncloud.android.utils.theme.ViewThemeUtils -class UploadNotificationManager(private val context: Context, private val viewThemeUtils: ViewThemeUtils) { +class UploadNotificationManager(private val context: Context, viewThemeUtils: ViewThemeUtils) { companion object { private const val ID = 411 } private var notification: Notification? = null - private var notificationBuilder: NotificationCompat.Builder - private val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - - init { - notificationBuilder = NotificationUtils.newNotificationBuilder(context, viewThemeUtils).apply { + private var notificationBuilder: NotificationCompat.Builder = + NotificationUtils.newNotificationBuilder(context, viewThemeUtils).apply { setContentTitle(context.getString(R.string.foreground_service_upload)) setSmallIcon(R.drawable.notification_icon) setLargeIcon(BitmapFactory.decodeResource(context.resources, R.drawable.notification_icon)) @@ -54,18 +51,16 @@ class UploadNotificationManager(private val context: Context, private val viewTh setChannelId(NotificationUtils.NOTIFICATION_CHANNEL_UPLOAD) } } + private val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + init { notification = notificationBuilder.build() } @Suppress("MagicNumber") fun prepareForStart(upload: UploadFileOperation, pendingIntent: PendingIntent, startIntent: PendingIntent) { - notificationBuilder = NotificationUtils.newNotificationBuilder(context, viewThemeUtils).apply { - setSmallIcon(R.drawable.notification_icon) - setOngoing(true) - setTicker(context.getString(R.string.foreground_service_upload)) + notificationBuilder.run { setContentTitle(context.getString(R.string.uploader_upload_in_progress_ticker)) - setProgress(100, 0, false) setContentText( String.format( context.getString(R.string.uploader_upload_in_progress), @@ -73,6 +68,9 @@ class UploadNotificationManager(private val context: Context, private val viewTh upload.fileName ) ) + setTicker(context.getString(R.string.foreground_service_upload)) + setProgress(100, 0, false) + setOngoing(true) clearActions() addAction( @@ -81,10 +79,6 @@ class UploadNotificationManager(private val context: Context, private val viewTh pendingIntent ) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - setChannelId(NotificationUtils.NOTIFICATION_CHANNEL_UPLOAD) - } - setContentIntent(startIntent) } @@ -192,4 +186,18 @@ class UploadNotificationManager(private val context: Context, private val viewTh fun dismissWorkerNotifications() { notificationManager.cancel(ID) } + + fun notifyPaused(intent: PendingIntent) { + notificationBuilder.apply { + setContentTitle(context.getString(R.string.upload_global_pause_title)) + setTicker(context.getString(R.string.upload_global_pause_title)) + setOngoing(true) + setAutoCancel(false) + setProgress(0, 0, false) + clearActions() + setContentIntent(intent) + } + + showNotification() + } } diff --git a/app/src/main/java/com/nextcloud/client/preferences/AppPreferences.java b/app/src/main/java/com/nextcloud/client/preferences/AppPreferences.java index bb93d3fa5ec8..febd96c6215d 100644 --- a/app/src/main/java/com/nextcloud/client/preferences/AppPreferences.java +++ b/app/src/main/java/com/nextcloud/client/preferences/AppPreferences.java @@ -387,6 +387,10 @@ default void onDarkThemeModeChanged(DarkMode mode) { void setCalendarLastBackup(long timestamp); + boolean isGlobalUploadPaused(); + + void setGlobalUploadPaused(boolean globalPausedState); + void setPdfZoomTipShownCount(int count); int getPdfZoomTipShownCount(); diff --git a/app/src/main/java/com/nextcloud/client/preferences/AppPreferencesImpl.java b/app/src/main/java/com/nextcloud/client/preferences/AppPreferencesImpl.java index 4643f18dbc82..ac69794aafcb 100644 --- a/app/src/main/java/com/nextcloud/client/preferences/AppPreferencesImpl.java +++ b/app/src/main/java/com/nextcloud/client/preferences/AppPreferencesImpl.java @@ -107,6 +107,8 @@ public final class AppPreferencesImpl implements AppPreferences { private static final String PREF__CALENDAR_AUTOMATIC_BACKUP = "calendar_automatic_backup"; private static final String PREF__CALENDAR_LAST_BACKUP = "calendar_last_backup"; + private static final String PREF__GLOBAL_PAUSE_STATE = "global_pause_state"; + private static final String PREF__PDF_ZOOM_TIP_SHOWN = "pdf_zoom_tip_shown"; private static final String PREF__MEDIA_FOLDER_LAST_PATH = "media_folder_last_path"; @@ -741,6 +743,16 @@ public void setCalendarLastBackup(long timestamp) { preferences.edit().putLong(PREF__CALENDAR_LAST_BACKUP, timestamp).apply(); } + @Override + public boolean isGlobalUploadPaused() { + return preferences.getBoolean(PREF__GLOBAL_PAUSE_STATE,false); + } + + @Override + public void setGlobalUploadPaused(boolean globalPausedState) { + preferences.edit().putBoolean(PREF__GLOBAL_PAUSE_STATE, globalPausedState).apply(); + } + @Override public void setPdfZoomTipShownCount(int count) { preferences.edit().putInt(PREF__PDF_ZOOM_TIP_SHOWN, count).apply(); 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 359ea619d24b..61ad9629e005 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 @@ -1155,6 +1155,10 @@ public boolean isDrawerIndicatorAvailable() { return true; } + public AppPreferences getAppPreferences(){ + return preferences; + } + @Override protected void onStart() { super.onStart(); 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 6bf94c222d89..543dee7bb4bf 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 @@ -24,6 +24,7 @@ package com.owncloud.android.ui.activity; import android.accounts.Account; +import android.annotation.SuppressLint; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -49,7 +50,6 @@ import com.owncloud.android.databinding.UploadListLayoutBinding; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.datamodel.UploadsStorageManager; -import com.owncloud.android.db.OCUpload; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.utils.Log_OC; @@ -65,11 +65,11 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager; import androidx.recyclerview.widget.GridLayoutManager; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; /** - * Activity listing pending, active, and completed uploads. User can delete - * completed uploads from view. Content of this list of coming from - * {@link UploadsStorageManager}. + * Activity listing pending, active, and completed uploads. User can delete completed uploads from view. Content of this + * list of coming from {@link UploadsStorageManager}. */ public class UploadListActivity extends FileActivity { @@ -210,13 +210,17 @@ private void loadItems() { private void refresh() { backgroundJobManager.startImmediateFilesSyncJob(false, true); - if(uploadsStorageManager.getFailedUploads().length > 0){ - new Thread(() -> FileUploadHelper.Companion.instance().retryFailedUploads( - uploadsStorageManager, - connectivityService, - userAccountManager, - powerManagementService)) - .start(); + if (uploadsStorageManager.getFailedUploads().length > 0) { + new Thread(() -> { + FileUploadHelper.Companion.instance().retryFailedUploads( + uploadsStorageManager, + connectivityService, + accountManager, + powerManagementService); + this.runOnUiThread(() -> { + uploadListAdapter.loadUploadItemsFromDb(); + }); + }).start(); DisplayUtils.showSnackMessage(this, R.string.uploader_local_files_uploaded); } @@ -265,13 +269,47 @@ protected void onPause() { public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.activity_upload_list, menu); - + updateGlobalPauseIcon(menu.getItem(0)); return true; } + @SuppressFBWarnings("RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT") + private void updateGlobalPauseIcon(MenuItem pauseMenuItem) { + if (pauseMenuItem.getItemId() != R.id.action_toggle_global_pause) { + return; + } + + int iconId; + String title; + if (preferences.isGlobalUploadPaused()) { + iconId = R.drawable.ic_play; + title = getString(R.string.upload_action_global_upload_resume); + } else { + iconId = R.drawable.ic_pause; + title = getString(R.string.upload_action_global_upload_pause); + } + + pauseMenuItem.setIcon(iconId); + pauseMenuItem.setTitle(title); + } + + @SuppressLint("NotifyDataSetChanged") + private void toggleGlobalPause(MenuItem pauseMenuItem) { + preferences.setGlobalUploadPaused(!preferences.isGlobalUploadPaused()); + updateGlobalPauseIcon(pauseMenuItem); + + for (User user : accountManager.getAllUsers()) { + if (user != null) { + FileUploadHelper.Companion.instance().cancelAndRestartUploadJob(user); + } + } + + uploadListAdapter.notifyDataSetChanged(); + } + @Override public boolean onOptionsItemSelected(MenuItem item) { - boolean retval = true; + int itemId = item.getItemId(); if (itemId == android.R.id.home) { @@ -280,17 +318,13 @@ public boolean onOptionsItemSelected(MenuItem item) { } else { openDrawer(); } - } else if (itemId == R.id.action_clear_failed_uploads) { - for (OCUpload upload : uploadsStorageManager.getFailedButNotDelayedUploadsForCurrentAccount()){ - uploadListAdapter.cancelOldErrorNotification(upload); - } - uploadsStorageManager.clearFailedButNotDelayedUploads(); - uploadListAdapter.loadUploadItemsFromDb(); + } else if (itemId == R.id.action_toggle_global_pause) { + toggleGlobalPause(item); } else { - retval = super.onOptionsItemSelected(item); + return super.onOptionsItemSelected(item); } - return retval; + return true; } @Override 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 3a5e26fc9732..50168c5b0dc0 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 @@ -29,7 +29,6 @@ import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; -import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.net.Uri; import android.text.format.DateUtils; @@ -123,29 +122,55 @@ public void onBindHeaderViewHolder(SectionedViewHolder holder, int section, bool switch (group.type) { case CURRENT, FINISHED -> headerViewHolder.binding.uploadListAction.setImageResource(R.drawable.ic_close); - case FAILED -> headerViewHolder.binding.uploadListAction.setImageResource(R.drawable.ic_sync); + case FAILED -> headerViewHolder.binding.uploadListAction.setImageResource(R.drawable.ic_dots_vertical); } headerViewHolder.binding.uploadListAction.setOnClickListener(v -> { switch (group.type) { case CURRENT -> { + // cancel all current uploads for (OCUpload upload : group.getItems()) { uploadHelper.cancelFileUpload(upload.getRemotePath(), upload.getAccountName()); } + loadUploadItemsFromDb(); + } + case FINISHED -> { + // clear successfully uploaded section + uploadsStorageManager.clearSuccessfulUploads(); + loadUploadItemsFromDb(); } - case FINISHED -> uploadsStorageManager.clearSuccessfulUploads(); - case FAILED -> new Thread(() -> FileUploadHelper.Companion.instance().retryFailedUploads( - uploadsStorageManager, - connectivityService, - accountManager, - powerManagementService)).start(); - default -> { + case FAILED -> { + // show popup with option clear or retry filed uploads + createFailedPopupMenu(headerViewHolder); } - // do nothing } + }); + } + + private void createFailedPopupMenu(HeaderViewHolder headerViewHolder) { + PopupMenu failedPopup = new PopupMenu(MainApp.getAppContext(), headerViewHolder.binding.uploadListAction); + failedPopup.inflate(R.menu.upload_list_failed_options); + failedPopup.setOnMenuItemClickListener(i -> { + int itemId = i.getItemId(); + + if (itemId == R.id.action_upload_list_failed_clear) { + uploadsStorageManager.clearFailedButNotDelayedUploads(); + loadUploadItemsFromDb(); + } else { - loadUploadItemsFromDb(); + new Thread(() -> { + FileUploadHelper.Companion.instance().retryFailedUploads( + uploadsStorageManager, + connectivityService, + accountManager, + powerManagementService); + parentActivity.runOnUiThread(this::loadUploadItemsFromDb); + }).start(); + + } + return true; }); + failedPopup.show(); } @Override @@ -228,7 +253,7 @@ public void onBindViewHolder(SectionedViewHolder holder, int section, int relati // file size if (item.getFileSize() != 0) { itemViewHolder.binding.uploadFileSize.setText(String.format("%s, ", - DisplayUtils.bytesToHumanReadable(item.getFileSize()))); + DisplayUtils.bytesToHumanReadable(item.getFileSize()))); } else { itemViewHolder.binding.uploadFileSize.setText(""); } @@ -260,7 +285,6 @@ public void onBindViewHolder(SectionedViewHolder holder, int section, int relati itemViewHolder.binding.uploadRemotePath.setVisibility(View.VISIBLE); itemViewHolder.binding.uploadFileSize.setVisibility(View.VISIBLE); itemViewHolder.binding.uploadStatus.setVisibility(View.VISIBLE); - itemViewHolder.binding.uploadStatus.setTypeface(null, Typeface.NORMAL); itemViewHolder.binding.uploadProgressBar.setVisibility(View.GONE); // Update information depending of upload details @@ -304,9 +328,8 @@ public void onBindViewHolder(SectionedViewHolder holder, int section, int relati } // show status if same file conflict or local file deleted - if (item.getUploadStatus() == UploadStatus.UPLOAD_SUCCEEDED && item.getLastResult() != UploadResult.UPLOADED){ + if (item.getUploadStatus() == UploadStatus.UPLOAD_SUCCEEDED && item.getLastResult() != UploadResult.UPLOADED) { itemViewHolder.binding.uploadStatus.setVisibility(View.VISIBLE); - itemViewHolder.binding.uploadStatus.setTypeface(null, Typeface.BOLD); itemViewHolder.binding.uploadDate.setVisibility(View.GONE); itemViewHolder.binding.uploadFileSize.setVisibility(View.GONE); } @@ -373,16 +396,16 @@ public void onBindViewHolder(SectionedViewHolder holder, int section, int relati DisplayUtils.showSnackMessage( v.getRootView().findViewById(android.R.id.content), R.string.local_file_not_found_message - ); + ); } }); - } else if (item.getUploadStatus() == UploadStatus.UPLOAD_SUCCEEDED){ + } else if (item.getUploadStatus() == UploadStatus.UPLOAD_SUCCEEDED) { itemViewHolder.binding.uploadListItemLayout.setOnClickListener(v -> onUploadedItemClick(item)); } // click on thumbnail to open locally - if (item.getUploadStatus() != UploadStatus.UPLOAD_SUCCEEDED){ + if (item.getUploadStatus() != UploadStatus.UPLOAD_SUCCEEDED) { itemViewHolder.binding.thumbnail.setOnClickListener(v -> onUploadingItemClick(item)); } @@ -395,17 +418,17 @@ public void onBindViewHolder(SectionedViewHolder holder, int section, int relati fakeFileToCheatThumbnailsCacheManagerInterface.setMimeType(item.getMimeType()); boolean allowedToCreateNewThumbnail = ThumbnailsCacheManager.cancelPotentialThumbnailWork( - fakeFileToCheatThumbnailsCacheManagerInterface, itemViewHolder.binding.thumbnail - ); + fakeFileToCheatThumbnailsCacheManagerInterface, itemViewHolder.binding.thumbnail + ); // TODO this code is duplicated; refactor to a common place if (MimeTypeUtil.isImage(fakeFileToCheatThumbnailsCacheManagerInterface) - && fakeFileToCheatThumbnailsCacheManagerInterface.getRemoteId() != null && - item.getUploadStatus() == UploadStatus.UPLOAD_SUCCEEDED) { + && fakeFileToCheatThumbnailsCacheManagerInterface.getRemoteId() != null && + item.getUploadStatus() == UploadStatus.UPLOAD_SUCCEEDED) { // Thumbnail in Cache? Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache( - String.valueOf(fakeFileToCheatThumbnailsCacheManagerInterface.getRemoteId()) - ); + String.valueOf(fakeFileToCheatThumbnailsCacheManagerInterface.getRemoteId()) + ); if (thumbnail != null && !fakeFileToCheatThumbnailsCacheManagerInterface.isUpdateThumbnailNeeded()) { itemViewHolder.binding.thumbnail.setImageBitmap(thumbnail); } else { @@ -413,11 +436,11 @@ public void onBindViewHolder(SectionedViewHolder holder, int section, int relati Optional user = parentActivity.getUser(); if (allowedToCreateNewThumbnail && user.isPresent()) { final ThumbnailsCacheManager.ThumbnailGenerationTask task = - new ThumbnailsCacheManager.ThumbnailGenerationTask( - itemViewHolder.binding.thumbnail, - parentActivity.getStorageManager(), - user.get() - ); + new ThumbnailsCacheManager.ThumbnailGenerationTask( + itemViewHolder.binding.thumbnail, + parentActivity.getStorageManager(), + user.get() + ); if (thumbnail == null) { if (MimeTypeUtil.isVideo(fakeFileToCheatThumbnailsCacheManagerInterface)) { thumbnail = ThumbnailsCacheManager.mDefaultVideo; @@ -426,20 +449,20 @@ public void onBindViewHolder(SectionedViewHolder holder, int section, int relati } } final ThumbnailsCacheManager.AsyncThumbnailDrawable asyncDrawable = - new ThumbnailsCacheManager.AsyncThumbnailDrawable( - parentActivity.getResources(), - thumbnail, - task - ); + new ThumbnailsCacheManager.AsyncThumbnailDrawable( + parentActivity.getResources(), + thumbnail, + task + ); itemViewHolder.binding.thumbnail.setImageDrawable(asyncDrawable); task.execute(new ThumbnailsCacheManager.ThumbnailGenerationTaskObject( - fakeFileToCheatThumbnailsCacheManagerInterface, null)); + fakeFileToCheatThumbnailsCacheManagerInterface, null)); } } if ("image/png".equals(item.getMimeType())) { itemViewHolder.binding.thumbnail.setBackgroundColor(parentActivity.getResources() - .getColor(R.color.bg_default)); + .getColor(R.color.bg_default)); } @@ -447,14 +470,14 @@ public void onBindViewHolder(SectionedViewHolder holder, int section, int relati File file = new File(item.getLocalPath()); // Thumbnail in Cache? Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache( - String.valueOf(file.hashCode())); + String.valueOf(file.hashCode())); if (thumbnail != null) { itemViewHolder.binding.thumbnail.setImageBitmap(thumbnail); } else { // generate new Thumbnail if (allowedToCreateNewThumbnail) { final ThumbnailsCacheManager.ThumbnailGenerationTask task = - new ThumbnailsCacheManager.ThumbnailGenerationTask(itemViewHolder.binding.thumbnail); + new ThumbnailsCacheManager.ThumbnailGenerationTask(itemViewHolder.binding.thumbnail); if (MimeTypeUtil.isVideo(file)) { thumbnail = ThumbnailsCacheManager.mDefaultVideo; @@ -474,7 +497,7 @@ public void onBindViewHolder(SectionedViewHolder holder, int section, int relati if ("image/png".equalsIgnoreCase(item.getMimeType())) { itemViewHolder.binding.thumbnail.setBackgroundColor(parentActivity.getResources() - .getColor(R.color.bg_default)); + .getColor(R.color.bg_default)); } } else { if (optionalUser.isPresent()) { @@ -605,8 +628,7 @@ private void openConflictActivity(OCFile file, OCUpload upload) { } /** - * Gets the status text to show to the user according to the status and last result of the - * the given upload. + * Gets the status text to show to the user according to the status and last result of the the given upload. * * @param upload Upload to describe. * @return Text describing the status of the given upload. @@ -614,30 +636,27 @@ private void openConflictActivity(OCFile file, OCUpload upload) { private String getStatusText(OCUpload upload) { String status; switch (upload.getUploadStatus()) { - case UPLOAD_IN_PROGRESS: + case UPLOAD_IN_PROGRESS -> { status = parentActivity.getString(R.string.uploads_view_later_waiting_to_upload); if (uploadHelper.isUploadingNow(upload)) { // really uploading, bind the progress bar to listen for progress updates status = parentActivity.getString(R.string.uploader_upload_in_progress_ticker); } - break; - - case UPLOAD_SUCCEEDED: - if (upload.getLastResult() == UploadResult.SAME_FILE_CONFLICT){ + if (parentActivity.getAppPreferences().isGlobalUploadPaused()) { + status = parentActivity.getString(R.string.upload_global_pause_title); + } + } + case UPLOAD_SUCCEEDED -> { + if (upload.getLastResult() == UploadResult.SAME_FILE_CONFLICT) { status = parentActivity.getString(R.string.uploads_view_upload_status_succeeded_same_file); - }else if (upload.getLastResult() == UploadResult.FILE_NOT_FOUND) { + } else if (upload.getLastResult() == UploadResult.FILE_NOT_FOUND) { status = getUploadFailedStatusText(upload.getLastResult()); } else { status = parentActivity.getString(R.string.uploads_view_upload_status_succeeded); } - break; - - case UPLOAD_FAILED: - status = getUploadFailedStatusText(upload.getLastResult()); - break; - - default: - status = "Uncontrolled status: " + upload.getUploadStatus(); + } + case UPLOAD_FAILED -> status = getUploadFailedStatusText(upload.getLastResult()); + default -> status = "Uncontrolled status: " + upload.getUploadStatus(); } return status; } @@ -690,8 +709,8 @@ private String getUploadFailedStatusText(UploadResult result) { case SSL_RECOVERABLE_PEER_UNVERIFIED: status = parentActivity.getString( - R.string.uploads_view_upload_status_failed_ssl_certificate_not_trusted - ); + R.string.uploads_view_upload_status_failed_ssl_certificate_not_trusted + ); break; case UNKNOWN: status = parentActivity.getString(R.string.uploads_view_upload_status_unknown_fail); @@ -701,7 +720,7 @@ private String getUploadFailedStatusText(UploadResult result) { break; case DELAYED_IN_POWER_SAVE_MODE: status = parentActivity.getString( - R.string.uploads_view_upload_status_waiting_exit_power_save_mode); + R.string.uploads_view_upload_status_waiting_exit_power_save_mode); break; case VIRUS_DETECTED: status = parentActivity.getString(R.string.uploads_view_upload_status_virus_detected); @@ -776,17 +795,17 @@ private void onUploadingItemClick(OCUpload file) { */ private void onUploadedItemClick(OCUpload upload) { final OCFile file = parentActivity.getStorageManager().getFileByEncryptedRemotePath(upload.getRemotePath()); - if (file == null){ + if (file == null) { DisplayUtils.showSnackMessage(parentActivity, R.string.error_retrieving_file); Log_OC.i(TAG, "Could not find uploaded file on remote."); return; } - if (PreviewImageFragment.canBePreviewed(file)){ + if (PreviewImageFragment.canBePreviewed(file)) { //show image preview and stay in uploads tab Intent intent = FileDisplayActivity.openFileIntent(parentActivity, parentActivity.getUser().get(), file); parentActivity.startActivity(intent); - }else{ + } else { Intent intent = new Intent(parentActivity, FileDisplayActivity.class); intent.setAction(Intent.ACTION_VIEW); intent.putExtra(FileDisplayActivity.KEY_FILE_PATH, upload.getRemotePath()); @@ -882,14 +901,16 @@ private int getGroupItemCount() { } } - public void cancelOldErrorNotification(OCUpload upload){ + public void cancelOldErrorNotification(OCUpload upload) { if (mNotificationManager == null) { mNotificationManager = (NotificationManager) parentActivity.getSystemService(parentActivity.NOTIFICATION_SERVICE); } - if (upload == null) return; - mNotificationManager.cancel(NotificationUtils.createUploadNotificationTag(upload.getRemotePath(),upload.getLocalPath()), + if (upload == null) { + return; + } + mNotificationManager.cancel(NotificationUtils.createUploadNotificationTag(upload.getRemotePath(), upload.getLocalPath()), FileUploadWorker.NOTIFICATION_ERROR_ID); } diff --git a/app/src/main/res/drawable/ic_pause.xml b/app/src/main/res/drawable/ic_pause.xml new file mode 100644 index 000000000000..3ee93a2b3321 --- /dev/null +++ b/app/src/main/res/drawable/ic_pause.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_play.xml b/app/src/main/res/drawable/ic_play.xml new file mode 100644 index 000000000000..5557cd3b7fae --- /dev/null +++ b/app/src/main/res/drawable/ic_play.xml @@ -0,0 +1,30 @@ + + + + + diff --git a/app/src/main/res/menu/activity_upload_list.xml b/app/src/main/res/menu/activity_upload_list.xml index 0f9c66208263..ee99cf634ff9 100644 --- a/app/src/main/res/menu/activity_upload_list.xml +++ b/app/src/main/res/menu/activity_upload_list.xml @@ -16,13 +16,16 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . --> - + + android:id="@+id/action_toggle_global_pause" + android:icon="@android:drawable/ic_media_pause" + android:title="@string/upload_action_global_upload_pause" + app:showAsAction="always" /> diff --git a/app/src/main/res/menu/upload_list_failed_options.xml b/app/src/main/res/menu/upload_list_failed_options.xml new file mode 100644 index 000000000000..9d263e8bd41a --- /dev/null +++ b/app/src/main/res/menu/upload_list_failed_options.xml @@ -0,0 +1,32 @@ + + + + + + + diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml index 08bf64d552ad..0c23e15f1550 100644 --- a/app/src/main/res/values-night/colors.xml +++ b/app/src/main/res/values-night/colors.xml @@ -33,6 +33,7 @@ #818181 #222222 #ffffff + #EAE0E5 @color/appbar diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 58fcdecf1fc2..b18023e7d3df 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -59,6 +59,7 @@ #000000 #ededed #000000 + #1D1B1E #ffffff #ffffff diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 275d65f20701..6fd30bc9360f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -522,8 +522,6 @@ %1$s (conversation) on %1$s - Clear failed uploads - Grid view List view @@ -845,9 +843,14 @@ Sync conflict, please resolve manually Cannot create local file File could not be copied to local storage + All uploads are paused Storage quota exceeded Server not available Delete entries + Retry failed uploads + Clear failed uploads + Pause all uploads + Resume all uploads Dismiss notification Clear all notifications Loading is taking longer than expected