Jelajahi Sumber

Add ability to toggle file lock from file context menu

Signed-off-by: Álvaro Brey Vilas <alvaro.brey@nextcloud.com>
Álvaro Brey Vilas 3 tahun lalu
induk
melakukan
356db04aa8

+ 25 - 1
app/src/main/java/com/owncloud/android/files/FileMenuFilter.java

@@ -201,6 +201,7 @@ public class FileMenuFilter {
         filterUnsetEncrypted(toShow, toHide, endToEndEncryptionEnabled);
         filterSetPictureAs(toShow, toHide);
         filterStream(toShow, toHide);
+        filterLock(toShow, toHide);
     }
 
     private void filterShareFile(List<Integer> toShow, List<Integer> toHide, OCCapability capability) {
@@ -252,9 +253,23 @@ public class FileMenuFilter {
         }
     }
 
+    private void filterLock(List<Integer> toShow, List<Integer> toHide) {
+        // TODO only allow locking files (not dirs), and only allow unlocking file if lock is owned by the user
+        if (files.isEmpty()) {
+            toHide.add(R.id.action_lock_file);
+            toHide.add(R.id.action_unlock_file);
+        } else if (allLocked()) {
+            toHide.add(R.id.action_lock_file);
+            toShow.add(R.id.action_unlock_file);
+        } else {
+            toHide.add(R.id.action_unlock_file);
+            toShow.add(R.id.action_lock_file);
+        }
+    }
+
     private void filterEncrypt(List<Integer> toShow, List<Integer> toHide, boolean endToEndEncryptionEnabled) {
         if (files.isEmpty() || !isSingleSelection() || isSingleFile() || isEncryptedFolder()
-                || !endToEndEncryptionEnabled) {
+            || !endToEndEncryptionEnabled) {
             toHide.add(R.id.action_encrypted);
         } else {
             toShow.add(R.id.action_encrypted);
@@ -572,4 +587,13 @@ public class FileMenuFilter {
         }
         return true;
     }
+
+    private boolean allLocked() {
+        for (OCFile file : files) {
+            if (!file.isLocked()) {
+                return false;
+            }
+        }
+        return true;
+    }
 }

+ 22 - 0
app/src/main/java/com/owncloud/android/ui/events/FileLockEvent.kt

@@ -0,0 +1,22 @@
+/**
+ * Nextcloud Android client application
+ *
+ * @author Álvaro
+ * Copyright (C) 2017 Mario Danic
+ *
+ * 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 <http:></http:>//www.gnu.org/licenses/>.
+ */
+package com.owncloud.android.ui.events
+
+data class FileLockEvent(val filePath: String, val shouldLock: Boolean)

+ 38 - 0
app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java

@@ -48,6 +48,7 @@ import com.google.android.material.appbar.AppBarLayout;
 import com.google.android.material.behavior.HideBottomViewOnScrollBehavior;
 import com.google.android.material.floatingactionbutton.FloatingActionButton;
 import com.google.android.material.snackbar.Snackbar;
+import com.nextcloud.android.lib.resources.files.ToggleFileLockRemoteOperation;
 import com.nextcloud.android.lib.richWorkspace.RichWorkspaceDirectEditingRemoteOperation;
 import com.nextcloud.client.account.User;
 import com.nextcloud.client.account.UserAccountManager;
@@ -56,6 +57,7 @@ import com.nextcloud.client.di.Injectable;
 import com.nextcloud.client.network.ClientFactory;
 import com.nextcloud.client.preferences.AppPreferences;
 import com.nextcloud.client.utils.Throttler;
+import com.nextcloud.common.NextcloudClient;
 import com.owncloud.android.MainApp;
 import com.owncloud.android.R;
 import com.owncloud.android.datamodel.ArbitraryDataProvider;
@@ -92,6 +94,7 @@ import com.owncloud.android.ui.events.ChangeMenuEvent;
 import com.owncloud.android.ui.events.CommentsEvent;
 import com.owncloud.android.ui.events.EncryptionEvent;
 import com.owncloud.android.ui.events.FavoriteEvent;
+import com.owncloud.android.ui.events.FileLockEvent;
 import com.owncloud.android.ui.events.SearchEvent;
 import com.owncloud.android.ui.helpers.FileOperationsHelper;
 import com.owncloud.android.ui.interfaces.OCFileListFragmentInterface;
