Эх сурвалжийг харах

Merge pull request #1451 from owncloud/share__edit_privileges

Share  edit privileges
Maria Asensio 9 жил өмнө
parent
commit
2e074864a2
31 өөрчлөгдсөн 1046 нэмэгдсэн , 179 устгасан
  1. 1 1
      owncloud-android-library
  2. BIN
      res/drawable-hdpi/ic_action_delete.png
  3. BIN
      res/drawable-hdpi/ic_navigate_next.png
  4. BIN
      res/drawable-mdpi/ic_action_delete.png
  5. BIN
      res/drawable-mdpi/ic_navigate_next.png
  6. BIN
      res/drawable-xhdpi/ic_action_delete.png
  7. BIN
      res/drawable-xhdpi/ic_navigate_next.png
  8. BIN
      res/drawable-xxhdpi/ic_action_delete.png
  9. BIN
      res/drawable-xxhdpi/ic_navigate_next.png
  10. BIN
      res/drawable-xxxhdpi/ic_action_delete.png
  11. BIN
      res/drawable-xxxhdpi/ic_navigate_next.png
  12. 83 0
      res/layout/edit_share_layout.xml
  13. 16 5
      res/layout/share_user_item.xml
  14. 2 0
      res/values/dims.xml
  15. 9 2
      res/values/strings.xml
  16. 95 40
      src/com/owncloud/android/datamodel/FileDataStorageManager.java
  17. 24 1
      src/com/owncloud/android/files/FileOperationsHelper.java
  18. 10 9
      src/com/owncloud/android/operations/CreateShareWithShareeOperation.java
  19. 115 0
      src/com/owncloud/android/operations/UpdateSharePermissionsOperation.java
  20. 5 5
      src/com/owncloud/android/operations/UpdateShareViaLinkOperation.java
  21. 13 5
      src/com/owncloud/android/services/OperationsService.java
  22. 52 11
      src/com/owncloud/android/ui/activity/FileActivity.java
  23. 1 26
      src/com/owncloud/android/ui/activity/FileDisplayActivity.java
  24. 1 26
      src/com/owncloud/android/ui/activity/FolderPickerActivity.java
  25. 57 3
      src/com/owncloud/android/ui/activity/ShareActivity.java
  26. 11 0
      src/com/owncloud/android/ui/adapter/ShareUserListAdapter.java
  27. 482 0
      src/com/owncloud/android/ui/fragment/EditShareFragment.java
  28. 10 16
      src/com/owncloud/android/ui/fragment/SearchShareesFragment.java
  29. 15 28
      src/com/owncloud/android/ui/fragment/ShareFileFragment.java
  30. 41 0
      src/com/owncloud/android/ui/fragment/ShareFragmentListener.java
  31. 3 1
      src/com/owncloud/android/utils/ErrorMessageAdapter.java

+ 1 - 1
owncloud-android-library

@@ -1 +1 @@
-Subproject commit 1b7a5690409b4c694fbff126791289b9720e29f7
+Subproject commit 95919c9005aac54e7707f0fdc3c5b9dfdb29dac1

BIN
res/drawable-hdpi/ic_action_delete.png


BIN
res/drawable-hdpi/ic_navigate_next.png


BIN
res/drawable-mdpi/ic_action_delete.png


BIN
res/drawable-mdpi/ic_navigate_next.png


BIN
res/drawable-xhdpi/ic_action_delete.png


BIN
res/drawable-xhdpi/ic_navigate_next.png


BIN
res/drawable-xxhdpi/ic_action_delete.png


BIN
res/drawable-xxhdpi/ic_navigate_next.png


BIN
res/drawable-xxxhdpi/ic_action_delete.png


BIN
res/drawable-xxxhdpi/ic_navigate_next.png


+ 83 - 0
res/layout/edit_share_layout.xml

@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ownCloud Android client application
+
+  @author David A. Velasco
+  Copyright (C) 2015 ownCloud Inc.
+
+  This program is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License version 2,
+  as published by the Free Software Foundation.
+
+  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 General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+-->
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+            xmlns:tools="http://schemas.android.com/tools"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            tools:context="com.owncloud.android.ui.fragment.EditShareFragment"
+            android:id="@+id/shareScroll">
+
+    <LinearLayout android:orientation="vertical"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content">
+
+        <Switch
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textSize="@dimen/two_line_primary_text_size"
+            android:text="@string/share_privilege_can_edit"
+            android:id="@+id/canEditSwitch"
+            android:layout_gravity="start"
+            android:padding="@dimen/standard_half_padding"
+            android:layout_marginTop="@dimen/standard_half_margin"
+            android:background="@color/actionbar_start_color"
+            android:textColor="@color/white"
+            />
+
+        <CheckBox
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:id="@+id/canEditCreateCheckBox"
+            android:text="@string/share_privilege_can_edit_create"
+            android:visibility="gone"
+            />
+
+        <CheckBox
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:id="@+id/canEditChangeCheckBox"
+            android:text="@string/share_privilege_can_edit_change"
+            android:visibility="gone"
+            />
+
+        <CheckBox
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:id="@+id/canEditDeleteCheckBox"
+            android:text="@string/share_privilege_can_edit_delete"
+            android:visibility="gone"
+            />
+
+        <Switch
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textSize="@dimen/two_line_primary_text_size"
+            android:text="@string/share_privilege_can_share"
+            android:id="@+id/canShareSwitch"
+            android:layout_gravity="start"
+            android:padding="@dimen/standard_half_padding"
+            android:layout_marginTop="@dimen/standard_half_margin"
+            android:background="@color/actionbar_start_color"
+            android:textColor="@color/white"
+            />
+
+    </LinearLayout>
+
+</ScrollView>

+ 16 - 5
res/layout/share_user_item.xml

@@ -29,8 +29,8 @@
         <TextView
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_weight="0.9"
-            android:textSize="16dip"
+            android:layout_weight="0.8"
+            android:textSize="@dimen/two_line_primary_text_size"
             android:text="@string/username"
             android:id="@+id/userOrGroupName"
             android:layout_margin="12dp"
@@ -43,10 +43,21 @@
             android:layout_height="match_parent"
             android:layout_weight="0.1"
             android:id="@+id/unshareButton"
-            android:src="@drawable/ic_cancel"
-            android:layout_marginRight="8dp"
-            android:layout_marginLeft="4dp"
+            android:src="@drawable/ic_action_delete"
+            android:layout_marginRight="@dimen/standard_margin"
+            android:layout_marginEnd="@dimen/standard_margin"
             android:layout_gravity="center_horizontal"/>
+
+        <ImageView
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_weight="0.1"
+            android:id="@+id/editShareButton"
+            android:src="@drawable/ic_navigate_next"
+            android:layout_marginRight="@dimen/standard_margin"
+            android:layout_marginEnd="@dimen/standard_margin"
+            android:layout_gravity="center_horizontal"/>
+
     </LinearLayout>
 
     <View

+ 2 - 0
res/values/dims.xml

@@ -25,7 +25,9 @@
     <dimen name="file_icon_size">32dp</dimen>
     <dimen name="file_icon_size_grid">128dp</dimen>
     <dimen name="standard_padding">16dp</dimen>
+    <dimen name="standard_half_padding">8dp</dimen>
     <dimen name="standard_margin">16dp</dimen>
+    <dimen name="standard_half_margin">8dp</dimen>
 
     <dimen name="two_line_primary_text_size">16sp</dimen>
     <dimen name="two_line_secondary_text_size">14sp</dimen>

+ 9 - 2
res/values/strings.xml

@@ -287,7 +287,7 @@
 	<string name="unshare_link_file_no_exist">Unable to unshare. Please check whether the file exists</string>
 	<string name="unshare_link_file_error">An error occurred while trying to unshare this file or folder</string>
     <string name="update_link_file_no_exist">Unable to update. Please check whether the file exists </string>
-    <string name="update_link_file_error">An error occurred while trying to update the shared link</string>
+    <string name="update_link_file_error">An error occurred while trying to update the share</string>
     <string name="share_link_password_title">Enter a password</string>
     <string name="share_link_empty_password">You must enter a password</string>
 
@@ -309,7 +309,7 @@
     <string name="forbidden_permissions_delete">to delete this file</string>
     <string name="share_link_forbidden_permissions">to share this file</string>
     <string name="unshare_link_forbidden_permissions">to unshare this file</string>
-    <string name="update_link_forbidden_permissions">to update this shared link</string>
+    <string name="update_link_forbidden_permissions">to update this share</string>
     <string name="forbidden_permissions_create">to create the file</string>
     <string name="uploader_upload_forbidden_permissions">to upload in this folder</string>
     <string name="downloader_download_file_not_found">The file is no longer available on the server</string>
