瀏覽代碼

Merge pull request #6504 from nextcloud/sharingPart2

Sharing part2
Tobias Kaminsky 4 年之前
父節點
當前提交
c781447559
共有 65 個文件被更改,包括 1791 次插入1145 次删除
  1. 二進制
      screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsSharing.png
  2. 二進制
      screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailSharingFragment.png
  3. 二進制
      screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileAllShareTypes.png
  4. 二進制
      screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileNone.png
  5. 二進制
      screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileResharingNotAllowed.png
  6. 二進制
      screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listShares_file_allShareTypes.png
  7. 二進制
      screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listShares_file_none.png
  8. 二進制
      screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listShares_file_resharing_not_allowed.png
  9. 二進制
      screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_publicLink_optionMenu.png
  10. 1 1
      scripts/analysis/findbugs-results.txt
  11. 1 1
      scripts/analysis/lint-results.txt
  12. 1 1
      src/androidTest/java/com/nextcloud/client/FileDisplayActivityIT.java
  13. 6 1
      src/androidTest/java/com/owncloud/android/AbstractIT.java
  14. 4 0
      src/androidTest/java/com/owncloud/android/authentication/AuthenticatorActivityIT.kt
  15. 0 2
      src/androidTest/java/com/owncloud/android/datamodel/OCFileUnitTest.java
  16. 2 1
      src/androidTest/java/com/owncloud/android/ui/fragment/AvatarIT.kt
  17. 5 1
      src/androidTest/java/com/owncloud/android/ui/fragment/AvatarTestFragment.kt
  18. 1 1
      src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailFragmentStaticServerIT.kt
  19. 630 0
      src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailSharingFragmentIT.kt
  20. 6 0
      src/debug/java/com/nextcloud/client/TestActivity.kt
  21. 3 9
      src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java
  22. 2 13
      src/main/java/com/owncloud/android/datamodel/OCFile.java
  23. 0 2
      src/main/java/com/owncloud/android/db/ProviderMeta.java
  24. 5 5
      src/main/java/com/owncloud/android/files/services/FileUploader.java
  25. 1 2
      src/main/java/com/owncloud/android/operations/CreateShareViaLinkOperation.java
  26. 0 1
      src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java
  27. 0 1
      src/main/java/com/owncloud/android/operations/SynchronizeFolderOperation.java
  28. 0 1
      src/main/java/com/owncloud/android/operations/UnshareOperation.java
  29. 0 14
      src/main/java/com/owncloud/android/operations/UpdateShareViaLinkOperation.java
  30. 2 2
      src/main/java/com/owncloud/android/operations/UploadFileOperation.java
  31. 0 5
      src/main/java/com/owncloud/android/providers/FileContentProvider.java
  32. 6 11
      src/main/java/com/owncloud/android/services/OperationsService.java
  33. 3 6
      src/main/java/com/owncloud/android/ui/activity/FileActivity.java
  34. 1 2
      src/main/java/com/owncloud/android/ui/activity/ShareActivity.java
  35. 74 0
      src/main/java/com/owncloud/android/ui/adapter/InternalShareViewHolder.java
  36. 74 0
      src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.java
  37. 7 22
      src/main/java/com/owncloud/android/ui/adapter/NewLinkShareViewHolder.java
  38. 0 68
      src/main/java/com/owncloud/android/ui/adapter/PublicShareListAdapter.java
  39. 102 0
      src/main/java/com/owncloud/android/ui/adapter/ShareViewHolder.java
  40. 101 326
      src/main/java/com/owncloud/android/ui/adapter/ShareeListAdapter.java
  41. 9 1
      src/main/java/com/owncloud/android/ui/adapter/ShareeListAdapterListener.java
  42. 1 1
      src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java
  43. 20 22
      src/main/java/com/owncloud/android/ui/dialog/SharePasswordDialogFragment.java
  44. 308 175
      src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java
  45. 7 3
      src/main/java/com/owncloud/android/ui/fragment/util/SharingMenuHelper.java
  46. 4 56
      src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java
  47. 0 23
      src/main/res/drawable/ic_hide.xml
  48. 0 25
      src/main/res/drawable/ic_show.xml
  49. 0 8
      src/main/res/drawable/password_visibility_selector.xml
  50. 1 2
      src/main/res/layout/file_details_fragment.xml
  51. 67 0
      src/main/res/layout/file_details_share_internal_share_link.xml
  52. 16 11
      src/main/res/layout/file_details_share_link_share_item.xml
  53. 3 4
      src/main/res/layout/file_details_share_public_link_add_new_item.xml
  54. 23 35
      src/main/res/layout/file_details_share_share_item.xml
  55. 63 124
      src/main/res/layout/file_details_sharing_fragment.xml
  56. 11 23
      src/main/res/layout/password_dialog.xml
  57. 0 74
      src/main/res/layout/share_user_item.xml
  58. 1 1
      src/main/res/layout/toolbar_standard.xml
  59. 68 0
      src/main/res/menu/fragment_file_detail_sharing_email_link.xml
  60. 23 20
      src/main/res/menu/fragment_file_detail_sharing_public_link.xml
  61. 9 20
      src/main/res/menu/item_user_sharing_settings.xml
  62. 2 4
      src/main/res/values/dims.xml
  63. 10 7
      src/main/res/values/strings.xml
  64. 0 7
      src/main/res/values/styles.xml
  65. 107 0
      src/test/java/com/owncloud/android/ui/adapter/ShareeListAdapterTest.kt

二進制
screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsSharing.png


二進制
screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailSharingFragment.png


二進制
screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileAllShareTypes.png


二進制
screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileNone.png


二進制
screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileResharingNotAllowed.png


二進制
screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listShares_file_allShareTypes.png


二進制
screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listShares_file_none.png


二進制
screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listShares_file_resharing_not_allowed.png


二進制
screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_publicLink_optionMenu.png


+ 1 - 1
scripts/analysis/findbugs-results.txt

@@ -1 +1 @@
-320
+319

+ 1 - 1
scripts/analysis/lint-results.txt

@@ -1,2 +1,2 @@
 DO NOT TOUCH; GENERATED BY DRONE
-      <span class="mdl-layout-title">Lint Report: 81 warnings</span>
+      <span class="mdl-layout-title">Lint Report: 80 warnings</span>

+ 1 - 1
src/androidTest/java/com/nextcloud/client/FileDisplayActivityIT.java

@@ -97,7 +97,7 @@ public class FileDisplayActivityIT extends AbstractOnServerIT {
                                                          "users",
                                                          false,
                                                          "",
-                                                         OCShare.DEFAULT_PERMISSION)
+                                                         OCShare.NO_PERMISSION)
                               .execute(client).isSuccess());
 
         // share folder to circle

+ 6 - 1
src/androidTest/java/com/owncloud/android/AbstractIT.java

@@ -58,6 +58,7 @@ import androidx.test.runner.lifecycle.Stage;
 import static androidx.test.InstrumentationRegistry.getInstrumentation;
 import static androidx.test.espresso.Espresso.onView;
 import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static com.owncloud.android.lib.common.accounts.AccountUtils.Constants.KEY_USER_ID;
 import static org.junit.Assert.assertTrue;
 
 
@@ -96,7 +97,7 @@ public abstract class AbstractIT {
             Account temp = new Account("test@https://server.com", MainApp.getAccountType(targetContext));
             platformAccountManager.addAccountExplicitly(temp, "password", null);
             platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_OC_BASE_URL, "https://server.com");
-            platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_USER_ID, "test");
+            platformAccountManager.setUserData(temp, KEY_USER_ID, "test");
 
             final UserAccountManager userAccountManager = UserAccountManagerImpl.fromContext(targetContext);
             account = userAccountManager.getAccountByName("test@https://server.com");
@@ -381,4 +382,8 @@ public abstract class AbstractIT {
 
         return name;
     }
+
+    public static String getUserId(User user) {
+        return AccountManager.get(targetContext).getUserData(user.toPlatformAccount(), KEY_USER_ID);
+    }
 }

+ 4 - 0
src/androidTest/java/com/owncloud/android/authentication/AuthenticatorActivityIT.kt

@@ -32,6 +32,7 @@ class AuthenticatorActivityIT {
     }
 
     @Test