@@ -1146,6 +1149,10 @@ public class OCFileListFragment extends ExtendedListFragment implements
             } else if (itemId == R.id.action_unset_encrypted) {
                 mContainerActivity.getFileOperationsHelper().toggleEncryption(singleFile, false);
                 return true;
+            } else if (itemId == R.id.action_lock_file) {
+                mContainerActivity.getFileOperationsHelper().toggleFileLock(singleFile, true);
+            } else if (itemId == R.id.action_unlock_file) {
+                mContainerActivity.getFileOperationsHelper().toggleFileLock(singleFile, false);
             }
         }
 
@@ -1192,6 +1199,8 @@ public class OCFileListFragment extends ExtendedListFragment implements
         } else if (itemId == R.id.action_send_file) {
             mContainerActivity.getFileOperationsHelper().sendFiles(checkedFiles);
             return true;
+        } else if (itemId == R.id.action_lock_file) {
+            // TODO call lock API
         }
 
         return false;
@@ -1642,6 +1651,35 @@ public class OCFileListFragment extends ExtendedListFragment implements
         }
     }
 
+    @Subscribe(threadMode = ThreadMode.BACKGROUND)
+    public void onMessageEvent(FileLockEvent event) {
+        final User user = accountManager.getUser();
+
+        try {
+            new Handler(Looper.getMainLooper()).post(() -> setLoading(true));
+            NextcloudClient client = clientFactory.createNextcloudClient(user);
+            ToggleFileLockRemoteOperation operation = new ToggleFileLockRemoteOperation(event.getShouldLock(), event.getFilePath());
+            RemoteOperationResult<Void> result = operation.execute(client);
+
+            if (result.isSuccess()) {
+                // TODO only refresh the modified file?
+                new Handler(Looper.getMainLooper()).post(this::onRefresh);
+            } else {
+                Snackbar.make(getRecyclerView(),
+                              R.string.error_file_lock,
+                              Snackbar.LENGTH_LONG).show();
+            }
+
+        } catch (ClientFactory.CreationException e) {
+            Log_OC.e(TAG, "Cannot create client", e);
+            Snackbar.make(getRecyclerView(),
+                          R.string.error_file_lock,
+                          Snackbar.LENGTH_LONG).show();
+        } finally {
+            new Handler(Looper.getMainLooper()).post(() -> setLoading(false));
+        }
+    }
+
     protected void setTitle(@StringRes final int title) {
         setTitle(getContext().getString(title));
     }

+ 7 - 0
app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java

@@ -79,6 +79,7 @@ import com.owncloud.android.ui.dialog.SendFilesDialog;
 import com.owncloud.android.ui.dialog.SendShareDialog;
 import com.owncloud.android.ui.events.EncryptionEvent;
 import com.owncloud.android.ui.events.FavoriteEvent;
+import com.owncloud.android.ui.events.FileLockEvent;
 import com.owncloud.android.ui.events.SyncEventFinished;
 import com.owncloud.android.utils.DisplayUtils;
 import com.owncloud.android.utils.FileStorageUtils;
@@ -918,6 +919,12 @@ public class FileOperationsHelper {
         }
     }
 
