浏览代码

Implement global pause functionality

Signed-off-by: Jonas Mayer <jonas.a.mayer@gmx.net>
Jonas Mayer 1 年之前
父节点
当前提交
d96c0d1d9e

+ 1 - 0
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
         )

+ 1 - 1
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)

+ 10 - 0
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 {
@@ -141,6 +143,14 @@ class FileUploadWorker(
         var currentPage = uploadsStorageManager.getCurrentAndPendingUploadsForAccountPageAscById(-1, accountName)
 
         while (currentPage.isNotEmpty() && !isStopped) {
+            if (preferences.globalUploadPaused){
+                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)

+ 3 - 3
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
         )

+ 21 - 0
app/src/main/java/com/nextcloud/client/jobs/upload/UploadNotificationManager.kt

@@ -37,6 +37,7 @@ import com.owncloud.android.utils.theme.ViewThemeUtils
 
 class UploadNotificationManager(private val context: Context, private val viewThemeUtils: ViewThemeUtils) {
     companion object {
+
         private const val ID = 411
     }
 
@@ -192,4 +193,24 @@ class UploadNotificationManager(private val context: Context, private val viewTh
     fun dismissWorkerNotifications() {
         notificationManager.cancel(ID)
     }
+
+    fun notifyPaused(startIntent: PendingIntent) {
+        notificationBuilder = NotificationUtils.newNotificationBuilder(context, viewThemeUtils).apply {
+            setSmallIcon(R.drawable.notification_icon)
+            setOngoing(true)
+            setTicker(context.getString(R.string.upload_global_pause))
+            setContentTitle(context.getString(R.string.upload_global_pause_title))
+            setContentText(context.getString(R.string.upload_global_pause))
+            clearActions()
+
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+                setChannelId(NotificationUtils.NOTIFICATION_CHANNEL_UPLOAD)
+            }
+
+            setContentIntent(startIntent)
+        }
+
+
+        showNotification()
+    }
 }

+ 4 - 0
app/src/main/java/com/nextcloud/client/preferences/AppPreferences.java

@@ -387,6 +387,10 @@ public interface AppPreferences {
 
     void setCalendarLastBackup(long timestamp);
 
+    boolean getGlobalUploadPaused();
+
+    void setGlobalUploadPaused(boolean globalPausedState);
+
     void setPdfZoomTipShownCount(int count);
 
     int getPdfZoomTipShownCount();

+ 12 - 0
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 final class AppPreferencesImpl implements AppPreferences {
         preferences.edit().putLong(PREF__CALENDAR_LAST_BACKUP, timestamp).apply();
     }
 
+    @Override
+    public boolean getGlobalUploadPaused() {
+        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();

+ 31 - 15
app/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java

@@ -211,12 +211,16 @@ public class UploadListActivity extends FileActivity {
         backgroundJobManager.startImmediateFilesSyncJob(false, true);
 
         if(uploadsStorageManager.getFailedUploads().length > 0){
-            new Thread(() -> FileUploadHelper.Companion.instance().retryFailedUploads(
-                uploadsStorageManager,
-                connectivityService,
-                userAccountManager,
-                powerManagementService))
-                .start();
+            new Thread(() -> {
+                FileUploadHelper.Companion.instance().retryFailedUploads(
+                    uploadsStorageManager,
+                    connectivityService,
+                    accountManager,
+                    powerManagementService);
+                this.runOnUiThread(() -> {
+                    uploadListAdapter.loadUploadItemsFromDb();
+                });
+            }).start();
             DisplayUtils.showSnackMessage(this, R.string.uploader_local_files_uploaded);
         }
 
@@ -266,12 +270,21 @@ public class UploadListActivity extends FileActivity {
         MenuInflater inflater = getMenuInflater();
         inflater.inflate(R.menu.activity_upload_list, menu);
 
+        if (menu.getItem(0).getItemId() == R.id.action_toogle_global_pause){
+            if (preferences.getGlobalUploadPaused()){
+                menu.getItem(0).setIcon(android.R.drawable.ic_media_play);
+            }else{
+                menu.getItem(0).setIcon(android.R.drawable.ic_media_pause);
+            }
+
+        }
+
         return true;
     }
 
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
-        boolean retval = true;
+
         int itemId = item.getItemId();
 
         if (itemId == android.R.id.home) {
@@ -280,17 +293,20 @@ public class UploadListActivity extends FileActivity {
             } else {
                 openDrawer();
             }
-        } else if (itemId == R.id.action_clear_failed_uploads) {
-            for (OCUpload upload : uploadsStorageManager.getFailedButNotDelayedUploadsForCurrentAccount()){
-                uploadListAdapter.cancelOldErrorNotification(upload);
+        } else if (itemId == R.id.action_toogle_global_pause) {
+            preferences.setGlobalUploadPaused(!preferences.getGlobalUploadPaused());
+            if (preferences.getGlobalUploadPaused()){
+                item.setIcon(android.R.drawable.ic_media_play);
+            }else{
+                item.setIcon(android.R.drawable.ic_media_pause);
+            }
+
+            for (User user: accountManager.getAllUsers()){
+                if (user != null) FileUploadHelper.Companion.instance().cancelAndRestartUploadJob(user);
             }
-            uploadsStorageManager.clearFailedButNotDelayedUploads();
-            uploadListAdapter.loadUploadItemsFromDb();
-        } else {
-            retval = super.onOptionsItemSelected(item);
         }
 
-        return retval;
+        return true;
     }
 
     @Override

+ 39 - 12
app/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java

@@ -122,29 +122,56 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter<SectionedVie
         headerViewHolder.binding.uploadListTitle.setOnClickListener(v -> toggleSectionExpanded(section));
 
         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 CURRENT: case FINISHED:
+                headerViewHolder.binding.uploadListAction.setImageResource(R.drawable.ic_close);
+                break;
+            case FAILED:
+                headerViewHolder.binding.uploadListAction.setImageResource(R.drawable.ic_dots_vertical);
+                break;
+
         }
 
         headerViewHolder.binding.uploadListAction.setOnClickListener(v -> {
+            PopupMenu popup;
+            final Optional<User> optionalUser = parentActivity.getUser();
             switch (group.type) {
                 case CURRENT -> {
                     for (OCUpload upload : group.getItems()) {
                         uploadHelper.cancelFileUpload(upload.getRemotePath(), upload.getAccountName());
                     }
+                    loadUploadItemsFromDb();
                 }
-                case FINISHED -> uploadsStorageManager.clearSuccessfulUploads();
-                case FAILED -> new Thread(() -> FileUploadHelper.Companion.instance().retryFailedUploads(
-                    uploadsStorageManager,
-                    connectivityService,
-                    accountManager,
-                    powerManagementService)).start();
-                default -> {
+                case FINISHED -> {
+                    uploadsStorageManager.clearSuccessfulUploads();
+                    loadUploadItemsFromDb();
                 }
-                // do nothing
-            }
+                case FAILED -> {
+                    popup = new PopupMenu(MainApp.getAppContext(), headerViewHolder.binding.uploadListAction);
+                    popup.inflate(R.menu.upload_list_failed_options);
+                    popup.setOnMenuItemClickListener(i -> {
+                        int itemId = i.getItemId();
+
+                        if (itemId == R.id.action_upload_list_failed_clear) {
+                            uploadsStorageManager.clearFailedButNotDelayedUploads();
+                            loadUploadItemsFromDb();
+                        } else {
+
+                            new Thread(() -> {
+                                FileUploadHelper.Companion.instance().retryFailedUploads(
+                                    uploadsStorageManager,
+                                    connectivityService,
+                                    accountManager,
+                                    powerManagementService);
+                                parentActivity.runOnUiThread(this::loadUploadItemsFromDb);
+                            }).start();
+
 
-            loadUploadItemsFromDb();
+                        }
+                        return true;
+                    });
+                    popup.show();
+                }
+            }
         });
     }
 

+ 6 - 3
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 <http://www.gnu.org/licenses/>.
 -->
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
+<menu xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:android="http://schemas.android.com/apk/res/android">
     <group
         android:id="@+id/upload_list_actions"
         android:checkableBehavior="none">
 
         <item
-            android:id="@+id/action_clear_failed_uploads"
-            android:title="@string/action_clear_failed_uploads" />
+            android:id="@+id/action_toogle_global_pause"
+            android:icon="@android:drawable/ic_media_pause"
+            android:title="@string/action_clear_failed_uploads"
+            app:showAsAction="always" />
     </group>
 </menu>

+ 32 - 0
app/src/main/res/menu/upload_list_failed_options.xml

@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Nextcloud Android client application
+
+ @author Jonas Mayer
+ Copyright (C) 2019 Jonas Mayer
+ Copyright (C) 2019 Nextcloud GmbH
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:id="@+id/action_upload_list_failed_retry"
+        android:icon="@drawable/ic_sync"
+        android:title="@string/upload_action_failed_retry" />
+
+    <item
+        android:id="@+id/action_upload_list_failed_clear"
+        android:title="@string/upload_action_failed_clear"
+        android:icon="@drawable/ic_close" />
+</menu>

+ 4 - 0
app/src/main/res/values/strings.xml

@@ -845,9 +845,13 @@
     <string name="upload_sync_conflict">Sync conflict, please resolve manually</string>
     <string name="upload_cannot_create_file">Cannot create local file</string>
     <string name="upload_local_storage_not_copied">File could not be copied to local storage</string>
+    <string name="upload_global_pause">Upload for all files is paused</string>
+    <string name="upload_global_pause_title">Upload paused</string>
     <string name="upload_quota_exceeded">Storage quota exceeded</string>
     <string name="host_not_available">Server not available</string>
     <string name="delete_entries">Delete entries</string>
+    <string name="upload_action_failed_retry">Retry failed uploads</string>
+    <string name="upload_action_failed_clear">Clear failed uploads</string>
     <string name="dismiss_notification_description">Dismiss notification</string>
     <string name="action_empty_notifications">Clear all notifications</string>
     <string name="timeout_richDocuments">Loading is taking longer than expected</string>