+    @Suppress("TooGenericExceptionCaught")
     fun tryCatch() {
         val color = try {
             Color.parseColor("1")
@@ -43,6 +44,7 @@ class AuthenticatorActivityIT {
     }
 
     @Test
+    @Suppress("TooGenericExceptionCaught")
     fun tryCatch2() {
         val color = try {
             Color.parseColor("")
@@ -54,6 +56,7 @@ class AuthenticatorActivityIT {
     }
 
     @Test
+    @Suppress("TooGenericExceptionCaught")
     fun tryCatch3() {
         val color = try {
             Color.parseColor(null)
@@ -65,6 +68,7 @@ class AuthenticatorActivityIT {
     }
 
     @Test
+    @Suppress("TooGenericExceptionCaught")
     fun tryCatch4() {
         val color = try {
             Color.parseColor("abc")

+ 0 - 2
src/androidTest/java/com/owncloud/android/datamodel/OCFileUnitTest.java

@@ -88,7 +88,6 @@ public class OCFileUnitTest {
         mFile.setEtag(ETAG);
         mFile.setSharedViaLink(true);
         mFile.setSharedWithSharee(true);
-        mFile.setPublicLink(PUBLIC_LINK);
         mFile.setPermissions(PERMISSIONS);
         mFile.setRemoteId(REMOTE_ID);
         mFile.setUpdateThumbnailNeeded(true);
@@ -122,7 +121,6 @@ public class OCFileUnitTest {
         assertThat(fileReadFromParcel.getEtag(), is(ETAG));
         assertThat(fileReadFromParcel.isSharedViaLink(), is(true));
         assertThat(fileReadFromParcel.isSharedWithSharee(), is(true));
-        assertThat(fileReadFromParcel.getPublicLink(), is(PUBLIC_LINK));
         assertThat(fileReadFromParcel.getPermissions(), is(PERMISSIONS));
         assertThat(fileReadFromParcel.getRemoteId(), is(REMOTE_ID));
         assertThat(fileReadFromParcel.isUpdateThumbnailNeeded(), is(true));

+ 2 - 1
src/androidTest/java/com/owncloud/android/ui/fragment/AvatarIT.kt

@@ -40,7 +40,7 @@ class AvatarIT : AbstractIT() {
     @ScreenshotTest
     fun showAvatars() {
         val avatarRadius = targetContext.resources.getDimension(R.dimen.list_item_avatar_icon_radius)
-        val width = DisplayUtils.convertDpToPixel(40f, targetContext)
+        val width = DisplayUtils.convertDpToPixel(2 * avatarRadius, targetContext)
         val sut = testActivityRule.launchActivity(null)
         val fragment = AvatarTestFragment()
 
@@ -56,6 +56,7 @@ class AvatarIT : AbstractIT() {
             fragment.addAvatar("email@server.com", avatarRadius, width, targetContext)
         }
 
+        shortSleep()
         waitForIdleSync()
         screenshot(sut)
     }

+ 5 - 1
src/androidTest/java/com/owncloud/android/ui/fragment/AvatarTestFragment.kt

@@ -45,7 +45,7 @@ internal class AvatarTestFragment : Fragment() {
     }
 
     fun addAvatar(name: String, avatarRadius: Float, width: Int, targetContext: Context) {
-        val margin = 10
+        val margin = padding
         val imageView = ImageView(targetContext)
         imageView.setImageDrawable(TextDrawable.createNamedAvatar(name, avatarRadius))
 
@@ -56,4 +56,8 @@ internal class AvatarTestFragment : Fragment() {
 
         list.addView(imageView)
     }
+
+    companion object {
+        private const val padding = 10
+    }
 }

+ 1 - 1
src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailFragmentStaticServerIT.kt

@@ -172,7 +172,7 @@ class FileDetailFragmentStaticServerIT : AbstractIT() {
 
     @Test
     @ScreenshotTest
-    fun showDetails_Sharing() {
+    fun showDetailsSharing() {
         val sut = testActivityRule.launchActivity(null)
         sut.addFragment(FileDetailFragment.newInstance(file, user, 1))
 

+ 630 - 0
src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailSharingFragmentIT.kt

@@ -0,0 +1,630 @@
+/*
+ *
+ * Nextcloud Android client application
+ *
+ * @author Tobias Kaminsky
+ * Copyright (C) 2020 Tobias Kaminsky
+ * Copyright (C) 2020 Nextcloud GmbH
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+package com.owncloud.android.ui.fragment
+
+import android.Manifest
+import android.widget.ImageView
+import androidx.appcompat.widget.PopupMenu
+import androidx.test.espresso.intent.rule.IntentsTestRule
+import androidx.test.rule.GrantPermissionRule
+import com.nextcloud.client.TestActivity
+import com.owncloud.android.AbstractIT
+import com.owncloud.android.R
+import com.owncloud.android.datamodel.OCFile
+import com.owncloud.android.lib.resources.shares.OCShare
+import com.owncloud.android.lib.resources.shares.OCShare.CREATE_PERMISSION_FLAG
+import com.owncloud.android.lib.resources.shares.OCShare.DELETE_PERMISSION_FLAG
+import com.owncloud.android.lib.resources.shares.OCShare.MAXIMUM_PERMISSIONS_FOR_FILE
+import com.owncloud.android.lib.resources.shares.OCShare.MAXIMUM_PERMISSIONS_FOR_FOLDER
+import com.owncloud.android.lib.resources.shares.OCShare.NO_PERMISSION
+import com.owncloud.android.lib.resources.shares.OCShare.READ_PERMISSION_FLAG
+import com.owncloud.android.lib.resources.shares.OCShare.SHARE_PERMISSION_FLAG
+import com.owncloud.android.lib.resources.shares.ShareType
+import com.owncloud.android.utils.ScreenshotTest
+import org.junit.After
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+
+class FileDetailSharingFragmentIT : AbstractIT() {
+    @get:Rule
+    val testActivityRule = IntentsTestRule(TestActivity::class.java, true, false)
+
+    @get:Rule
+    val permissionRule: GrantPermissionRule = GrantPermissionRule.grant(Manifest.permission.WRITE_EXTERNAL_STORAGE)
+
+    lateinit var file: OCFile
+    lateinit var folder: OCFile
+    lateinit var activity: TestActivity
+
+    @Before
+    fun before() {
+        activity = testActivityRule.launchActivity(null)
+        file = OCFile("/test.md").apply {
+            parentId = activity.storageManager.getFileByEncryptedRemotePath("/").fileId
+            permissions = OCFile.PERMISSION_CAN_RESHARE
+        }
+
+        folder = OCFile("/test").apply {
+            setFolder()
+            parentId = activity.storageManager.getFileByEncryptedRemotePath("/").fileId
+            permissions = OCFile.PERMISSION_CAN_RESHARE
+        }
+    }
+
+    @Test
+    @ScreenshotTest
+    fun listSharesFileNone() {
+        show(file)
+    }
+
+    @Test
+    @ScreenshotTest
+    fun listSharesFileResharingNotAllowed() {
+        file.permissions = ""
+
+        show(file)
+    }
+
+    @Test
+    @ScreenshotTest
+    @Suppress("MagicNumber")
+    fun listSharesFileAllShareTypes() {
+        OCShare(file.decryptedRemotePath).apply {
+            remoteId = 1
+            shareType = ShareType.USER
+            sharedWithDisplayName = "Admin"
+            permissions = MAXIMUM_PERMISSIONS_FOR_FILE
+            userId = getUserId(user)
+            activity.storageManager.saveShare(this)
+        }
+
+        OCShare(file.decryptedRemotePath).apply {
+            remoteId = 2
+            shareType = ShareType.GROUP
+            sharedWithDisplayName = "Group"
+            permissions = MAXIMUM_PERMISSIONS_FOR_FILE
+            userId = getUserId(user)
+            activity.storageManager.saveShare(this)
+        }
+
+        OCShare(file.decryptedRemotePath).apply {
+            remoteId = 3
+            shareType = ShareType.EMAIL
+            sharedWithDisplayName = "admin@nextcloud.server.com"
+            userId = getUserId(user)
+            activity.storageManager.saveShare(this)
+        }
+
+        OCShare(file.decryptedRemotePath).apply {
+            remoteId = 4
+            shareType = ShareType.PUBLIC_LINK
+            sharedWithDisplayName = "Customer"
+            activity.storageManager.saveShare(this)
+        }
+
+        OCShare(file.decryptedRemotePath).apply {
+            remoteId = 5
+            shareType = ShareType.PUBLIC_LINK
+            sharedWithDisplayName = "Colleagues"
+            activity.storageManager.saveShare(this)
+        }
+
+        OCShare(file.decryptedRemotePath).apply {
+            remoteId = 6
+            shareType = ShareType.FEDERATED
+            sharedWithDisplayName = "admin@nextcloud.remoteserver.com"
+            permissions = OCShare.FEDERATED_PERMISSIONS_FOR_FILE
+            userId = getUserId(user)
+            activity.storageManager.saveShare(this)
+        }
+
+        OCShare(file.decryptedRemotePath).apply {
+            remoteId = 7
+            shareType = ShareType.CIRCLE
+            sharedWithDisplayName = "Private circle"
+            permissions = SHARE_PERMISSION_FLAG
+            userId = getUserId(user)
+            activity.storageManager.saveShare(this)
+        }
+
+        OCShare(file.decryptedRemotePath).apply {
+            remoteId = 8
+            shareType = ShareType.ROOM
+            sharedWithDisplayName = "Meeting"
+            permissions = SHARE_PERMISSION_FLAG
+            userId = getUserId(user)
+            activity.storageManager.saveShare(this)
+        }
+
+        show(file)
+    }
+
+    private fun show(file: OCFile) {
+        val fragment = FileDetailSharingFragment.newInstance(file, user)
+
+        activity.addFragment(fragment)
+
+        waitForIdleSync()
+
+        screenshot(activity)
+    }
+
+    @Test
+    @Suppress("MagicNumber")
+    // public link and email are handled the same way
+    fun publicLinkOptionMenuFolder() {
+        val sut = FileDetailSharingFragment.newInstance(file, user)
+        activity.addFragment(sut)
+        shortSleep()
+        sut.refreshCapabilitiesFromDB()
+
+        val overflowMenuShareLink = ImageView(targetContext)
+        val popup = PopupMenu(targetContext, overflowMenuShareLink)
+        popup.inflate(R.menu.fragment_file_detail_sharing_public_link)
+        val publicShare = OCShare().apply {
+            isFolder = true
+            shareType = ShareType.PUBLIC_LINK
+            permissions = 17
+        }
+
+        sut.prepareLinkOptionsMenu(popup.menu, publicShare)
+
+        // check if items are visible
+        assertTrue(popup.menu.findItem(R.id.action_hide_file_download).isVisible)
+        assertTrue(popup.menu.findItem(R.id.action_password).isVisible)
+        assertTrue(popup.menu.findItem(R.id.action_share_expiration_date).isVisible)
+        assertTrue(popup.menu.findItem(R.id.action_share_send_link).isVisible)
+        assertTrue(popup.menu.findItem(R.id.action_share_send_note).isVisible)
+        assertTrue(popup.menu.findItem(R.id.action_edit_label).isVisible)
+        assertTrue(popup.menu.findItem(R.id.action_unshare).isVisible)
+        assertTrue(popup.menu.findItem(R.id.action_add_another_public_share_link).isVisible)
+
+        // read-only
+        assertTrue(popup.menu.findItem(R.id.link_share_read_only).isChecked)
+        assertFalse(popup.menu.findItem(R.id.link_share_allow_upload_and_editing).isChecked)
+        assertFalse(popup.menu.findItem(R.id.link_share_file_drop).isChecked)
+
+        // upload and editing
+        publicShare.permissions = MAXIMUM_PERMISSIONS_FOR_FOLDER
+        sut.prepareLinkOptionsMenu(popup.menu, publicShare)
+        assertFalse(popup.menu.findItem(R.id.link_share_read_only).isChecked)
+        assertTrue(popup.menu.findItem(R.id.link_share_allow_upload_and_editing).isChecked)
+        assertFalse(popup.menu.findItem(R.id.link_share_file_drop).isChecked)
+
+        // file drop
+        publicShare.permissions = 4
+        sut.prepareLinkOptionsMenu(popup.menu, publicShare)
+        assertFalse(popup.menu.findItem(R.id.link_share_read_only).isChecked)
+        assertFalse(popup.menu.findItem(R.id.link_share_allow_upload_and_editing).isChecked)
+        assertTrue(popup.menu.findItem(R.id.link_share_file_drop).isChecked)
+
+        // password protection
+        publicShare.shareWith = "someValue"
+        sut.prepareLinkOptionsMenu(popup.menu, publicShare)
+        assertTrue(
+            popup.menu.findItem(R.id.action_password).title == targetContext.getString(R.string.share_password_title)
+        )
+
+        publicShare.shareWith = ""
+        sut.prepareLinkOptionsMenu(popup.menu, publicShare)
+        assertTrue(
+            popup.menu.findItem(R.id.action_password).title == targetContext.getString(R.string.share_no_password_title)
+        )
+
+        // hide download
+        publicShare.isHideFileDownload = true
+        publicShare.permissions = MAXIMUM_PERMISSIONS_FOR_FOLDER
+        sut.prepareLinkOptionsMenu(popup.menu, publicShare)
+        assertTrue(popup.menu.findItem(R.id.action_hide_file_download).isChecked)
+
+        publicShare.isHideFileDownload = false
+        sut.prepareLinkOptionsMenu(popup.menu, publicShare)
+        assertFalse(popup.menu.findItem(R.id.action_hide_file_download).isChecked)
+
+        publicShare.expirationDate = 1582019340000
+        sut.prepareLinkOptionsMenu(popup.menu, publicShare)
+        assertTrue(
+            popup.menu.findItem(R.id.action_share_expiration_date).title
+                .startsWith(targetContext.getString(R.string.share_expiration_date_label).split(" ")[0])
+        )
+
+        publicShare.expirationDate = 0
+        sut.prepareLinkOptionsMenu(popup.menu, publicShare)
+        assertTrue(
+            popup.menu.findItem(R.id.action_share_expiration_date).title ==
+                targetContext.getString(R.string.share_no_expiration_date_label)
+        )
+
+        // file
+        publicShare.isFolder = false
+        publicShare.permissions = READ_PERMISSION_FLAG
+        sut.prepareLinkOptionsMenu(popup.menu, publicShare)
+        // check if items are visible
+        assertTrue(popup.menu.findItem(R.id.action_hide_file_download).isVisible)
+        assertTrue(popup.menu.findItem(R.id.action_password).isVisible)
+        assertTrue(popup.menu.findItem(R.id.action_share_expiration_date).isVisible)
+        assertTrue(popup.menu.findItem(R.id.action_share_send_link).isVisible)
+        assertTrue(popup.menu.findItem(R.id.action_share_send_note).isVisible)
+        assertTrue(popup.menu.findItem(R.id.action_edit_label).isVisible)
+        assertTrue(popup.menu.findItem(R.id.action_unshare).isVisible)
+        assertTrue(popup.menu.findItem(R.id.action_add_another_public_share_link).isVisible)
+
+        assertFalse(popup.menu.findItem(R.id.link_share_read_only).isVisible)
+        assertFalse(popup.menu.findItem(R.id.link_share_allow_upload_and_editing).isVisible)
+        assertFalse(popup.menu.findItem(R.id.link_share_file_drop).isVisible)
+        assertTrue(popup.menu.findItem(R.id.allow_editing).isVisible)
+
+        // allow editing
+        publicShare.permissions = 17 // from server
+        sut.prepareLinkOptionsMenu(popup.menu, publicShare)
+        assertFalse(popup.menu.findItem(R.id.allow_editing).isChecked)
+
+        publicShare.permissions = 19 // from server
+        sut.prepareLinkOptionsMenu(popup.menu, publicShare)
+        assertTrue(popup.menu.findItem(R.id.allow_editing).isChecked)
+
+        // hide download
+        publicShare.isHideFileDownload = true
+        sut.prepareLinkOptionsMenu(popup.menu, publicShare)
+        assertTrue(popup.menu.findItem(R.id.action_hide_file_download).isChecked)
+
+        publicShare.isHideFileDownload = false
+        sut.prepareLinkOptionsMenu(popup.menu, publicShare)
+        assertFalse(popup.menu.findItem(R.id.action_hide_file_download).isChecked)
+
+        // password protection
+        publicShare.isPasswordProtected = true
+        publicShare.shareWith = "someValue"
+        sut.prepareLinkOptionsMenu(popup.menu, publicShare)
+        assertTrue(
+            popup.menu.findItem(R.id.action_password).title == targetContext.getString(R.string.share_password_title)
+        )
+
+        publicShare.isPasswordProtected = false
+        publicShare.shareWith = ""
+        sut.prepareLinkOptionsMenu(popup.menu, publicShare)
+        assertTrue(
+            popup.menu.findItem(R.id.action_password).title == targetContext.getString(R.string.share_no_password_title)
+        )
+
+        // expires
+        publicShare.expirationDate = 1582019340
+        sut.prepareLinkOptionsMenu(popup.menu, publicShare)
+        assertTrue(
+            popup.menu.findItem(R.id.action_share_expiration_date).title
+                .startsWith(targetContext.getString(R.string.share_expiration_date_label).split(" ")[0])
+        )
+
+        publicShare.expirationDate = 0
+        sut.prepareLinkOptionsMenu(popup.menu, publicShare)
+        assertTrue(
+            popup.menu.findItem(R.id.action_share_expiration_date).title ==
+                targetContext.getString(R.string.share_no_expiration_date_label)
+        )
+    }
+
+    @Test
+    @Suppress("MagicNumber")
+    // public link and email are handled the same way
+    fun publicLinkOptionMenuFile() {
+        val sut = FileDetailSharingFragment.newInstance(file, user)
+        activity.addFragment(sut)
+        shortSleep()
+        sut.refreshCapabilitiesFromDB()
+
+        val overflowMenuShareLink = ImageView(targetContext)
+        val popup = PopupMenu(targetContext, overflowMenuShareLink)
+        popup.inflate(R.menu.fragment_file_detail_sharing_public_link)
+        val publicShare = OCShare().apply {
+            isFolder = false
+            shareType = ShareType.PUBLIC_LINK
+            permissions = 17
+        }
+
+        sut.prepareLinkOptionsMenu(popup.menu, publicShare)
+
+        // check if items are visible
+        assertTrue(popup.menu.findItem(R.id.action_hide_file_download).isVisible)
+        assertTrue(popup.menu.findItem(R.id.action_password).isVisible)
+        assertTrue(popup.menu.findItem(R.id.action_share_expiration_date).isVisible)
+        assertTrue(popup.menu.findItem(R.id.action_share_send_link).isVisible)
+        assertTrue(popup.menu.findItem(R.id.action_share_send_note).isVisible)
+        assertTrue(popup.menu.findItem(R.id.action_edit_label).isVisible)
+        assertTrue(popup.menu.findItem(R.id.action_unshare).isVisible)
+        assertTrue(popup.menu.findItem(R.id.action_add_another_public_share_link).isVisible)
+
+        assertFalse(popup.menu.findItem(R.id.link_share_read_only).isVisible)
+        assertFalse(popup.menu.findItem(R.id.link_share_allow_upload_and_editing).isVisible)
+        assertFalse(popup.menu.findItem(R.id.link_share_file_drop).isVisible)
+        assertTrue(popup.menu.findItem(R.id.allow_editing).isVisible)
+
+        // password protection
+        publicShare.shareWith = "someValue"
+        sut.prepareLinkOptionsMenu(popup.menu, publicShare)
+        assertTrue(
+            popup.menu.findItem(R.id.action_password).title == targetContext.getString(R.string.share_password_title)
+        )
+
+        publicShare.shareWith = ""
+        sut.prepareLinkOptionsMenu(popup.menu, publicShare)
+        assertTrue(
+            popup.menu.findItem(R.id.action_password).title == targetContext.getString(R.string.share_no_password_title)
+        )
+
+        // hide download
+        publicShare.isHideFileDownload = true
+        sut.prepareLinkOptionsMenu(popup.menu, publicShare)
+        assertTrue(popup.menu.findItem(R.id.action_hide_file_download).isChecked)
+
+        publicShare.isHideFileDownload = false
+        sut.prepareLinkOptionsMenu(popup.menu, publicShare)
+        assertFalse(popup.menu.findItem(R.id.action_hide_file_download).isChecked)
+
+        // expiration date
+        publicShare.expirationDate = 1582019340000
+        sut.prepareLinkOptionsMenu(popup.menu, publicShare)
+        assertTrue(
+            popup.menu.findItem(R.id.action_share_expiration_date).title
+                .startsWith(targetContext.getString(R.string.share_expiration_date_label).split(" ")[0])
+        )
+
+        publicShare.expirationDate = 0
+        sut.prepareLinkOptionsMenu(popup.menu, publicShare)
+        assertTrue(
+            popup.menu.findItem(R.id.action_share_expiration_date).title ==
+                targetContext.getString(R.string.share_no_expiration_date_label)
+        )
+
+        publicShare.isFolder = false
+        publicShare.permissions = READ_PERMISSION_FLAG
+        sut.prepareLinkOptionsMenu(popup.menu, publicShare)
+
+        // allow editing
+        publicShare.permissions = 17 // from server
+        assertFalse(popup.menu.findItem(R.id.allow_editing).isChecked)
+
+        publicShare.permissions = 19 // from server
+        sut.prepareLinkOptionsMenu(popup.menu, publicShare)
+        assertTrue(popup.menu.findItem(R.id.allow_editing).isChecked)
+    }
+
+    @Test
+    @Suppress("MagicNumber")
+    // also applies for
+    // group
+    // conversation
+    // circle
+    // federated share
+    fun userOptionMenuFile() {
+        val sut = FileDetailSharingFragment.newInstance(file, user)
+        activity.addFragment(sut)
+        shortSleep()
+        sut.refreshCapabilitiesFromDB()
+
+        val overflowMenuShareLink = ImageView(targetContext)
+        val popup = PopupMenu(targetContext, overflowMenuShareLink)
+        popup.inflate(R.menu.item_user_sharing_settings)
+        val userShare = OCShare().apply {
+            isFolder = false
+            shareType = ShareType.USER
+            permissions = 17
+        }
+
+        sut.prepareUserOptionsMenu(popup.menu, userShare)
+        assertFalse(popup.menu.findItem(R.id.allow_creating).isVisible)
+        assertFalse(popup.menu.findItem(R.id.allow_deleting).isVisible)
+
+        // allow editing
+        userShare.permissions = 17 // from server
+        assertFalse(popup.menu.findItem(R.id.allow_editing).isChecked)
+
+        userShare.permissions = 19 // from server
+        sut.prepareUserOptionsMenu(popup.menu, userShare)
+        assertTrue(popup.menu.findItem(R.id.allow_editing).isChecked)
+
+        // allow reshare
+        userShare.permissions = 1 // from server
+        sut.prepareUserOptionsMenu(popup.menu, userShare)
+        assertFalse(popup.menu.findItem(R.id.allow_resharing).isChecked)
+
+        userShare.permissions = 17 // from server
+        sut.prepareUserOptionsMenu(popup.menu, userShare)
+        assertTrue(popup.menu.findItem(R.id.allow_resharing).isChecked)
+
+        // set expiration date
+        userShare.expirationDate = 1582019340000
+        sut.prepareUserOptionsMenu(popup.menu, userShare)
+        assertTrue(
+            popup.menu.findItem(R.id.action_expiration_date).title
+                .startsWith(targetContext.getString(R.string.share_expiration_date_label).split(" ")[0])
+        )
+
+        userShare.expirationDate = 0
+        sut.prepareUserOptionsMenu(popup.menu, userShare)
+        assertTrue(
+            popup.menu.findItem(R.id.action_expiration_date).title ==
+                targetContext.getString(R.string.share_no_expiration_date_label)
+        )
+
+        // note
+        assertTrue(popup.menu.findItem(R.id.action_share_send_note).isVisible)
+    }
+
+    @Test
+    @Suppress("MagicNumber")
+    // also applies for
+    // group
+    // conversation
+    // circle
+    // federated share
+    fun userOptionMenuFolder() {
+        val sut = FileDetailSharingFragment.newInstance(file, user)
+        activity.addFragment(sut)
+        shortSleep()
+        sut.refreshCapabilitiesFromDB()
+
+        val overflowMenuShareLink = ImageView(targetContext)
+        val popup = PopupMenu(targetContext, overflowMenuShareLink)
+        popup.inflate(R.menu.item_user_sharing_settings)
+        val userShare = OCShare().apply {
+            isFolder = true
+            shareType = ShareType.USER
+            permissions = 17
+        }
+
+        sut.prepareUserOptionsMenu(popup.menu, userShare)
+        assertTrue(popup.menu.findItem(R.id.allow_creating).isVisible)
+        assertTrue(popup.menu.findItem(R.id.allow_deleting).isVisible)
+
+        // allow editing
+        userShare.permissions = 17 // from server
+        assertFalse(popup.menu.findItem(R.id.allow_editing).isChecked)
+
+        userShare.permissions = 19 // from server
+        sut.prepareUserOptionsMenu(popup.menu, userShare)
+        assertTrue(popup.menu.findItem(R.id.allow_editing).isChecked)
+
+        // allow reshare
+        userShare.permissions = 1 // from server
+        sut.prepareUserOptionsMenu(popup.menu, userShare)
+        assertFalse(popup.menu.findItem(R.id.allow_resharing).isChecked)
+
+        userShare.permissions = 17 // from server
+        sut.prepareUserOptionsMenu(popup.menu, userShare)
+        assertTrue(popup.menu.findItem(R.id.allow_resharing).isChecked)
+
+        // set expiration date
+        userShare.expirationDate = 1582019340000
+        sut.prepareUserOptionsMenu(popup.menu, userShare)
+        assertTrue(
+            popup.menu.findItem(R.id.action_expiration_date).title
+                .startsWith(targetContext.getString(R.string.share_expiration_date_label).split(" ")[0])
+        )
+
+        userShare.expirationDate = 0
+        sut.prepareUserOptionsMenu(popup.menu, userShare)
+        assertTrue(
+            popup.menu.findItem(R.id.action_expiration_date).title ==
+                targetContext.getString(R.string.share_no_expiration_date_label)
+        )
+
+        // note
+        assertTrue(popup.menu.findItem(R.id.action_share_send_note).isVisible)
+    }
+
+    @Test
+    fun testUploadAndEditingSharePermissions() {
+        val sut = FileDetailSharingFragment()
+
+        val share = OCShare().apply {
+            permissions = MAXIMUM_PERMISSIONS_FOR_FOLDER
+        }
+        assertTrue(sut.isUploadAndEditingAllowed(share))
+
+        share.permissions = NO_PERMISSION
+        assertFalse(sut.isUploadAndEditingAllowed(share))
+
+        share.permissions = READ_PERMISSION_FLAG
+        assertFalse(sut.isUploadAndEditingAllowed(share))
+
+        share.permissions = CREATE_PERMISSION_FLAG
+        assertFalse(sut.isUploadAndEditingAllowed(share))
+
+        share.permissions = DELETE_PERMISSION_FLAG
+        assertFalse(sut.isUploadAndEditingAllowed(share))
+
+        share.permissions = SHARE_PERMISSION_FLAG
+        assertFalse(sut.isUploadAndEditingAllowed(share))
+    }
+
+    @Test
+    @Suppress("MagicNumber")
+    fun testReadOnlySharePermissions() {
+        val sut = FileDetailSharingFragment()
+
+        val share = OCShare().apply {
+            permissions = 17
+        }
+        assertTrue(sut.isReadOnly(share))
+
+        share.permissions = NO_PERMISSION
+        assertFalse(sut.isReadOnly(share))
+
+        share.permissions = READ_PERMISSION_FLAG
+        assertTrue(sut.isReadOnly(share))
+
+        share.permissions = CREATE_PERMISSION_FLAG
+        assertFalse(sut.isReadOnly(share))
+
+        share.permissions = DELETE_PERMISSION_FLAG
+        assertFalse(sut.isReadOnly(share))
+
+        share.permissions = SHARE_PERMISSION_FLAG
+        assertFalse(sut.isReadOnly(share))
+
+        share.permissions = MAXIMUM_PERMISSIONS_FOR_FOLDER
+        assertFalse(sut.isReadOnly(share))
+
+        share.permissions = MAXIMUM_PERMISSIONS_FOR_FILE
+        assertFalse(sut.isReadOnly(share))
+    }
+
+    @Test
+    @Suppress("MagicNumber")
+    fun testFileDropSharePermissions() {
+        val sut = FileDetailSharingFragment()
+
+        val share = OCShare().apply {
+            permissions = 4
+        }
+        assertTrue(sut.isFileDrop(share))
+
+        share.permissions = NO_PERMISSION
+        assertFalse(sut.isFileDrop(share))
+
+        share.permissions = READ_PERMISSION_FLAG
+        assertFalse(sut.isFileDrop(share))
+
+        share.permissions = CREATE_PERMISSION_FLAG
+        assertTrue(sut.isFileDrop(share))
+
+        share.permissions = DELETE_PERMISSION_FLAG
+        assertFalse(sut.isFileDrop(share))
+
+        share.permissions = SHARE_PERMISSION_FLAG
+        assertFalse(sut.isFileDrop(share))
+
+        share.permissions = MAXIMUM_PERMISSIONS_FOR_FOLDER
+        assertFalse(sut.isFileDrop(share))
+
+        share.permissions = MAXIMUM_PERMISSIONS_FOR_FILE
+        assertFalse(sut.isFileDrop(share))
+    }
+
+    @After
+    fun after() {
+        activity.storageManager.cleanShares()
+    }
+}

+ 6 - 0
src/debug/java/com/nextcloud/client/TestActivity.kt

@@ -32,6 +32,8 @@ import com.owncloud.android.datamodel.FileDataStorageManager
 import com.owncloud.android.datamodel.OCFile
 import com.owncloud.android.files.services.FileDownloader
 import com.owncloud.android.files.services.FileUploader
+import com.owncloud.android.lib.resources.status.OCCapability
+import com.owncloud.android.lib.resources.status.OwnCloudVersion
 import com.owncloud.android.services.OperationsService
 import com.owncloud.android.ui.activity.FileActivity
 import com.owncloud.android.ui.activity.OnEnforceableRefreshListener
@@ -101,6 +103,10 @@ class TestActivity :
     override fun getStorageManager(): FileDataStorageManager {
         if (!this::storage.isInitialized) {
             storage = FileDataStorageManager(account, contentResolver)
+
+            val ocCapability = OCCapability()
+            ocCapability.versionMayor = OwnCloudVersion.nextcloud_20.majorVersionNumber
+            storage.saveCapabilities(ocCapability)
         }
 
         return storage

+ 3 - 9
src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java

@@ -68,6 +68,7 @@ import java.util.Set;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
 
 
 public class FileDataStorageManager {
@@ -230,7 +231,6 @@ public class FileDataStorageManager {
         cv.put(ProviderTableMeta.FILE_ETAG_ON_SERVER, ocFile.getEtagOnServer());
         cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, ocFile.isSharedViaLink() ? 1 : 0);
         cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, ocFile.isSharedWithSharee() ? 1 : 0);
-        cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, ocFile.getPublicLink());
         cv.put(ProviderTableMeta.FILE_PERMISSIONS, ocFile.getPermissions());
         cv.put(ProviderTableMeta.FILE_REMOTE_ID, ocFile.getRemoteId());
         cv.put(ProviderTableMeta.FILE_UPDATE_THUMBNAIL, ocFile.isUpdateThumbnailNeeded());
@@ -485,7 +485,6 @@ public class FileDataStorageManager {
         cv.put(ProviderTableMeta.FILE_ETAG_ON_SERVER, folder.getEtagOnServer());
         cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, folder.isSharedViaLink() ? 1 : 0);
         cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, folder.isSharedWithSharee() ? 1 : 0);
-        cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, folder.getPublicLink());
         cv.put(ProviderTableMeta.FILE_PERMISSIONS, folder.getPermissions());
         cv.put(ProviderTableMeta.FILE_REMOTE_ID, folder.getRemoteId());
         cv.put(ProviderTableMeta.FILE_FAVORITE, folder.isFavorite());
@@ -520,7 +519,6 @@ public class FileDataStorageManager {
         cv.put(ProviderTableMeta.FILE_ETAG_ON_SERVER, file.getEtagOnServer());
         cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, file.isSharedViaLink() ? 1 : 0);
         cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, file.isSharedWithSharee() ? 1 : 0);
-        cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, file.getPublicLink());
         cv.put(ProviderTableMeta.FILE_PERMISSIONS, file.getPermissions());
         cv.put(ProviderTableMeta.FILE_REMOTE_ID, file.getRemoteId());
         cv.put(ProviderTableMeta.FILE_UPDATE_THUMBNAIL, file.isUpdateThumbnailNeeded());
@@ -1006,7 +1004,6 @@ public class FileDataStorageManager {
             ocFile.setEtagOnServer(cursor.getString(cursor.getColumnIndex(ProviderTableMeta.FILE_ETAG_ON_SERVER)));
             ocFile.setSharedViaLink(cursor.getInt(cursor.getColumnIndex(ProviderTableMeta.FILE_SHARED_VIA_LINK)) == 1);
             ocFile.setSharedWithSharee(cursor.getInt(cursor.getColumnIndex(ProviderTableMeta.FILE_SHARED_WITH_SHAREE)) == 1);
-            ocFile.setPublicLink(cursor.getString(cursor.getColumnIndex(ProviderTableMeta.FILE_PUBLIC_LINK)));
             ocFile.setPermissions(cursor.getString(cursor.getColumnIndex(ProviderTableMeta.FILE_PERMISSIONS)));
             ocFile.setRemoteId(cursor.getString(cursor.getColumnIndex(ProviderTableMeta.FILE_REMOTE_ID)));
             ocFile.setUpdateThumbnailNeeded(cursor.getInt(cursor.getColumnIndex(ProviderTableMeta.FILE_UPDATE_THUMBNAIL)) == 1);
@@ -1296,7 +1293,6 @@ public class FileDataStorageManager {
         ContentValues cv = new ContentValues();
         cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, Boolean.FALSE);
         cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, Boolean.FALSE);
-        cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, "");
         String where = ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?";
         String[] whereArgs = new String[]{account.name};
 
@@ -1316,7 +1312,6 @@ public class FileDataStorageManager {
         ContentValues contentValues = new ContentValues();
         contentValues.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, Boolean.FALSE);
         contentValues.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, Boolean.FALSE);
-        contentValues.put(ProviderTableMeta.FILE_PUBLIC_LINK, "");
         String where = ProviderTableMeta.FILE_ACCOUNT_OWNER + AND + ProviderTableMeta.FILE_PARENT + " = ?";
         String[] whereArgs = new String[]{account.name, String.valueOf(folder.getFileId())};
 
@@ -1336,7 +1331,6 @@ public class FileDataStorageManager {
         ContentValues contentValues = new ContentValues();
         contentValues.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, Boolean.FALSE);
         contentValues.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, Boolean.FALSE);
-        contentValues.put(ProviderTableMeta.FILE_PUBLIC_LINK, "");
         String where = ProviderTableMeta.FILE_ACCOUNT_OWNER + AND + ProviderTableMeta.FILE_PATH + " = ?";
         String[] whereArgs = new String[]{account.name, filePath};
 
@@ -1352,7 +1346,8 @@ public class FileDataStorageManager {
         }
     }
 
-    private void cleanShares() {
+    @VisibleForTesting
+    public void cleanShares() {
         String where = ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?";
         String[] whereArgs = new String[]{account.name};
 
@@ -1447,7 +1442,6 @@ public class FileDataStorageManager {
                 cv.put(ProviderTableMeta.FILE_ETAG_ON_SERVER, file.getEtagOnServer());
                 cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, file.isSharedViaLink() ? 1 : 0);
                 cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, file.isSharedWithSharee() ? 1 : 0);
-                cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, file.getPublicLink());
                 cv.put(ProviderTableMeta.FILE_PERMISSIONS, file.getPermissions());
                 cv.put(ProviderTableMeta.FILE_REMOTE_ID, file.getRemoteId());
                 cv.put(ProviderTableMeta.FILE_FAVORITE, file.isFavorite());

+ 2 - 13
src/main/java/com/owncloud/android/datamodel/OCFile.java

@@ -47,7 +47,8 @@ import third_parties.daveKoeller.AlphanumComparator;
 
 public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterface {
     private final static String PERMISSION_SHARED_WITH_ME = "S";
-    private final static String PERMISSION_CAN_RESHARE = "R";
+    @VisibleForTesting
+    public final static String PERMISSION_CAN_RESHARE = "R";
     private final static String PERMISSION_CAN_WRITE = "CK";
 
     public static final String PATH_SEPARATOR = "/";
@@ -75,7 +76,6 @@ public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterfa
     private String etag;
     private String etagOnServer;
     private boolean sharedViaLink;
-    private String publicLink;
     private String permissions;
     private String remoteId; // The fileid namespaced by the instance fileId, globally unique
     private boolean updateThumbnailNeeded;
@@ -150,7 +150,6 @@ public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterfa
         etag = source.readString();
         etagOnServer = source.readString();
         sharedViaLink = source.readInt() == 1;
-        publicLink = source.readString();
         permissions = source.readString();
         remoteId = source.readString();
         updateThumbnailNeeded = source.readInt() == 1;
@@ -184,7 +183,6 @@ public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterfa
         dest.writeString(etag);
         dest.writeString(etagOnServer);
         dest.writeInt(sharedViaLink ? 1 : 0);
-        dest.writeString(publicLink);
         dest.writeString(permissions);
         dest.writeString(remoteId);
         dest.writeInt(updateThumbnailNeeded ? 1 : 0);
@@ -450,7 +448,6 @@ public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterfa
         etag = null;
         etagOnServer = null;
         sharedViaLink = false;
-        publicLink = null;
         permissions = null;
         remoteId = null;
         updateThumbnailNeeded = false;
@@ -646,10 +643,6 @@ public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterfa
         return this.sharedViaLink;
     }
 
-    public String getPublicLink() {
-        return this.publicLink;
-    }
-
     public String getPermissions() {
         return this.permissions;
     }
@@ -758,10 +751,6 @@ public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterfa
         this.sharedViaLink = sharedViaLink;
     }
 
-    public void setPublicLink(String publicLink) {
-        this.publicLink = publicLink;
-    }
-
     public void setPermissions(String permissions) {
         this.permissions = permissions;
     }

+ 0 - 2
src/main/java/com/owncloud/android/db/ProviderMeta.java

@@ -102,7 +102,6 @@ public class ProviderMeta {
         public static final String FILE_ETAG_ON_SERVER = "etag_on_server";
         public static final String FILE_SHARED_VIA_LINK = "share_by_link";
         public static final String FILE_SHARED_WITH_SHAREE = "shared_via_users";
-        public static final String FILE_PUBLIC_LINK = "public_link";
         public static final String FILE_PERMISSIONS = "permissions";
         public static final String FILE_REMOTE_ID = "remote_id";
         public static final String FILE_UPDATE_THUMBNAIL = "update_thumbnail";
@@ -137,7 +136,6 @@ public class ProviderMeta {
             FILE_ETAG_ON_SERVER,
             FILE_SHARED_VIA_LINK,
             FILE_SHARED_WITH_SHAREE,
-            FILE_PUBLIC_LINK,
             FILE_PERMISSIONS,
             FILE_REMOTE_ID,
             FILE_UPDATE_THUMBNAIL,

+ 5 - 5
src/main/java/com/owncloud/android/files/services/FileUploader.java

@@ -426,7 +426,7 @@ public class FileUploader extends Service
                     createdBy,
                     file,
                     disableRetries
-                );
+                              );
             }
         } catch (IllegalArgumentException e) {
             Log_OC.e(TAG, "Not enough information provided in intent: " + e.getMessage());
@@ -455,7 +455,7 @@ public class FileUploader extends Service
         int createdBy,
         OCFile file,
         boolean disableRetries
-    ) {
+                               ) {
         OCUpload ocUpload = new OCUpload(file, user.toPlatformAccount());
         ocUpload.setFileSize(file.getFileLength());
         ocUpload.setNameCollisionPolicy(nameCollisionPolicy);
@@ -975,7 +975,7 @@ public class FileUploader extends Service
         OCFile existingFile,
         Integer behaviour,
         NameCollisionPolicy nameCollisionPolicy
-    ) {
+                                       ) {
         uploadUpdateFile(context, account, new OCFile[]{existingFile}, behaviour, nameCollisionPolicy, true);
     }
 
@@ -989,7 +989,7 @@ public class FileUploader extends Service
         Integer behaviour,
         NameCollisionPolicy nameCollisionPolicy,
         boolean disableRetries
-    ) {
+                                       ) {
         uploadUpdateFile(context, account, new OCFile[]{existingFile}, behaviour, nameCollisionPolicy, disableRetries);
     }
 
@@ -1003,7 +1003,7 @@ public class FileUploader extends Service
         Integer behaviour,
         NameCollisionPolicy nameCollisionPolicy,
         boolean disableRetries
-    ) {
+                                       ) {
         Intent intent = new Intent(context, FileUploader.class);
 
         intent.putExtra(FileUploader.KEY_ACCOUNT, account);

+ 1 - 2
src/main/java/com/owncloud/android/operations/CreateShareViaLinkOperation.java

@@ -52,7 +52,7 @@ public class CreateShareViaLinkOperation extends SyncOperation {
                                                                              "",
                                                                              false,
                                                                              password,
-                                                                             OCShare.DEFAULT_PERMISSION);
+                                                                             OCShare.NO_PERMISSION);
         createOp.setGetShareDetails(true);
         RemoteOperationResult result = createOp.execute(client);
 
@@ -88,7 +88,6 @@ public class CreateShareViaLinkOperation extends SyncOperation {
         // Update OCFile with data from share: ShareByLink  and publicLink
         OCFile file = getStorageManager().getFileByEncryptedRemotePath(path);
         if (file != null) {
-            file.setPublicLink(share.getShareLink());
             file.setSharedViaLink(true);
             getStorageManager().saveFile(file);
         }

+ 0 - 1
src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java

@@ -576,7 +576,6 @@ public class RefreshFolderOperation extends RemoteOperation {
                 Log.d(TAG, "Image " + remoteFile.getFileName() + " updated on the server");
             }
 
-            updatedFile.setPublicLink(localFile.getPublicLink());
             updatedFile.setSharedViaLink(localFile.isSharedViaLink());
             updatedFile.setSharedWithSharee(localFile.isSharedWithSharee());
         } else {

+ 0 - 1
src/main/java/com/owncloud/android/operations/SynchronizeFolderOperation.java

@@ -360,7 +360,6 @@ public class SynchronizeFolderOperation extends SyncOperation {
                 updatedFile.setUpdateThumbnailNeeded(true);
                 Log.d(TAG, "Image " + remoteFile.getFileName() + " updated on the server");
             }
-            updatedFile.setPublicLink(localFile.getPublicLink());
             updatedFile.setSharedViaLink(localFile.isSharedViaLink());
             updatedFile.setSharedWithSharee(localFile.isSharedWithSharee());
             updatedFile.setEtagInConflict(localFile.getEtagInConflict());

+ 0 - 1
src/main/java/com/owncloud/android/operations/UnshareOperation.java

@@ -68,7 +68,6 @@ public class UnshareOperation extends SyncOperation {
 
                 if (ShareType.PUBLIC_LINK.equals(share.getShareType())) {
                     file.setSharedViaLink(false);
-                    file.setPublicLink("");
                 } else if (ShareType.USER.equals(share.getShareType()) || ShareType.GROUP.equals(share.getShareType())
                     || ShareType.FEDERATED.equals(share.getShareType())) {
                     // Check if it is the last share

+ 0 - 14
src/main/java/com/owncloud/android/operations/UpdateShareViaLinkOperation.java

@@ -34,10 +34,6 @@ import com.owncloud.android.operations.common.SyncOperation;
  */
 public class UpdateShareViaLinkOperation extends SyncOperation {
     private String password;
-    /**
-     * Enable upload permissions to update in Share resource.
-     */
-    private boolean publicUpload;
     private Boolean hideFileDownload;
     private long expirationDateInMillis;
     private long shareId;
@@ -58,12 +54,6 @@ public class UpdateShareViaLinkOperation extends SyncOperation {
         updateOp.setHideFileDownload(hideFileDownload);
         updateOp.setLabel(label);
 
-        if (publicShare.isFolder()) {
-            updateOp.setPublicUploadOnFolder(publicUpload);
-        } else {
-            updateOp.setPublicUploadOnFile(publicUpload);
-        }
-
         RemoteOperationResult result = updateOp.execute(client);
 
         if (result.isSuccess()) {
@@ -87,10 +77,6 @@ public class UpdateShareViaLinkOperation extends SyncOperation {
         this.password = password;
     }
 
-    public void setPublicUpload(boolean publicUpload) {
-        this.publicUpload = publicUpload;
-    }
-
     public void setHideFileDownload(Boolean hideFileDownload) {
         this.hideFileDownload = hideFileDownload;
     }

+ 2 - 2
src/main/java/com/owncloud/android/operations/UploadFileOperation.java

@@ -207,8 +207,8 @@ public class UploadFileOperation extends SyncOperation {
         }
         if (TextUtils.isEmpty(upload.getLocalPath())) {
             throw new IllegalArgumentException(
-                    "Illegal file in UploadFileOperation; storage path invalid: "
-                            + upload.getLocalPath());
+                "Illegal file in UploadFileOperation; storage path invalid: "
+                    + upload.getLocalPath());
         }
 
         this.uploadsStorageManager = uploadsStorageManager;

+ 0 - 5
src/main/java/com/owncloud/android/providers/FileContentProvider.java

@@ -708,7 +708,6 @@ public class FileContentProvider extends ContentProvider {
                        + ProviderTableMeta.FILE_ETAG + TEXT
                        + ProviderTableMeta.FILE_ETAG_ON_SERVER + TEXT
                        + ProviderTableMeta.FILE_SHARED_VIA_LINK + INTEGER
-                       + ProviderTableMeta.FILE_PUBLIC_LINK + TEXT
                        + ProviderTableMeta.FILE_PERMISSIONS + " TEXT null,"
                        + ProviderTableMeta.FILE_REMOTE_ID + " TEXT null,"
                        + ProviderTableMeta.FILE_UPDATE_THUMBNAIL + INTEGER //boolean
@@ -1148,10 +1147,6 @@ public class FileContentProvider extends ContentProvider {
                                    ADD_COLUMN + ProviderTableMeta.FILE_SHARED_VIA_LINK + " INTEGER " +
                                    " DEFAULT 0");
 
-                    db.execSQL(ALTER_TABLE + ProviderTableMeta.FILE_TABLE_NAME +
-                                   ADD_COLUMN + ProviderTableMeta.FILE_PUBLIC_LINK + " TEXT " +
-                                   " DEFAULT NULL");
-
                     // Create table OCShares
                     createOCSharesTable(db);
 

+ 6 - 11
src/main/java/com/owncloud/android/services/OperationsService.java

@@ -89,12 +89,10 @@ public class OperationsService extends Service {
 
     public static final String EXTRA_ACCOUNT = "ACCOUNT";
     public static final String EXTRA_SERVER_URL = "SERVER_URL";
-    public static final String EXTRA_OAUTH2_QUERY_PARAMETERS = "OAUTH2_QUERY_PARAMETERS";
     public static final String EXTRA_REMOTE_PATH = "REMOTE_PATH";
     public static final String EXTRA_NEWNAME = "NEWNAME";
     public static final String EXTRA_REMOVE_ONLY_LOCAL = "REMOVE_LOCAL_COPY";
     public static final String EXTRA_SYNC_FILE_CONTENTS = "SYNC_FILE_CONTENTS";
-    public static final String EXTRA_RESULT = "RESULT";
     public static final String EXTRA_NEW_PARENT_PATH = "NEW_PARENT_PATH";
     public static final String EXTRA_FILE = "FILE";
     public static final String EXTRA_FILE_VERSION = "FILE_VERSION";
@@ -103,7 +101,6 @@ public class OperationsService extends Service {
     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_PERMISSIONS = "SHARE_PERMISSIONS";
-    public static final String EXTRA_SHARE_PUBLIC_UPLOAD = "SHARE_PUBLIC_UPLOAD";
     public static final String EXTRA_SHARE_PUBLIC_LABEL = "SHARE_PUBLIC_LABEL";
     public static final String EXTRA_SHARE_HIDE_FILE_DOWNLOAD = "HIDE_FILE_DOWNLOAD";
     public static final String EXTRA_SHARE_ID = "SHARE_ID";
@@ -384,9 +381,7 @@ public class OperationsService extends Service {
      * Created with the Looper of a new thread, started in {@link OperationsService#onCreate()}.
      */
     private static class ServiceHandler extends Handler {
-        // don't make it a final class, and don't remove the static ; lint will warn about a p
-        // ossible memory leak
-
+        // don't make it a final class, and don't remove the static ; lint will warn about a possible memory leak
 
         OperationsService mService;
 
@@ -428,7 +423,7 @@ public class OperationsService extends Service {
 
             if (next != null) {
                 mCurrentOperation = next.second;
-                RemoteOperationResult result = null;
+                RemoteOperationResult result;
                 try {
                     /// prepare client object to send the request to the ownCloud server
                     if (mLastTarget == null || !mLastTarget.equals(next.first)) {
@@ -553,9 +548,9 @@ public class OperationsService extends Service {
                                                                                        false);
                             updateLinkOperation.setHideFileDownload(hideFileDownload);
 
-                            if (operationIntent.hasExtra(EXTRA_SHARE_PUBLIC_UPLOAD)) {
-                                updateLinkOperation.setPublicUpload(true);
-                            }
+//                            if (operationIntent.hasExtra(EXTRA_SHARE_PUBLIC_UPLOAD)) {
+//                                updateLinkOperation.setPublicUpload(true);
+//                            }
 
                             if (operationIntent.hasExtra(EXTRA_SHARE_PUBLIC_LABEL)) {
                                 updateLinkOperation.setLabel(operationIntent.getStringExtra(EXTRA_SHARE_PUBLIC_LABEL));
@@ -698,7 +693,7 @@ public class OperationsService extends Service {
         }
 
         if (operation != null) {
-            return new Pair<Target, RemoteOperation>(target, operation);
+            return new Pair<>(target, operation);
         } else {
             return null;
         }

+ 3 - 6
src/main/java/com/owncloud/android/ui/activity/FileActivity.java

@@ -746,7 +746,6 @@ public abstract class FileActivity extends DrawerActivity
 
         if (result.isSuccess()) {
             if (sharingFragment != null) {
-                sharingFragment.refreshPublicShareFromDB();
                 sharingFragment.onUpdateShareInformation(result, getFile());
             }
         } else {
@@ -761,7 +760,6 @@ public abstract class FileActivity extends DrawerActivity
         if (result.isSuccess()) {
             updateFileFromDB();
             if (sharingFragment != null) {
-                sharingFragment.refreshPublicShareFromDB();
                 sharingFragment.onUpdateShareInformation(result, getFile());
             }
         } else if (sharingFragment != null && sharingFragment.getView() != null) {
@@ -807,7 +805,6 @@ public abstract class FileActivity extends DrawerActivity
             copyAndShareFileLink(this, file, link);
 
             if (sharingFragment != null) {
-                sharingFragment.refreshPublicShareFromDB();
                 sharingFragment.onUpdateShareInformation(result, getFile());
             }
         } else {
@@ -828,7 +825,7 @@ public abstract class FileActivity extends DrawerActivity
 
             } else {
                 if (sharingFragment != null) {
-                    sharingFragment.refreshPublicShareFromDB();
+                    sharingFragment.refreshSharesFromDB();
                 }
                 Snackbar snackbar = Snackbar.make(findViewById(android.R.id.content),
                                                   ErrorMessageAdapter.getErrorCauseMessage(result,
@@ -899,8 +896,8 @@ public abstract class FileActivity extends DrawerActivity
         if (getFile().isSharedWithMe()) {
             return OCShare.READ_PERMISSION_FLAG;    // minimum permissions
         } else if (ShareType.FEDERATED.equals(shareType)) {
-            return getFile().isFolder() ? OCShare.FEDERATED_PERMISSIONS_FOR_FOLDER_AFTER_OC9 :
-                OCShare.FEDERATED_PERMISSIONS_FOR_FILE_AFTER_OC9;
+            return getFile().isFolder() ? OCShare.FEDERATED_PERMISSIONS_FOR_FOLDER :
+                OCShare.FEDERATED_PERMISSIONS_FOR_FILE;
         } else {
             return getFile().isFolder() ? OCShare.MAXIMUM_PERMISSIONS_FOR_FOLDER :
                 OCShare.MAXIMUM_PERMISSIONS_FOR_FILE;

+ 1 - 2
src/main/java/com/owncloud/android/ui/activity/ShareActivity.java

@@ -145,8 +145,7 @@ public class ShareActivity extends FileActivity {
         if (shareFileFragment != null
                 && shareFileFragment.isAdded()) {   // only if added to the view hierarchy!!
             shareFileFragment.refreshCapabilitiesFromDB();
-            //shareFileFragment.refrefreshUsersOrGroupsListFromDB();
-            shareFileFragment.refreshPublicShareFromDB();
+            shareFileFragment.refreshSharesFromDB();
         }
     }
 

+ 74 - 0
src/main/java/com/owncloud/android/ui/adapter/InternalShareViewHolder.java

@@ -0,0 +1,74 @@
+/*
+ *
+ * Nextcloud Android client application
+ *
+ * @author Tobias Kaminsky
+ * Copyright (C) 2020 Tobias Kaminsky
+ * Copyright (C) 2020 Nextcloud GmbH
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package com.owncloud.android.ui.adapter;
+
+import android.content.Context;
+import android.graphics.PorterDuff;
+import android.view.View;
+
+import com.owncloud.android.R;
+import com.owncloud.android.databinding.FileDetailsShareInternalShareLinkBinding;
+import com.owncloud.android.lib.resources.shares.OCShare;
+
+import androidx.annotation.NonNull;
+import androidx.core.content.res.ResourcesCompat;
+import androidx.recyclerview.widget.RecyclerView;
+
+class InternalShareViewHolder extends RecyclerView.ViewHolder {
+    private FileDetailsShareInternalShareLinkBinding binding;
+    private Context context;
+
+    public InternalShareViewHolder(@NonNull View itemView) {
+        super(itemView);
+    }
+
+    public InternalShareViewHolder(FileDetailsShareInternalShareLinkBinding binding, Context context) {
+        this(binding.getRoot());
+        this.binding = binding;
+        this.context = context;
+    }
+
+    public void bind(OCShare share, ShareeListAdapterListener listener) {
+        binding.copyInternalLinkIcon
+            .getBackground()
+            .setColorFilter(ResourcesCompat.getColor(context.getResources(),
+                                                     R.color.grey_db,
+                                                     null),
+                            PorterDuff.Mode.SRC_IN);
+        binding.copyInternalLinkIcon
+            .getDrawable()
+            .mutate()
+            .setColorFilter(ResourcesCompat.getColor(context.getResources(),
+                                                     R.color.black,
+                                                     null),
+                            PorterDuff.Mode.SRC_IN);
+
+        if (share.isFolder()) {
+            binding.shareInternalLinkText.setText(context.getString(R.string.share_internal_link_to_folder_text));
+        } else {
+            binding.shareInternalLinkText.setText(context.getString(R.string.share_internal_link_to_file_text));
+        }
+
+        binding.copyInternalContainer.setOnClickListener(l -> listener.copyInternalLink());
+    }
+}

+ 74 - 0
src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.java

@@ -0,0 +1,74 @@
+/*
+ *
+ * Nextcloud Android client application
+ *
+ * @author Tobias Kaminsky
+ * Copyright (C) 2020 Tobias Kaminsky
+ * Copyright (C) 2020 Nextcloud GmbH
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package com.owncloud.android.ui.adapter;
+
+import android.content.Context;
+import android.text.TextUtils;
+import android.view.View;
+
+import com.owncloud.android.R;
+import com.owncloud.android.databinding.FileDetailsShareLinkShareItemBinding;
+import com.owncloud.android.lib.resources.shares.OCShare;
+import com.owncloud.android.lib.resources.shares.ShareType;
+import com.owncloud.android.utils.ThemeUtils;
+
+import androidx.annotation.NonNull;
+import androidx.core.content.res.ResourcesCompat;
+import androidx.recyclerview.widget.RecyclerView;
+
+class LinkShareViewHolder extends RecyclerView.ViewHolder {
+    private FileDetailsShareLinkShareItemBinding binding;
+    private Context context;
+
+    public LinkShareViewHolder(@NonNull View itemView) {
+        super(itemView);
+    }
+
+    public LinkShareViewHolder(FileDetailsShareLinkShareItemBinding binding, Context context) {
+        this(binding.getRoot());
+        this.binding = binding;
+        this.context = context;
+    }
+
+    public void bind(OCShare publicShare, ShareeListAdapterListener listener) {
+        if (ShareType.EMAIL == publicShare.getShareType()) {
+            binding.name.setText(publicShare.getSharedWithDisplayName());
+            binding.icon.setImageDrawable(ResourcesCompat.getDrawable(context.getResources(),
+                                                                      R.drawable.ic_email,
+                                                                      null));
+            binding.copyLink.setVisibility(View.GONE);
+        } else {
+            if (!TextUtils.isEmpty(publicShare.getLabel())) {
+                String text = String.format(context.getString(R.string.share_link_with_label), publicShare.getLabel());
+                binding.name.setText(text);
+            } else {
+                binding.name.setText(R.string.share_link);
+            }
+        }
+
+        ThemeUtils.colorIconImageViewWithBackground(binding.icon, context);
+
+        binding.copyLink.setOnClickListener(v -> listener.copyLink(publicShare));
+        binding.overflowMenu.setOnClickListener(v -> listener.showLinkOverflowMenu(publicShare, binding.overflowMenu));
+    }
+}

+ 7 - 22
src/main/java/com/owncloud/android/ui/adapter/PublicShareViewHolder.java → src/main/java/com/owncloud/android/ui/adapter/NewLinkShareViewHolder.java

@@ -22,41 +22,26 @@
 
 package com.owncloud.android.ui.adapter;
 
-import android.content.Context;
-import android.text.TextUtils;
 import android.view.View;
 
-import com.owncloud.android.databinding.FileDetailsSharePublicLinkItemBinding;
-import com.owncloud.android.lib.resources.shares.OCShare;
-import com.owncloud.android.utils.ThemeUtils;
+import com.owncloud.android.databinding.FileDetailsSharePublicLinkAddNewItemBinding;
 
 import androidx.annotation.NonNull;
 import androidx.recyclerview.widget.RecyclerView;
 
-class PublicShareViewHolder extends RecyclerView.ViewHolder {
-    private FileDetailsSharePublicLinkItemBinding binding;
-    private Context context;
+class NewLinkShareViewHolder extends RecyclerView.ViewHolder {
+    private FileDetailsSharePublicLinkAddNewItemBinding binding;
 
-    public PublicShareViewHolder(@NonNull View itemView) {
+    public NewLinkShareViewHolder(@NonNull View itemView) {
         super(itemView);
     }
 
-    public PublicShareViewHolder(FileDetailsSharePublicLinkItemBinding binding, Context context) {
+    public NewLinkShareViewHolder(FileDetailsSharePublicLinkAddNewItemBinding binding) {
         this(binding.getRoot());
         this.binding = binding;
-        this.context = context;
     }
 
-    public void bind(OCShare publicShare, PublicShareInterface listener) {
-        if (!TextUtils.isEmpty(publicShare.getLabel())) {
-            binding.publicShareLabel.setText(publicShare.getLabel());
-        }
-
-        ThemeUtils.colorIconImageViewWithBackground(binding.copyInternalLinkIcon, context);
-
-        binding.shareLinkCopyIcon.setOnClickListener(v -> listener.copyLink(publicShare));
-
-        binding.overflowMenuShareLink.setOnClickListener(
-            v -> listener.showLinkOverflowMenu(publicShare, binding.overflowMenuShareLink));
+    public void bind(ShareeListAdapterListener listener) {
+        binding.addNewPublicShareLink.setOnClickListener(v -> listener.createPublicShareLink());
     }
 }

+ 0 - 68
src/main/java/com/owncloud/android/ui/adapter/PublicShareListAdapter.java

@@ -1,68 +0,0 @@
-/*
- *
- * Nextcloud Android client application
- *
- * @author Tobias Kaminsky
- * Copyright (C) 2020 Tobias Kaminsky
- * Copyright (C) 2020 Nextcloud GmbH
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-
-package com.owncloud.android.ui.adapter;
-
-import android.content.Context;
-import android.view.LayoutInflater;
-import android.view.ViewGroup;
-
-import com.owncloud.android.databinding.FileDetailsSharePublicLinkItemBinding;
-import com.owncloud.android.lib.resources.shares.OCShare;
-
-import java.util.List;
-
-import androidx.annotation.NonNull;
-import androidx.recyclerview.widget.RecyclerView;
-
-public class PublicShareListAdapter extends RecyclerView.Adapter<PublicShareViewHolder> {
-    private Context context;
-    private List<OCShare> shares;
-    private PublicShareInterface listener;
-
-    public PublicShareListAdapter(Context context, List<OCShare> shares, PublicShareInterface listener) {
-        this.context = context;
-        this.shares = shares;
-        this.listener = listener;
-    }
-
-    @NonNull
-    @Override
-    public PublicShareViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
-        FileDetailsSharePublicLinkItemBinding binding =
-            FileDetailsSharePublicLinkItemBinding.inflate(LayoutInflater.from(context), parent, false);
-
-        return new PublicShareViewHolder(binding, context);
-    }
-
-    @Override
-    public void onBindViewHolder(@NonNull PublicShareViewHolder holder, int position) {
-        OCShare share = shares.get(position);
-
-        holder.bind(share, listener);
-    }
-
-    @Override
-    public int getItemCount() {
-        return shares.size();
-    }
-}

+ 102 - 0
src/main/java/com/owncloud/android/ui/adapter/ShareViewHolder.java

@@ -0,0 +1,102 @@
+/*
+ *
+ * Nextcloud Android client application
+ *
+ * @author Tobias Kaminsky
+ * Copyright (C) 2020 Tobias Kaminsky
+ * Copyright (C) 2020 Nextcloud GmbH
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package com.owncloud.android.ui.adapter;
+
+import android.content.Context;
+import android.view.View;
+import android.widget.ImageView;
+
+import com.owncloud.android.R;
+import com.owncloud.android.databinding.FileDetailsShareShareItemBinding;
+import com.owncloud.android.lib.resources.shares.OCShare;
+import com.owncloud.android.ui.TextDrawable;
+
+import java.security.NoSuchAlgorithmException;
+
+import androidx.annotation.DrawableRes;
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+class ShareViewHolder extends RecyclerView.ViewHolder {
+    private FileDetailsShareShareItemBinding binding;
+    private float avatarRadiusDimension;
+    private Context context;
+
+    public ShareViewHolder(@NonNull View itemView) {
+        super(itemView);
+    }
+
+    public ShareViewHolder(FileDetailsShareShareItemBinding binding, Context context) {
+        this(binding.getRoot());
+        this.binding = binding;
+        this.context = context;
+    }
+
+    public void bind(OCShare share,
+                     ShareeListAdapterListener listener,
+                     String userId,
+                     float avatarRadiusDimension) {
+        this.avatarRadiusDimension = avatarRadiusDimension;
+        String name = share.getSharedWithDisplayName();
+
+        switch (share.getShareType()) {
+            case GROUP:
+                name = context.getString(R.string.share_group_clarification, name);
+                setImage(binding.icon, share.getSharedWithDisplayName(), R.drawable.ic_group);
+                break;
+            case ROOM:
+                name = context.getString(R.string.share_room_clarification, name);
+                setImage(binding.icon, share.getSharedWithDisplayName(), R.drawable.ic_chat_bubble);
+                break;
+            case CIRCLE:
+                binding.icon.setImageResource(R.drawable.ic_circles);
+                break;
+            case FEDERATED:
+                name = context.getString(R.string.share_remote_clarification, name);
+                setImage(binding.icon, share.getSharedWithDisplayName(), R.drawable.ic_user);
+                break;
+            default:
+                setImage(binding.icon, name, R.drawable.ic_user);
+                break;
+        }
+
+        binding.name.setText(name);
+
+        if (share.getShareWith().equalsIgnoreCase(userId) || share.getUserId().equalsIgnoreCase(userId)) {
+            binding.overflowMenu.setVisibility(View.VISIBLE);
+
+            // bind listener to edit privileges
+            binding.overflowMenu.setOnClickListener(v -> listener.showUserOverflowMenu(share, binding.overflowMenu));
+        } else {
+            binding.overflowMenu.setVisibility(View.GONE);
+        }
+    }
+
+    private void setImage(ImageView avatar, String name, @DrawableRes int fallback) {
+        try {
+            avatar.setImageDrawable(TextDrawable.createNamedAvatar(name, avatarRadiusDimension));
+        } catch (NoSuchAlgorithmException | StringIndexOutOfBoundsException e) {
+            avatar.setImageResource(fallback);
+        }
+    }
+}

+ 101 - 326
src/main/java/com/owncloud/android/ui/adapter/ShareeListAdapter.java

@@ -25,132 +25,108 @@
 
 package com.owncloud.android.ui.adapter;
 
-import android.accounts.Account;
 import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ImageView;
-import android.widget.PopupMenu;
-import android.widget.TextView;
 
 import com.owncloud.android.R;
-import com.owncloud.android.datamodel.FileDataStorageManager;
-import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.databinding.FileDetailsShareInternalShareLinkBinding;
+import com.owncloud.android.databinding.FileDetailsShareLinkShareItemBinding;
+import com.owncloud.android.databinding.FileDetailsSharePublicLinkAddNewItemBinding;
+import com.owncloud.android.databinding.FileDetailsShareShareItemBinding;
 import com.owncloud.android.lib.resources.shares.OCShare;
 import com.owncloud.android.lib.resources.shares.ShareType;
-import com.owncloud.android.lib.resources.status.OCCapability;
-import com.owncloud.android.lib.resources.status.OwnCloudVersion;
-import com.owncloud.android.ui.TextDrawable;
-import com.owncloud.android.ui.dialog.ExpirationDatePickerDialogFragment;
-import com.owncloud.android.ui.dialog.NoteDialogFragment;
-import com.owncloud.android.ui.fragment.util.SharingMenuHelper;
 import com.owncloud.android.utils.DisplayUtils;
-import com.owncloud.android.utils.ThemeUtils;
 
-import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
-import androidx.annotation.DrawableRes;
 import androidx.annotation.NonNull;
-import androidx.appcompat.widget.AppCompatCheckBox;
-import androidx.fragment.app.FragmentManager;
 import androidx.recyclerview.widget.RecyclerView;
-import butterknife.BindView;
-import butterknife.ButterKnife;
 
 /**
  * Adapter to show a user/group/email/remote in Sharing list in file details view.
  */
-public class ShareeListAdapter extends RecyclerView.Adapter<ShareeListAdapter.UserViewHolder>
-        implements DisplayUtils.AvatarGenerationListener {
+public class ShareeListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
+    implements DisplayUtils.AvatarGenerationListener {
 
     private ShareeListAdapterListener listener;
-    private OCCapability capabilities;
-    private FragmentManager fragmentManager;
     private Context context;
-    private int accentColor;
     private List<OCShare> shares;
     private float avatarRadiusDimension;
-    private OCFile file;
     private String userId;
 
-    public ShareeListAdapter(FragmentManager fragmentManager, Context context, List<OCShare> shares, Account account,
-                             OCFile file, ShareeListAdapterListener listener, String userId) {
+    public ShareeListAdapter(Context context,
+                             List<OCShare> shares,
+                             ShareeListAdapterListener listener,
+                             String userId) {
         this.context = context;
-        this.fragmentManager = fragmentManager;
         this.shares = shares;
         this.listener = listener;
-        this.file = file;
         this.userId = userId;
 
-        accentColor = ThemeUtils.primaryAccentColor(context);
-        capabilities = new FileDataStorageManager(account, context.getContentResolver()).getCapability(account.name);
         avatarRadiusDimension = context.getResources().getDimension(R.dimen.user_icon_radius);
+
+        sortShares();
     }
 
-    @NonNull
     @Override
-    public UserViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
-        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.file_details_share_user_item, parent, false);
-        return new UserViewHolder(v);
+    public int getItemViewType(int position) {
+        return shares.get(position).getShareType().getValue();
     }
 
+    @NonNull
     @Override
-    public void onBindViewHolder(@NonNull UserViewHolder holder, int position) {
-        if (shares != null && shares.size() > position) {
-            final OCShare share = shares.get(position);
-
-            String name = share.getSharedWithDisplayName();
-
-            switch (share.getShareType()) {
-                case GROUP:
-                    name = context.getString(R.string.share_group_clarification, name);
-                    setImage(holder, name, R.drawable.ic_group);
-                    break;
-                case EMAIL:
-                    name = context.getString(R.string.share_email_clarification, name);
-                    setImage(holder, name, R.drawable.ic_email);
-                    break;
-                case ROOM:
-                    name = context.getString(R.string.share_room_clarification, name);
-                    setImage(holder, name, R.drawable.ic_chat_bubble);
-                    break;
-                case CIRCLE:
-                    holder.avatar.setImageResource(R.drawable.ic_circles);
-                    break;
-                default:
-                    setImage(holder, name, R.drawable.ic_user);
-                    break;
-            }
-
-            holder.name.setText(name);
-
-            if (share.getShareWith().equalsIgnoreCase(userId) || share.getUserId().equalsIgnoreCase(userId)) {
-                holder.allowEditing.setVisibility(View.VISIBLE);
-                holder.editShareButton.setVisibility(View.VISIBLE);
-
-                ThemeUtils.tintCheckbox(holder.allowEditing, accentColor);
-                holder.allowEditing.setChecked(canEdit(share));
-                holder.allowEditing.setOnClickListener(v -> allowEditClick(holder.allowEditing, share));
-
-                // bind listener to edit privileges
-                holder.editShareButton.setOnClickListener(v -> onOverflowIconClicked(v, holder.allowEditing, share));
-            } else {
-                holder.allowEditing.setVisibility(View.GONE);
-                holder.editShareButton.setVisibility(View.GONE);
-            }
+    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+        switch (ShareType.fromValue(viewType)) {
+            case PUBLIC_LINK:
+            case EMAIL:
+                return new LinkShareViewHolder(
+                    FileDetailsShareLinkShareItemBinding.inflate(LayoutInflater.from(context),
+                                                                 parent,
+                                                                 false),
+                    context);
+            case NEW_PUBLIC_LINK:
+                return new NewLinkShareViewHolder(
+                    FileDetailsSharePublicLinkAddNewItemBinding.inflate(LayoutInflater.from(context),
+                                                                        parent,
+                                                                        false)
+                );
+            case INTERNAL:
+                return new InternalShareViewHolder(
+                    FileDetailsShareInternalShareLinkBinding.inflate(LayoutInflater.from(context), parent, false),
+                    context);
+            default:
+                return new ShareViewHolder(FileDetailsShareShareItemBinding.inflate(LayoutInflater.from(context),
+                                                                                    parent,
+                                                                                    false),
+                                           context);
         }
     }
 
-    private void setImage(UserViewHolder holder, String name, @DrawableRes int fallback) {
-        try {
-            holder.avatar.setImageDrawable(TextDrawable.createNamedAvatar(name, avatarRadiusDimension));
-        } catch (NoSuchAlgorithmException e) {
-            holder.avatar.setImageResource(fallback);
+    @Override
+    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
+        if (shares == null || shares.size() <= position) {
+            return;
+        }
+
+        final OCShare share = shares.get(position);
+
+        if (holder instanceof LinkShareViewHolder) {
+            LinkShareViewHolder publicShareViewHolder = (LinkShareViewHolder) holder;
+            publicShareViewHolder.bind(share, listener);
+        } else if (holder instanceof InternalShareViewHolder) {
+            InternalShareViewHolder internalShareViewHolder = (InternalShareViewHolder) holder;
+            internalShareViewHolder.bind(share, listener);
+        } else if (holder instanceof NewLinkShareViewHolder) {
+            NewLinkShareViewHolder newLinkShareViewHolder = (NewLinkShareViewHolder) holder;
+            newLinkShareViewHolder.bind(listener);
+        } else {
+            ShareViewHolder userViewHolder = (ShareViewHolder) holder;
+            userViewHolder.bind(share, listener, userId, avatarRadiusDimension);
         }
     }
 
@@ -164,204 +140,10 @@ public class ShareeListAdapter extends RecyclerView.Adapter<ShareeListAdapter.Us
         return shares.size();
     }
 
-    private void allowEditClick(AppCompatCheckBox checkBox, @NonNull OCShare share) {
-        if (!share.isFolder()) {
-            share.setPermissions(listener.updatePermissionsToShare(
-                    share,
-                    canReshare(share),
-                    checkBox.isChecked(),
-                    false,
-                    false,
-                    false
-            ));
-        } else {
-            share.setPermissions(listener.updatePermissionsToShare(
-                    share,
-                    canReshare(share),
-                    checkBox.isChecked(),
-                    checkBox.isChecked(),
-                    checkBox.isChecked(),
-                    checkBox.isChecked()
-            ));
-        }
-    }
-
-    private void onOverflowIconClicked(View view, AppCompatCheckBox allowEditsCheckBox, OCShare share) {
-        // use grey as fallback for elements where custom theming is not available
-        if (ThemeUtils.themingEnabled(context)) {
-            context.getTheme().applyStyle(R.style.FallbackThemingTheme, true);
-        }
-        PopupMenu popup = new PopupMenu(context, view);
-        popup.inflate(R.menu.item_user_sharing_settings);
-
-        prepareOptionsMenu(popup.getMenu(), share);
-
-        popup.setOnMenuItemClickListener(item -> optionsItemSelected(popup.getMenu(), item, allowEditsCheckBox, share));
-        popup.show();
-    }
-
-    /**
-     * Updates the sharee's menu with the current permissions of the {@link OCShare}
-     *
-     * @param menu  the menu of the sharee/shared file
-     * @param share the shared file
-     */
-    private void prepareOptionsMenu(Menu menu, OCShare share) {
-
-        MenuItem editCreateItem = menu.findItem(R.id.action_can_edit_create);
-        MenuItem editChangeItem = menu.findItem(R.id.action_can_edit_change);
-        MenuItem editDeleteItem = menu.findItem(R.id.action_can_edit_delete);
-
-        MenuItem hideFileListingItem = menu.findItem(R.id.action_hide_file_listing);
-        MenuItem passwordItem = menu.findItem(R.id.action_password);
-        MenuItem expirationDateItem = menu.findItem(R.id.action_expiration_date);
-
-        MenuItem reshareItem = menu.findItem(R.id.action_can_reshare);
-
-        MenuItem sendNoteItem = menu.findItem(R.id.action_share_send_note);
-
-        if (isReshareForbidden(share)) {
-            reshareItem.setVisible(false);
-        }
-        reshareItem.setChecked(canReshare(share));
-
-        if (share.getShareType() == ShareType.EMAIL) {
-            SharingMenuHelper.setupHideFileListingMenuItem(
-                    hideFileListingItem,
-                    file.isFolder(),
-                    canEdit(share),
-                    share.getPermissions()
-            );
-            SharingMenuHelper.setupPasswordMenuItem(passwordItem, share.isPasswordProtected());
-
-            reshareItem.setVisible(false);
-            editCreateItem.setVisible(false);
-            editChangeItem.setVisible(false);
-            editDeleteItem.setVisible(false);
-        } else {
-            if (file.isFolder() && isEditOptionsAvailable(share)) {
-                /// TODO change areEditOptionsAvailable in order to delete !isFederated
-                editCreateItem.setChecked(canCreate(share));
-                editChangeItem.setChecked(canUpdate(share));
-                editDeleteItem.setChecked(canDelete(share));
-            } else {
-                editCreateItem.setVisible(false);
-                editChangeItem.setVisible(false);
-                editDeleteItem.setVisible(false);
-            }
-
-            hideFileListingItem.setVisible(false);
-            passwordItem.setVisible(false);
-
-            if (!capabilities.getVersion().isNewerOrEqual(OwnCloudVersion.nextcloud_18)) {
-                expirationDateItem.setVisible(false);
-            }
-        }
-
-        SharingMenuHelper.setupExpirationDateMenuItem(
-                menu.findItem(R.id.action_expiration_date), share.getExpirationDate(), context.getResources());
-
-        sendNoteItem.setVisible(capabilities.getVersion().isNoteOnShareSupported());
-    }
-
-    private boolean isEditOptionsAvailable(OCShare share) {
-        return !ShareType.FEDERATED.equals(share.getShareType());
-    }
-
-    private boolean isReshareForbidden(OCShare share) {
-        return ShareType.FEDERATED.equals(share.getShareType()) ||
-                (capabilities != null && capabilities.getFilesSharingResharing().isFalse());
-    }
-
-    private boolean canEdit(OCShare share) {
-        return (share.getPermissions() &
-                (OCShare.CREATE_PERMISSION_FLAG | OCShare.UPDATE_PERMISSION_FLAG | OCShare.DELETE_PERMISSION_FLAG)) > 0;
-    }
-
-    private boolean canCreate(OCShare share) {
-        return (share.getPermissions() & OCShare.CREATE_PERMISSION_FLAG) > 0;
-    }
-
-    private boolean canUpdate(OCShare share) {
-        return (share.getPermissions() & OCShare.UPDATE_PERMISSION_FLAG) > 0;
-    }
-
-    private boolean canDelete(OCShare share) {
-        return (share.getPermissions() & OCShare.DELETE_PERMISSION_FLAG) > 0;
-    }
-
-    private boolean canReshare(OCShare share) {
-        return (share.getPermissions() & OCShare.SHARE_PERMISSION_FLAG) > 0;
-    }
-
-    private boolean optionsItemSelected(Menu menu, MenuItem item, AppCompatCheckBox allowEditsCheckBox, OCShare share) {
-        switch (item.getItemId()) {
-            case R.id.action_can_edit_create:
-            case R.id.action_can_edit_change:
-            case R.id.action_can_edit_delete: {
-                item.setChecked(!item.isChecked());
-                if (item.isChecked() && !allowEditsCheckBox.isChecked()) {
-                    allowEditsCheckBox.setChecked(true);
-                }
-                share.setPermissions(
-                        updatePermissionsToShare(
-                                share,
-                                menu.findItem(R.id.action_can_reshare).isChecked(),
-                                allowEditsCheckBox.isChecked(),
-                                menu.findItem(R.id.action_can_edit_create).isChecked(),
-                                menu.findItem(R.id.action_can_edit_change).isChecked(),
-                                menu.findItem(R.id.action_can_edit_delete).isChecked())
-                );
-                return true;
-            }
-            case R.id.action_can_reshare: {
-                item.setChecked(!item.isChecked());
-                share.setPermissions(
-                        updatePermissionsToShare(
-                                share,
-                                menu.findItem(R.id.action_can_reshare).isChecked(),
-                                allowEditsCheckBox.isChecked(),
-                                menu.findItem(R.id.action_can_edit_create).isChecked(),
-                                menu.findItem(R.id.action_can_edit_change).isChecked(),
-                                menu.findItem(R.id.action_can_edit_delete).isChecked())
-                );
-                return true;
-            }
-            case R.id.action_unshare: {
-                listener.unshareWith(share);
-                shares.remove(share);
-                notifyDataSetChanged();
-                return true;
-            }
-            case R.id.action_password: {
-                listener.requestPasswordForShare(share, false);
-                return true;
-            }
-            case R.id.action_expiration_date: {
-                ExpirationDatePickerDialogFragment dialog = ExpirationDatePickerDialogFragment
-                    .newInstance(share, share.getExpirationDate());
-                dialog.show(fragmentManager, ExpirationDatePickerDialogFragment.DATE_PICKER_DIALOG);
-                return true;
-            }
-            case R.id.action_share_send_note:
-                NoteDialogFragment dialog = NoteDialogFragment.newInstance(share);
-                dialog.show(fragmentManager, NoteDialogFragment.NOTE_FRAGMENT);
-                return true;
-            default:
-                return true;
-        }
-    }
-
-    private int updatePermissionsToShare(OCShare share, boolean canReshare, boolean canEdit, boolean canEditCreate,
-                                         boolean canEditChange, boolean canEditDelete) {
-        return listener.updatePermissionsToShare(
-                share,
-                canReshare,
-                canEdit,
-                canEditCreate,
-                canEditChange,
-                canEditDelete
-        );
+    public void addShares(List<OCShare> sharesToAdd) {
+        shares.addAll(sharesToAdd);
+        sortShares();
+        notifyDataSetChanged();
     }
 
     @Override
@@ -381,53 +163,46 @@ public class ShareeListAdapter extends RecyclerView.Adapter<ShareeListAdapter.Us
         return false;
     }
 
-    class UserViewHolder extends RecyclerView.ViewHolder {
-        @BindView(R.id.avatar)
-        ImageView avatar;
-        @BindView(R.id.name)
-        TextView name;
-        @BindView(R.id.allowEditing)
-        AppCompatCheckBox allowEditing;
-        @BindView(R.id.editShareButton)
-        ImageView editShareButton;
+    public void remove(OCShare share) {
+        shares.remove(share);
+        notifyDataSetChanged();
+    }
 
-        UserViewHolder(View itemView) {
-            super(itemView);
-            ButterKnife.bind(this, itemView);
+    /**
+     * sort all by creation time, then email/link shares on top
+     */
+    protected final void sortShares() {
+        List<OCShare> links = new ArrayList<>();
+        List<OCShare> users = new ArrayList<>();
+
+        for (OCShare share : shares) {
+            if (ShareType.PUBLIC_LINK == share.getShareType() || ShareType.EMAIL == share.getShareType()) {
+                links.add(share);
+            } else if (share.getShareType() != ShareType.INTERNAL) {
+                users.add(share);
+            }
         }
-    }
 
-    public interface ShareeListAdapterListener {
-        /**
-         * unshare with given sharee {@link OCShare}.
-         *
-         * @param share the share
-         */
-        void unshareWith(OCShare share);
+        Collections.sort(links, (o1, o2) -> Long.compare(o2.getSharedDate(), o1.getSharedDate()));
+        Collections.sort(users, (o1, o2) -> Long.compare(o2.getSharedDate(), o1.getSharedDate()));
+
+        shares = links;
+        shares.addAll(users);
+
+        // add internal share link at end
+        shares.add(new OCShare().setShareType(ShareType.INTERNAL));
+    }
 
-        /**
-         * Updates the permissions of the {@link OCShare}.
-         *
-         * @param share         the share to be updated
-         * @param canReshare    reshare permission
-         * @param canEdit       edit permission
-         * @param canEditCreate create permission (folders only)
-         * @param canEditChange change permission (folders only)
-         * @param canEditDelete delete permission (folders only)
-         * @return permissions value set
-         */
-        int updatePermissionsToShare(OCShare share,
-                                     boolean canReshare,
-                                     boolean canEdit,
-                                     boolean canEditCreate,
-                                     boolean canEditChange,
-                                     boolean canEditDelete);
+    public List<OCShare> getShares() {
+        return shares;
+    }
 
-        /**
-         * Starts a dialog that requests a password to the user to protect a share.
-         *
-         * @param share the share for which a password shall be configured/removed
-         */
-        void requestPasswordForShare(OCShare share, boolean askForPassword);
+    public void removeNewPublicShare() {
+        for (OCShare share : shares) {
+            if (share.getShareType() == ShareType.NEW_PUBLIC_LINK) {
+                shares.remove(share);
+                break;
+            }
+        }
     }
 }

+ 9 - 1
src/main/java/com/owncloud/android/ui/adapter/PublicShareInterface.java → src/main/java/com/owncloud/android/ui/adapter/ShareeListAdapterListener.java

@@ -26,8 +26,16 @@ import android.widget.ImageView;
 
 import com.owncloud.android.lib.resources.shares.OCShare;
 
-public interface PublicShareInterface {
+public interface ShareeListAdapterListener {
     void copyLink(OCShare share);
 
     void showLinkOverflowMenu(OCShare publicShare, ImageView overflowMenuShareLink);
+
+    void showUserOverflowMenu(OCShare share, ImageView overflowMenu);
+
+    void copyInternalLink();
+
+    void createPublicShareLink();
+
+    void requestPasswordForShare(OCShare share, boolean askForPassword);
 }

+ 1 - 1
src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java

@@ -716,7 +716,7 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter<SectionedVie
     /**
      * Load upload items from {@link UploadsStorageManager}.
      */
-    public void loadUploadItemsFromDb() {
+    public final void loadUploadItemsFromDb() {
         Log_OC.d(TAG, "loadUploadItemsFromDb");
 
         for (UploadGroup group : uploadGroups) {

+ 20 - 22
src/main/java/com/owncloud/android/ui/dialog/SharePasswordDialogFragment.java

@@ -27,13 +27,12 @@ import android.os.Bundle;
 import android.text.TextUtils;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.Window;
 import android.view.WindowManager;
 import android.widget.EditText;
-import android.widget.TextView;
 
 import com.owncloud.android.R;
+import com.owncloud.android.databinding.PasswordDialogBinding;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.lib.resources.shares.OCShare;
 import com.owncloud.android.ui.activity.FileActivity;
@@ -41,7 +40,6 @@ import com.owncloud.android.utils.DisplayUtils;
 import com.owncloud.android.utils.ThemeUtils;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import androidx.appcompat.app.AlertDialog;
 import androidx.fragment.app.DialogFragment;
 
@@ -58,6 +56,7 @@ public class SharePasswordDialogFragment extends DialogFragment implements Dialo
     private static final String ARG_ASK_FOR_PASSWORD = "ASK_FOR_PASSWORD";
     public static final String PASSWORD_FRAGMENT = "PASSWORD_FRAGMENT";
 
+    private PasswordDialogBinding binding;
     private OCFile file;
     private OCShare share;
     private boolean createShare;
@@ -122,12 +121,6 @@ public class SharePasswordDialogFragment extends DialogFragment implements Dialo
         return frag;
     }
 
-    @Nullable
-    @Override
-    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
-        return super.onCreateView(inflater, container, savedInstanceState);
-    }
-
     @NonNull
     @Override
     public Dialog onCreateDialog(Bundle savedInstanceState) {
@@ -137,17 +130,18 @@ public class SharePasswordDialogFragment extends DialogFragment implements Dialo
         askForPassword = getArguments().getBoolean(ARG_ASK_FOR_PASSWORD, false);
 
         // Inflate the layout for the dialog
-        LayoutInflater inflater = getActivity().getLayoutInflater();
-        View v = inflater.inflate(R.layout.password_dialog, null);
+        LayoutInflater inflater = requireActivity().getLayoutInflater();
+        binding = PasswordDialogBinding.inflate(inflater, null, false);
+        View view = binding.getRoot();
 
         // Setup layout
-        EditText inputText = v.findViewById(R.id.share_password);
-        inputText.getBackground().setColorFilter(
-                ThemeUtils.primaryAccentColor(getContext()),
-                PorterDuff.Mode.SRC_ATOP
-        );
+        EditText inputText = binding.sharePassword;
+        inputText.setHighlightColor(ThemeUtils.primaryColor(getActivity()));
         inputText.setText("");
+        ThemeUtils.themeEditText(getContext(), inputText, false);
         inputText.requestFocus();
+        inputText.getBackground().setColorFilter(ThemeUtils.primaryAccentColor(getContext()),
+                                                 PorterDuff.Mode.SRC_ATOP);
 
         int title;
         if (askForPassword) {
@@ -157,10 +151,9 @@ public class SharePasswordDialogFragment extends DialogFragment implements Dialo
         }
 
         // Build the dialog
-        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(),
-                R.style.Theme_ownCloud_Dialog_NoButtonBarStyle);
+        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
 
-        builder.setView(v)
+        builder.setView(view)
                 .setPositiveButton(R.string.common_ok, this)
                 .setNegativeButton(R.string.common_cancel, this)
                 .setNeutralButton(R.string.common_delete, this)
@@ -178,11 +171,10 @@ public class SharePasswordDialogFragment extends DialogFragment implements Dialo
     @Override
     public void onClick(DialogInterface dialog, int which) {
         if (which == AlertDialog.BUTTON_POSITIVE) {
-            String password = ((TextView) (getDialog().findViewById(R.id.share_password))).getText().toString();
+            String password = binding.sharePassword.getText().toString();
 
             if (!askForPassword && TextUtils.isEmpty(password)) {
-                DisplayUtils.showSnackMessage(getActivity().findViewById(android.R.id.content),
-                                              R.string.share_link_empty_password);
+                DisplayUtils.showSnackMessage(binding.getRoot(), R.string.share_link_empty_password);
                 return;
             }
 
@@ -217,4 +209,10 @@ public class SharePasswordDialogFragment extends DialogFragment implements Dialo
     private void setPassword(OCShare share, String password) {
         ((FileActivity) getActivity()).getFileOperationsHelper().setPasswordToShare(share, password);
     }
+
+    @Override
+    public void onDestroyView() {
+        super.onDestroyView();
+        binding = null;
+    }
 }

+ 308 - 175
src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java

@@ -27,7 +27,6 @@ import android.accounts.AccountManager;
 import android.app.SearchManager;
 import android.content.Context;
 import android.content.res.Resources;
-import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.text.InputType;
@@ -38,13 +37,12 @@ import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
 
 import com.nextcloud.client.account.User;
 import com.nextcloud.client.account.UserAccountManager;
 import com.nextcloud.client.di.Injectable;
 import com.owncloud.android.R;
+import com.owncloud.android.databinding.FileDetailsSharingFragmentBinding;
 import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.lib.common.OwnCloudAccount;
@@ -53,11 +51,11 @@ import com.owncloud.android.lib.resources.shares.OCShare;
 import com.owncloud.android.lib.resources.shares.SharePermissionsBuilder;
 import com.owncloud.android.lib.resources.shares.ShareType;
 import com.owncloud.android.lib.resources.status.OCCapability;
+import com.owncloud.android.lib.resources.status.OwnCloudVersion;
 import com.owncloud.android.ui.activity.FileActivity;
 import com.owncloud.android.ui.activity.FileDisplayActivity;
-import com.owncloud.android.ui.adapter.PublicShareInterface;
-import com.owncloud.android.ui.adapter.PublicShareListAdapter;
 import com.owncloud.android.ui.adapter.ShareeListAdapter;
+import com.owncloud.android.ui.adapter.ShareeListAdapterListener;
 import com.owncloud.android.ui.decoration.SimpleListItemDividerDecoration;
 import com.owncloud.android.ui.dialog.ExpirationDatePickerDialogFragment;
 import com.owncloud.android.ui.dialog.NoteDialogFragment;
@@ -70,25 +68,29 @@ import com.owncloud.android.utils.ClipboardUtil;
 import com.owncloud.android.utils.DisplayUtils;
 import com.owncloud.android.utils.ThemeUtils;
 
+import java.util.ArrayList;
 import java.util.List;
 
 import javax.inject.Inject;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
 import androidx.appcompat.widget.PopupMenu;
-import androidx.appcompat.widget.SearchView;
 import androidx.fragment.app.Fragment;
 import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
-import butterknife.BindView;
-import butterknife.ButterKnife;
-import butterknife.OnClick;
-import butterknife.Unbinder;
 
-public class FileDetailSharingFragment extends Fragment implements ShareeListAdapter.ShareeListAdapterListener,
+import static com.owncloud.android.lib.resources.shares.OCShare.CREATE_PERMISSION_FLAG;
+import static com.owncloud.android.lib.resources.shares.OCShare.DELETE_PERMISSION_FLAG;
+import static com.owncloud.android.lib.resources.shares.OCShare.MAXIMUM_PERMISSIONS_FOR_FILE;
+import static com.owncloud.android.lib.resources.shares.OCShare.MAXIMUM_PERMISSIONS_FOR_FOLDER;
+import static com.owncloud.android.lib.resources.shares.OCShare.NO_PERMISSION;
+import static com.owncloud.android.lib.resources.shares.OCShare.READ_PERMISSION_FLAG;
+import static com.owncloud.android.lib.resources.shares.OCShare.SHARE_PERMISSION_FLAG;
+import static com.owncloud.android.lib.resources.shares.OCShare.UPDATE_PERMISSION_FLAG;
+
+public class FileDetailSharingFragment extends Fragment implements ShareeListAdapterListener,
     DisplayUtils.AvatarGenerationListener,
-    PublicShareInterface,
     Injectable {
 
     private static final String ARG_FILE = "FILE";
@@ -103,40 +105,7 @@ public class FileDetailSharingFragment extends Fragment implements ShareeListAda
     private FileActivity fileActivity;
     private FileDataStorageManager fileDataStorageManager;
 
-    private Unbinder unbinder;
-
-    @BindView(R.id.searchView)
-    SearchView searchView;
-
-    @BindView(R.id.shareUsersList)
-    RecyclerView usersList;
-
-    @BindView(R.id.publicShareList)
-    RecyclerView publicShareList;
-
-    @BindView(R.id.new_public_share)
-    View addPublicShare;
-
-    @BindView(R.id.shared_with_you_container)
-    LinearLayout sharedWithYouContainer;
-
-    @BindView(R.id.shared_with_you_avatar)
-    ImageView sharedWithYouAvatar;
-
-    @BindView(R.id.shared_with_you_username)
-    TextView sharedWithYouUsername;
-
-    @BindView(R.id.shared_with_you_note_container)
-    View sharedWithYouNoteContainer;
-
-    @BindView(R.id.shared_with_you_note)
-    TextView sharedWithYouNote;
-
-    @BindView(R.id.copy_internal_link_icon)
-    ImageView internalLinkIcon;
-
-    @BindView(R.id.shareInternalLinkText)
-    TextView internalLinkText;
+    private FileDetailsSharingFragmentBinding binding;
 
     @Inject UserAccountManager accountManager;
 
@@ -184,30 +153,29 @@ public class FileDetailSharingFragment extends Fragment implements ShareeListAda
         super.onActivityCreated(savedInstanceState);
 
         refreshCapabilitiesFromDB();
-        refreshPublicShareFromDB();
+        refreshSharesFromDB();
     }
 
     @Override
     public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
-        View view = inflater.inflate(R.layout.file_details_sharing_fragment, container, false);
-        unbinder = ButterKnife.bind(this, view);
+        binding = FileDetailsSharingFragmentBinding.inflate(inflater, container, false);
+        View view = binding.getRoot();
 
         fileOperationsHelper = fileActivity.getFileOperationsHelper();
         fileDataStorageManager = fileActivity.getStorageManager();
 
-        setupView();
+        AccountManager accountManager = AccountManager.get(getContext());
+        String userId = accountManager.getUserData(user.toPlatformAccount(),
+                                                   com.owncloud.android.lib.common.accounts.AccountUtils.Constants.KEY_USER_ID);
 
-        // todo extract
-        internalLinkIcon.getBackground().setColorFilter(getResources().getColor(R.color.grey_db),
-                                                        PorterDuff.Mode.SRC_IN);
-        internalLinkIcon.getDrawable().mutate().setColorFilter(getResources().getColor(R.color.black),
-                                                               PorterDuff.Mode.SRC_IN);
+        binding.sharesList.setAdapter(new ShareeListAdapter(fileActivity,
+                                                            new ArrayList<>(),
+                                                            this,
+                                                            userId));
+        binding.sharesList.setLayoutManager(new LinearLayoutManager(getContext()));
+        binding.sharesList.addItemDecoration(new SimpleListItemDividerDecoration(getContext()));
 
-        if (file.isFolder()) {
-            internalLinkText.setText(getString(R.string.share_internal_link_to_folder_text));
-        } else {
-            internalLinkText.setText(getString(R.string.share_internal_link_to_file_text));
-        }
+        setupView();
 
         return view;
     }
@@ -215,7 +183,7 @@ public class FileDetailSharingFragment extends Fragment implements ShareeListAda
     @Override
     public void onDestroy() {
         super.onDestroy();
-        unbinder.unbind();
+        binding = null;
     }
 
     @Override
@@ -230,16 +198,17 @@ public class FileDetailSharingFragment extends Fragment implements ShareeListAda
         setShareWithYou();
 
         FileDetailSharingFragmentHelper.setupSearchView(
-            (SearchManager) fileActivity.getSystemService(Context.SEARCH_SERVICE), searchView,
+            (SearchManager) fileActivity.getSystemService(Context.SEARCH_SERVICE),
+            binding.searchView,
             fileActivity.getComponentName());
-        ThemeUtils.themeSearchView(searchView, requireContext());
+        ThemeUtils.themeSearchView(binding.searchView, requireContext());
 
         if (file.canReshare()) {
-            setShareWithUserInfo();
+            binding.searchView.setQueryHint(getResources().getString(R.string.share_search));
         } else {
-            searchView.setQueryHint(getResources().getString(R.string.reshare_not_allowed));
-            searchView.setInputType(InputType.TYPE_NULL);
-            disableSearchView(searchView);
+            binding.searchView.setQueryHint(getResources().getString(R.string.reshare_not_allowed));
+            binding.searchView.setInputType(InputType.TYPE_NULL);
+            disableSearchView(binding.searchView);
         }
     }
 
@@ -257,52 +226,32 @@ public class FileDetailSharingFragment extends Fragment implements ShareeListAda
 
     private void setShareWithYou() {
         if (accountManager.userOwnsFile(file, user)) {
-            sharedWithYouContainer.setVisibility(View.GONE);
+            binding.sharedWithYouContainer.setVisibility(View.GONE);
         } else {
-            sharedWithYouUsername.setText(
+            binding.sharedWithYouUsername.setText(
                 String.format(getString(R.string.shared_with_you_by), file.getOwnerDisplayName()));
-            DisplayUtils.setAvatar(user, file.getOwnerId(), this, getResources().getDimension(
-                R.dimen.file_list_item_avatar_icon_radius), getResources(), sharedWithYouAvatar,
-                getContext());
-            sharedWithYouAvatar.setVisibility(View.VISIBLE);
+            DisplayUtils.setAvatar(user,
+                                   file.getOwnerId(),
+                                   this,
+                                   getResources().getDimension(
+                                       R.dimen.file_list_item_avatar_icon_radius),
+                                   getResources(),
+                                   binding.sharedWithYouAvatar,
+                                   getContext());
+            binding.sharedWithYouAvatar.setVisibility(View.VISIBLE);
 
             String note = file.getNote();
 
             if (!TextUtils.isEmpty(note)) {
-                sharedWithYouNote.setText(file.getNote());
-                sharedWithYouNoteContainer.setVisibility(View.VISIBLE);
+                binding.sharedWithYouNote.setText(file.getNote());
+                binding.sharedWithYouNoteContainer.setVisibility(View.VISIBLE);
             } else {
-                sharedWithYouNoteContainer.setVisibility(View.GONE);
+                binding.sharedWithYouNoteContainer.setVisibility(View.GONE);
             }
         }
     }
 
-    private void setShareWithUserInfo() {
-        // TODO Refactoring: create a new {@link ShareUserListAdapter} instance with every call should not be needed
-        // to show share with users/groups info
-        List<OCShare> shares = fileDataStorageManager.getSharesWithForAFile(file.getRemotePath(),
-                                                                            user.toPlatformAccount().name);
-        if (shares.size() > 0) {
-            AccountManager accountManager = AccountManager.get(getContext());
-            String userId = accountManager.getUserData(user.toPlatformAccount(),
-                                                       com.owncloud.android.lib.common.accounts.AccountUtils.Constants.KEY_USER_ID);
-
-            usersList.setVisibility(View.VISIBLE);
-            usersList.setAdapter(new ShareeListAdapter(fileActivity.getSupportFragmentManager(),
-                                                       fileActivity,
-                                                       shares,
-                                                       user.toPlatformAccount(),
-                                                       file,
-                                                       this,
-                                                       userId));
-            usersList.setLayoutManager(new LinearLayoutManager(getContext()));
-            usersList.addItemDecoration(new SimpleListItemDividerDecoration(getContext()));
-        } else {
-            usersList.setVisibility(View.GONE);
-        }
-    }
-
-    @OnClick(R.id.copy_internal_container)
+    @Override
     public void copyInternalLink() {
         OwnCloudAccount account = accountManager.getCurrentOwnCloudAccount();
 
@@ -318,7 +267,8 @@ public class FileDetailSharingFragment extends Fragment implements ShareeListAda
         return account.getBaseUri() + "/index.php/f/" + file.getLocalId();
     }
 
-    private void createShareLink() {
+    @Override
+    public void createPublicShareLink() {
         if (capabilities != null && (capabilities.getFilesSharingPublicPasswordEnforced().isTrue() ||
             capabilities.getFilesSharingPublicAskForOptionalPassword().isTrue())) {
             // password enforced by server, request to the user before trying to create
@@ -331,26 +281,80 @@ public class FileDetailSharingFragment extends Fragment implements ShareeListAda
         }
     }
 
-    private void showSendLinkTo() {
+    private void showSendLinkTo(OCShare publicShare) {
         if (file.isSharedViaLink()) {
-            if (TextUtils.isEmpty(file.getPublicLink())) {
+            if (TextUtils.isEmpty(publicShare.getShareLink())) {
                 fileOperationsHelper.getFileWithLink(file);
             } else {
-                FileDisplayActivity.showShareLinkDialog(fileActivity, file, file.getPublicLink());
+                FileDisplayActivity.showShareLinkDialog(fileActivity, file, publicShare.getShareLink());
             }
         }
     }
 
     public void copyLink(OCShare share) {
         if (file.isSharedViaLink()) {
-            if (TextUtils.isEmpty(file.getPublicLink())) {
+            if (TextUtils.isEmpty(share.getShareLink())) {
                 fileOperationsHelper.getFileWithLink(file);
             } else {
-                ClipboardUtil.copyToClipboard(getActivity(), file.getPublicLink());
+                ClipboardUtil.copyToClipboard(getActivity(), share.getShareLink());
             }
         }
     }
 
+    @Override
+    public void showUserOverflowMenu(OCShare share, ImageView overflowMenu) {
+        // use grey as fallback for elements where custom theming is not available
+        if (ThemeUtils.themingEnabled(requireContext())) {
+            requireContext().getTheme().applyStyle(R.style.FallbackThemingTheme, true);
+        }
+        PopupMenu popup = new PopupMenu(requireContext(), overflowMenu);
+        popup.inflate(R.menu.item_user_sharing_settings);
+        prepareUserOptionsMenu(popup.getMenu(), share);
+        popup.setOnMenuItemClickListener(item -> userOptionsItemSelected(popup.getMenu(), item, share));
+        popup.show();
+    }
+
+    /**
+     * Updates the sharee's menu with the current permissions of the {@link OCShare}
+     *
+     * @param menu  the menu of the sharee/shared file
+     * @param share the shared file
+     */
+    @VisibleForTesting
+    public void prepareUserOptionsMenu(Menu menu, OCShare share) {
+        MenuItem allowEditingItem = menu.findItem(R.id.allow_editing);
+        MenuItem allowCreatingItem = menu.findItem(R.id.allow_creating);
+        MenuItem allowDeletingItem = menu.findItem(R.id.allow_deleting);
+        MenuItem expirationDateItem = menu.findItem(R.id.action_expiration_date);
+        MenuItem reshareItem = menu.findItem(R.id.allow_resharing);
+        MenuItem sendNoteItem = menu.findItem(R.id.action_share_send_note);
+
+        allowEditingItem.setChecked(canEdit(share));
+
+        if (isReshareForbidden(share)) {
+            reshareItem.setVisible(false);
+        }
+        reshareItem.setChecked(canReshare(share));
+
+        if (file.isFolder() || share.isFolder()) {
+            allowCreatingItem.setChecked(canCreate(share));
+            allowDeletingItem.setChecked(canDelete(share));
+        } else {
+            allowCreatingItem.setVisible(false);
+            allowDeletingItem.setVisible(false);
+        }
+
+        if (!capabilities.getVersion().isNewerOrEqual(OwnCloudVersion.nextcloud_18)) {
+            expirationDateItem.setVisible(false);
+        }
+
+        SharingMenuHelper.setupExpirationDateMenuItem(menu.findItem(R.id.action_expiration_date),
+                                                      share.getExpirationDate(),
+                                                      getResources());
+
+        sendNoteItem.setVisible(capabilities.getVersion().isNoteOnShareSupported());
+    }
+
     public void showLinkOverflowMenu(OCShare publicShare, ImageView overflowMenuShareLink) {
         if (ThemeUtils.themingEnabled(requireContext())) {
             // use grey as fallback for elements where custom theming is not available
@@ -358,21 +362,45 @@ public class FileDetailSharingFragment extends Fragment implements ShareeListAda
         }
 
         PopupMenu popup = new PopupMenu(requireContext(), overflowMenuShareLink);
-        popup.inflate(R.menu.fragment_file_detail_sharing_link);
-        prepareOptionsMenu(popup.getMenu(), publicShare);
-        popup.setOnMenuItemClickListener(menuItem -> optionsItemSelected(menuItem, publicShare));
+        if (ShareType.EMAIL == publicShare.getShareType()) {
+            popup.inflate(R.menu.fragment_file_detail_sharing_email_link);
+        } else {
+            popup.inflate(R.menu.fragment_file_detail_sharing_public_link);
+        }
+        prepareLinkOptionsMenu(popup.getMenu(), publicShare);
+        popup.setOnMenuItemClickListener(menuItem -> linkOptionsItemSelected(menuItem, publicShare));
         popup.show();
     }
 
-    private void prepareOptionsMenu(Menu menu, OCShare publicShare) {
-        Resources res = requireContext().getResources();
-        SharingMenuHelper.setupHideFileListingMenuItem(menu.findItem(R.id.action_hide_file_listing),
-                                                       file.isFolder(),
-                                                       menu.findItem(R.id.action_allow_editing).isChecked(),
-                                                       publicShare.getPermissions());
+    @VisibleForTesting
+    public void prepareLinkOptionsMenu(Menu menu, OCShare publicShare) {
+        if (publicShare.isFolder()) {
+            menu.setGroupVisible(R.id.folder_permission, true);
+            menu.findItem(R.id.allow_editing).setVisible(false);
+
+            // read only / allow upload and editing / file drop
+            if (isUploadAndEditingAllowed(publicShare)) {
+                menu.findItem(R.id.link_share_allow_upload_and_editing).setChecked(true);
+            } else if (isFileDrop(publicShare)) {
+                menu.findItem(R.id.link_share_file_drop).setChecked(true);
+            } else if (isReadOnly(publicShare)) {
+                menu.findItem(R.id.link_share_read_only).setChecked(true);
+            }
+        } else {
+            menu.setGroupVisible(R.id.folder_permission, false);
+            menu.findItem(R.id.allow_editing).setVisible(true);
+
+            if (publicShare.getPermissions() > PERMISSION_EDITING_ALLOWED) {
+                menu.findItem(R.id.allow_editing).setChecked(true);
+            } else {
+                menu.findItem(R.id.allow_editing).setChecked(false);
+            }
+        }
 
+        Resources res = requireContext().getResources();
         SharingMenuHelper.setupHideFileDownload(menu.findItem(R.id.action_hide_file_download),
                                                 publicShare.isHideFileDownload(),
+                                                isFileDrop(publicShare),
                                                 capabilities);
 
         SharingMenuHelper.setupPasswordMenuItem(menu.findItem(R.id.action_password),
@@ -383,36 +411,104 @@ public class FileDetailSharingFragment extends Fragment implements ShareeListAda
                                                       res);
 
         menu.findItem(R.id.action_share_send_note).setVisible(capabilities.getVersion().isNoteOnShareSupported());
+    }
 
-        if (publicShare.getPermissions() > PERMISSION_EDITING_ALLOWED) {
-            menu.findItem(R.id.action_allow_editing).setChecked(true);
-        } else {
-            menu.findItem(R.id.action_allow_editing).setChecked(false);
+    @VisibleForTesting
+    public boolean isUploadAndEditingAllowed(OCShare share) {
+        if (share.getPermissions() == NO_PERMISSION) {
+            return false;
         }
+
+        return (share.getPermissions() & MAXIMUM_PERMISSIONS_FOR_FOLDER) == MAXIMUM_PERMISSIONS_FOR_FOLDER;
     }
 
-    public boolean optionsItemSelected(MenuItem item, OCShare publicShare) {
+    @VisibleForTesting
+    public boolean isReadOnly(OCShare share) {
+        if (share.getPermissions() == NO_PERMISSION) {
+            return false;
+        }
+
+        return (share.getPermissions() & ~SHARE_PERMISSION_FLAG) == READ_PERMISSION_FLAG;
+    }
+
+    @VisibleForTesting
+    public boolean isFileDrop(OCShare share) {
+        if (share.getPermissions() == NO_PERMISSION) {
+            return false;
+        }
+
+        return (share.getPermissions() & ~SHARE_PERMISSION_FLAG) == CREATE_PERMISSION_FLAG;
+    }
+
+    private boolean userOptionsItemSelected(Menu menu, MenuItem item, OCShare share) {
         switch (item.getItemId()) {
-            case R.id.action_allow_editing:
+            case R.id.allow_editing:
+            case R.id.allow_creating:
+            case R.id.allow_deleting:
+            case R.id.allow_resharing: {
+                item.setChecked(!item.isChecked());
+                share.setPermissions(updatePermissionsToShare(share,
+                                                              menu.findItem(R.id.allow_resharing).isChecked(),
+                                                              menu.findItem(R.id.allow_editing).isChecked(),
+                                                              menu.findItem(R.id.allow_creating).isChecked(),
+                                                              menu.findItem(R.id.allow_deleting).isChecked()));
+                return true;
+            }
+            case R.id.action_unshare: {
+                unshareWith(share);
+                ShareeListAdapter adapter = (ShareeListAdapter) binding.sharesList.getAdapter();
+                if (adapter == null) {
+                    DisplayUtils.showSnackMessage(getView(), getString(R.string.failed_update_ui));
+                    return true;
+                }
+                adapter.remove(share);
+
+                return true;
+            }
+            case R.id.action_expiration_date: {
+                ExpirationDatePickerDialogFragment dialog = ExpirationDatePickerDialogFragment
+                    .newInstance(share, share.getExpirationDate());
+                dialog.show(fileActivity.getSupportFragmentManager(),
+                            ExpirationDatePickerDialogFragment.DATE_PICKER_DIALOG);
+                return true;
+            }
+            case R.id.action_share_send_note:
+                NoteDialogFragment dialog = NoteDialogFragment.newInstance(share);
+                dialog.show(fileActivity.getSupportFragmentManager(), NoteDialogFragment.NOTE_FRAGMENT);
+                return true;
+            default:
+                return true;
+        }
+    }
+
+    public boolean linkOptionsItemSelected(MenuItem item, OCShare publicShare) {
+        switch (item.getItemId()) {
+            case R.id.link_share_read_only:
+                item.setChecked(true);
+                fileOperationsHelper.setPermissionsToShare(publicShare, READ_PERMISSION_FLAG);
+                return true;
+            case R.id.link_share_allow_upload_and_editing:
+                item.setChecked(true);
+                if (publicShare.isFolder()) {
+                    fileOperationsHelper.setPermissionsToShare(publicShare, MAXIMUM_PERMISSIONS_FOR_FOLDER);
+                } else {
+                    fileOperationsHelper.setPermissionsToShare(publicShare, MAXIMUM_PERMISSIONS_FOR_FILE);
+                }
+                return true;
+            case R.id.link_share_file_drop: {
+                item.setChecked(true);
+                fileOperationsHelper.setPermissionsToShare(publicShare, CREATE_PERMISSION_FLAG);
+                return true;
+            }
+            case R.id.allow_editing:
                 if (file.isSharedViaLink()) {
                     item.setChecked(!item.isChecked());
                     fileOperationsHelper.setUploadPermissionsToPublicShare(publicShare, item.isChecked());
                 }
                 return true;
-            case R.id.action_hide_file_listing: {
-                item.setChecked(!item.isChecked());
-                fileOperationsHelper.setHideFileListingPermissionsToPublicShare(publicShare, item.isChecked());
-                return true;
-            }
             case R.id.action_hide_file_download:
                 item.setChecked(!item.isChecked());
                 fileOperationsHelper.setHideFileDownloadPermissionsToPublicShare(publicShare, item.isChecked());
-
-                return true;
-            case R.id.action_edit_label:
-                RenamePublicShareDialogFragment renameDialog = RenamePublicShareDialogFragment.newInstance(publicShare);
-                renameDialog.show(fileActivity.getSupportFragmentManager(),
-                                  RenamePublicShareDialogFragment.RENAME_PUBLIC_SHARE_FRAGMENT);
                 return true;
             case R.id.action_password: {
                 requestPasswordForShare(publicShare,
@@ -427,10 +523,10 @@ public class FileDetailSharingFragment extends Fragment implements ShareeListAda
                 return true;
             }
             case R.id.action_share_send_link: {
-                if (file.isSharedViaLink() && !TextUtils.isEmpty(file.getPublicLink())) {
-                    FileDisplayActivity.showShareLinkDialog(fileActivity, file, file.getPublicLink());
+                if (file.isSharedViaLink() && !TextUtils.isEmpty(publicShare.getShareLink())) {
+                    FileDisplayActivity.showShareLinkDialog(fileActivity, file, publicShare.getShareLink());
                 } else {
-                    showSendLinkTo();
+                    showSendLinkTo(publicShare);
                 }
                 return true;
             }
@@ -438,12 +534,17 @@ public class FileDetailSharingFragment extends Fragment implements ShareeListAda
                 NoteDialogFragment noteDialog = NoteDialogFragment.newInstance(publicShare);
                 noteDialog.show(fileActivity.getSupportFragmentManager(), NoteDialogFragment.NOTE_FRAGMENT);
                 return true;
-            case R.id.action_add_another_public_share_link:
-                createShareLink();
+            case R.id.action_edit_label:
+                RenamePublicShareDialogFragment renameDialog = RenamePublicShareDialogFragment.newInstance(publicShare);
+                renameDialog.show(fileActivity.getSupportFragmentManager(),
+                                  RenamePublicShareDialogFragment.RENAME_PUBLIC_SHARE_FRAGMENT);
                 return true;
             case R.id.action_unshare:
                 fileOperationsHelper.unshareShare(file, publicShare);
                 return true;
+            case R.id.action_add_another_public_share_link:
+                createPublicShareLink();
+                return true;
             default:
                 return super.onOptionsItemSelected(item);
         }
@@ -469,28 +570,25 @@ public class FileDetailSharingFragment extends Fragment implements ShareeListAda
      * Get {@link OCShare} instance from DB and updates the UI.
      */
     private void refreshUiFromDB() {
+        refreshSharesFromDB();
         // Updates UI with new state
         setupView();
     }
 
-    @Override
-    public void unshareWith(OCShare share) {
+    private void unshareWith(OCShare share) {
         fileOperationsHelper.unshareShare(file, share);
     }
 
-    @Override
-    public int updatePermissionsToShare(OCShare share,
-                                        boolean canReshare,
-                                        boolean canEdit,
-                                        boolean canEditCreate,
-                                        boolean canEditChange,
-                                        boolean canEditDelete) {
+    private int updatePermissionsToShare(OCShare share,
+                                         boolean canReshare,
+                                         boolean canEdit,
+                                         boolean canEditCreate,
+                                         boolean canEditDelete) {
         SharePermissionsBuilder spb = new SharePermissionsBuilder();
         spb.setSharePermission(canReshare);
 
         if (file.isFolder()) {
-            spb.setUpdatePermission(canEditChange)
-                .setCreatePermission(canEditCreate)
+            spb.setCreatePermission(canEditCreate)
                 .setDeletePermission(canEditDelete);
         } else {
             spb.setUpdatePermission(canEdit);
@@ -531,38 +629,51 @@ public class FileDetailSharingFragment extends Fragment implements ShareeListAda
 
     /**
      * Get public link from the DB to fill in the "Share link" section in the UI.
-     *
      * Takes into account server capabilities before reading database.
      */
-    public void refreshPublicShareFromDB() {
+    public void refreshSharesFromDB() {
+        ShareeListAdapter adapter = (ShareeListAdapter) binding.sharesList.getAdapter();
+
+        if (adapter == null) {
+            DisplayUtils.showSnackMessage(getView(), getString(R.string.could_not_retrieve_shares));
+            return;
+        }
+        adapter.getShares().clear();
+
+        // to show share with users/groups info
+        List<OCShare> shares = fileDataStorageManager.getSharesWithForAFile(file.getRemotePath(),
+                                                                            user.toPlatformAccount().name);
+
+        adapter.addShares(shares);
+
         if (FileDetailSharingFragmentHelper.isPublicShareDisabled(capabilities) || !file.canReshare()) {
-            publicShareList.setVisibility(View.GONE);
             return;
         }
 
         // Get public share
-        List<OCShare> shares = fileDataStorageManager.getSharesByPathAndType(file.getRemotePath(),
-                                                                             ShareType.PUBLIC_LINK,
-                                                                             "");
-
-        if (shares.isEmpty()) {
-            addPublicShare.setVisibility(View.VISIBLE);
-            publicShareList.setVisibility(View.GONE);
-            ImageView icon = requireView().findViewById(R.id.copy_internal_link_icon);
-            icon.getBackground().setColorFilter(requireContext()
-                                                    .getResources()
-                                                    .getColor(R.color.primary_button_background_color),
-                                                PorterDuff.Mode.SRC_IN);
-            icon.getDrawable().mutate().setColorFilter(requireContext().getResources().getColor(R.color.black),
-                                                       PorterDuff.Mode.SRC_IN);
-            requireView().findViewById(R.id.add_new_public_share_link).setOnClickListener(v -> createShareLink());
+        List<OCShare> publicShares = fileDataStorageManager.getSharesByPathAndType(file.getRemotePath(),
+                                                                                   ShareType.PUBLIC_LINK,
+                                                                                   "");
+
+
+        if (publicShares.isEmpty() && containsNoNewPublicShare(adapter.getShares())) {
+            publicShares.add(new OCShare().setShareType(ShareType.NEW_PUBLIC_LINK));
         } else {
-            addPublicShare.setVisibility(View.GONE);
-            publicShareList.setVisibility(View.VISIBLE);
-            publicShareList.setAdapter(new PublicShareListAdapter(getContext(), shares, this));
-            publicShareList.setLayoutManager(new LinearLayoutManager(getContext()));
-            publicShareList.addItemDecoration(new SimpleListItemDividerDecoration(getContext()));
+            adapter.removeNewPublicShare();
         }
+
+        adapter.addShares(publicShares);
+    }
+
+
+    private boolean containsNoNewPublicShare(List<OCShare> shares) {
+        for (OCShare share : shares) {
+            if (share.getShareType() == ShareType.NEW_PUBLIC_LINK) {
+                return false;
+            }
+        }
+
+        return true;
     }
 
     @Override
@@ -575,11 +686,33 @@ public class FileDetailSharingFragment extends Fragment implements ShareeListAda
 
     @Override
     public void avatarGenerated(Drawable avatarDrawable, Object callContext) {
-        sharedWithYouAvatar.setImageDrawable(avatarDrawable);
+        binding.sharedWithYouAvatar.setImageDrawable(avatarDrawable);
     }
 
     @Override
     public boolean shouldCallGeneratedCallback(String tag, Object callContext) {
         return false;
     }
+
+    private boolean isReshareForbidden(OCShare share) {
+        return ShareType.FEDERATED.equals(share.getShareType()) ||
+            capabilities != null && capabilities.getFilesSharingResharing().isFalse();
+    }
+
+    private boolean canEdit(OCShare share) {
+        return (share.getPermissions() &
+            (CREATE_PERMISSION_FLAG | UPDATE_PERMISSION_FLAG | DELETE_PERMISSION_FLAG)) > 0;
+    }
+
+    private boolean canCreate(OCShare share) {
+        return (share.getPermissions() & CREATE_PERMISSION_FLAG) > 0;
+    }
+
+    private boolean canDelete(OCShare share) {
+        return (share.getPermissions() & DELETE_PERMISSION_FLAG) > 0;
+    }
+
+    private boolean canReshare(OCShare share) {
+        return (share.getPermissions() & SHARE_PERMISSION_FLAG) > 0;
+    }
 }

+ 7 - 3
src/main/java/com/owncloud/android/ui/fragment/util/SharingMenuHelper.java

@@ -65,11 +65,15 @@ public final class SharingMenuHelper {
 
     /**
      * Sets checked/visibility state on the given {@link MenuItem} based on the given criteria.
-     *  @param menuItem the {@link MenuItem} to be setup
+     *
+     * @param menuItem     the {@link MenuItem} to be setup
      * @param capabilities Capabilities of server to check if hide download is supported
      */
-    public static void setupHideFileDownload(MenuItem menuItem, boolean hideFileDownload, OCCapability capabilities) {
-        if (!capabilities.getVersion().isHideFileDownloadSupported()) {
+    public static void setupHideFileDownload(MenuItem menuItem,
+                                             boolean hideFileDownload,
+                                             boolean isFileDrop,
+                                             OCCapability capabilities) {
+        if (!capabilities.getVersion().isHideFileDownloadSupported() || isFileDrop) {
             menuItem.setVisible(false);
         } else {
             menuItem.setVisible(true);

+ 4 - 56
src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java

@@ -578,23 +578,6 @@ public class FileOperationsHelper {
         fileActivity.startActivity(intent);
     }
 
-
-    /**
-     * Updates a public share on a file to set its password. Starts a request to do it in {@link OperationsService}
-     *
-     * @param password Password to set for the public link; null or empty string to clear the current password
-     */
-    public void setPasswordToPublicShare(OCShare share, String password) {
-        // Set password updating share
-        Intent updateShareIntent = new Intent(fileActivity, OperationsService.class);
-        updateShareIntent.setAction(OperationsService.ACTION_UPDATE_PUBLIC_SHARE);
-        updateShareIntent.putExtra(OperationsService.EXTRA_ACCOUNT, fileActivity.getAccount());
-        updateShareIntent.putExtra(OperationsService.EXTRA_SHARE_ID, share.getId());
-        updateShareIntent.putExtra(OperationsService.EXTRA_SHARE_PASSWORD, (password == null) ? "" : password);
-
-        queueShareIntent(updateShareIntent);
-    }
-
     /**
      * Updates a public share on a file to set its label. Starts a request to do it in {@link OperationsService}
      *
@@ -633,23 +616,6 @@ public class FileOperationsHelper {
     }
 
 
-    /**
-     * Updates a public share on a file to set its expiration date. Starts a request to do it in {@link
-     * OperationsService}
-     *
-     * @param share                  {@link OCShare} instance which permissions will be updated.
-     * @param expirationTimeInMillis Expiration date to set. A negative value clears the current expiration date,
-     *                               leaving the link unrestricted. Zero makes no change.
-     */
-    public void setExpirationDateToPublicShare(OCShare share, long expirationTimeInMillis) {
-        Intent updateShareIntent = new Intent(fileActivity, OperationsService.class);
-        updateShareIntent.setAction(OperationsService.ACTION_UPDATE_PUBLIC_SHARE);
-        updateShareIntent.putExtra(OperationsService.EXTRA_ACCOUNT, fileActivity.getAccount());
-        updateShareIntent.putExtra(OperationsService.EXTRA_SHARE_ID, share.getId());
-        updateShareIntent.putExtra(OperationsService.EXTRA_SHARE_EXPIRATION_DATE_IN_MILLIS, expirationTimeInMillis);
-        queueShareIntent(updateShareIntent);
-    }
-
     /**
      * Updates a public share on a file to set its expiration date.
      * Starts a request to do it in {@link OperationsService}
@@ -693,31 +659,13 @@ public class FileOperationsHelper {
      */
     public void setUploadPermissionsToPublicShare(OCShare share, boolean uploadPermission) {
         Intent updateShareIntent = new Intent(fileActivity, OperationsService.class);
-        updateShareIntent.setAction(OperationsService.ACTION_UPDATE_PUBLIC_SHARE);
-        updateShareIntent.putExtra(OperationsService.EXTRA_ACCOUNT, fileActivity.getAccount());
-        updateShareIntent.putExtra(OperationsService.EXTRA_SHARE_ID, share.getId());
-        updateShareIntent.putExtra(OperationsService.EXTRA_SHARE_PUBLIC_UPLOAD, uploadPermission);
-        queueShareIntent(updateShareIntent);
-    }
-
-    /**
-     * Updates a public share on a folder to set its hide file listing permission.
-     * Starts a request to do it in {@link OperationsService}
-     *
-     * @param share           {@link OCShare} instance which permissions will be updated.
-     * @param hideFileListing New state of the permission for editing the folder shared via link.
-     */
-    public void setHideFileListingPermissionsToPublicShare(OCShare share, boolean hideFileListing) {
-        Intent updateShareIntent = new Intent(fileActivity, OperationsService.class);
-        updateShareIntent.setAction(OperationsService.ACTION_UPDATE_PUBLIC_SHARE);
+        updateShareIntent.setAction(OperationsService.ACTION_UPDATE_USER_SHARE);
         updateShareIntent.putExtra(OperationsService.EXTRA_ACCOUNT, fileActivity.getAccount());
         updateShareIntent.putExtra(OperationsService.EXTRA_SHARE_ID, share.getId());
-
-        if (hideFileListing) {
-            updateShareIntent.putExtra(OperationsService.EXTRA_SHARE_PERMISSIONS, OCShare.CREATE_PERMISSION_FLAG);
+        if (uploadPermission) {
+            updateShareIntent.putExtra(OperationsService.EXTRA_SHARE_PERMISSIONS, 3);
         } else {
-            updateShareIntent.putExtra(OperationsService.EXTRA_SHARE_PERMISSIONS,
-                                       OCShare.FEDERATED_PERMISSIONS_FOR_FOLDER_AFTER_OC9);
+            updateShareIntent.putExtra(OperationsService.EXTRA_SHARE_PERMISSIONS, 1);
         }
 
         queueShareIntent(updateShareIntent);

+ 0 - 23
src/main/res/drawable/ic_hide.xml

@@ -1,23 +0,0 @@
-<!--
-    @author Google LLC
-    Copyright (C) 2018 Google LLC
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:height="24dp"
-    android:width="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
-    <path android:fillColor="#FFFFFF" android:pathData="M11.83,9L15,12.16C15,12.11 15,12.05 15,12A3,3 0 0,0 12,9C11.94,9 11.89,9 11.83,9M7.53,9.8L9.08,11.35C9.03,11.56 9,11.77 9,12A3,3 0 0,0 12,15C12.22,15 12.44,14.97 12.65,14.92L14.2,16.47C13.53,16.8 12.79,17 12,17A5,5 0 0,1 7,12C7,11.21 7.2,10.47 7.53,9.8M2,4.27L4.28,6.55L4.73,7C3.08,8.3 1.78,10 1,12C2.73,16.39 7,19.5 12,19.5C13.55,19.5 15.03,19.2 16.38,18.66L16.81,19.08L19.73,22L21,20.73L3.27,3M12,7A5,5 0 0,1 17,12C17,12.64 16.87,13.26 16.64,13.82L19.57,16.75C21.07,15.5 22.27,13.86 23,12C21.27,7.61 17,4.5 12,4.5C10.6,4.5 9.26,4.75 8,5.2L10.17,7.35C10.74,7.13 11.35,7 12,7Z" />
-</vector>

+ 0 - 25
src/main/res/drawable/ic_show.xml

@@ -1,25 +0,0 @@
-<!--
-  Nextcloud Android client application
-
-  Copyright (C) 2020 Nextcloud.
-
-  This program is free software; you can redistribute it and/or
-  modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
-  License as published by the Free Software Foundation; either
-  version 3 of the License, or 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://www.gnu.org/licenses/>.
-
-  Icon provided by Android Material Library in Apache License 2.0
--->
-<vector android:height="24dp" android:tint="#FFFFFF"
-    android:viewportHeight="24.0" android:viewportWidth="24.0"
-    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
-    <path android:fillColor="#FF000000" android:pathData="M12,4.5C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zM12,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z"/>
-</vector>

+ 0 - 8
src/main/res/drawable/password_visibility_selector.xml

@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:id="@+id/visible"
-        android:drawable="@drawable/ic_show"
-        android:state_checked="true" />
-    <item android:id="@+id/masked"
-        android:drawable="@drawable/ic_hide" />
-</selector>

+ 1 - 2
src/main/res/layout/file_details_fragment.xml

@@ -23,7 +23,6 @@
     android:id="@+id/detail_container"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:fillViewport="true"
     android:orientation="vertical">
 
     <LinearLayout
@@ -184,7 +183,7 @@
     <androidx.viewpager.widget.ViewPager
         android:id="@+id/pager"
         android:layout_width="match_parent"
-        android:layout_height="fill_parent" />
+        android:layout_height="match_parent" />
 
     <include layout="@layout/empty_list" />
 </LinearLayout>

+ 67 - 0
src/main/res/layout/file_details_share_internal_share_link.xml

@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~
+  ~ Nextcloud Android client application
+  ~
+  ~ @author Tobias Kaminsky
+  ~ Copyright (C) 2020 Tobias Kaminsky
+  ~ Copyright (C) 2020 Nextcloud GmbH
+  ~
+  ~ This program is free software: you can redistribute it and/or modify
+  ~ it under the terms of the GNU Affero General Public License as published by
+  ~ the Free Software Foundation, either version 3 of the License, or
+  ~ (at your option) any later version.
+  ~
+  ~ This program is distributed in the hope that it will be useful,
+  ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+  ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+  ~ GNU Affero General Public License for more details.
+  ~
+  ~ You should have received a copy of the GNU Affero General Public License
+  ~ along with this program. If not, see <https://www.gnu.org/licenses/>.
+  -->
+
+<LinearLayout android:id="@+id/copy_internal_container"
+    android:layout_height="wrap_content"
+    android:layout_width="match_parent"
+    android:orientation="horizontal"
+    android:paddingBottom="@dimen/standard_half_padding"
+    android:paddingEnd="@dimen/standard_padding"
+    android:paddingStart="@dimen/zero"
+    android:paddingTop="@dimen/standard_half_padding"
+    android:layout_weight="1"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools">
+
+    <ImageView
+        android:background="@drawable/round_bgnd"
+        android:contentDescription="@string/share"
+        android:id="@+id/copy_internal_link_icon"
+        android:layout_gravity="top"
+        android:layout_height="@dimen/share_icon_size"
+        android:layout_marginEnd="@dimen/standard_margin"
+        android:layout_marginStart="@dimen/standard_margin"
+        android:layout_width="@dimen/share_icon_size"
+        android:padding="@dimen/standard_half_padding"
+        android:src="@drawable/ic_external" />
+
+    <LinearLayout
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/standard_half_margin"
+        android:layout_width="match_parent"
+        android:orientation="vertical">
+
+        <TextView
+            android:id="@+id/shareInternalLink"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:text="@string/share_internal_link"
+            android:textColor="@color/text_color" />
+
+        <TextView
+            android:id="@+id/shareInternalLinkText"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            tools:text="@string/share_internal_link_to_folder_text" />
+    </LinearLayout>
+
+</LinearLayout>

+ 16 - 11
src/main/res/layout/file_details_share_public_link_item.xml → src/main/res/layout/file_details_share_link_share_item.xml

@@ -25,11 +25,11 @@
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/share_by_link_container"
     android:layout_width="match_parent"
-    android:layout_height="@dimen/standard_list_item_size"
+    android:layout_height="@dimen/sharee_list_item_size"
     android:orientation="horizontal">
 
     <ImageView
-        android:id="@+id/copy_internal_link_icon"
+        android:id="@+id/icon"
         android:layout_width="@dimen/share_icon_size"
         android:layout_height="@dimen/share_icon_size"
         android:layout_gravity="center_vertical"
@@ -41,28 +41,33 @@
         android:src="@drawable/shared_via_link" />
 
     <TextView
-        android:id="@+id/public_share_label"
-        android:layout_width="wrap_content"
+        android:id="@+id/name"
+        android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:layout_gravity="center_vertical"
+        android:textColor="@color/text_color"
+        android:ellipsize="end"
+        android:singleLine="true"
+        android:layout_weight="1"
+        android:gravity="center_vertical"
+        android:textSize="@dimen/file_details_username_text_size"
         android:text="@string/share_via_link_section_title" />
 
     <ImageView
-        android:id="@+id/share_link_copy_icon"
-        android:layout_width="0dp"
+        android:id="@+id/copy_link"
+        android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="center_vertical"
-        android:layout_weight="1"
         android:contentDescription="@string/copy_link"
-        android:paddingStart="@dimen/standard_half_margin"
         android:paddingTop="@dimen/standard_quarter_margin"
-        android:paddingEnd="@dimen/standard_eighth_margin"
         android:paddingBottom="@dimen/standard_quarter_margin"
-        android:scaleType="fitStart"
+        android:scaleType="fitCenter"
+        android:paddingStart="@dimen/standard_padding"
+        android:paddingEnd="@dimen/standard_padding"
         android:src="@drawable/ic_content_copy" />
 
     <ImageView
-        android:id="@+id/overflow_menu_share_link"
+        android:id="@+id/overflow_menu"
         android:layout_width="wrap_content"
         android:layout_height="match_parent"
         android:contentDescription="@string/overflow_menu"

+ 3 - 4
src/main/res/layout/file_details_share_public_link_add_new_item.xml

@@ -23,11 +23,10 @@
 -->
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/new_public_share"
+    android:id="@+id/add_public_share"
     android:layout_width="match_parent"
-    android:layout_height="@dimen/standard_list_item_size"
-    android:orientation="horizontal"
-    android:visibility="gone">
+    android:layout_height="@dimen/sharee_list_item_size"
+    android:orientation="horizontal">
 
     <ImageView
         android:layout_width="@dimen/share_icon_size"

+ 23 - 35
src/main/res/layout/file_details_share_user_item.xml → src/main/res/layout/file_details_share_share_item.xml

@@ -20,13 +20,13 @@
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
     android:orientation="horizontal"
+    android:layout_height="@dimen/sharee_list_item_size"
     android:weightSum="1"
     tools:ignore="UseCompoundDrawables">
 
         <ImageView
-            android:id="@+id/avatar"
+            android:id="@+id/icon"
             android:layout_width="@dimen/user_icon_size"
             android:layout_height="@dimen/user_icon_size"
             android:layout_gravity="center_vertical"
@@ -39,37 +39,25 @@
             android:contentDescription="@string/user_icon"
             android:src="@drawable/ic_user" />
 
-        <TextView
-            android:id="@+id/name"
-            android:layout_width="0dp"
-            android:layout_height="match_parent"
-            android:layout_marginEnd="@dimen/standard_quarter_margin"
-            android:layout_weight="1"
-            android:ellipsize="end"
-            android:gravity="center_vertical"
-            android:singleLine="true"
-            android:text="@string/username"
-            android:textColor="@color/text_color"
-            android:textSize="@dimen/file_details_username_text_size" />
-
-    <androidx.appcompat.widget.AppCompatCheckBox
-            android:id="@+id/allowEditing"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center_vertical"
-            android:textSize="16sp"
-            android:text="@string/edit_permission_label" />
-
-        <ImageView
-            android:id="@+id/editShareButton"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center_vertical"
-            android:layout_marginLeft="@dimen/standard_half_margin"
-            android:layout_marginStart="@dimen/standard_half_margin"
-            android:layout_marginEnd="@dimen/standard_half_margin"
-            android:layout_marginRight="@dimen/standard_half_margin"
-            android:contentDescription="@string/overflow_menu"
-            android:padding="@dimen/standard_half_padding"
-            android:src="@drawable/ic_dots_vertical" />
+    <TextView
+        android:id="@+id/name"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:textColor="@color/text_color"
+        android:ellipsize="end"
+        android:singleLine="true"
+        android:layout_weight="1"
+        android:gravity="center_vertical"
+        android:textSize="@dimen/file_details_username_text_size"
+        android:text="@string/username" />
+
+    <ImageView
+        android:id="@+id/overflow_menu"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:contentDescription="@string/overflow_menu"
+        android:paddingStart="@dimen/standard_padding"
+        android:paddingEnd="@dimen/standard_padding"
+        android:src="@drawable/ic_dots_vertical" />
 </LinearLayout>

+ 63 - 124
src/main/res/layout/file_details_sharing_fragment.xml

@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
   Nextcloud Android client application
 
   Copyright (C) 2018 Andy Scherzinger
@@ -17,147 +16,87 @@
   You should have received a copy of the GNU Affero 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"
+<LinearLayout android:id="@+id/shareContainer"
     android:layout_height="match_parent"
-    android:layout_weight="1">
+    android:layout_width="match_parent"
+    android:orientation="vertical"
+    android:paddingTop="@dimen/standard_eight_padding"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools">
+
+    <androidx.appcompat.widget.SearchView
+        style="@style/ownCloud.SearchView"
+        android:hint="@string/share_search"
+        android:id="@+id/searchView"
+        android:layout_height="wrap_content"
+        android:layout_marginEnd="@dimen/standard_margin"
+        android:layout_marginStart="@dimen/standard_quarter_margin"
+        android:layout_width="match_parent" />
 
     <LinearLayout
-        android:id="@+id/shareContainer"
-        android:layout_width="match_parent"
+        android:id="@+id/shared_with_you_container"
         android:layout_height="wrap_content"
-        android:orientation="vertical"
-        android:paddingTop="@dimen/standard_eigth_padding">
-
-        <androidx.appcompat.widget.SearchView
-            android:id="@+id/searchView"
-            style="@style/ownCloud.SearchView"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginStart="@dimen/standard_quarter_margin"
-            android:layout_marginEnd="@dimen/standard_margin"
-            android:hint="@string/share_search" />
+        android:layout_marginBottom="@dimen/standard_half_margin"
+        android:layout_width="match_parent"
+        android:orientation="horizontal"
+        android:paddingLeft="@dimen/standard_padding"
+        android:paddingRight="@dimen/standard_padding"
+        android:paddingTop="@dimen/standard_padding">
+
+        <ImageView
+            android:contentDescription="@string/avatar"
+            android:id="@+id/shared_with_you_avatar"
+            android:layout_height="@dimen/user_icon_size"
+            android:layout_width="@dimen/user_icon_size"
+            android:src="@drawable/ic_user" />
 
         <LinearLayout
-            android:id="@+id/shared_with_you_container"
-            android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_marginBottom="@dimen/standard_half_margin"
-            android:orientation="horizontal"
+            android:layout_width="match_parent"
+            android:orientation="vertical"
             android:paddingLeft="@dimen/standard_padding"
-            android:paddingTop="@dimen/standard_padding"
-            android:paddingRight="@dimen/standard_padding">
-
-            <ImageView
-                android:id="@+id/shared_with_you_avatar"
-                android:layout_width="@dimen/user_icon_size"
-                android:layout_height="@dimen/user_icon_size"
-                android:contentDescription="@string/avatar"
-                android:src="@drawable/ic_user" />
+            android:paddingRight="@dimen/standard_padding"
+            android:paddingTop="@dimen/standard_half_padding">
 
-            <LinearLayout
-                android:layout_width="match_parent"
+            <TextView
+                android:id="@+id/shared_with_you_username"
                 android:layout_height="wrap_content"
-                android:orientation="vertical"
-                android:paddingLeft="@dimen/standard_padding"
-                android:paddingTop="@dimen/standard_half_padding"
-                android:paddingRight="@dimen/standard_padding">
-
-                <TextView
-                    android:id="@+id/shared_with_you_username"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:text="@string/shared_with_you_by"
-                    android:textSize="16sp" />
-
-                <LinearLayout
-                    android:id="@+id/shared_with_you_note_container"
-                    android:layout_width="match_parent"
-                    android:layout_height="match_parent"
-                    android:orientation="horizontal"
-                    android:paddingTop="@dimen/standard_half_padding"
-                    tools:ignore="UseCompoundDrawables">
-
-                    <ImageView
-                        android:layout_width="16dp"
-                        android:layout_height="16dp"
-                        android:src="@drawable/file_text"
-                        android:contentDescription="@string/note_icon_hint" />
-
-                    <TextView
-                        android:id="@+id/shared_with_you_note"
-                        android:layout_width="0dp"
-                        android:layout_height="wrap_content"
-                        android:paddingStart="@dimen/standard_half_padding"
-                        android:paddingEnd="@dimen/standard_half_padding"
-                        android:textSize="16sp"
-                        android:layout_weight="1" />
-                </LinearLayout>
-
-            </LinearLayout>
-        </LinearLayout>
-
-        <androidx.recyclerview.widget.RecyclerView
-            android:id="@+id/publicShareList"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:divider="@drawable/divider"
-            android:dividerHeight="1dp" />
-
-        <include layout="@layout/file_details_share_public_link_add_new_item" />
-
-        <androidx.recyclerview.widget.RecyclerView
-            android:id="@+id/shareUsersList"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:divider="@drawable/divider"
-            android:dividerHeight="1dp" />
-
-        <LinearLayout
-            android:id="@+id/copy_internal_container"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:orientation="horizontal"
-            android:paddingStart="@dimen/zero"
-            android:paddingTop="@dimen/standard_half_padding"
-            android:paddingEnd="@dimen/standard_padding"
-            android:paddingBottom="@dimen/standard_padding">
-
-            <ImageView
-                android:id="@+id/copy_internal_link_icon"
-                android:layout_width="@dimen/share_icon_size"
-                android:layout_height="@dimen/share_icon_size"
-                android:layout_gravity="top"
-                android:layout_marginStart="@dimen/standard_margin"
-                android:layout_marginEnd="@dimen/standard_margin"
-                android:background="@drawable/round_bgnd"
-                android:contentDescription="@string/share"
-                android:padding="@dimen/standard_half_padding"
-                android:src="@drawable/ic_external" />
+                android:layout_width="match_parent"
+                android:text="@string/shared_with_you_by"
+                android:textSize="16sp" />
 
             <LinearLayout
+                android:id="@+id/shared_with_you_note_container"
+                android:layout_height="match_parent"
                 android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_marginTop="@dimen/standard_half_margin"
-                android:orientation="vertical">
+                android:orientation="horizontal"
+                android:paddingTop="@dimen/standard_half_padding"
+                tools:ignore="UseCompoundDrawables">
 
-                <TextView
-                    android:id="@+id/shareInternalLink"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:text="@string/share_internal_link"
-                    android:textColor="@color/text_color" />
+                <ImageView
+                    android:contentDescription="@string/note_icon_hint"
+                    android:layout_height="16dp"
+                    android:layout_width="16dp"
+                    android:src="@drawable/file_text" />
 
                 <TextView
-                    android:id="@+id/shareInternalLinkText"
-                    android:layout_width="wrap_content"
+                    android:id="@+id/shared_with_you_note"
                     android:layout_height="wrap_content"
-                    tools:text="@string/share_internal_link_to_folder_text" />
+                    android:layout_weight="1"
+                    android:layout_width="0dp"
+                    android:paddingEnd="@dimen/standard_half_padding"
+                    android:paddingStart="@dimen/standard_half_padding"
+                    android:textSize="16sp" />
             </LinearLayout>
 
         </LinearLayout>
-
     </LinearLayout>
 
-</ScrollView>
+    <androidx.recyclerview.widget.RecyclerView
+        android:divider="@drawable/divider"
+        android:dividerHeight="1dp"
+        android:id="@+id/sharesList"
+        android:layout_height="match_parent"
+        android:layout_width="match_parent" />
+
+</LinearLayout>

+ 11 - 23
src/main/res/layout/password_dialog.xml

@@ -18,32 +18,20 @@
 -->
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:orientation="vertical"
     android:layout_width="match_parent"
-    android:layout_height="match_parent">
+    android:layout_height="match_parent"
+    android:gravity="clip_horizontal"
+    android:orientation="vertical"
+    android:padding="@dimen/standard_padding">
 
-    <com.google.android.material.textfield.TextInputLayout
+    <com.google.android.material.textfield.TextInputEditText
+        android:id="@+id/share_password"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:theme="@style/TextInputLayout"
-        app:hintTextColor="@color/fg_inverse"
-        app:passwordToggleDrawable="@drawable/password_visibility_selector"
-        app:boxBackgroundColor="@color/bg_default"
-        app:boxStrokeColor="@color/bg_fallback_highlight">
-
-        <com.google.android.material.textfield.TextInputEditText
-            android:id="@+id/share_password"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:ems="10"
-            android:hint="@string/hint_password"
-            android:inputType="textPassword"
-            android:layout_margin="@dimen/standard_margin"
-            android:autofillHints="password"
-            android:textColorHint="@color/bg_fallback_highlight">
-        </com.google.android.material.textfield.TextInputEditText>
-
-    </com.google.android.material.textfield.TextInputLayout>
+        android:ems="10"
+        android:hint="@string/hint_password"
+        android:inputType="textPassword"
+        android:autofillHints="password"
+        android:textColorHint="@color/bg_fallback_highlight"></com.google.android.material.textfield.TextInputEditText>
 
 </LinearLayout>

+ 0 - 74
src/main/res/layout/share_user_item.xml

@@ -1,74 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ownCloud Android client application
-  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/>.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="vertical">
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal"
-        android:weightSum="1"
-        android:longClickable="true">
-        <ImageView
-            android:layout_width="@dimen/file_details_user_icon_layout_width"
-            android:layout_height="@dimen/file_details_user_icon_layout_height"
-            android:id="@+id/icon"
-            android:src="@drawable/ic_user"
-            android:layout_gravity="center_vertical"
-            android:contentDescription="@string/user_icon"/>
-
-        <TextView
-            android:layout_width="0dp"
-            android:layout_height="match_parent"
-            android:layout_weight="1"
-            android:textSize="@dimen/two_line_primary_text_size"
-            android:text="@string/username"
-            android:id="@+id/userOrGroupName"
-            android:layout_marginStart="@dimen/standard_half_margin"
-            android:textColor="@color/text_color"
-            android:singleLine="true"
-            android:ellipsize="middle"
-            android:gravity="center_vertical"/>
-
-        <ImageView
-            android:layout_width="@dimen/unshare_icon_size"
-            android:layout_height="@dimen/unshare_icon_size"
-            android:id="@+id/unshareButton"
-            android:src="@drawable/ic_action_delete_grey"
-            android:layout_gravity="center_vertical"
-            android:padding="@dimen/standard_half_padding"
-            android:contentDescription="@string/common_delete"/>
-
-        <ImageView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:id="@+id/editShareButton"
-            android:src="@drawable/ic_dots_vertical"
-            android:padding="@dimen/standard_half_padding"
-            android:layout_gravity="center_vertical"
-            android:contentDescription="@string/overflow_menu"/>
-
-    </LinearLayout>
-
-    <View
-        android:layout_width="match_parent"
-        android:layout_height="1dp"
-        android:background="@color/list_divider_background" />
-</LinearLayout>

+ 1 - 1
src/main/res/layout/toolbar_standard.xml

@@ -39,7 +39,7 @@
             android:layout_height="wrap_content"
             android:layout_marginTop="?attr/actionBarSize"
             android:background="@color/bg_default"
-            android:paddingTop="@dimen/standard_eigth_padding"
+            android:paddingTop="@dimen/standard_eight_padding"
             android:visibility="gone"
             tools:visibility="visible">
 

+ 68 - 0
src/main/res/menu/fragment_file_detail_sharing_email_link.xml

@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Nextcloud Android client application
+
+  Copyright (C) 2018 Andy Scherzinger
+
+  This program is free software; you can redistribute it and/or
+  modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+  License as published by the Free Software Foundation; either
+  version 3 of the License, or 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://www.gnu.org/licenses/>.
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    tools:ignore="AppCompatResource">
+    <group
+        android:id="@+id/folder_permission"
+        android:checkableBehavior="single">
+        <item
+            android:id="@+id/link_share_read_only"
+            android:title="@string/link_share_read_only" />
+        <item
+            android:id="@+id/link_share_allow_upload_and_editing"
+            android:title="@string/link_share_allow_upload_and_editing" />
+        <item
+            android:id="@+id/link_share_file_drop"
+            android:title="@string/link_share_file_drop" />
+    </group>
+    <item
+        android:id="@+id/allow_editing"
+        android:checkable="true"
+        android:showAsAction="never"
+        android:title="@string/allow_editing"
+        app:showAsAction="never" />
+    <item
+        android:id="@+id/action_hide_file_download"
+        android:checkable="true"
+        android:showAsAction="never"
+        android:title="@string/share_via_link_hide_download"
+        app:showAsAction="never" />
+    <item
+        android:id="@+id/action_password"
+        android:showAsAction="never"
+        android:title="@string/share_via_link_menu_password_label"
+        app:showAsAction="never" />
+    <item
+        android:id="@+id/action_share_expiration_date"
+        android:showAsAction="never"
+        android:title="@string/share_expiration_date_label"
+        app:showAsAction="never" />
+    <item
+        android:id="@+id/action_share_send_note"
+        android:showAsAction="never"
+        android:title="@string/share_send_note"
+        app:showAsAction="never" />
+    <item
+        android:id="@+id/action_unshare"
+        android:showAsAction="never"
+        android:title="@string/unshare"
+        app:showAsAction="never" />
+</menu>

+ 23 - 20
src/main/res/menu/fragment_file_detail_sharing_link.xml → src/main/res/menu/fragment_file_detail_sharing_public_link.xml

@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Nesxtcloud Android client application
+<?xml version="1.0" encoding="utf-8"?><!--
+  Nextcloud Android client application
 
   Copyright (C) 2018 Andy Scherzinger
 
@@ -21,31 +20,30 @@
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
     tools:ignore="AppCompatResource">
-
+    <group
+        android:id="@+id/folder_permission"
+        android:checkableBehavior="single">
+        <item
+            android:id="@+id/link_share_read_only"
+            android:title="@string/link_share_read_only" />
+        <item
+            android:id="@+id/link_share_allow_upload_and_editing"
+            android:title="@string/link_share_allow_upload_and_editing" />
+        <item
+            android:id="@+id/link_share_file_drop"
+            android:title="@string/link_share_file_drop" />
+    </group>
     <item
-        android:id="@+id/action_allow_editing"
-        android:showAsAction="never"
-        android:title="@string/allow_editing"
+        android:id="@+id/allow_editing"
         android:checkable="true"
-        android:textSize="16sp"
-        app:showAsAction="never" />
-
-    <item
-        android:id="@+id/action_hide_file_listing"
         android:showAsAction="never"
-        android:title="@string/share_via_link_hide_file_listing_permission_label"
-        android:checkable="true"
+        android:title="@string/allow_editing"
         app:showAsAction="never" />
     <item
         android:id="@+id/action_hide_file_download"
-        android:showAsAction="never"
-        android:title="@string/share_via_link_hide_download"
         android:checkable="true"
-        app:showAsAction="never" />
-    <item
-        android:id="@+id/action_edit_label"
         android:showAsAction="never"
-        android:title="@string/edit_label"
+        android:title="@string/share_via_link_hide_download"
         app:showAsAction="never" />
     <item
         android:id="@+id/action_password"
@@ -67,6 +65,11 @@
         android:showAsAction="never"
         android:title="@string/share_send_note"
         app:showAsAction="never" />
+    <item
+        android:id="@+id/action_edit_label"
+        android:showAsAction="never"
+        android:title="@string/edit_label"
+        app:showAsAction="never" />
     <item
         android:id="@+id/action_unshare"
         android:showAsAction="never"

+ 9 - 20
src/main/res/menu/item_user_sharing_settings.xml

@@ -21,41 +21,30 @@
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
     tools:ignore="AppCompatResource">
-
-    <item
-        android:id="@+id/action_can_edit_create"
-        android:checkable="true"
-        android:showAsAction="never"
-        android:title="@string/share_privilege_can_edit_create"
-        app:showAsAction="never" />
     <item
-        android:id="@+id/action_can_edit_change"
+        android:id="@+id/allow_editing"
         android:checkable="true"
         android:showAsAction="never"
-        android:title="@string/share_privilege_can_edit_change"
+        android:title="@string/allow_editing"
         app:showAsAction="never" />
+
     <item
-        android:id="@+id/action_can_edit_delete"
+        android:id="@+id/allow_creating"
         android:checkable="true"
         android:showAsAction="never"
-        android:title="@string/share_privilege_can_edit_delete"
+        android:title="@string/allow_creating"
         app:showAsAction="never" />
     <item
-        android:id="@+id/action_can_reshare"
+        android:id="@+id/allow_deleting"
         android:checkable="true"
         android:showAsAction="never"
-        android:title="@string/share_privilege_can_share"
+        android:title="@string/allow_deleting"
         app:showAsAction="never" />
     <item
-        android:id="@+id/action_hide_file_listing"
-        android:showAsAction="never"
-        android:title="@string/share_via_link_hide_file_listing_permission_label"
+        android:id="@+id/allow_resharing"
         android:checkable="true"
-        app:showAsAction="never" />
-    <item
-        android:id="@+id/action_password"
         android:showAsAction="never"
-        android:title="@string/share_via_link_menu_password_label"
+        android:title="@string/allow_resharing"
         app:showAsAction="never" />
     <item
         android:id="@+id/action_expiration_date"

+ 2 - 4
src/main/res/values/dims.xml

@@ -31,7 +31,7 @@
     <dimen name="standard_double_padding">32dp</dimen>
     <dimen name="standard_half_padding">8dp</dimen>
     <dimen name="standard_quarter_padding">4dp</dimen>
-    <dimen name="standard_eigth_padding">2dp</dimen>
+    <dimen name="standard_eight_padding">2dp</dimen>
     <dimen name="standard_margin">16dp</dimen>
     <dimen name="standard_icon_list_horizontal_margin">24dp</dimen>
     <dimen name="standard_double_margin">32dp</dimen>
@@ -40,6 +40,7 @@
     <dimen name="standard_eighth_margin">2dp</dimen>
     <dimen name="min_list_item_size">56dp</dimen>
     <dimen name="standard_list_item_size">72dp</dimen>
+    <dimen name="sharee_list_item_size">56dp</dimen>
     <dimen name="two_line_primary_text_size">16sp</dimen>
     <dimen name="two_line_secondary_text_size">12sp</dimen>
     <dimen name="list_item_share_right_margin">12dp</dimen>
@@ -71,7 +72,6 @@
     <dimen name="search_users_groups_layout_width">200dp</dimen>
     <dimen name="search_users_groups_layout_list_view_margin">20dp</dimen>
     <dimen name="share_file_layout_text_size">12sp</dimen>
-    <dimen name="unshare_icon_size">36dp</dimen>
     <dimen name="ssl_untrusted_cert_layout_padding">20dp</dimen>
     <dimen name="scroll_view_height">180dp</dimen>
     <dimen name="upload_list_item_frame_layout_width">60dp</dimen>
@@ -102,8 +102,6 @@
     <dimen name="empty_list_icon_layout_height">72dp</dimen>
     <dimen name="empty_list_progress_layout_width">72dp</dimen>
     <dimen name="empty_list_progress_layout_height">72dp</dimen>
-    <dimen name="file_details_user_icon_layout_width">32dp</dimen>
-    <dimen name="file_details_user_icon_layout_height">32dp</dimen>
     <dimen name="file_details_username_text_size">16sp</dimen>
     <dimen name="grid_image_shared_icon_layout_top_margin">24dp</dimen>
     <dimen name="grid_image_local_file_indicator_layout_width">16dp</dimen>

+ 10 - 7
src/main/res/values/strings.xml

@@ -469,8 +469,6 @@
     <string name="share_via_link_send_link_label">Send link</string>
     <string name="share_password_title">Password-protected</string>
     <string name="share_no_password_title">Set password</string>
-    <string name="edit_permission_label">edit</string>
-    <string name="share_via_link_hide_file_listing_permission_label">Hide file listing</string>
     <string name="share_with_title">Share with…</string>
     <string name="share_via_link_unset_password">Unset</string>
 
@@ -483,10 +481,6 @@
     <string name="share_known_remote_clarification">%1$s ( at %2$s )</string>
 
     <string name="share_privilege_unshare">Unshare</string>
-    <string name="share_privilege_can_share">can share</string>
-    <string name="share_privilege_can_edit_create">can create</string>
-    <string name="share_privilege_can_edit_change">can change</string>
-    <string name="share_privilege_can_edit_delete">can delete</string>
 
     <string name="action_clear_failed_uploads">Clear failed uploads</string>
 
@@ -905,7 +899,6 @@
     <string name="failed_to_start_editor">Failed to start editor</string>
     <string name="create_rich_workspace">Add folder info</string>
     <string name="creates_rich_workspace">creates folder info</string>
-    <string name="edit_rich_workspace">edit folder info</string>
     <string name="uploader_upload_failed_sync_conflict_error">File upload conflict</string>
     <string name="uploader_upload_failed_sync_conflict_error_content">Pick which version to keep of %1$s</string>
     <string name="upload_list_resolve_conflict">Resolve conflict</string>
@@ -933,4 +926,14 @@
     <string name="add_new_public_share">Add new public share link</string>
     <string name="edit_label">Change name</string>
     <string name="public_share_name">New name</string>
+    <string name="share_link_with_label">Share link (%1$s)</string>
+    <string name="share_link">Share link</string>
+    <string name="allow_creating">Allow creating</string>
+    <string name="allow_deleting">Allow deleting</string>
+    <string name="allow_resharing">Allow resharing</string>
+    <string name="link_share_read_only">Read only</string>
+    <string name="link_share_allow_upload_and_editing">Allow upload and editing</string>
+    <string name="link_share_file_drop">File drop (upload only)</string>
+    <string name="could_not_retrieve_shares">Could not retrieve shares</string>
+    <string name="failed_update_ui">Failed to update UI</string>
 </resources>

+ 0 - 7
src/main/res/values/styles.xml

@@ -236,13 +236,6 @@
 		<item name="colorAccent">@color/color_accent</item>
 	</style>
 
-	<style name="Theme.ownCloud.Dialog.NoButtonBarStyle" parent="@style/Theme.MaterialComponents.DayNight.Dialog.Alert">
-		<item name="windowNoTitle">false</item>
-		<item name="colorAccent">@color/bg_fallback_highlight</item>
-        <item name="android:colorBackground">@color/bg_default</item>
-        <item name="colorControlActivated">@color/bg_fallback_highlight</item>
-	</style>
-
 	<style name="NavigationView_ItemTextAppearance">
 		<item name="android:ellipsize">end</item>
 		<item name="android:listDivider">@color/transparent</item>

+ 107 - 0
src/test/java/com/owncloud/android/ui/adapter/ShareeListAdapterTest.kt

@@ -0,0 +1,107 @@
+/*
+ *
+ * Nextcloud Android client application
+ *
+ * @author Tobias Kaminsky
+ * Copyright (C) 2020 Tobias Kaminsky
+ * Copyright (C) 2020 Nextcloud GmbH
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+package com.owncloud.android.ui.adapter
+
+import android.content.Context
+import android.content.res.Resources
+import com.owncloud.android.lib.resources.shares.OCShare
+import com.owncloud.android.lib.resources.shares.ShareType
+import org.junit.Assert
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.MockitoAnnotations
+import java.util.ArrayList
+
+class ShareeListAdapterTest {
+    @Mock
+    private val context: Context? = null
+
+    @Test
+    fun testSorting() {
+        MockitoAnnotations.openMocks(this)
+        val resources = Mockito.mock(Resources::class.java)
+        Mockito.`when`(context!!.resources).thenReturn(resources)
+        val expectedSortOrder: MutableList<OCShare?> = ArrayList()
+        expectedSortOrder.add(
+            OCShare("/1").apply {
+                shareType = ShareType.EMAIL
+                sharedDate = 1004
+            }
+        )
+        expectedSortOrder.add(
+            OCShare("/2").apply {
+                shareType = ShareType.PUBLIC_LINK
+                sharedDate = 1003
+            }
+        )
+        expectedSortOrder.add(
+            OCShare("/3").apply {
+                shareType = ShareType.PUBLIC_LINK
+                sharedDate = 1001
+            }
+        )
+        expectedSortOrder.add(
+            OCShare("/4").apply {
+                shareType = ShareType.EMAIL
+                sharedDate = 1000
+            }
+        )
+        expectedSortOrder.add(
+            OCShare("/5").apply {
+                shareType = ShareType.USER
+                sharedDate = 80
+            }
+        )
+        expectedSortOrder.add(
+            OCShare("/6").apply {
+                shareType = ShareType.CIRCLE
+                sharedDate = 20
+            }
+        )
+
+        val randomOrder: MutableList<OCShare?> = ArrayList(expectedSortOrder)
+        randomOrder.shuffle()
+        val sut = ShareeListAdapter(context, randomOrder, null, null)
+        sut.sortShares()
+
+        // compare
+        var compare = true
+        var i = 0
+        while (i < expectedSortOrder.size && compare) {
+            compare = expectedSortOrder[i] === sut.shares[i]
+            i++
+        }
+        if (!compare) {
+            println("Expected:")
+            for (item in expectedSortOrder) {
+                println(item!!.path + " " + item.shareType + " " + item.sharedDate)
+            }
+            println()
+            println("Actual:")
+            for (item in sut.shares) {
+                println(item.path + " " + item.shareType + " " + item.sharedDate)
+            }
+        }
+        Assert.assertTrue(compare)
+    }
+}