+    public void toggleFileLock(OCFile file, boolean shouldBeLocked) {
+        if (file.isLocked() != shouldBeLocked) {
+            EventBus.getDefault().post(new FileLockEvent(file.getRemotePath(), shouldBeLocked));
+        }
+    }
+
     public void renameFile(OCFile file, String newFilename) {
         // RenameFile
         Intent service = new Intent(fileActivity, OperationsService.class);

+ 18 - 28
app/src/main/res/layout/list_item.xml

@@ -80,21 +80,6 @@
             app:layout_constraintStart_toEndOf="@+id/thumbnail_layout"
             app:layout_constraintTop_toTopOf="@+id/thumbnail_layout" />
 
-        <ImageView
-            android:id="@+id/lock_indicator"
-            android:layout_width="@dimen/list_item_local_file_indicator_layout_width"
-            android:layout_height="@dimen/list_item_local_file_indicator_layout_width"
-            android:contentDescription="@string/file_locked"
-            android:src="@drawable/ic_lock_white"
-            android:visibility="gone"
-            app:tint="@color/secondary_text_color"
-            tools:visibility="visible"
-            app:layout_constraintBottom_toBottomOf="@+id/thumbnail_layout"
-            app:layout_constraintEnd_toStartOf="@+id/thumbnail_layout"
-            app:layout_constraintStart_toStartOf="@+id/thumbnail_layout"
-            app:layout_constraintTop_toBottomOf="@+id/thumbnail_layout" />
-
-
     </androidx.constraintlayout.widget.ConstraintLayout>
 
     <LinearLayout
@@ -153,10 +138,11 @@
 
     </LinearLayout>
 
-    <RelativeLayout
+    <LinearLayout
         android:layout_width="wrap_content"
         android:layout_height="match_parent"
-        android:layout_gravity="center_vertical"
+        android:gravity="center_vertical|end"
+        android:orientation="horizontal"
         android:paddingStart="@dimen/standard_half_padding"
         android:paddingEnd="@dimen/zero">
 
@@ -164,7 +150,6 @@
             android:id="@+id/unreadComments"
             android:layout_width="wrap_content"
             android:layout_height="match_parent"
-            android:layout_centerVertical="true"
             android:clickable="true"
             android:contentDescription="@string/unread_comments"
             android:focusable="true"
@@ -177,8 +162,6 @@
             android:id="@+id/sharedIcon"
             android:layout_width="48dp"
             android:layout_height="match_parent"
-            android:layout_centerVertical="true"
-            android:layout_toEndOf="@id/unreadComments"
             android:clickable="true"
             android:contentDescription="@string/shared_icon_share"
             android:focusable="true"
@@ -189,19 +172,28 @@
         <com.owncloud.android.ui.AvatarGroupLayout
             android:id="@+id/sharedAvatars"
             android:layout_width="100dp"
+            android:gravity="center_vertical"
             android:layout_height="@dimen/file_icon_size"
-            android:layout_alignEnd="@id/sharedIcon"
-            android:layout_centerVertical="true"
-            android:layout_toEndOf="@id/sharedIcon"
             android:contentDescription="@string/shared_avatar_desc"
             android:visibility="visible" />
 
+        <ImageButton
+            android:id="@+id/lock_indicator"
+            android:layout_width="48dp"
+            android:layout_height="48dp"
+            android:contentDescription="@string/shared_icon_share"
+            android:background="?selectableItemBackgroundBorderless"
+            android:paddingStart="@dimen/list_item_share_right_margin"
+            android:paddingEnd="@dimen/list_item_share_right_margin"
+            android:src="@drawable/ic_lock_white"
+            android:visibility="gone"
+            app:tint="#666666"
+            tools:visibility="visible" />
+
         <ImageView
             android:id="@+id/custom_checkbox"
             android:layout_width="wrap_content"
             android:layout_height="match_parent"
-            android:layout_centerVertical="true"
-            android:layout_toEndOf="@id/sharedAvatars"
             android:clickable="false"
             android:contentDescription="@string/checkbox"
             android:focusable="false"
@@ -213,8 +205,6 @@
             android:id="@+id/overflow_menu"
             android:layout_width="48dp"
             android:layout_height="match_parent"
-            android:layout_centerVertical="true"
-            android:layout_toEndOf="@id/custom_checkbox"
             android:clickable="true"
             android:contentDescription="@string/overflow_menu"
             android:focusable="true"
@@ -222,5 +212,5 @@
             android:paddingEnd="12dp"
             android:src="@drawable/ic_dots_vertical" />
 
-    </RelativeLayout>
+    </LinearLayout>
 </LinearLayout>

+ 12 - 0
app/src/main/res/menu/item_file.xml

@@ -40,6 +40,18 @@
         app:showAsAction="never"
         android:showAsAction="never" />
 
+    <item
+        android:id="@+id/action_lock_file"
+        android:title="@string/lock_file"
+        app:showAsAction="never"
+        android:showAsAction="never" />
+
+    <item
+        android:id="@+id/action_unlock_file"
+        android:title="@string/unlock_file"
+        app:showAsAction="never"
+        android:showAsAction="never" />
+
     <item
         android:id="@+id/action_see_details"
         android:title="@string/actionbar_see_details"

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

@@ -1009,4 +1009,7 @@
     <string name="storage_permission_full_access">Full access</string>
     <string name="storage_permission_media_read_only">Media read-only</string>
     <string name="file_locked">This file is locked</string>
+    <string name="lock_file">Lock</string>
+    <string name="unlock_file">Unlock</string>
+    <string name="error_file_lock">Error changing file lock status</string>
 </resources>