@@ -394,6 +394,13 @@
 
     <string name="share_sharee_unavailable">Sorry, your server version does not allow share with users within clients.
         \nPlease contact your administrator</string>
+    <string name="share_privilege_can_share">can share</string>
+    <string name="share_privilege_can_edit">can edit</string>
+    <string name="share_privilege_can_edit_create">create</string>
+    <string name="share_privilege_can_edit_change">change</string>
+    <string name="share_privilege_can_edit_delete">delete</string>
+    <string name="edit_share_unshare">Stop sharing</string>
+    <string name="edit_share_done">done</string>
 
     <string name="action_switch_grid_view">Switch to grid view</string>
     <string name="action_switch_list_view">Switch to list view</string>

+ 95 - 40
src/com/owncloud/android/datamodel/FileDataStorageManager.java

@@ -100,7 +100,7 @@ public class FileDataStorageManager {
 
 
     public OCFile getFileByPath(String path) {
-        Cursor c = getCursorForValue(ProviderTableMeta.FILE_PATH, path);
+        Cursor c = getFileCursorForValue(ProviderTableMeta.FILE_PATH, path);
         OCFile file = null;
         if (c.moveToFirst()) {
             file = createFileInstance(c);
@@ -114,7 +114,7 @@ public class FileDataStorageManager {
 
 
     public OCFile getFileById(long id) {
-        Cursor c = getCursorForValue(ProviderTableMeta._ID, String.valueOf(id));
+        Cursor c = getFileCursorForValue(ProviderTableMeta._ID, String.valueOf(id));
         OCFile file = null;
         if (c.moveToFirst()) {
             file = createFileInstance(c);
@@ -124,7 +124,7 @@ public class FileDataStorageManager {
     }
 
     public OCFile getFileByLocalPath(String path) {
-        Cursor c = getCursorForValue(ProviderTableMeta.FILE_STORAGE_PATH, path);
+        Cursor c = getFileCursorForValue(ProviderTableMeta.FILE_STORAGE_PATH, path);
         OCFile file = null;
         if (c.moveToFirst()) {
             file = createFileInstance(c);
@@ -831,7 +831,7 @@ public class FileDataStorageManager {
         return retval;
     }
 
-    private Cursor getCursorForValue(String key, String value) {
+    private Cursor getFileCursorForValue(String key, String value) {
         Cursor c = null;
         if (getContentResolver() != null) {
             c = getContentResolver()
@@ -936,7 +936,7 @@ public class FileDataStorageManager {
         cv.put(ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED, share.getRemoteId());
         cv.put(ProviderTableMeta.OCSHARES_ACCOUNT_OWNER, mAccount.name);
 
-        if (shareExists(share.getRemoteId())) {// for renamed files; no more delete and create
+        if (shareExistsForRemoteId(share.getRemoteId())) {// for renamed files; no more delete and create
             overriden = true;
             if (getContentResolver() != null) {
                 getContentResolver().update(ProviderTableMeta.CONTENT_URI_SHARE, cv,
@@ -978,6 +978,95 @@ public class FileDataStorageManager {
         return overriden;
     }
 
+    /**
+     * Retrieves an stored {@link OCShare} given its id.
+     *
+     * @param id    Identifier.
+     * @return      Stored {@link OCShare} given its id.
+     */
+    public OCShare getShareById(long id) {
+        OCShare share = null;
+        Cursor c = getShareCursorForValue(
+                ProviderTableMeta._ID,
+                String.valueOf(id)
+        );
+        if (c != null) {
+            if (c.moveToFirst()) {
+                share = createShareInstance(c);
+            }
+            c.close();
+        }
+        return share;
+    }
+
+
+    /**
+     * Checks the existance of an stored {@link OCShare} matching the given remote id (not to be confused with
+     * the local id) in the current account.
+     *
+     * @param remoteId      Remote of the share in the server.
+     * @return              'True' if a matching {@link OCShare} is stored in the current account.
+     */
+    private boolean shareExistsForRemoteId(long remoteId) {
+        return shareExistsForValue(ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED, String.valueOf(remoteId));
+    }
+
+
+    /**
+     * Checks the existance of an stored {@link OCShare} in the current account
+     * matching a given column and a value for that column
+     *
+     * @param key           Name of the column to match.
+     * @param value         Value of the column to match.
+     * @return              'True' if a matching {@link OCShare} is stored in the current account.
+     */
+    private boolean shareExistsForValue(String key, String value) {
+        Cursor c = getShareCursorForValue(key, value);
+        boolean retval = c.moveToFirst();
+        c.close();
+        return retval;
+    }
+
+
+    /**
+     * Gets a {@link Cursor} for an stored {@link OCShare} in the current account
+     * matching a given column and a value for that column
+     *
+     * @param key           Name of the column to match.
+     * @param value         Value of the column to match.
+     * @return              'True' if a matching {@link OCShare} is stored in the current account.
+     */
+    private Cursor getShareCursorForValue(String key, String value) {
+        Cursor c;
+        if (getContentResolver() != null) {
+            c = getContentResolver()
+                    .query(ProviderTableMeta.CONTENT_URI_SHARE,
+                            null,
+                            key + "=? AND "
+                                + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?",
+                            new String[]{value, mAccount.name},
+                            null
+                    );
+        } else {
+            try {
+                c = getContentProviderClient().query(
+                        ProviderTableMeta.CONTENT_URI_SHARE,
+                        null,
+                        key + "=? AND "
+                                + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?",
+                        new String[]{value, mAccount.name},
+                        null
+                );
+            } catch (RemoteException e) {
+                Log_OC.w(TAG,
+                        "Could not get details, assuming share does not exist: "+ e.getMessage());
+                c = null;
+            }
+        }
+        return c;
+    }
+
+
 
     /**
      * Get first share bound to a file with a known path and given {@link ShareType}.
@@ -1074,40 +1163,6 @@ public class FileDataStorageManager {
         return share;
     }
 
-    private boolean shareExists(String cmp_key, String value) {
-        Cursor c;
-        if (getContentResolver() != null) {
-            c = getContentResolver()
-                    .query(ProviderTableMeta.CONTENT_URI_SHARE,
-                            null,
-                            cmp_key + "=? AND "
-                                    + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER
-                                    + "=?",
-                            new String[]{value, mAccount.name}, null);
-        } else {
-            try {
-                c = getContentProviderClient().query(
-                        ProviderTableMeta.CONTENT_URI_SHARE,
-                        null,
-                        cmp_key + "=? AND "
-                                + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?",
-                        new String[]{value, mAccount.name}, null);
-            } catch (RemoteException e) {
-                Log_OC.e(TAG,
-                        "Couldn't determine file existance, assuming non existance: "
-                                + e.getMessage());
-                return false;
-            }
-        }
-        boolean retval = c.moveToFirst();
-        c.close();
-        return retval;
-    }
-
-    private boolean shareExists(long remoteId) {
-        return shareExists(ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED, String.valueOf(remoteId));
-    }
-
     private void resetShareFlagsInAllFiles() {
         ContentValues cv = new ContentValues();
         cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, false);
@@ -1217,7 +1272,7 @@ public class FileDataStorageManager {
                 cv.put(ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED, share.getRemoteId());
                 cv.put(ProviderTableMeta.OCSHARES_ACCOUNT_OWNER, mAccount.name);
 
-                if (shareExists(share.getRemoteId())) {
+                if (shareExistsForRemoteId(share.getRemoteId())) {
                     // updating an existing file
                     operations.add(
                             ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI_SHARE).

+ 24 - 1
src/com/owncloud/android/files/FileOperationsHelper.java

@@ -39,6 +39,7 @@ import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
 import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
 import com.owncloud.android.lib.common.network.WebdavUtils;
 import com.owncloud.android.lib.common.utils.Log_OC;
+import com.owncloud.android.lib.resources.shares.OCShare;
 import com.owncloud.android.lib.resources.shares.ShareType;
 import com.owncloud.android.lib.resources.status.OwnCloudVersion;
 import com.owncloud.android.services.OperationsService;
@@ -227,8 +228,9 @@ public class FileOperationsHelper {
      * @param file          The file to share.
      * @param shareeName    Name (user name or group name) of the target sharee.
      * @param shareType     The share type determines the sharee type.
+     * @param permissions   Permissions to grant to sharee on the shared file.
      */
-    public void shareFileWithSharee(OCFile file, String shareeName, ShareType shareType) {
+    public void shareFileWithSharee(OCFile file, String shareeName, ShareType shareType, int permissions) {
         if (file != null) {
             // TODO check capability?
             mFileActivity.showLoadingDialog(mFileActivity.getApplicationContext().
@@ -240,6 +242,7 @@ public class FileOperationsHelper {
             service.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath());
             service.putExtra(OperationsService.EXTRA_SHARE_WITH, shareeName);
             service.putExtra(OperationsService.EXTRA_SHARE_TYPE, shareType);
+            service.putExtra(OperationsService.EXTRA_SHARE_PERMISSIONS, permissions);
             mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(service);
 
         } else {
@@ -387,6 +390,26 @@ public class FileOperationsHelper {
     }
 
 
+    /**
+     * Updates a share on a file to set its access permissions.
+     * Starts a request to do it in {@link OperationsService}
+     *
+     * @param share                     {@link OCShare} instance which permissions will be updated.
+     * @param permissions               New permissions to set. A value <= 0 makes no update.
+     */
+    public void setPermissionsToShare(OCShare share, int permissions) {
+        Intent updateShareIntent = new Intent(mFileActivity, OperationsService.class);
+        updateShareIntent.setAction(OperationsService.ACTION_UPDATE_SHARE);
+        updateShareIntent.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
+        updateShareIntent.putExtra(OperationsService.EXTRA_SHARE_ID, share.getId());
+        updateShareIntent.putExtra(
+                OperationsService.EXTRA_SHARE_PERMISSIONS,
+                permissions
+        );
+        queueShareIntent(updateShareIntent);
+    }
+
+
     /**
      * @return 'True' if the server supports the Search Users API
      */

+ 10 - 9
src/com/owncloud/android/operations/CreateShareWithShareeOperation.java

@@ -26,12 +26,16 @@ package com.owncloud.android.operations;
  */
 
 
+import android.accounts.Account;
+
 import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.lib.common.OwnCloudClient;
+import com.owncloud.android.lib.common.operations.RemoteOperation;
 import com.owncloud.android.lib.common.operations.RemoteOperationResult;
 import com.owncloud.android.lib.resources.files.FileUtils;
 import com.owncloud.android.lib.resources.shares.CreateRemoteShareOperation;
+import com.owncloud.android.lib.resources.shares.GetRemoteSharesForFileOperation;
 import com.owncloud.android.lib.resources.shares.OCShare;
 import com.owncloud.android.lib.resources.shares.ShareType;
 import com.owncloud.android.operations.common.SyncOperation;
@@ -43,6 +47,7 @@ public class CreateShareWithShareeOperation extends SyncOperation {
     private String mPath;
     private String mShareeName;
     private ShareType mShareType;
+    private int mPermissions;
 
     /**
      * Constructor.
@@ -51,25 +56,21 @@ public class CreateShareWithShareeOperation extends SyncOperation {
      * @param shareeName    User or group name of the target sharee.
      * @param shareType     Type of share determines type of sharee; {@link ShareType#USER} and {@link ShareType#GROUP}
      *                      are the only valid values for the moment.
+     * @param permissions   Share permissions key as detailed in
+     *                      https://doc.owncloud.org/server/8.2/developer_manual/core/ocs-share-api.html .
      */
-    public CreateShareWithShareeOperation(String path, String shareeName, ShareType shareType) {
+    public CreateShareWithShareeOperation(String path, String shareeName, ShareType shareType, int permissions) {
         if (!ShareType.USER.equals(shareType) && !ShareType.GROUP.equals(shareType)) {
             throw new IllegalArgumentException("Illegal share type " + shareType);
         }
         mPath = path;
         mShareeName = shareeName;
         mShareType = shareType;
+        mPermissions = permissions;
     }
 
     @Override
     protected RemoteOperationResult run(OwnCloudClient client) {
-        // Check if the share link already exists
-        // TODO or not
-        /*
-        RemoteOperation operation = new GetRemoteSharesForFileOperation(mPath, false, false);
-        RemoteOperationResult result = operation.execute(client);
-        if (!result.isSuccess() || result.getData().size() <= 0) {
-        */
 
         CreateRemoteShareOperation operation = new CreateRemoteShareOperation(
                 mPath,
@@ -77,7 +78,7 @@ public class CreateShareWithShareeOperation extends SyncOperation {
                 mShareeName,
                 false,
                 "",
-                OCShare.DEFAULT_PERMISSION
+                mPermissions
         );
         operation.setGetShareDetails(true);
         RemoteOperationResult result = operation.execute(client);

+ 115 - 0
src/com/owncloud/android/operations/UpdateSharePermissionsOperation.java

@@ -0,0 +1,115 @@
+/**
+ *   ownCloud Android client application
+ *
+ *   @author David A. Velasco
+ *   Copyright (C) 2015 ownCloud Inc.
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License version 2,
+ *   as published by the Free Software Foundation.
+ *
+ *   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 General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package com.owncloud.android.operations;
+
+import com.owncloud.android.lib.common.OwnCloudClient;
+import com.owncloud.android.lib.common.operations.RemoteOperation;
+import com.owncloud.android.lib.common.operations.RemoteOperationResult;
+import com.owncloud.android.lib.resources.files.FileUtils;
+import com.owncloud.android.lib.resources.shares.GetRemoteShareOperation;
+import com.owncloud.android.lib.resources.shares.OCShare;
+import com.owncloud.android.lib.resources.shares.UpdateRemoteShareOperation;
+import com.owncloud.android.operations.common.SyncOperation;
+
+
+/**
+ * Updates an existing private share for a given file
+ */
+
+public class UpdateSharePermissionsOperation extends SyncOperation {
+
+    private long mShareId;
+    private int mPermissions;
+    private String mPath;
+
+    /**
+     * Constructor
+     *
+     * @param shareId       Private {@link OCShare} to update. Mandatory argument
+     */
+    public UpdateSharePermissionsOperation(long shareId) {
+        mShareId = shareId;
+        mPermissions = -1;
+    }
+
+
+    /**
+     * Set permissions to update in private share.
+     *
+     * @param permissions   Permissions to set to the private share.
+     *                      Values <= 0 result in no update applied to the permissions.
+     */
+    public void setPermissions(int permissions) {
+        mPermissions = permissions;
+    }
+
+
+    @Override
+    protected RemoteOperationResult run(OwnCloudClient client) {
+
+        OCShare share = getStorageManager().getShareById(mShareId); // ShareType.USER | ShareType.GROUP
+
+        if (share == null) {
+            // TODO try to get remote share before failing?
+            return new RemoteOperationResult(
+                    RemoteOperationResult.ResultCode.SHARE_NOT_FOUND
+            );
+        }
+
+        mPath = share.getPath();
+
+        // Update remote share with password
+        UpdateRemoteShareOperation updateOp = new UpdateRemoteShareOperation(
+            share.getRemoteId()
+        );
+        updateOp.setPermissions(mPermissions);
+        RemoteOperationResult result = updateOp.execute(client);
+
+        if (result.isSuccess()) {
+            RemoteOperation getShareOp = new GetRemoteShareOperation(share.getRemoteId());
+            result = getShareOp.execute(client);
+            if (result.isSuccess()) {
+                share = (OCShare) result.getData().get(0);
+                // TODO check permissions are being saved
+                updateData(share);
+            }
+        }
+
+        return result;
+    }
+
+    public String getPath() {
+        return mPath;
+    }
+
+    private void updateData(OCShare share) {
+        // Update DB with the response
+        share.setPath(mPath);   // TODO - check if may be moved to UpdateRemoteShareOperation
+        if (mPath.endsWith(FileUtils.PATH_SEPARATOR)) {
+            share.setIsFolder(true);
+        } else {
+            share.setIsFolder(false);
+        }
+        getStorageManager().saveShare(share);
+    }
+
+}
+

+ 5 - 5
src/com/owncloud/android/operations/UpdateShareViaLinkOperation.java

@@ -99,15 +99,15 @@ public class UpdateShareViaLinkOperation extends SyncOperation {
         }
 
         // Update remote share with password
-        UpdateRemoteShareOperation udpateOp = new UpdateRemoteShareOperation(
+        UpdateRemoteShareOperation updateOp = new UpdateRemoteShareOperation(
             publicShare.getRemoteId()
         );
-        udpateOp.setPassword(mPassword);
-        udpateOp.setExpirationDate(mExpirationDateInMillis);
-        RemoteOperationResult result = udpateOp.execute(client);
+        updateOp.setPassword(mPassword);
+        updateOp.setExpirationDate(mExpirationDateInMillis);
+        RemoteOperationResult result = updateOp.execute(client);
 
         if (result.isSuccess()) {
-            // Retrieve updated share / save directly with password? -> no; the password is not be saved
+            // Retrieve updated share / save directly with password? -> no; the password is not to be saved
             RemoteOperation getShareOp = new GetRemoteShareOperation(publicShare.getRemoteId());
             result = getShareOp.execute(client);
             if (result.isSuccess()) {

+ 13 - 5
src/com/owncloud/android/services/OperationsService.java

@@ -64,11 +64,11 @@ import com.owncloud.android.operations.RenameFileOperation;
 import com.owncloud.android.operations.SynchronizeFileOperation;
 import com.owncloud.android.operations.SynchronizeFolderOperation;
 import com.owncloud.android.operations.UnshareOperation;
+import com.owncloud.android.operations.UpdateSharePermissionsOperation;
 import com.owncloud.android.operations.UpdateShareViaLinkOperation;
 import com.owncloud.android.operations.common.SyncOperation;
 
 import java.io.IOException;
-import java.util.Calendar;
 import java.util.Iterator;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentLinkedQueue;
@@ -94,8 +94,8 @@ public class OperationsService extends Service {
     public static final String EXTRA_SHARE_TYPE = "SHARE_TYPE";
     public static final String EXTRA_SHARE_WITH = "SHARE_WITH";
     public static final String EXTRA_SHARE_EXPIRATION_DATE_IN_MILLIS = "SHARE_EXPIRATION_YEAR";
-    public static final String EXTRA_SHARE_EXPIRATION_MONTH_OF_YEAR = "SHARE_EXPIRATION_MONTH_OF_YEAR";
-    public static final String EXTRA_SHARE_EXPIRATION_DAY_OF_MONTH = "SHARE_EXPIRATION_DAY_OF_MONTH";
+    public static final String EXTRA_SHARE_PERMISSIONS = "SHARE_PERMISSIONS";
+    public static final String EXTRA_SHARE_ID = "SHARE_ID";
 
     public static final String EXTRA_COOKIE = "COOKIE";
 
@@ -571,7 +571,8 @@ public class OperationsService extends Service {
 
                 } else if (ACTION_UPDATE_SHARE.equals(action)) {
                     String remotePath = operationIntent.getStringExtra(EXTRA_REMOTE_PATH);
-                    if (remotePath.length() > 0) {
+                    long shareId = operationIntent.getLongExtra(EXTRA_SHARE_ID, -1);
+                    if (remotePath != null && remotePath.length() > 0) {
                         operation = new UpdateShareViaLinkOperation(remotePath);
 
                         String password = operationIntent.getStringExtra(EXTRA_SHARE_PASSWORD);
@@ -584,6 +585,11 @@ public class OperationsService extends Service {
                         ((UpdateShareViaLinkOperation)operation).setExpirationDate(
                                 expirationDate
                         );
+
+                    } else if (shareId > 0) {
+                        operation = new UpdateSharePermissionsOperation(shareId);
+                        int permissions = operationIntent.getIntExtra(EXTRA_SHARE_PERMISSIONS, 1);
+                        ((UpdateSharePermissionsOperation)operation).setPermissions(permissions);
                     }
 
                 } else if (action.equals(ACTION_CREATE_SHARE_WITH_SHAREE)) {
@@ -591,11 +597,13 @@ public class OperationsService extends Service {
                     String remotePath = operationIntent.getStringExtra(EXTRA_REMOTE_PATH);
                     String shareeName = operationIntent.getStringExtra(EXTRA_SHARE_WITH);
                     ShareType shareType = (ShareType) operationIntent.getSerializableExtra(EXTRA_SHARE_TYPE);
+                    int permissions = operationIntent.getIntExtra(EXTRA_SHARE_PERMISSIONS, -1);
                     if (remotePath.length() > 0) {
                         operation = new CreateShareWithShareeOperation(
                                 remotePath,
                                 shareeName,
-                                shareType
+                                shareType,
+                                permissions
                         );
                     }
 

+ 52 - 11
src/com/owncloud/android/ui/activity/FileActivity.java

@@ -62,6 +62,10 @@ import com.owncloud.android.files.services.FileDownloader;
 import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
 import com.owncloud.android.files.services.FileUploader;
 import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
+import com.owncloud.android.lib.common.OwnCloudAccount;
+import com.owncloud.android.lib.common.OwnCloudClient;
+import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
+import com.owncloud.android.lib.common.OwnCloudCredentials;
 import com.owncloud.android.lib.common.operations.OnRemoteOperationListener;
 import com.owncloud.android.lib.common.operations.RemoteOperation;
 import com.owncloud.android.lib.common.operations.RemoteOperationResult;
@@ -74,6 +78,7 @@ import com.owncloud.android.operations.GetSharesForFileOperation;
 import com.owncloud.android.operations.SynchronizeFileOperation;
 import com.owncloud.android.operations.SynchronizeFolderOperation;
 import com.owncloud.android.operations.UnshareOperation;
+import com.owncloud.android.operations.UpdateSharePermissionsOperation;
 import com.owncloud.android.operations.UpdateShareViaLinkOperation;
 import com.owncloud.android.services.OperationsService;
 import com.owncloud.android.services.OperationsService.OperationsServiceBinder;
@@ -104,7 +109,6 @@ public class FileActivity extends AppCompatActivity
 
     private static final String KEY_WAITING_FOR_OP_ID = "WAITING_FOR_OP_ID";
     private static final String DIALOG_SHARE_PASSWORD = "DIALOG_SHARE_PASSWORD";
-    private static final String KEY_TRY_SHARE_AGAIN = "TRY_SHARE_AGAIN";
     private static final String KEY_ACTION_BAR_TITLE = "ACTION_BAR_TITLE";
 
     protected static final long DELAY_TO_REQUEST_OPERATIONS_LATER = 200;
@@ -739,7 +743,7 @@ public class FileActivity extends AppCompatActivity
                 (result.isException() && result.getException() instanceof AuthenticatorException)
                 )) {
 
-            requestCredentialsUpdate();
+            requestCredentialsUpdate(this);
 
             if (result.getCode() == ResultCode.UNAUTHORIZED) {
                 dismissLoadingDialog();
@@ -753,7 +757,8 @@ public class FileActivity extends AppCompatActivity
                 operation instanceof CreateShareWithShareeOperation ||
                 operation instanceof UnshareOperation ||
                 operation instanceof SynchronizeFolderOperation ||
-                operation instanceof UpdateShareViaLinkOperation
+                operation instanceof UpdateShareViaLinkOperation ||
+                operation instanceof UpdateSharePermissionsOperation
                 ) {
             if (result.isSuccess()) {
                 updateFileFromDB();
@@ -784,14 +789,50 @@ public class FileActivity extends AppCompatActivity
         }
     }
 
-    protected void requestCredentialsUpdate() {
-        Intent updateAccountCredentials = new Intent(this, AuthenticatorActivity.class);
-        updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, getAccount());
-        updateAccountCredentials.putExtra(
-                AuthenticatorActivity.EXTRA_ACTION,
-                AuthenticatorActivity.ACTION_UPDATE_EXPIRED_TOKEN);
-        updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-        startActivity(updateAccountCredentials);
+    /**
+     * Invalidates the credentials stored for the current OC account and requests new credentials to the user,
+     * navigating to {@link AuthenticatorActivity}
+     *
+     * @param context   Android Context needed to access the {@link AccountManager}. Received as a parameter
+     *                  to make the method accessible to {@link android.content.BroadcastReceiver}s.
+     */
+    protected void requestCredentialsUpdate(Context context) {
+
+        try {
+            /// step 1 - invalidate credentials of current account
+            OwnCloudClient client;
+            OwnCloudAccount ocAccount =
+                    new OwnCloudAccount(getAccount(), context);
+            client = (OwnCloudClientManagerFactory.getDefaultSingleton().
+                    removeClientFor(ocAccount));
+            if (client != null) {
+                OwnCloudCredentials cred = client.getCredentials();
+                if (cred != null) {
+                    AccountManager am = AccountManager.get(context);
+                    if (cred.authTokenExpires()) {
+                        am.invalidateAuthToken(
+                                getAccount().type,
+                                cred.getAuthToken()
+                        );
+                    } else {
+                        am.clearPassword(getAccount());
+                    }
+                }
+            }
+
+            /// step 2 - request credentials to user
+            Intent updateAccountCredentials = new Intent(this, AuthenticatorActivity.class);
+            updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, getAccount());
+            updateAccountCredentials.putExtra(
+                    AuthenticatorActivity.EXTRA_ACTION,
+                    AuthenticatorActivity.ACTION_UPDATE_EXPIRED_TOKEN);
+            updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+            startActivity(updateAccountCredentials);
+
+        } catch (com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException e) {
+            Toast.makeText(context, R.string.auth_account_does_not_exist, Toast.LENGTH_SHORT).show();
+        }
+
     }
 
 

+ 1 - 26
src/com/owncloud/android/ui/activity/FileDisplayActivity.java

@@ -968,32 +968,7 @@ public class FileDisplayActivity extends HookActivity
                                         (synchResult.isException() && synchResult.getException()
                                                 instanceof AuthenticatorException))) {
 
-
-                            try {
-                                OwnCloudClient client;
-                                OwnCloudAccount ocAccount =
-                                        new OwnCloudAccount(getAccount(), context);
-                                client = (OwnCloudClientManagerFactory.getDefaultSingleton().
-                                        removeClientFor(ocAccount));
-                                if (client != null) {
-                                    OwnCloudCredentials cred = client.getCredentials();
-                                    if (cred != null) {
-                                        AccountManager am = AccountManager.get(context);
-                                        if (cred.authTokenExpires()) {
-                                            am.invalidateAuthToken(
-                                                    getAccount().type,
-                                                    cred.getAuthToken()
-                                            );
-                                        } else {
-                                            am.clearPassword(getAccount());
-                                        }
-                                    }
-                                }
-                                requestCredentialsUpdate();
-
-                            } catch (AccountNotFoundException e) {
-                                Log_OC.e(TAG, "Account " + getAccount() + " was removed!", e);
-                            }
+                            requestCredentialsUpdate(context);
 
                         }
 

+ 1 - 26
src/com/owncloud/android/ui/activity/FolderPickerActivity.java

@@ -491,32 +491,7 @@ public class FolderPickerActivity extends FileActivity implements FileFragment.C
                                     (synchResult.isException() && synchResult.getException() 
                                             instanceof AuthenticatorException))) {
 
-                            try {
-                                OwnCloudClient client;
-                                OwnCloudAccount ocAccount =
-                                        new OwnCloudAccount(getAccount(), context);
-                                client = (OwnCloudClientManagerFactory.getDefaultSingleton().
-                                        removeClientFor(ocAccount));
-
-                                if (client != null) {
-                                    OwnCloudCredentials cred = client.getCredentials();
-                                    if (cred != null) {
-                                        AccountManager am = AccountManager.get(context);
-                                        if (cred.authTokenExpires()) {
-                                            am.invalidateAuthToken(
-                                                    getAccount().type,
-                                                    cred.getAuthToken()
-                                            );
-                                        } else {
-                                            am.clearPassword(getAccount());
-                                        }
-                                    }
-                                }
-                                requestCredentialsUpdate();
-
-                            } catch (AccountNotFoundException e) {
-                                Log_OC.e(TAG, "Account " + getAccount() + " was removed!", e);
-                            }
+                            requestCredentialsUpdate(context);
 
                         }
                     }

+ 57 - 3
src/com/owncloud/android/ui/activity/ShareActivity.java

@@ -33,6 +33,8 @@ import com.owncloud.android.R;
 import com.owncloud.android.lib.common.utils.Log_OC;
 import com.owncloud.android.operations.CreateShareViaLinkOperation;
 import com.owncloud.android.operations.GetSharesForFileOperation;
+import com.owncloud.android.operations.UnshareOperation;
+import com.owncloud.android.operations.UpdateSharePermissionsOperation;
 import com.owncloud.android.providers.UsersAndGroupsSearchProvider;
 
 import com.owncloud.android.lib.common.operations.RemoteOperation;
@@ -41,8 +43,10 @@ import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.lib.resources.shares.OCShare;
 import com.owncloud.android.lib.resources.shares.ShareType;
 import com.owncloud.android.ui.dialog.ShareLinkToDialog;
+import com.owncloud.android.ui.fragment.EditShareFragment;
 import com.owncloud.android.ui.fragment.SearchShareesFragment;
 import com.owncloud.android.ui.fragment.ShareFileFragment;
+import com.owncloud.android.ui.fragment.ShareFragmentListener;
 import com.owncloud.android.utils.GetShareWithUsersAsyncTask;
 
 import org.apache.http.protocol.HTTP;
@@ -53,13 +57,13 @@ import org.apache.http.protocol.HTTP;
  */
 
 public class ShareActivity extends FileActivity
-        implements ShareFileFragment.OnShareFragmentInteractionListener,
-        SearchShareesFragment.OnSearchFragmentInteractionListener {
+        implements ShareFragmentListener {
 
     private static final String TAG = ShareActivity.class.getSimpleName();
 
     private static final String TAG_SHARE_FRAGMENT = "SHARE_FRAGMENT";
     private static final String TAG_SEARCH_FRAGMENT = "SEARCH_USER_AND_GROUPS_FRAGMENT";
+    private static final String TAG_EDIT_SHARE_FRAGMENT = "EDIT_SHARE_FRAGMENT";
 
     /** Tag for dialog */
     private static final String FTAG_CHOOSER_DIALOG = "CHOOSER_DIALOG";
@@ -118,10 +122,25 @@ public class ShareActivity extends FileActivity
         getFileOperationsHelper().shareFileWithSharee(
                 getFile(),
                 shareeName,
-                (isGroup ? ShareType.GROUP : ShareType.USER)
+                (isGroup ? ShareType.GROUP : ShareType.USER),
+                getAppropiatePermissions()
         );
     }
 
+
+    private int getAppropiatePermissions() {
+        if (getFile().isSharedWithMe()) {
+            return OCShare.READ_PERMISSION_FLAG;    // minimum permissions
+
+        } else if (getFile().isFolder()) {
+            return OCShare.MAXIMUM_PERMISSIONS_FOR_FOLDER;
+
+        } else {    // isFile
+            return OCShare.MAXIMUM_PERMISSIONS_FOR_FILE;
+        }
+    }
+
+
     @Override
     public void showSearchUsersAndGroups() {
         // replace ShareFragment with SearchFragment on demand
@@ -132,6 +151,16 @@ public class ShareActivity extends FileActivity
         ft.commit();
     }
 
+    @Override
+    public void showEditShare(OCShare share) {
+        // replace current fragment with EditShareFragment on demand
+        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+        Fragment editShareFragment = EditShareFragment.newInstance(share, getFile(), getAccount());
+        ft.replace(R.id.share_fragment_container, editShareFragment, TAG_EDIT_SHARE_FRAGMENT);
+        ft.addToBackStack(null);    // BACK button will recover the previous fragment
+        ft.commit();
+    }
+
     @Override
     // Call to Unshare operation
     public void unshareWith(OCShare share) {
@@ -185,6 +214,15 @@ public class ShareActivity extends FileActivity
             chooserDialog.show(getSupportFragmentManager(), FTAG_CHOOSER_DIALOG);
         }
 
+        if (operation instanceof UnshareOperation && result.isSuccess() && getEditShareFragment() != null) {
+            getSupportFragmentManager().popBackStack();
+        }
+
+        if (operation instanceof UpdateSharePermissionsOperation
+                && getEditShareFragment() != null && getEditShareFragment().isAdded()) {
+            getEditShareFragment().onUpdateSharePermissionsFinished(result);
+        }
+
     }
 
 
@@ -206,6 +244,13 @@ public class ShareActivity extends FileActivity
                 searchShareesFragment.isAdded()) {  // only if added to the view hierarchy!!
             searchShareesFragment.refreshUsersOrGroupsListFromDB();
         }
+
+        EditShareFragment editShareFragment = getEditShareFragment();
+        if (editShareFragment != null &&
+                editShareFragment.isAdded())    {
+            editShareFragment.refreshUiFromDB();
+        }
+
     }
 
     /**
@@ -226,4 +271,13 @@ public class ShareActivity extends FileActivity
         return (SearchShareesFragment) getSupportFragmentManager().findFragmentByTag(TAG_SEARCH_FRAGMENT);
     }
 
+    /**
+     * Shortcut to get access to the {@link EditShareFragment} instance, if any
+     *
+     * @return  A {@link EditShareFragment} instance, or null
+     */
+    private EditShareFragment getEditShareFragment() {
+        return (EditShareFragment) getSupportFragmentManager().findFragmentByTag(TAG_EDIT_SHARE_FRAGMENT);
+    }
+
 }

+ 11 - 0
src/com/owncloud/android/ui/adapter/ShareUserListAdapter.java

@@ -82,6 +82,16 @@ public class ShareUserListAdapter extends ArrayAdapter {
             }
             userName.setText(name);
 
+            /// bind listener to edit privileges
+            final ImageView editShareButton = (ImageView) view.findViewById(R.id.editShareButton);
+            editShareButton.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    mListener.editShare(mShares.get(position));
+                }
+            });
+
+            /// bind listener to unshare
             final ImageView unshareButton = (ImageView) view.findViewById(R.id.unshareButton);
             unshareButton.setOnClickListener(new View.OnClickListener() {
                 @Override
@@ -96,6 +106,7 @@ public class ShareUserListAdapter extends ArrayAdapter {
 
     public interface ShareUserAdapterListener {
         void unshareButtonPressed(OCShare share);
+        void editShare(OCShare share);
     }
 
 

+ 482 - 0
src/com/owncloud/android/ui/fragment/EditShareFragment.java

@@ -0,0 +1,482 @@
+/**
+ *   ownCloud Android client application
+ *
+ *   @author masensio
+ *   @author David A. Velasco
+ *   Copyright (C) 2015 ownCloud Inc.
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License version 2,
+ *   as published by the Free Software Foundation.
+ *
+ *   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 General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package com.owncloud.android.ui.fragment;
+
+import android.accounts.Account;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.Switch;
+
+import com.owncloud.android.R;
+import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.lib.common.operations.RemoteOperationResult;
+import com.owncloud.android.lib.common.utils.Log_OC;
+import com.owncloud.android.lib.resources.shares.OCShare;
+import com.owncloud.android.lib.resources.shares.SharePermissionsBuilder;
+import com.owncloud.android.ui.activity.FileActivity;
+
+public class EditShareFragment extends Fragment {
+
+    private static final String TAG = EditShareFragment.class.getSimpleName();
+
+    /** The fragment initialization parameters */
+    private static final String ARG_SHARE = "SHARE";
+    private static final String ARG_FILE = "FILE";
+    private static final String ARG_ACCOUNT = "ACCOUNT";
+
+    /** Ids of CheckBoxes depending on R.id.canEdit CheckBox */
+    private static final int sSubordinateCheckBoxIds[] = {
+            R.id.canEditCreateCheckBox,
+            R.id.canEditChangeCheckBox,
+            R.id.canEditDeleteCheckBox
+    };
+
+    /** Share to show & edit, received as a parameter in construction time */
+    private OCShare mShare;
+
+    /** File bound to mShare, received as a parameter in construction time */
+    private OCFile mFile;
+
+    /** OC account holding the shared file, received as a parameter in construction time */
+    private Account mAccount;
+
+    /** Listener for changes on privilege checkboxes */
+    private CompoundButton.OnCheckedChangeListener mOnPrivilegeChangeListener;
+
+
+    /**
+     * Public factory method to create new EditShareFragment instances.
+     *
+     * @param shareToEdit   An {@link OCShare} to show and edit in the fragment
+     * @param sharedFile    The {@link OCFile} bound to 'shareToEdit'
+     * @param account       The ownCloud account holding 'sharedFile'
+     * @return A new instance of fragment EditShareFragment.
+     */
+    public static EditShareFragment newInstance(OCShare shareToEdit, OCFile sharedFile, Account account) {
+        EditShareFragment fragment = new EditShareFragment();
+        Bundle args = new Bundle();
+        args.putParcelable(ARG_SHARE, shareToEdit);
+        args.putParcelable(ARG_FILE, sharedFile);
+        args.putParcelable(ARG_ACCOUNT, account);
+        fragment.setArguments(args);
+        return fragment;
+    }
+
+    /**
+     * Required empty public constructor
+     */
+    public EditShareFragment() {
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Log_OC.d(TAG, "onCreate");
+        if (getArguments() != null) {
+            mShare = getArguments().getParcelable(ARG_SHARE);
+            mFile = getArguments().getParcelable(ARG_FILE);
+            mAccount = getArguments().getParcelable(ARG_ACCOUNT);
+        }
+    }
+
+
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        Log_OC.d(TAG, "onActivityCreated");
+        getActivity().setTitle(mShare.getSharedWithDisplayName());
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                             Bundle savedInstanceState) {
+        Log_OC.d(TAG, "onCreateView");
+
+        // Inflate the layout for this fragment
+        View view = inflater.inflate(R.layout.edit_share_layout, container, false);
+
+        // Setup layout
+        refreshUiFromState(view);
+        setPermissionsListening(view, true);
+
+        return view;
+    }
+
+
+    /**
+     * Updates the UI with the current permissions in the edited {@OCShare}
+     *
+     * @param editShareView     Root view in the fragment.
+     */
+    private void refreshUiFromState(View editShareView) {
+        if (editShareView != null) {
+            setPermissionsListening(editShareView, false);
+
+            int sharePermissions = mShare.getPermissions();
+            CompoundButton compound;
+
+            compound = (CompoundButton) editShareView.findViewById(R.id.canShareSwitch);
+            compound.setChecked((sharePermissions & OCShare.SHARE_PERMISSION_FLAG) > 0);
+
+            compound = (CompoundButton) editShareView.findViewById(R.id.canEditSwitch);
+            int anyUpdatePermission =
+                    OCShare.CREATE_PERMISSION_FLAG |
+                            OCShare.UPDATE_PERMISSION_FLAG |
+                            OCShare.DELETE_PERMISSION_FLAG;
+            boolean canEdit = (sharePermissions & anyUpdatePermission) > 0;
+            compound.setChecked(canEdit);
+
+            if (mFile.isFolder()) {
+                compound = (CompoundButton) editShareView.findViewById(R.id.canEditCreateCheckBox);
+                compound.setChecked((sharePermissions & OCShare.CREATE_PERMISSION_FLAG) > 0);
+                compound.setVisibility(canEdit ? View.VISIBLE : View.GONE);
+
+                compound = (CompoundButton) editShareView.findViewById(R.id.canEditChangeCheckBox);
+                compound.setChecked((sharePermissions & OCShare.UPDATE_PERMISSION_FLAG) > 0);
+                compound.setVisibility(canEdit ? View.VISIBLE : View.GONE);
+
+                compound = (CompoundButton) editShareView.findViewById(R.id.canEditDeleteCheckBox);
+                compound.setChecked((sharePermissions & OCShare.DELETE_PERMISSION_FLAG) > 0);
+                compound.setVisibility(canEdit ? View.VISIBLE : View.GONE);
+            }
+
+            setPermissionsListening(editShareView, true);
+
+        }
+    }
+
+
+    /**
+     * Binds or unbinds listener for user actions to enable or disable a permission on the edited share
+     * to the views receiving the user events.
+     *
+     * @param editShareView     Root view in the fragment.
+     * @param enable            When 'true', listener is bound to view; when 'false', it is unbound.
+     */
+    private void setPermissionsListening(View editShareView, boolean enable) {
+        if (enable && mOnPrivilegeChangeListener == null) {
+            mOnPrivilegeChangeListener = new OnPrivilegeChangeListener();
+        }
+        CompoundButton.OnCheckedChangeListener changeListener = enable ? mOnPrivilegeChangeListener : null;
+        CompoundButton compound;
+
+        compound = (CompoundButton) editShareView.findViewById(R.id.canShareSwitch);
+        compound.setOnCheckedChangeListener(changeListener);
+
+        compound = (CompoundButton) editShareView.findViewById(R.id.canEditSwitch);
+        compound.setOnCheckedChangeListener(changeListener);
+
+        if (mFile.isFolder()) {
+            compound = (CompoundButton) editShareView.findViewById(R.id.canEditCreateCheckBox);
+            compound.setOnCheckedChangeListener(changeListener);
+
+            compound = (CompoundButton) editShareView.findViewById(R.id.canEditChangeCheckBox);
+            compound.setOnCheckedChangeListener(changeListener);
+
+            compound = (CompoundButton) editShareView.findViewById(R.id.canEditDeleteCheckBox);
+            compound.setOnCheckedChangeListener(changeListener);
+        }
+
+    }
+
+    /**
+     * Listener for user actions that enable or disable a privilege
+     */
+    private class OnPrivilegeChangeListener
+            implements CompoundButton.OnCheckedChangeListener {
+
+        /**
+         * Called by every {@link Switch} and {@link CheckBox} in the fragment to update
+         * the state of its associated permission.
+         *
+         * @param compound  {@link CompoundButton} toggled by the user
+         * @param isChecked     New switch state.
+         */
+        @Override
+        public void onCheckedChanged(CompoundButton compound, boolean isChecked) {
+            if (!isResumed()) {
+                // very important, setCheched(...) is called automatically during
+                // Fragment recreation on device rotations
+                return;
+            }
+            /// else, getView() cannot be NULL
+
+            CompoundButton subordinate;
+            switch(compound.getId()) {
+                case R.id.canShareSwitch:
+                    Log_OC.v(TAG, "canShareCheckBox toggled to " + isChecked);
+                    updatePermissionsToShare();
+                    break;
+
+                case R.id.canEditSwitch:
+                    Log_OC.v(TAG, "canEditCheckBox toggled to " + isChecked);
+                    /// sync subordinate CheckBoxes
+                    if (mFile.isFolder()) {
+                        if (isChecked) {
+                            for (int i = 0; i < sSubordinateCheckBoxIds.length; i++) {
+                                //noinspection ConstantConditions, prevented in the method beginning
+                                subordinate = (CompoundButton) getView().findViewById(sSubordinateCheckBoxIds[i]);
+                                subordinate.setVisibility(View.VISIBLE);
+                                if (!subordinate.isChecked() &&
+                                        !mFile.isSharedWithMe()) {          // see (1)
+                                    toggleDisablingListener(subordinate);
+                                }
+                            }
+                            if (!mFile.isSharedWithMe()) {
+                                updatePermissionsToShare(); // see (1)
+                            }
+
+                        } else {
+                            for (int i = 0; i < sSubordinateCheckBoxIds.length; i++) {
+                                //noinspection ConstantConditions, prevented in the method beginning
+                                subordinate = (CompoundButton) getView().findViewById(sSubordinateCheckBoxIds[i]);
+                                subordinate.setVisibility(View.GONE);
+                                if (subordinate.isChecked()) {
+                                    toggleDisablingListener(subordinate);
+                                }
+                            }
+                            updatePermissionsToShare(); // see (1)
+                        }
+                    } else {
+                        updatePermissionsToShare();
+                    }
+                    // updatePermissionsToShare()   // see (1)
+                    // (1) These modifications result in an exceptional UI behaviour for the case
+                    // where the switch 'can edit' is enabled for a *reshared folder*; if the same
+                    // behaviour was applied than for owned folder, and the user did not have full
+                    // permissions to update the folder, an error would be reported by the server
+                    // and the children checkboxes would be automatically hidden again
+                    break;
+
+                case R.id.canEditCreateCheckBox:
+                    Log_OC.v(TAG, "canEditCreateCheckBox toggled to " + isChecked);
+                    syncCanEditSwitch(compound, isChecked);
+                    updatePermissionsToShare();
+                    break;
+
+                case R.id.canEditChangeCheckBox:
+                    Log_OC.v(TAG, "canEditChangeCheckBox toggled to " + isChecked);
+                    syncCanEditSwitch(compound, isChecked);
+                    updatePermissionsToShare();
+                    break;
+
+                case R.id.canEditDeleteCheckBox:
+                    Log_OC.v(TAG, "canEditDeleteCheckBox toggled to " + isChecked);
+                    syncCanEditSwitch(compound, isChecked);
+                    updatePermissionsToShare();
+                    break;
+            }
+
+        }
+
+        /**
+         * Sync value of "can edit" {@link Switch} according to a change in one of its subordinate checkboxes.
+         *
+         * If all the subordinates are disabled, "can edit" has to be disabled.
+         *
+         * If any subordinate is enabled, "can edit" has to be enabled.
+         *
+         * @param subordinateCheckBoxView   Subordinate {@link CheckBox} that was changed.
+         * @param isChecked                 'true' iif subordinateCheckBoxView was checked.
+         */
+        private void syncCanEditSwitch(View subordinateCheckBoxView, boolean isChecked) {
+            CompoundButton canEditCompound = (CompoundButton) getView().findViewById(R.id.canEditSwitch);
+            if (isChecked) {
+                if (!canEditCompound.isChecked()) {
+                    toggleDisablingListener(canEditCompound);
+                }
+            } else {
+                boolean allDisabled = true;
+                for (int i=0; allDisabled && i<sSubordinateCheckBoxIds.length; i++) {
+                    allDisabled &=
+                            sSubordinateCheckBoxIds[i] == subordinateCheckBoxView.getId() ||
+                                    !((CheckBox) getView().findViewById(sSubordinateCheckBoxIds[i])).isChecked()
+                    ;
+                }
+                if (canEditCompound.isChecked() && allDisabled) {
+                    toggleDisablingListener(canEditCompound);
+                    for (int i=0; i<sSubordinateCheckBoxIds.length; i++) {
+                        getView().findViewById(sSubordinateCheckBoxIds[i]).setVisibility(View.GONE);
+                    }
+                }
+            }
+        }
+
+
+        /**
+         * Toggle value of received {@link CompoundButton} granting that its change listener is not called.
+         *
+         * @param compound      {@link CompoundButton} (switch or checkBox) to toggle without reporting to
+         *                      the change listener
+         */
+        private void toggleDisablingListener(CompoundButton compound) {
+            compound.setOnCheckedChangeListener(null);
+            compound.toggle();
+            compound.setOnCheckedChangeListener(this);
+        }
+    }
+
+
+    /**
+     * Updates the UI after the result of an update operation on the edited {@link OCShare} permissions.
+     *
+     * @param result        Result of an update on the edited {@link OCShare} permissions.
+     */
+    public void onUpdateSharePermissionsFinished(RemoteOperationResult result) {
+        if (result.isSuccess()) {
+            refreshUiFromDB(getView());
+        } else if (result.getCode() == RemoteOperationResult.ResultCode.SHARE_NOT_FOUND) {
+            // share or file was deleted from other client before completing the operation
+            int backStackCount = getFragmentManager().getBackStackEntryCount();
+            for (int i=0; i<backStackCount; i++) {
+                getFragmentManager().popBackStack();
+            }
+            ((ShareFragmentListener)getActivity()).
+                    refreshUsersOrGroupsListFromServer();
+        } else {
+            refreshUiFromState(getView());
+        }
+    }
+
+
+    /**
+     * Get {@link OCShare} instance from DB and updates the UI.
+     *
+     * Depends on the parent Activity provides a {@link com.owncloud.android.datamodel.FileDataStorageManager}
+     * instance ready to use. If not ready, does nothing.
+     */
+    public void refreshUiFromDB() {
+        if (getView() != null) {
+            refreshUiFromDB(getView());
+        }
+    }
+
+
+    /**
+     * Get {@link OCShare} instance from DB and updates the UI.
+     *
+     * Depends on the parent Activity provides a {@link com.owncloud.android.datamodel.FileDataStorageManager}
+     * instance ready to use. If not ready, does nothing.
+     *
+     * @param editShareView     Root view in the fragment.
+     */
+    private void refreshUiFromDB(View editShareView) {
+        FileDataStorageManager storageManager = ((FileActivity) getActivity()).getStorageManager();
+        if (storageManager != null) {
+            // Get edited share
+            mShare = storageManager.getShareById(mShare.getId());
+
+            // Updates UI with new state
+            refreshUiFromState(editShareView);
+        }
+    }
+
+
+    /**
+     * Updates the permissions of the {@link OCShare} according to the values set in the UI
+     */
+    private void updatePermissionsToShare() {
+        SharePermissionsBuilder spb = new SharePermissionsBuilder();
+        spb.setSharePermission(getCanShareSwitch().isChecked());
+        if (mFile.isFolder()) {
+            spb.setUpdatePermission(getCanEditChangeCheckBox().isChecked())
+                    .setCreatePermission(getCanEditCreateCheckBox().isChecked())
+                    .setDeletePermission(getCanEditDeleteCheckBox().isChecked());
+        } else {
+            spb.setUpdatePermission(getCanEditSwitch().isChecked());
+        }
+        int permissions = spb.build();
+
+        ((FileActivity) getActivity()).getFileOperationsHelper().
+                setPermissionsToShare(
+                        mShare,
+                        permissions
+                )
+        ;
+    }
+
+    /**
+     * Shortcut to access {@link Switch} R.id.canShareSwitch
+     *
+     * @return  {@link Switch} R.id.canShareCheckBox or null if called before
+     *          {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)} finished.
+     */
+    private Switch getCanShareSwitch() {
+        return (Switch) getView().findViewById(R.id.canShareSwitch);
+    }
+
+    /**
+     * Shortcut to access {@link Switch} R.id.canEditSwitch
+     *
+     * @return  {@link Switch} R.id.canEditSwitch or null if called before
+     *          {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)} finished.
+     */
+    private Switch getCanEditSwitch() {
+        return (Switch) getView().findViewById(R.id.canEditSwitch);
+    }
+
+    /**
+     * Shortcut to access {@link CheckBox} R.id.canEditCreateCheckBox
+     *
+     * @return  {@link CheckBox} R.id.canEditCreateCheckBox or null if called before
+     *          {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)} finished.
+     */
+    private CheckBox getCanEditCreateCheckBox() {
+        return (CheckBox) getView().findViewById(R.id.canEditCreateCheckBox);
+    }
+
+    /**
+     * Shortcut to access {@link CheckBox} R.id.canEditChangeCheckBox
+     *
+     * @return  {@link CheckBox} R.id.canEditChangeCheckBox or null if called before
+     *          {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)} finished.
+     */
+    private CheckBox getCanEditChangeCheckBox() {
+        return (CheckBox) getView().findViewById(R.id.canEditChangeCheckBox);
+    }
+
+    /**
+     * Shortcut to access {@link CheckBox} R.id.canEditDeleteCheckBox
+     *
+     * @return  {@link CheckBox} R.id.canEditDeleteCheckBox or null if called before
+     *          {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)} finished.
+     */
+    private CheckBox getCanEditDeleteCheckBox() {
+        return (CheckBox) getView().findViewById(R.id.canEditDeleteCheckBox);
+    }
+
+}

+ 10 - 16
src/com/owncloud/android/ui/fragment/SearchShareesFragment.java

@@ -40,7 +40,6 @@ import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.lib.common.utils.Log_OC;
 import com.owncloud.android.lib.resources.shares.OCShare;
 import com.owncloud.android.ui.activity.FileActivity;
-import com.owncloud.android.ui.activity.ShareActivity;
 import com.owncloud.android.ui.adapter.ShareUserListAdapter;
 
 import java.util.ArrayList;
@@ -51,7 +50,7 @@ import java.util.ArrayList;
  * A simple {@link Fragment} subclass.
  *
  * Activities that contain this fragment must implement the
- * {@link SearchShareesFragment.OnSearchFragmentInteractionListener} interface
+ * {@link ShareFragmentListener} interface
  * to handle interaction events.
  *
  * Use the {@link SearchShareesFragment#newInstance} factory method to
@@ -71,7 +70,7 @@ public class SearchShareesFragment extends Fragment implements ShareUserListAdap
     // other members
     private ArrayList<OCShare> mShares;
     private ShareUserListAdapter mUserGroupsAdapter = null;
-    private OnSearchFragmentInteractionListener mListener;
+    private ShareFragmentListener mListener;
 
 
     /**
@@ -148,6 +147,8 @@ public class SearchShareesFragment extends Fragment implements ShareUserListAdap
     public void onActivityCreated(Bundle savedInstanceState) {
         super.onActivityCreated(savedInstanceState);
 
+        getActivity().setTitle(R.string.share_dialog_title);
+
         // Load data into the list
         refreshUsersOrGroupsListFromDB();
     }
@@ -196,7 +197,7 @@ public class SearchShareesFragment extends Fragment implements ShareUserListAdap
     public void onAttach(Activity activity) {
         super.onAttach(activity);
         try {
-            mListener = (OnSearchFragmentInteractionListener) activity;
+            mListener = (ShareFragmentListener) activity;
         } catch (ClassCastException e) {
             throw new ClassCastException(activity.toString()
                     + " must implement OnFragmentInteractionListener");
@@ -230,18 +231,11 @@ public class SearchShareesFragment extends Fragment implements ShareUserListAdap
         Log_OC.d(TAG, "Unshare - " + share.getSharedWithDisplayName());
     }
 
-    /**
-     * This interface must be implemented by activities that contain this
-     * fragment to allow an interaction in this fragment to be communicated
-     * to the activity and potentially other fragments contained in that
-     * activity.
-     * <p/>
-     * See the Android Training lesson <a href=
-     * "http://developer.android.com/training/basics/fragments/communicating.html"
-     * >Communicating with Other Fragments</a> for more information.
-     */
-    public interface OnSearchFragmentInteractionListener {
-        void unshareWith(OCShare share);
+    @Override
+    public void editShare(OCShare share) {
+        // move to fragment to edit share
+        Log_OC.d(TAG, "Editing " + share.getSharedWithDisplayName());
+        mListener.showEditShare(share);
     }
 
 }

+ 15 - 28
src/com/owncloud/android/ui/fragment/ShareFileFragment.java

@@ -66,7 +66,7 @@ import java.util.Date;
  * A simple {@link Fragment} subclass.
  *
  * Activities that contain this fragment must implement the
- * {@link ShareFileFragment.OnShareFragmentInteractionListener} interface
+ * {@link ShareFragmentListener} interface
  * to handle interaction events.
  *
  * Use the {@link ShareFileFragment#newInstance} factory method to
@@ -91,7 +91,7 @@ public class ShareFileFragment extends Fragment
     private Account mAccount;
 
     /** Reference to parent listener */
-    private OnShareFragmentInteractionListener mListener;
+    private ShareFragmentListener mListener;
 
     /** List of private shares bound to the file */
     private ArrayList<OCShare> mPrivateShares;
@@ -135,8 +135,10 @@ public class ShareFileFragment extends Fragment
         return fragment;
     }
 
+    /**
+     * Required empty public constructor
+     */
     public ShareFileFragment() {
-        // Required empty public constructor
     }
 
     /**
@@ -204,13 +206,6 @@ public class ShareFileFragment extends Fragment
             }
         });
 
-        // Switch to create public share
-        mOnShareViaLinkSwitchCheckedChangeListener = new CompoundButton.OnCheckedChangeListener() {
-            @Override
-            public void onCheckedChanged(CompoundButton switchView, boolean isChecked) {
-            }
-        };
-
         // Set listener for user actions on switch for sharing/unsharing via link
         initShareViaLinkListener(view);
 
@@ -443,6 +438,8 @@ public class ShareFileFragment extends Fragment
         super.onActivityCreated(savedInstanceState);
         Log_OC.d(TAG, "onActivityCreated");
 
+        getActivity().setTitle(R.string.share_dialog_title);
+
         // Load known capabilities of the server from DB
         refreshCapabilitiesFromDB();
 
@@ -457,7 +454,7 @@ public class ShareFileFragment extends Fragment
     public void onAttach(Activity activity) {
         super.onAttach(activity);
         try {
-            mListener = (OnShareFragmentInteractionListener) activity;
+            mListener = (ShareFragmentListener) activity;
         } catch (ClassCastException e) {
             throw new ClassCastException(activity.toString()
                     + " must implement OnShareFragmentInteractionListener");
@@ -536,10 +533,16 @@ public class ShareFileFragment extends Fragment
     @Override
     public void unshareButtonPressed(OCShare share) {
         // Unshare
+        Log_OC.d(TAG, "Unsharing " + share.getSharedWithDisplayName());
         mListener.unshareWith(share);
-        Log_OC.d(TAG, "Unshare - " + share.getSharedWithDisplayName());
     }
 
+    @Override
+    public void editShare(OCShare share) {
+        // move to fragment to edit share
+        Log_OC.d(TAG, "Editing " + share.getSharedWithDisplayName());
+        mListener.showEditShare(share);
+    }
 
 
     /**
@@ -736,20 +739,4 @@ public class ShareFileFragment extends Fragment
         listView.requestLayout();
     }
 
-    /**
-     * This interface must be implemented by activities that contain this
-     * fragment to allow an interaction in this fragment to be communicated
-     * to the activity and potentially other fragments contained in that
-     * activity.
-     * <p/>
-     * See the Android Training lesson <a href=
-     * "http://developer.android.com/training/basics/fragments/communicating.html"
-     * >Communicating with Other Fragments</a> for more information.
-     */
-    public interface OnShareFragmentInteractionListener {
-        void showSearchUsersAndGroups();
-        void refreshUsersOrGroupsListFromServer();
-        void unshareWith(OCShare share);
-    }
-
 }

+ 41 - 0
src/com/owncloud/android/ui/fragment/ShareFragmentListener.java

@@ -0,0 +1,41 @@
+/**
+ *   ownCloud Android client application
+ *
+ *   @author masensio
+ *   @author David A. Velasco
+ *   Copyright (C) 2015 ownCloud Inc.
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License version 2,
+ *   as published by the Free Software Foundation.
+ *
+ *   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 General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package com.owncloud.android.ui.fragment;
+
+import com.owncloud.android.lib.resources.shares.OCShare;
+
+/**
+ * This interface must be implemented by activities that contain this
+ * fragment to allow an interaction in fragments handling {@link OCShare}s
+ * to be communicated to the parent activity and potentially other fragments
+ * contained in that activity.
+ * <p/>
+ * See the Android Training lesson <a href=
+ * "http://developer.android.com/training/basics/fragments/communicating.html"
+ * >Communicating with Other Fragments</a> for more information.
+ */
+public interface ShareFragmentListener {
+    void showSearchUsersAndGroups();
+    void showEditShare(OCShare share);
+    void refreshUsersOrGroupsListFromServer();
+    void unshareWith(OCShare share);
+}

+ 3 - 1
src/com/owncloud/android/utils/ErrorMessageAdapter.java

@@ -39,6 +39,7 @@ import com.owncloud.android.operations.RenameFileOperation;
 import com.owncloud.android.operations.SynchronizeFileOperation;
 import com.owncloud.android.operations.SynchronizeFolderOperation;
 import com.owncloud.android.operations.UnshareOperation;
+import com.owncloud.android.operations.UpdateSharePermissionsOperation;
 import com.owncloud.android.operations.UpdateShareViaLinkOperation;
 import com.owncloud.android.operations.UploadFileOperation;
 
@@ -204,7 +205,8 @@ public class ErrorMessageAdapter {
                 message = res.getString(R.string.unshare_link_file_error);
             }
 
-        } else if (operation instanceof UpdateShareViaLinkOperation) {
+        } else if (operation instanceof UpdateShareViaLinkOperation ||
+                    operation instanceof UpdateSharePermissionsOperation) {
 
             if (result.getData() != null && result.getData().size() > 0) {
                 message = (String) result.getData().get(0);     // share API sends its own error messages