Browse Source

Merge pull request #8848 from nextcloud/sharingDialog

Sharing dialog improvement
Tobias Kaminsky 3 years ago
parent
commit
6e76954fcf
41 changed files with 3021 additions and 934 deletions
  1. 426 245
      src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailSharingFragmentIT.kt
  2. 31 2
      src/debug/java/com/nextcloud/client/TestActivity.kt
  3. 3 2
      src/main/java/com/nextcloud/client/files/DeepLinkHandler.kt
  4. 1 0
      src/main/java/com/nextcloud/ui/SetStatusDialogFragment.kt
  5. 24 0
      src/main/java/com/owncloud/android/datamodel/QuickPermissionModel.kt
  6. 64 4
      src/main/java/com/owncloud/android/operations/CreateShareWithShareeOperation.java
  7. 151 0
      src/main/java/com/owncloud/android/operations/UpdateShareInfoOperation.java
  8. 103 57
      src/main/java/com/owncloud/android/services/OperationsService.java
  9. 11 1
      src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java
  10. 40 17
      src/main/java/com/owncloud/android/ui/activity/FileActivity.java
  11. 18 1
      src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.java
  12. 84 0
      src/main/java/com/owncloud/android/ui/adapter/QuickSharingPermissionsAdapter.kt
  13. 19 1
      src/main/java/com/owncloud/android/ui/adapter/ShareViewHolder.java
  14. 5 5
      src/main/java/com/owncloud/android/ui/adapter/ShareeListAdapterListener.java
  15. 23 19
      src/main/java/com/owncloud/android/ui/dialog/ExpirationDatePickerDialogFragment.java
  16. 54 0
      src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java
  17. 81 286
      src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java
  18. 124 0
      src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingMenuBottomSheetDialog.java
  19. 61 0
      src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingMenuBottomSheetActions.java
  20. 538 0
      src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingProcessFragment.kt
  21. 165 0
      src/main/java/com/owncloud/android/ui/fragment/QuickSharingPermissionsBottomSheetDialog.java
  22. 82 27
      src/main/java/com/owncloud/android/ui/fragment/util/SharingMenuHelper.java
  23. 75 1
      src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java
  24. 30 0
      src/main/res/drawable/ic_baseline_arrow_drop_down_24.xml
  25. 30 0
      src/main/res/drawable/ic_baseline_check_24.xml
  26. 9 0
      src/main/res/layout/file_details_fragment.xml
  27. 36 12
      src/main/res/layout/file_details_share_link_share_item.xml
  28. 48 25
      src/main/res/layout/file_details_share_share_item.xml
  29. 233 0
      src/main/res/layout/file_details_sharing_menu_bottom_sheet_fragment.xml
  30. 278 0
      src/main/res/layout/file_details_sharing_process_fragment.xml
  31. 52 0
      src/main/res/layout/item_quick_share_permissions.xml
  32. 35 0
      src/main/res/layout/quick_sharing_permissions_bottom_sheet_fragment.xml
  33. 13 3
      src/main/res/layout/test_layout.xml
  34. 0 68
      src/main/res/menu/fragment_file_detail_sharing_email_link.xml
  35. 0 83
      src/main/res/menu/fragment_file_detail_sharing_public_link.xml
  36. 0 64
      src/main/res/menu/item_user_sharing_settings.xml
  37. 20 0
      src/main/res/values-b+en+001/strings.xml
  38. 23 3
      src/main/res/values-de/strings.xml
  39. 11 0
      src/main/res/values/attrs.xml
  40. 19 8
      src/main/res/values/strings.xml
  41. 1 0
      src/test/java/com/owncloud/android/ui/adapter/ShareeListAdapterTest.kt

+ 426 - 245
src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailSharingFragmentIT.kt

@@ -3,8 +3,10 @@
  * Nextcloud Android client application
  * Nextcloud Android client application
  *
  *
  * @author Tobias Kaminsky
  * @author Tobias Kaminsky
+ * @author TSI-mc
  * Copyright (C) 2020 Tobias Kaminsky
  * Copyright (C) 2020 Tobias Kaminsky
  * Copyright (C) 2020 Nextcloud GmbH
  * Copyright (C) 2020 Nextcloud GmbH
+ * Copyright (C) 2021 TSI-mc
  *
  *
  * This program is free software: you can redistribute it and/or modify
  * 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
  * it under the terms of the GNU Affero General Public License as published by
@@ -21,9 +23,21 @@
  */
  */
 package com.owncloud.android.ui.fragment
 package com.owncloud.android.ui.fragment
 
 
-import android.widget.ImageView
-import androidx.appcompat.widget.PopupMenu
+import android.view.View
+import androidx.test.espresso.Espresso.onView
+import androidx.test.espresso.accessibility.AccessibilityChecks
+import androidx.test.espresso.action.ViewActions
+import androidx.test.espresso.assertion.ViewAssertions.matches
 import androidx.test.espresso.intent.rule.IntentsTestRule
 import androidx.test.espresso.intent.rule.IntentsTestRule
+import androidx.test.espresso.matcher.ViewMatchers
+import androidx.test.espresso.matcher.ViewMatchers.isChecked
+import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
+import androidx.test.espresso.matcher.ViewMatchers.isNotChecked
+import androidx.test.espresso.matcher.ViewMatchers.withText
+import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckResultBaseUtils.matchesCheckNames
+import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckResultUtils.matchesViews
+import com.google.android.material.floatingactionbutton.FloatingActionButton
+import com.nextcloud.client.RetryTestRule
 import com.nextcloud.client.TestActivity
 import com.nextcloud.client.TestActivity
 import com.owncloud.android.AbstractIT
 import com.owncloud.android.AbstractIT
 import com.owncloud.android.R
 import com.owncloud.android.R
@@ -37,7 +51,13 @@ 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.READ_PERMISSION_FLAG
 import com.owncloud.android.lib.resources.shares.OCShare.SHARE_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.lib.resources.shares.ShareType
+import com.owncloud.android.ui.activity.FileDisplayActivity
+import com.owncloud.android.ui.fragment.util.SharingMenuHelper
 import com.owncloud.android.utils.ScreenshotTest
 import com.owncloud.android.utils.ScreenshotTest
+import org.hamcrest.CoreMatchers.`is`
+import org.hamcrest.CoreMatchers.allOf
+import org.hamcrest.CoreMatchers.anyOf
+import org.hamcrest.CoreMatchers.not
 import org.junit.After
 import org.junit.After
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
 import org.junit.Assert.assertTrue
@@ -45,10 +65,14 @@ import org.junit.Before
 import org.junit.Rule
 import org.junit.Rule
 import org.junit.Test
 import org.junit.Test
 
 
+@Suppress("TooManyFunctions")
 class FileDetailSharingFragmentIT : AbstractIT() {
 class FileDetailSharingFragmentIT : AbstractIT() {
     @get:Rule
     @get:Rule
     val testActivityRule = IntentsTestRule(TestActivity::class.java, true, false)
     val testActivityRule = IntentsTestRule(TestActivity::class.java, true, false)
 
 
+    @get:Rule
+    val retryRule = RetryTestRule()
+
     lateinit var file: OCFile
     lateinit var file: OCFile
     lateinit var folder: OCFile
     lateinit var folder: OCFile
     lateinit var activity: TestActivity
     lateinit var activity: TestActivity
@@ -57,6 +81,7 @@ class FileDetailSharingFragmentIT : AbstractIT() {
     fun before() {
     fun before() {
         activity = testActivityRule.launchActivity(null)
         activity = testActivityRule.launchActivity(null)
         file = OCFile("/test.md").apply {
         file = OCFile("/test.md").apply {
+            remoteId = "00000001"
             parentId = activity.storageManager.getFileByEncryptedRemotePath("/").fileId
             parentId = activity.storageManager.getFileByEncryptedRemotePath("/").fileId
             permissions = OCFile.PERMISSION_CAN_RESHARE
             permissions = OCFile.PERMISSION_CAN_RESHARE
         }
         }
@@ -208,240 +233,239 @@ class FileDetailSharingFragmentIT : AbstractIT() {
     @Test
     @Test
     @Suppress("MagicNumber")
     @Suppress("MagicNumber")
     // public link and email are handled the same way
     // public link and email are handled the same way
-    fun publicLinkOptionMenuFolder() {
+    // for advanced permissions
+    fun publicLinkOptionMenuFolderAdvancePermission() {
         val sut = FileDetailSharingFragment.newInstance(file, user)
         val sut = FileDetailSharingFragment.newInstance(file, user)
         activity.addFragment(sut)
         activity.addFragment(sut)
+        setupSecondaryFragment()
         shortSleep()
         shortSleep()
         sut.refreshCapabilitiesFromDB()
         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 {
         val publicShare = OCShare().apply {
             isFolder = true
             isFolder = true
             shareType = ShareType.PUBLIC_LINK
             shareType = ShareType.PUBLIC_LINK
             permissions = 17
             permissions = 17
         }
         }
 
 
-        sut.prepareLinkOptionsMenu(popup.menu, publicShare)
+        activity.runOnUiThread { sut.showSharingMenuActionSheet(publicShare) }
+        shortSleep()
+        waitForIdleSync()
 
 
         // check if items are visible
         // 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)
+        onView(ViewMatchers.withId(R.id.menu_share_open_in)).check(matches(not(isDisplayed())))
+        onView(ViewMatchers.withId(R.id.menu_share_advanced_permissions)).check(matches(isDisplayed()))
+        onView(ViewMatchers.withId(R.id.menu_share_send_new_email)).check(matches(isDisplayed()))
+        onView(ViewMatchers.withId(R.id.menu_share_send_link)).check(matches(isDisplayed()))
+        onView(ViewMatchers.withId(R.id.menu_share_unshare)).check(matches(isDisplayed()))
+        onView(ViewMatchers.withId(R.id.menu_share_add_another_link)).check(matches(isDisplayed()))
+
+        // click event
+        onView(ViewMatchers.withId(R.id.menu_share_advanced_permissions)).perform(ViewActions.click())
+
+        // validate view shown on screen
+        onView(ViewMatchers.withId(R.id.share_process_permission_read_only)).check(matches(isDisplayed()))
+        onView(ViewMatchers.withId(R.id.share_process_permission_upload_editing)).check(matches(isDisplayed()))
+        onView(ViewMatchers.withId(R.id.share_process_permission_file_drop)).check(matches(isDisplayed()))
+        onView(ViewMatchers.withId(R.id.share_process_hide_download_checkbox)).check(matches(isDisplayed()))
+        onView(ViewMatchers.withId(R.id.share_process_set_password_switch)).check(matches(isDisplayed()))
+        onView(ViewMatchers.withId(R.id.share_process_change_name_switch)).check(matches(isDisplayed()))
+        onView(ViewMatchers.withId(R.id.share_process_allow_resharing_checkbox)).check(matches(not(isDisplayed())))
 
 
         // read-only
         // 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)
+        onView(ViewMatchers.withId(R.id.share_process_permission_read_only)).check(matches(isChecked()))
+        onView(ViewMatchers.withId(R.id.share_process_permission_upload_editing)).check(matches(isNotChecked()))
+        onView(ViewMatchers.withId(R.id.share_process_permission_file_drop)).check(matches(isNotChecked()))
+        goBack()
 
 
         // upload and editing
         // upload and editing
         publicShare.permissions = MAXIMUM_PERMISSIONS_FOR_FOLDER
         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)
+        openAdvancedPermissions(sut, publicShare)
+        onView(ViewMatchers.withId(R.id.share_process_permission_read_only)).check(matches(isNotChecked()))
+        onView(ViewMatchers.withId(R.id.share_process_permission_upload_editing)).check(matches(isChecked()))
+        onView(ViewMatchers.withId(R.id.share_process_permission_file_drop)).check(matches(isNotChecked()))
+        goBack()
 
 
         // file drop
         // file drop
         publicShare.permissions = 4
         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)
+        openAdvancedPermissions(sut, publicShare)
+        onView(ViewMatchers.withId(R.id.share_process_permission_read_only)).check(matches(isNotChecked()))
+        onView(ViewMatchers.withId(R.id.share_process_permission_upload_editing)).check(matches(isNotChecked()))
+        onView(ViewMatchers.withId(R.id.share_process_permission_file_drop)).check(matches(isChecked()))
+        goBack()
 
 
         // password protection
         // password protection
         publicShare.shareWith = "someValue"
         publicShare.shareWith = "someValue"
-        sut.prepareLinkOptionsMenu(popup.menu, publicShare)
-        assertTrue(
-            popup.menu.findItem(R.id.action_password).title == targetContext.getString(R.string.share_password_title)
-        )
+        openAdvancedPermissions(sut, publicShare)
+        onView(ViewMatchers.withId(R.id.share_process_set_password_switch)).check(matches(isChecked()))
+        goBack()
 
 
         publicShare.shareWith = ""
         publicShare.shareWith = ""
-        sut.prepareLinkOptionsMenu(popup.menu, publicShare)
-        assertTrue(
-            popup.menu.findItem(R.id.action_password).title == targetContext.getString(R.string.share_no_password_title)
-        )
+        openAdvancedPermissions(sut, publicShare)
+        onView(ViewMatchers.withId(R.id.share_process_set_password_switch)).check(matches(isNotChecked()))
+        goBack()
 
 
         // hide download
         // hide download
         publicShare.isHideFileDownload = true
         publicShare.isHideFileDownload = true
         publicShare.permissions = MAXIMUM_PERMISSIONS_FOR_FOLDER
         publicShare.permissions = MAXIMUM_PERMISSIONS_FOR_FOLDER
-        sut.prepareLinkOptionsMenu(popup.menu, publicShare)
-        assertTrue(popup.menu.findItem(R.id.action_hide_file_download).isChecked)
+        openAdvancedPermissions(sut, publicShare)
+        onView(ViewMatchers.withId(R.id.share_process_hide_download_checkbox)).check(matches(isChecked()))
+        goBack()
 
 
         publicShare.isHideFileDownload = false
         publicShare.isHideFileDownload = false
-        sut.prepareLinkOptionsMenu(popup.menu, publicShare)
-        assertFalse(popup.menu.findItem(R.id.action_hide_file_download).isChecked)
+        openAdvancedPermissions(sut, publicShare)
+        onView(ViewMatchers.withId(R.id.share_process_hide_download_checkbox)).check(matches(isNotChecked()))
+        goBack()
 
 
         publicShare.expirationDate = 1582019340000
         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])
-        )
+        openAdvancedPermissions(sut, publicShare)
+        onView(ViewMatchers.withId(R.id.share_process_set_exp_date_switch)).check(matches(isChecked()))
+        onView(ViewMatchers.withId(R.id.share_process_select_exp_date)).check(matches(not(withText(""))))
+        goBack()
 
 
         publicShare.expirationDate = 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)
+        openAdvancedPermissions(sut, publicShare)
+        onView(ViewMatchers.withId(R.id.share_process_set_exp_date_switch)).check(matches(isNotChecked()))
+        onView(ViewMatchers.withId(R.id.share_process_select_exp_date)).check(matches(withText("")))
+    }
+
+    @Test
+    @Suppress("MagicNumber")
+    // public link and email are handled the same way
+    // for send new email
+    fun publicLinkOptionMenuFolderSendNewEmail() {
+        val sut = FileDetailSharingFragment.newInstance(file, user)
+        activity.addFragment(sut)
+        setupSecondaryFragment()
+        shortSleep()
+        sut.refreshCapabilitiesFromDB()
+
+        val publicShare = OCShare().apply {
+            isFolder = true
+            shareType = ShareType.PUBLIC_LINK
+            permissions = 17
+        }
+
+        verifySendNewEmail(sut, publicShare)
+    }
+
+    private fun setupSecondaryFragment() {
+        val secondary = FileDetailFragment.newInstance(file, user)
+        activity.addSecondaryFragment(secondary, FileDisplayActivity.TAG_LIST_OF_FILES)
+        activity.addView(
+            FloatingActionButton(activity).apply { // needed for some reason
+                visibility = View.GONE
+                id = R.id.fab_main
+            }
         )
         )
+    }
+
+    @Test
+    @Suppress("MagicNumber")
+    // public link and email are handled the same way
+    // for advanced permissions
+    fun publicLinkOptionMenuFileAdvancePermission() {
+        val sut = FileDetailSharingFragment.newInstance(file, user)
+        activity.addFragment(sut)
+        setupSecondaryFragment()
+        shortSleep()
+        sut.refreshCapabilitiesFromDB()
+
+        val publicShare = OCShare().apply {
+            isFolder = false
+            shareType = ShareType.PUBLIC_LINK
+            permissions = 17
+        }
+        activity.handler.post { sut.showSharingMenuActionSheet(publicShare) }
+        waitForIdleSync()
 
 
-        // file
-        publicShare.isFolder = false
-        publicShare.permissions = READ_PERMISSION_FLAG
-        sut.prepareLinkOptionsMenu(popup.menu, publicShare)
         // check if items are visible
         // 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
+        onView(ViewMatchers.withId(R.id.menu_share_open_in)).check(matches(isDisplayed()))
+        onView(ViewMatchers.withId(R.id.menu_share_advanced_permissions)).check(matches(isDisplayed()))
+        onView(ViewMatchers.withId(R.id.menu_share_send_new_email)).check(matches(isDisplayed()))
+        onView(ViewMatchers.withId(R.id.menu_share_send_link)).check(matches(isDisplayed()))
+        onView(ViewMatchers.withId(R.id.menu_share_unshare)).check(matches(isDisplayed()))
+        onView(ViewMatchers.withId(R.id.menu_share_add_another_link)).check(matches(isDisplayed()))
+
+        // click event
+        onView(ViewMatchers.withId(R.id.menu_share_advanced_permissions)).perform(ViewActions.click())
+
+        // validate view shown on screen
+        onView(ViewMatchers.withId(R.id.share_process_permission_read_only)).check(matches(isDisplayed()))
+        onView(ViewMatchers.withId(R.id.share_process_permission_upload_editing)).check(matches(isDisplayed()))
+        onView(ViewMatchers.withId(R.id.share_process_permission_file_drop)).check(matches(not(isDisplayed())))
+        onView(ViewMatchers.withId(R.id.share_process_hide_download_checkbox)).check(matches(isDisplayed()))
+        onView(ViewMatchers.withId(R.id.share_process_set_password_switch)).check(matches(isDisplayed()))
+        onView(ViewMatchers.withId(R.id.share_process_change_name_switch)).check(matches(isDisplayed()))
+        onView(ViewMatchers.withId(R.id.share_process_allow_resharing_checkbox)).check(matches(not(isDisplayed())))
+
+        // read-only
         publicShare.permissions = 17 // from server
         publicShare.permissions = 17 // from server
-        sut.prepareLinkOptionsMenu(popup.menu, publicShare)
-        assertFalse(popup.menu.findItem(R.id.allow_editing).isChecked)
+        onView(ViewMatchers.withId(R.id.share_process_permission_read_only)).check(matches(isChecked()))
+        onView(ViewMatchers.withId(R.id.share_process_permission_upload_editing)).check(matches(isNotChecked()))
+        goBack()
 
 
-        publicShare.permissions = 19 // from server
-        sut.prepareLinkOptionsMenu(popup.menu, publicShare)
-        assertTrue(popup.menu.findItem(R.id.allow_editing).isChecked)
+        // editing
+        publicShare.permissions = MAXIMUM_PERMISSIONS_FOR_FILE // from server
+        openAdvancedPermissions(sut, publicShare)
+        onView(ViewMatchers.withId(R.id.share_process_permission_read_only)).check(matches(isNotChecked()))
+        onView(ViewMatchers.withId(R.id.share_process_permission_upload_editing)).check(matches(isChecked()))
+        goBack()
 
 
         // hide download
         // hide download
         publicShare.isHideFileDownload = true
         publicShare.isHideFileDownload = true
-        sut.prepareLinkOptionsMenu(popup.menu, publicShare)
-        assertTrue(popup.menu.findItem(R.id.action_hide_file_download).isChecked)
+        openAdvancedPermissions(sut, publicShare)
+        onView(ViewMatchers.withId(R.id.share_process_hide_download_checkbox)).check(matches(isChecked()))
+        goBack()
 
 
         publicShare.isHideFileDownload = false
         publicShare.isHideFileDownload = false
-        sut.prepareLinkOptionsMenu(popup.menu, publicShare)
-        assertFalse(popup.menu.findItem(R.id.action_hide_file_download).isChecked)
+        openAdvancedPermissions(sut, publicShare)
+        onView(ViewMatchers.withId(R.id.share_process_hide_download_checkbox)).check(matches(isNotChecked()))
+        goBack()
 
 
         // password protection
         // password protection
         publicShare.isPasswordProtected = true
         publicShare.isPasswordProtected = true
         publicShare.shareWith = "someValue"
         publicShare.shareWith = "someValue"
-        sut.prepareLinkOptionsMenu(popup.menu, publicShare)
-        assertTrue(
-            popup.menu.findItem(R.id.action_password).title == targetContext.getString(R.string.share_password_title)
-        )
+        openAdvancedPermissions(sut, publicShare)
+        onView(ViewMatchers.withId(R.id.share_process_set_password_switch)).check(matches(isChecked()))
+        goBack()
 
 
         publicShare.isPasswordProtected = false
         publicShare.isPasswordProtected = false
         publicShare.shareWith = ""
         publicShare.shareWith = ""
-        sut.prepareLinkOptionsMenu(popup.menu, publicShare)
-        assertTrue(
-            popup.menu.findItem(R.id.action_password).title == targetContext.getString(R.string.share_no_password_title)
-        )
+        openAdvancedPermissions(sut, publicShare)
+        onView(ViewMatchers.withId(R.id.share_process_set_password_switch)).check(matches(isNotChecked()))
+        goBack()
 
 
         // expires
         // expires
         publicShare.expirationDate = 1582019340
         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])
-        )
+        openAdvancedPermissions(sut, publicShare)
+        onView(ViewMatchers.withId(R.id.share_process_set_exp_date_switch)).check(matches(isChecked()))
+        onView(ViewMatchers.withId(R.id.share_process_select_exp_date)).check(matches(not(withText(""))))
+        goBack()
 
 
         publicShare.expirationDate = 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)
-        )
+        openAdvancedPermissions(sut, publicShare)
+        onView(ViewMatchers.withId(R.id.share_process_set_exp_date_switch)).check(matches(isNotChecked()))
+        onView(ViewMatchers.withId(R.id.share_process_select_exp_date)).check(matches(withText("")))
     }
     }
 
 
     @Test
     @Test
     @Suppress("MagicNumber")
     @Suppress("MagicNumber")
     // public link and email are handled the same way
     // public link and email are handled the same way
-    fun publicLinkOptionMenuFile() {
+    // for send new email
+    fun publicLinkOptionMenuFileSendNewEmail() {
         val sut = FileDetailSharingFragment.newInstance(file, user)
         val sut = FileDetailSharingFragment.newInstance(file, user)
         activity.addFragment(sut)
         activity.addFragment(sut)
+        setupSecondaryFragment()
         shortSleep()
         shortSleep()
         sut.refreshCapabilitiesFromDB()
         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 {
         val publicShare = OCShare().apply {
             isFolder = false
             isFolder = false
             shareType = ShareType.PUBLIC_LINK
             shareType = ShareType.PUBLIC_LINK
             permissions = 17
             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)
+        verifySendNewEmail(sut, publicShare)
     }
     }
 
 
     @Test
     @Test
@@ -451,59 +475,99 @@ class FileDetailSharingFragmentIT : AbstractIT() {
     // conversation
     // conversation
     // circle
     // circle
     // federated share
     // federated share
-    fun userOptionMenuFile() {
+    // for advanced permissions
+    fun userOptionMenuFileAdvancePermission() {
         val sut = FileDetailSharingFragment.newInstance(file, user)
         val sut = FileDetailSharingFragment.newInstance(file, user)
+        suppressFDFAccessibilityChecks()
         activity.addFragment(sut)
         activity.addFragment(sut)
+        setupSecondaryFragment()
         shortSleep()
         shortSleep()
         sut.refreshCapabilitiesFromDB()
         sut.refreshCapabilitiesFromDB()
 
 
-        val overflowMenuShareLink = ImageView(targetContext)
-        val popup = PopupMenu(targetContext, overflowMenuShareLink)
-        popup.inflate(R.menu.item_user_sharing_settings)
         val userShare = OCShare().apply {
         val userShare = OCShare().apply {
             isFolder = false
             isFolder = false
             shareType = ShareType.USER
             shareType = ShareType.USER
             permissions = 17
             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)
+        activity.runOnUiThread { sut.showSharingMenuActionSheet(userShare) }
+        shortSleep()
+        waitForIdleSync()
 
 
-        // allow editing
+        // check if items are visible
+        onView(ViewMatchers.withId(R.id.menu_share_open_in)).check(matches(isDisplayed()))
+        onView(ViewMatchers.withId(R.id.menu_share_advanced_permissions)).check(matches(isDisplayed()))
+        onView(ViewMatchers.withId(R.id.menu_share_send_new_email)).check(matches(isDisplayed()))
+        onView(ViewMatchers.withId(R.id.menu_share_send_link)).check(matches(not(isDisplayed())))
+        onView(ViewMatchers.withId(R.id.menu_share_unshare)).check(matches(isDisplayed()))
+        onView(ViewMatchers.withId(R.id.menu_share_add_another_link)).check(matches(not(isDisplayed())))
+
+        // click event
+        onView(ViewMatchers.withId(R.id.menu_share_advanced_permissions)).perform(ViewActions.click())
+        shortSleep()
+        waitForIdleSync()
+
+        // validate view shown on screen
+        onView(ViewMatchers.withId(R.id.share_process_permission_read_only)).check(matches(isDisplayed()))
+        onView(ViewMatchers.withId(R.id.share_process_permission_upload_editing)).check(matches(isDisplayed()))
+        onView(ViewMatchers.withId(R.id.share_process_permission_file_drop)).check(matches(not(isDisplayed())))
+        onView(ViewMatchers.withId(R.id.share_process_hide_download_checkbox)).check(matches(not(isDisplayed())))
+        onView(ViewMatchers.withId(R.id.share_process_set_password_switch)).check(matches(not(isDisplayed())))
+        onView(ViewMatchers.withId(R.id.share_process_change_name_switch)).check(matches(not(isDisplayed())))
+        onView(ViewMatchers.withId(R.id.share_process_allow_resharing_checkbox)).check(matches(isDisplayed()))
+
+        // read-only
         userShare.permissions = 17 // from server
         userShare.permissions = 17 // from server
-        assertFalse(popup.menu.findItem(R.id.allow_editing).isChecked)
+        onView(ViewMatchers.withId(R.id.share_process_permission_read_only)).check(matches(isChecked()))
+        onView(ViewMatchers.withId(R.id.share_process_permission_upload_editing)).check(matches(isNotChecked()))
+        goBack()
 
 
-        userShare.permissions = 19 // from server
-        sut.prepareUserOptionsMenu(popup.menu, userShare)
-        assertTrue(popup.menu.findItem(R.id.allow_editing).isChecked)
+        // editing
+        userShare.permissions = MAXIMUM_PERMISSIONS_FOR_FILE // from server
+        openAdvancedPermissions(sut, userShare)
+        onView(ViewMatchers.withId(R.id.share_process_permission_read_only)).check(matches(isNotChecked()))
+        onView(ViewMatchers.withId(R.id.share_process_permission_upload_editing)).check(matches(isChecked()))
+        goBack()
 
 
         // allow reshare
         // allow reshare
         userShare.permissions = 1 // from server
         userShare.permissions = 1 // from server
-        sut.prepareUserOptionsMenu(popup.menu, userShare)
-        assertFalse(popup.menu.findItem(R.id.allow_resharing).isChecked)
+        openAdvancedPermissions(sut, userShare)
+        onView(ViewMatchers.withId(R.id.share_process_allow_resharing_checkbox)).check(matches(isNotChecked()))
+        goBack()
 
 
         userShare.permissions = 17 // from server
         userShare.permissions = 17 // from server
-        sut.prepareUserOptionsMenu(popup.menu, userShare)
-        assertTrue(popup.menu.findItem(R.id.allow_resharing).isChecked)
+        openAdvancedPermissions(sut, userShare)
+        onView(ViewMatchers.withId(R.id.share_process_allow_resharing_checkbox)).check(matches(isChecked()))
+        goBack()
 
 
         // set expiration date
         // set expiration date
         userShare.expirationDate = 1582019340000
         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])
-        )
+        openAdvancedPermissions(sut, userShare)
+        onView(ViewMatchers.withId(R.id.share_process_set_exp_date_switch)).check(matches(isChecked()))
+        onView(ViewMatchers.withId(R.id.share_process_select_exp_date)).check(matches(not(withText(""))))
+        goBack()
 
 
         userShare.expirationDate = 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)
-        )
+        openAdvancedPermissions(sut, userShare)
+        onView(ViewMatchers.withId(R.id.share_process_set_exp_date_switch)).check(matches(isNotChecked()))
+        onView(ViewMatchers.withId(R.id.share_process_select_exp_date)).check(matches(withText("")))
+    }
 
 
-        // note
-        assertTrue(popup.menu.findItem(R.id.action_share_send_note).isVisible)
+    private fun suppressFDFAccessibilityChecks() {
+        AccessibilityChecks.enable().apply {
+            setSuppressingResultMatcher(
+                allOf(
+                    anyOf(
+                        matchesCheckNames(`is`("TouchTargetSizeCheck")),
+                        matchesCheckNames(`is`("SpeakableTextPresentCheck")),
+                    ),
+                    anyOf(
+                        matchesViews(ViewMatchers.withId(R.id.favorite)),
+                        matchesViews(ViewMatchers.withId(R.id.last_modification_timestamp))
+                    )
+                )
+            )
+        }
     }
     }
 
 
     @Test
     @Test
@@ -513,152 +577,269 @@ class FileDetailSharingFragmentIT : AbstractIT() {
     // conversation
     // conversation
     // circle
     // circle
     // federated share
     // federated share
-    fun userOptionMenuFolder() {
+    // for send new email
+    fun userOptionMenuFileSendNewEmail() {
         val sut = FileDetailSharingFragment.newInstance(file, user)
         val sut = FileDetailSharingFragment.newInstance(file, user)
         activity.addFragment(sut)
         activity.addFragment(sut)
+        setupSecondaryFragment()
+        shortSleep()
+        sut.refreshCapabilitiesFromDB()
+
+        val userShare = OCShare().apply {
+            isFolder = false
+            shareType = ShareType.USER
+            permissions = 17
+        }
+
+        verifySendNewEmail(sut, userShare)
+    }
+
+    @Test
+    @Suppress("MagicNumber")
+    // also applies for
+    // group
+    // conversation
+    // circle
+    // federated share
+    // for advanced permissions
+    fun userOptionMenuFolderAdvancePermission() {
+        val sut = FileDetailSharingFragment.newInstance(file, user)
+        activity.addFragment(sut)
+        setupSecondaryFragment()
+        suppressFDFAccessibilityChecks()
         shortSleep()
         shortSleep()
         sut.refreshCapabilitiesFromDB()
         sut.refreshCapabilitiesFromDB()
 
 
-        val overflowMenuShareLink = ImageView(targetContext)
-        val popup = PopupMenu(targetContext, overflowMenuShareLink)
-        popup.inflate(R.menu.item_user_sharing_settings)
         val userShare = OCShare().apply {
         val userShare = OCShare().apply {
             isFolder = true
             isFolder = true
             shareType = ShareType.USER
             shareType = ShareType.USER
             permissions = 17
             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)
+        activity.runOnUiThread { sut.showSharingMenuActionSheet(userShare) }
+        shortSleep()
+        waitForIdleSync()
 
 
-        // allow editing
+        // check if items are visible
+        onView(ViewMatchers.withId(R.id.menu_share_open_in)).check(matches(not(isDisplayed())))
+        onView(ViewMatchers.withId(R.id.menu_share_advanced_permissions)).check(matches(isDisplayed()))
+        onView(ViewMatchers.withId(R.id.menu_share_send_new_email)).check(matches(isDisplayed()))
+        onView(ViewMatchers.withId(R.id.menu_share_send_link)).check(matches(not(isDisplayed())))
+        onView(ViewMatchers.withId(R.id.menu_share_unshare)).check(matches(isDisplayed()))
+        onView(ViewMatchers.withId(R.id.menu_share_add_another_link)).check(matches(not(isDisplayed())))
+
+        // click event
+        onView(ViewMatchers.withId(R.id.menu_share_advanced_permissions)).perform(ViewActions.click())
+
+        // validate view shown on screen
+        onView(ViewMatchers.withId(R.id.share_process_permission_read_only)).check(matches(isDisplayed()))
+        onView(ViewMatchers.withId(R.id.share_process_permission_upload_editing)).check(matches(isDisplayed()))
+        onView(ViewMatchers.withId(R.id.share_process_permission_file_drop)).check(matches(isDisplayed()))
+        onView(ViewMatchers.withId(R.id.share_process_hide_download_checkbox)).check(matches(not(isDisplayed())))
+        onView(ViewMatchers.withId(R.id.share_process_set_password_switch)).check(matches(not(isDisplayed())))
+        onView(ViewMatchers.withId(R.id.share_process_change_name_switch)).check(matches(not(isDisplayed())))
+        onView(ViewMatchers.withId(R.id.share_process_allow_resharing_checkbox)).check(matches(isDisplayed()))
+
+        // read-only
         userShare.permissions = 17 // from server
         userShare.permissions = 17 // from server
-        assertFalse(popup.menu.findItem(R.id.allow_editing).isChecked)
+        onView(ViewMatchers.withId(R.id.share_process_permission_read_only)).check(matches(isChecked()))
+        onView(ViewMatchers.withId(R.id.share_process_permission_upload_editing)).check(matches(isNotChecked()))
+        onView(ViewMatchers.withId(R.id.share_process_permission_file_drop)).check(matches(isNotChecked()))
+        goBack()
+
+        // allow upload & editing
+        userShare.permissions = MAXIMUM_PERMISSIONS_FOR_FOLDER // from server
+        openAdvancedPermissions(sut, userShare)
+        onView(ViewMatchers.withId(R.id.share_process_permission_read_only)).check(matches(isNotChecked()))
+        onView(ViewMatchers.withId(R.id.share_process_permission_upload_editing)).check(matches(isChecked()))
+        onView(ViewMatchers.withId(R.id.share_process_permission_file_drop)).check(matches(isNotChecked()))
+        goBack()
 
 
-        userShare.permissions = 19 // from server
-        sut.prepareUserOptionsMenu(popup.menu, userShare)
-        assertTrue(popup.menu.findItem(R.id.allow_editing).isChecked)
+        // file drop
+        userShare.permissions = 4
+        openAdvancedPermissions(sut, userShare)
+        onView(ViewMatchers.withId(R.id.share_process_permission_read_only)).check(matches(isNotChecked()))
+        onView(ViewMatchers.withId(R.id.share_process_permission_upload_editing)).check(matches(isNotChecked()))
+        onView(ViewMatchers.withId(R.id.share_process_permission_file_drop)).check(matches(isChecked()))
+        goBack()
 
 
         // allow reshare
         // allow reshare
         userShare.permissions = 1 // from server
         userShare.permissions = 1 // from server
-        sut.prepareUserOptionsMenu(popup.menu, userShare)
-        assertFalse(popup.menu.findItem(R.id.allow_resharing).isChecked)
+        openAdvancedPermissions(sut, userShare)
+        onView(ViewMatchers.withId(R.id.share_process_allow_resharing_checkbox)).check(matches(isNotChecked()))
+        goBack()
 
 
         userShare.permissions = 17 // from server
         userShare.permissions = 17 // from server
-        sut.prepareUserOptionsMenu(popup.menu, userShare)
-        assertTrue(popup.menu.findItem(R.id.allow_resharing).isChecked)
+        openAdvancedPermissions(sut, userShare)
+        onView(ViewMatchers.withId(R.id.share_process_allow_resharing_checkbox)).check(matches(isChecked()))
+        goBack()
 
 
         // set expiration date
         // set expiration date
         userShare.expirationDate = 1582019340000
         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])
-        )
+        openAdvancedPermissions(sut, userShare)
+        onView(ViewMatchers.withId(R.id.share_process_set_exp_date_switch)).check(matches(isChecked()))
+        onView(ViewMatchers.withId(R.id.share_process_select_exp_date)).check(matches(not(withText(""))))
+        goBack()
 
 
         userShare.expirationDate = 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)
-        )
+        openAdvancedPermissions(sut, userShare)
+        onView(ViewMatchers.withId(R.id.share_process_set_exp_date_switch)).check(matches(isNotChecked()))
+        onView(ViewMatchers.withId(R.id.share_process_select_exp_date)).check(matches(withText("")))
+    }
 
 
-        // note
-        assertTrue(popup.menu.findItem(R.id.action_share_send_note).isVisible)
+    // open bottom sheet with actions
+    private fun openAdvancedPermissions(
+        sut: FileDetailSharingFragment,
+        userShare: OCShare
+    ) {
+        activity.handler.post {
+            sut.showSharingMenuActionSheet(userShare)
+        }
+        shortSleep()
+        waitForIdleSync()
+        onView(ViewMatchers.withId(R.id.menu_share_advanced_permissions)).perform(ViewActions.click())
+    }
+
+    // remove the fragment shown
+    private fun goBack() {
+        activity.handler.post {
+            val processFragment =
+                activity.supportFragmentManager.findFragmentByTag(FileDetailsSharingProcessFragment.TAG) as
+                    FileDetailsSharingProcessFragment
+            processFragment.onBackPressed()
+        }
+        shortSleep()
+        waitForIdleSync()
+    }
+
+    @Test
+    @Suppress("MagicNumber")
+    // also applies for
+    // group
+    // conversation
+    // circle
+    // federated share
+    // for send new email
+    fun userOptionMenuFolderSendNewEmail() {
+        val sut = FileDetailSharingFragment.newInstance(file, user)
+        activity.addFragment(sut)
+        setupSecondaryFragment()
+        shortSleep()
+        sut.refreshCapabilitiesFromDB()
+
+        val userShare = OCShare().apply {
+            isFolder = true
+            shareType = ShareType.USER
+            permissions = 17
+        }
+
+        verifySendNewEmail(sut, userShare)
+    }
+
+    /**
+     * verify send new email note text
+     */
+    private fun verifySendNewEmail(
+        sut: FileDetailSharingFragment,
+        userShare: OCShare
+    ) {
+        activity.runOnUiThread { sut.showSharingMenuActionSheet(userShare) }
+
+        waitForIdleSync()
+        // click event
+        onView(ViewMatchers.withId(R.id.menu_share_send_new_email)).perform(ViewActions.click())
+
+        // validate view shown on screen
+        onView(ViewMatchers.withId(R.id.note_text)).check(matches(isDisplayed()))
     }
     }
 
 
     @Test
     @Test
     fun testUploadAndEditingSharePermissions() {
     fun testUploadAndEditingSharePermissions() {
-        val sut = FileDetailSharingFragment()
 
 
         val share = OCShare().apply {
         val share = OCShare().apply {
             permissions = MAXIMUM_PERMISSIONS_FOR_FOLDER
             permissions = MAXIMUM_PERMISSIONS_FOR_FOLDER
         }
         }
-        assertTrue(sut.isUploadAndEditingAllowed(share))
+        assertTrue(SharingMenuHelper.isUploadAndEditingAllowed(share))
 
 
         share.permissions = NO_PERMISSION
         share.permissions = NO_PERMISSION
-        assertFalse(sut.isUploadAndEditingAllowed(share))
+        assertFalse(SharingMenuHelper.isUploadAndEditingAllowed(share))
 
 
         share.permissions = READ_PERMISSION_FLAG
         share.permissions = READ_PERMISSION_FLAG
-        assertFalse(sut.isUploadAndEditingAllowed(share))
+        assertFalse(SharingMenuHelper.isUploadAndEditingAllowed(share))
 
 
         share.permissions = CREATE_PERMISSION_FLAG
         share.permissions = CREATE_PERMISSION_FLAG
-        assertFalse(sut.isUploadAndEditingAllowed(share))
+        assertFalse(SharingMenuHelper.isUploadAndEditingAllowed(share))
 
 
         share.permissions = DELETE_PERMISSION_FLAG
         share.permissions = DELETE_PERMISSION_FLAG
-        assertFalse(sut.isUploadAndEditingAllowed(share))
+        assertFalse(SharingMenuHelper.isUploadAndEditingAllowed(share))
 
 
         share.permissions = SHARE_PERMISSION_FLAG
         share.permissions = SHARE_PERMISSION_FLAG
-        assertFalse(sut.isUploadAndEditingAllowed(share))
+        assertFalse(SharingMenuHelper.isUploadAndEditingAllowed(share))
     }
     }
 
 
     @Test
     @Test
     @Suppress("MagicNumber")
     @Suppress("MagicNumber")
     fun testReadOnlySharePermissions() {
     fun testReadOnlySharePermissions() {
-        val sut = FileDetailSharingFragment()
-
         val share = OCShare().apply {
         val share = OCShare().apply {
             permissions = 17
             permissions = 17
         }
         }
-        assertTrue(sut.isReadOnly(share))
+        assertTrue(SharingMenuHelper.isReadOnly(share))
 
 
         share.permissions = NO_PERMISSION
         share.permissions = NO_PERMISSION
-        assertFalse(sut.isReadOnly(share))
+        assertFalse(SharingMenuHelper.isReadOnly(share))
 
 
         share.permissions = READ_PERMISSION_FLAG
         share.permissions = READ_PERMISSION_FLAG
-        assertTrue(sut.isReadOnly(share))
+        assertTrue(SharingMenuHelper.isReadOnly(share))
 
 
         share.permissions = CREATE_PERMISSION_FLAG
         share.permissions = CREATE_PERMISSION_FLAG
-        assertFalse(sut.isReadOnly(share))
+        assertFalse(SharingMenuHelper.isReadOnly(share))
 
 
         share.permissions = DELETE_PERMISSION_FLAG
         share.permissions = DELETE_PERMISSION_FLAG
-        assertFalse(sut.isReadOnly(share))
+        assertFalse(SharingMenuHelper.isReadOnly(share))
 
 
         share.permissions = SHARE_PERMISSION_FLAG
         share.permissions = SHARE_PERMISSION_FLAG
-        assertFalse(sut.isReadOnly(share))
+        assertFalse(SharingMenuHelper.isReadOnly(share))
 
 
         share.permissions = MAXIMUM_PERMISSIONS_FOR_FOLDER
         share.permissions = MAXIMUM_PERMISSIONS_FOR_FOLDER
-        assertFalse(sut.isReadOnly(share))
+        assertFalse(SharingMenuHelper.isReadOnly(share))
 
 
         share.permissions = MAXIMUM_PERMISSIONS_FOR_FILE
         share.permissions = MAXIMUM_PERMISSIONS_FOR_FILE
-        assertFalse(sut.isReadOnly(share))
+        assertFalse(SharingMenuHelper.isReadOnly(share))
     }
     }
 
 
     @Test
     @Test
     @Suppress("MagicNumber")
     @Suppress("MagicNumber")
     fun testFileDropSharePermissions() {
     fun testFileDropSharePermissions() {
-        val sut = FileDetailSharingFragment()
-
         val share = OCShare().apply {
         val share = OCShare().apply {
             permissions = 4
             permissions = 4
         }
         }
-        assertTrue(sut.isFileDrop(share))
+        assertTrue(SharingMenuHelper.isFileDrop(share))
 
 
         share.permissions = NO_PERMISSION
         share.permissions = NO_PERMISSION
-        assertFalse(sut.isFileDrop(share))
+        assertFalse(SharingMenuHelper.isFileDrop(share))
 
 
         share.permissions = READ_PERMISSION_FLAG
         share.permissions = READ_PERMISSION_FLAG
-        assertFalse(sut.isFileDrop(share))
+        assertFalse(SharingMenuHelper.isFileDrop(share))
 
 
         share.permissions = CREATE_PERMISSION_FLAG
         share.permissions = CREATE_PERMISSION_FLAG
-        assertTrue(sut.isFileDrop(share))
+        assertTrue(SharingMenuHelper.isFileDrop(share))
 
 
         share.permissions = DELETE_PERMISSION_FLAG
         share.permissions = DELETE_PERMISSION_FLAG
-        assertFalse(sut.isFileDrop(share))
+        assertFalse(SharingMenuHelper.isFileDrop(share))
 
 
         share.permissions = SHARE_PERMISSION_FLAG
         share.permissions = SHARE_PERMISSION_FLAG
-        assertFalse(sut.isFileDrop(share))
+        assertFalse(SharingMenuHelper.isFileDrop(share))
 
 
         share.permissions = MAXIMUM_PERMISSIONS_FOR_FOLDER
         share.permissions = MAXIMUM_PERMISSIONS_FOR_FOLDER
-        assertFalse(sut.isFileDrop(share))
+        assertFalse(SharingMenuHelper.isFileDrop(share))
 
 
         share.permissions = MAXIMUM_PERMISSIONS_FOR_FILE
         share.permissions = MAXIMUM_PERMISSIONS_FOR_FILE
-        assertFalse(sut.isFileDrop(share))
+        assertFalse(SharingMenuHelper.isFileDrop(share))
     }
     }
 
 
     @After
     @After
     fun after() {
     fun after() {
         activity.storageManager.cleanShares()
         activity.storageManager.cleanShares()
+        activity.finish()
     }
     }
 }
 }

+ 31 - 2
src/debug/java/com/nextcloud/client/TestActivity.kt

@@ -22,11 +22,13 @@
 package com.nextcloud.client
 package com.nextcloud.client
 
 
 import android.os.Bundle
 import android.os.Bundle
+import android.view.View
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.Fragment
 import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
 import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
 import com.nextcloud.client.network.Connectivity
 import com.nextcloud.client.network.Connectivity
 import com.nextcloud.client.network.ConnectivityService
 import com.nextcloud.client.network.ConnectivityService
 import com.owncloud.android.R
 import com.owncloud.android.R
+import com.owncloud.android.databinding.TestLayoutBinding
 import com.owncloud.android.datamodel.FileDataStorageManager
 import com.owncloud.android.datamodel.FileDataStorageManager
 import com.owncloud.android.datamodel.OCFile
 import com.owncloud.android.datamodel.OCFile
 import com.owncloud.android.files.services.FileDownloader
 import com.owncloud.android.files.services.FileDownloader
@@ -45,8 +47,11 @@ class TestActivity :
     SwipeRefreshLayout.OnRefreshListener,
     SwipeRefreshLayout.OnRefreshListener,
     OnEnforceableRefreshListener {
     OnEnforceableRefreshListener {
     lateinit var fragment: Fragment
     lateinit var fragment: Fragment
+    lateinit var secondaryFragment: Fragment
+
     private lateinit var storage: FileDataStorageManager
     private lateinit var storage: FileDataStorageManager
     private lateinit var fileOperation: FileOperationsHelper
     private lateinit var fileOperation: FileOperationsHelper
+    private lateinit var binding: TestLayoutBinding
 
 
     private val connectivityServiceMock: ConnectivityService = object : ConnectivityService {
     private val connectivityServiceMock: ConnectivityService = object : ConnectivityService {
         override fun isInternetWalled(): Boolean {
         override fun isInternetWalled(): Boolean {
@@ -61,16 +66,40 @@ class TestActivity :
     override fun onCreate(savedInstanceState: Bundle?) {
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         super.onCreate(savedInstanceState)
 
 
-        setContentView(R.layout.test_layout)
+        binding = TestLayoutBinding.inflate(layoutInflater)
+        setContentView(binding.root)
     }
     }
 
 
     fun addFragment(fragment: Fragment) {
     fun addFragment(fragment: Fragment) {
         this.fragment = fragment
         this.fragment = fragment
         val transaction = supportFragmentManager.beginTransaction()
         val transaction = supportFragmentManager.beginTransaction()
-        transaction.replace(R.id.root, fragment)
+        transaction.replace(R.id.main_fragment, fragment)
+        transaction.commit()
+    }
+
+    /**
+     * Adds a secondary fragment to the activity with the given tag.
+     *
+     * If you have to use this, your Fragments are coupled, and you should feel bad.
+     */
+    fun addSecondaryFragment(fragment: Fragment, tag: String) {
+        this.secondaryFragment = fragment
+        val transaction = supportFragmentManager.beginTransaction()
+        transaction.replace(R.id.secondary_fragment, fragment, tag)
         transaction.commit()
         transaction.commit()
     }
     }
 
 
+    /**
+     * Adds a View to the activity.
+     *
+     * If you have to use this, your Fragment is coupled to your Activity and you should feel bad.
+     */
+    fun addView(view: View) {
+        handler.post {
+            binding.rootLayout.addView(view)
+        }
+    }
+
     override fun onBrowsedDownTo(folder: OCFile?) {
     override fun onBrowsedDownTo(folder: OCFile?) {
         TODO("Not yet implemented")
         TODO("Not yet implemented")
     }
     }

+ 3 - 2
src/main/java/com/nextcloud/client/files/DeepLinkHandler.kt

@@ -1,4 +1,4 @@
-/**
+/*
  * Nextcloud Android client application
  * Nextcloud Android client application
  *
  *
  * @author Chris Narkiewicz
  * @author Chris Narkiewicz
@@ -27,9 +27,10 @@ import com.nextcloud.client.account.UserAccountManager
  * This component parses and matches deep links.
  * This component parses and matches deep links.
  * Result is returned to the UI for further processing.
  * Result is returned to the UI for further processing.
  *
  *
- * TODO: This is intermediate refactring step; this component should be moved into
+ * TODO: This is intermediate refactoring step; this component should be moved into
  *       [com.nextcloud.client.mixins.ActivityMixin] and handle UI callbacks as well
  *       [com.nextcloud.client.mixins.ActivityMixin] and handle UI callbacks as well
  */
  */
+@Suppress("ForbiddenComment")
 class DeepLinkHandler(
 class DeepLinkHandler(
     private val userAccountManager: UserAccountManager
     private val userAccountManager: UserAccountManager
 ) {
 ) {

+ 1 - 0
src/main/java/com/nextcloud/ui/SetStatusDialogFragment.kt

@@ -221,6 +221,7 @@ class SetStatusDialogFragment :
         )
         )
     }
     }
 
 
+    @Suppress("ComplexMethod")
     private fun setClearStatusAfterValue(item: Int) {
     private fun setClearStatusAfterValue(item: Int) {
         when (item) {
         when (item) {
             POS_DONT_CLEAR -> {
             POS_DONT_CLEAR -> {

+ 24 - 0
src/main/java/com/owncloud/android/datamodel/QuickPermissionModel.kt

@@ -0,0 +1,24 @@
+/*
+ * Nextcloud Android client application
+ *
+ * @author TSI-mc
+ * Copyright (C) 2021 TSI-mc
+ * Copyright (C) 2021 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.datamodel
+
+data class QuickPermissionModel(val permissionName: String, val isSelected: Boolean)

+ 64 - 4
src/main/java/com/owncloud/android/operations/CreateShareWithShareeOperation.java

@@ -3,7 +3,9 @@
  *
  *
  *   @author masensio
  *   @author masensio
  *   @author David A. Velasco
  *   @author David A. Velasco
+ *   @author TSI-mc
  *   Copyright (C) 2015 ownCloud Inc.
  *   Copyright (C) 2015 ownCloud Inc.
+ *   Copyright (C) 2021 TSI-mc
  *
  *
  *   This program is free software: you can redistribute it and/or modify
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License version 2,
  *   it under the terms of the GNU General Public License version 2,
@@ -21,6 +23,8 @@
 
 
 package com.owncloud.android.operations;
 package com.owncloud.android.operations;
 
 
+import android.text.TextUtils;
+
 import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.lib.common.OwnCloudClient;
 import com.owncloud.android.lib.common.OwnCloudClient;
@@ -44,6 +48,11 @@ public class CreateShareWithShareeOperation extends SyncOperation {
     private String shareeName;
     private String shareeName;
     private ShareType shareType;
     private ShareType shareType;
     private int permissions;
     private int permissions;
+    private String noteMessage;
+    private String sharePassword;
+    private boolean hideFileDownload;
+    private long expirationDateInMillis;
+    private String label;
 
 
     private static final List<ShareType> supportedShareTypes = new ArrayList<>(Arrays.asList(ShareType.USER,
     private static final List<ShareType> supportedShareTypes = new ArrayList<>(Arrays.asList(ShareType.USER,
                                                                                              ShareType.GROUP,
                                                                                              ShareType.GROUP,
@@ -78,6 +87,40 @@ public class CreateShareWithShareeOperation extends SyncOperation {
         this.permissions = permissions;
         this.permissions = permissions;
     }
     }
 
 
+    /**
+     * Constructor.
+     *
+     * @param path        Full path of the file/folder being shared.
+     * @param shareeName  User or group name of the target sharee.
+     * @param shareType   Type of share determines type of sharee; {@link ShareType#USER} and {@link ShareType#GROUP}
+     *                    are the only valid values for the moment.
+     * @param permissions Share permissions key as detailed in https://doc.owncloud.org/server/8.2/developer_manual/core/ocs-share-api.html
+     *                    .
+     */
+    public CreateShareWithShareeOperation(String path,
+                                          String shareeName,
+                                          ShareType shareType,
+                                          int permissions,
+                                          String noteMessage,
+                                          String sharePassword,
+                                          long expirationDateInMillis,
+                                          boolean hideFileDownload,
+                                          FileDataStorageManager storageManager) {
+        super(storageManager);
+
+        if (!supportedShareTypes.contains(shareType)) {
+            throw new IllegalArgumentException("Illegal share type " + shareType);
+        }
+        this.path = path;
+        this.shareeName = shareeName;
+        this.shareType = shareType;
+        this.permissions = permissions;
+        this.expirationDateInMillis = expirationDateInMillis;
+        this.hideFileDownload = hideFileDownload;
+        this.noteMessage = noteMessage;
+        this.sharePassword = sharePassword;
+    }
+
     @Override
     @Override
     protected RemoteOperationResult run(OwnCloudClient client) {
     protected RemoteOperationResult run(OwnCloudClient client) {
 
 
@@ -86,7 +129,7 @@ public class CreateShareWithShareeOperation extends SyncOperation {
             shareType,
             shareType,
             shareeName,
             shareeName,
             false,
             false,
-            "",
+            sharePassword,
             permissions
             permissions
         );
         );
         operation.setGetShareDetails(true);
         operation.setGetShareDetails(true);
@@ -95,7 +138,20 @@ public class CreateShareWithShareeOperation extends SyncOperation {
 
 
         if (result.isSuccess() && result.getData().size() > 0) {
         if (result.isSuccess() && result.getData().size() > 0) {
             OCShare share = (OCShare) result.getData().get(0);
             OCShare share = (OCShare) result.getData().get(0);
-            updateData(share);
+
+            //once creating share link update other information
+            UpdateShareInfoOperation updateShareInfoOperation = new UpdateShareInfoOperation(share, getStorageManager());
+            updateShareInfoOperation.setExpirationDateInMillis(expirationDateInMillis);
+            updateShareInfoOperation.setHideFileDownload(hideFileDownload);
+            updateShareInfoOperation.setNote(noteMessage);
+            updateShareInfoOperation.setLabel(label);
+
+            //execute and save the result in database
+            RemoteOperationResult updateShareInfoResult = updateShareInfoOperation.execute(client);
+            if (updateShareInfoResult.isSuccess() && updateShareInfoResult.getData().size() > 0) {
+                OCShare shareUpdated = (OCShare) updateShareInfoResult.getData().get(0);
+                updateData(shareUpdated);
+            }
         }
         }
 
 
         return result;
         return result;
@@ -105,12 +161,12 @@ public class CreateShareWithShareeOperation extends SyncOperation {
         // Update DB with the response
         // Update DB with the response
         share.setPath(path);
         share.setPath(path);
         share.setFolder(path.endsWith(FileUtils.PATH_SEPARATOR));
         share.setFolder(path.endsWith(FileUtils.PATH_SEPARATOR));
-
+        share.setPasswordProtected(!TextUtils.isEmpty(sharePassword));
         getStorageManager().saveShare(share);
         getStorageManager().saveShare(share);
 
 
         // Update OCFile with data from share: ShareByLink  and publicLink
         // Update OCFile with data from share: ShareByLink  and publicLink
         OCFile file = getStorageManager().getFileByPath(path);
         OCFile file = getStorageManager().getFileByPath(path);
-        if (file!=null) {
+        if (file != null) {
             file.setSharedWithSharee(true);    // TODO - this should be done by the FileContentProvider, as part of getStorageManager().saveShare(share)
             file.setSharedWithSharee(true);    // TODO - this should be done by the FileContentProvider, as part of getStorageManager().saveShare(share)
             getStorageManager().saveFile(file);
             getStorageManager().saveFile(file);
         }
         }
@@ -119,4 +175,8 @@ public class CreateShareWithShareeOperation extends SyncOperation {
     public String getPath() {
     public String getPath() {
         return this.path;
         return this.path;
     }
     }
+
+    public void setLabel(String label) {
+        this.label = label;
+    }
 }
 }

+ 151 - 0
src/main/java/com/owncloud/android/operations/UpdateShareInfoOperation.java

@@ -0,0 +1,151 @@
+/*
+ * Nextcloud Android client application
+ *
+ * @author TSI-mc
+ * Copyright (C) 2021 TSI-mc
+ * Copyright (C) 2021 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.operations;
+
+import android.text.TextUtils;
+
+import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.owncloud.android.lib.common.OwnCloudClient;
+import com.owncloud.android.lib.common.operations.RemoteOperation;
+import com.owncloud.android.lib.common.operations.RemoteOperationResult;
+import com.owncloud.android.lib.resources.shares.GetShareRemoteOperation;
+import com.owncloud.android.lib.resources.shares.OCShare;
+import com.owncloud.android.lib.resources.shares.UpdateShareRemoteOperation;
+import com.owncloud.android.operations.common.SyncOperation;
+
+
+/**
+ * Updates an existing private share for a given file.
+ */
+public class UpdateShareInfoOperation extends SyncOperation {
+
+    private OCShare share;
+    private long shareId;
+    private long expirationDateInMillis;
+    private String note;
+    private boolean hideFileDownload;
+    private int permissions = -1;
+    private String password;
+    private String label;
+
+    /**
+     * Constructor
+     *
+     * @param share {@link OCShare} to update. Mandatory argument
+     *              <p>
+     *              this will be triggered while creating new share
+     */
+    public UpdateShareInfoOperation(OCShare share, FileDataStorageManager storageManager) {
+        super(storageManager);
+
+        this.share = share;
+        expirationDateInMillis = 0L;
+        note = null;
+    }
+
+    /**
+     * Constructor
+     *
+     * @param shareId {@link OCShare} to update. Mandatory argument
+     *                <p>
+     *                this will be triggered while modifying existing share
+     */
+    public UpdateShareInfoOperation(long shareId, FileDataStorageManager storageManager) {
+        super(storageManager);
+        
+        this.shareId = shareId;
+        expirationDateInMillis = 0L;
+        note = null;
+    }
+
+    @Override
+    protected RemoteOperationResult run(OwnCloudClient client) {
+
+        OCShare share;
+        if (shareId > 0) {
+            share = getStorageManager().getShareById(shareId);
+        } else {
+            share = this.share;
+        }
+
+        if (share == null) {
+            // TODO try to get remote share before failing?
+            return new RemoteOperationResult(RemoteOperationResult.ResultCode.SHARE_NOT_FOUND);
+        }
+
+        // Update remote share
+        UpdateShareRemoteOperation updateOp = new UpdateShareRemoteOperation(share.getRemoteId());
+        updateOp.setExpirationDate(expirationDateInMillis);
+        updateOp.setHideFileDownload(hideFileDownload);
+        if (!TextUtils.isEmpty(note)) {
+            updateOp.setNote(note);
+        }
+        if (permissions > -1) {
+            updateOp.setPermissions(permissions);
+        }
+        updateOp.setPassword(password);
+        updateOp.setLabel(label);
+
+        RemoteOperationResult result = updateOp.execute(client);
+
+        if (result.isSuccess()) {
+            RemoteOperation getShareOp = new GetShareRemoteOperation(share.getRemoteId());
+            result = getShareOp.execute(client);
+
+            //only update the share in storage if shareId is available
+            //this will be triggered by editing existing share
+            if (result.isSuccess() && shareId > 0) {
+                OCShare ocShare = (OCShare) result.getData().get(0);
+                ocShare.setPasswordProtected(!TextUtils.isEmpty(password));
+                getStorageManager().saveShare(ocShare);
+            }
+
+        }
+
+        return result;
+    }
+
+    public void setExpirationDateInMillis(long expirationDateInMillis) {
+        this.expirationDateInMillis = expirationDateInMillis;
+    }
+
+    public void setNote(String note) {
+        this.note = note;
+    }
+
+    public void setHideFileDownload(boolean hideFileDownload) {
+        this.hideFileDownload = hideFileDownload;
+    }
+
+    public void setPermissions(int permissions) {
+        this.permissions = permissions;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    public void setLabel(String label) {
+        this.label = label;
+    }
+}
+

+ 103 - 57
src/main/java/com/owncloud/android/services/OperationsService.java

@@ -4,8 +4,10 @@
  *   @author masensio
  *   @author masensio
  *   @author David A. Velasco
  *   @author David A. Velasco
  *   @author Andy Scherzinger
  *   @author Andy Scherzinger
+ *   @author TSI-mc
  *   Copyright (C) 2015 ownCloud Inc.
  *   Copyright (C) 2015 ownCloud Inc.
  *   Copyright (C) 2018 Andy Scherzinger
  *   Copyright (C) 2018 Andy Scherzinger
+ *   Copyright (C) 2021 TSI-mc
  *
  *
  *   This program is free software: you can redistribute it and/or modify
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License version 2,
  *   it under the terms of the GNU General Public License version 2,
@@ -67,6 +69,7 @@ import com.owncloud.android.operations.SynchronizeFileOperation;
 import com.owncloud.android.operations.SynchronizeFolderOperation;
 import com.owncloud.android.operations.SynchronizeFolderOperation;
 import com.owncloud.android.operations.UnshareOperation;
 import com.owncloud.android.operations.UnshareOperation;
 import com.owncloud.android.operations.UpdateNoteForShareOperation;
 import com.owncloud.android.operations.UpdateNoteForShareOperation;
+import com.owncloud.android.operations.UpdateShareInfoOperation;
 import com.owncloud.android.operations.UpdateSharePermissionsOperation;
 import com.owncloud.android.operations.UpdateSharePermissionsOperation;
 import com.owncloud.android.operations.UpdateShareViaLinkOperation;
 import com.owncloud.android.operations.UpdateShareViaLinkOperation;
 
 
@@ -112,6 +115,7 @@ public class OperationsService extends Service {
     public static final String ACTION_UPDATE_PUBLIC_SHARE = "UPDATE_PUBLIC_SHARE";
     public static final String ACTION_UPDATE_PUBLIC_SHARE = "UPDATE_PUBLIC_SHARE";
     public static final String ACTION_UPDATE_USER_SHARE = "UPDATE_USER_SHARE";
     public static final String ACTION_UPDATE_USER_SHARE = "UPDATE_USER_SHARE";
     public static final String ACTION_UPDATE_SHARE_NOTE = "UPDATE_SHARE_NOTE";
     public static final String ACTION_UPDATE_SHARE_NOTE = "UPDATE_SHARE_NOTE";
+    public static final String ACTION_UPDATE_SHARE_INFO = "UPDATE_SHARE_INFO";
     public static final String ACTION_GET_SERVER_INFO = "GET_SERVER_INFO";
     public static final String ACTION_GET_SERVER_INFO = "GET_SERVER_INFO";
     public static final String ACTION_GET_USER_NAME = "GET_USER_NAME";
     public static final String ACTION_GET_USER_NAME = "GET_USER_NAME";
     public static final String ACTION_RENAME = "RENAME";
     public static final String ACTION_RENAME = "RENAME";
@@ -130,7 +134,7 @@ public class OperationsService extends Service {
     private SyncFolderHandler mSyncFolderHandler;
     private SyncFolderHandler mSyncFolderHandler;
 
 
     private ConcurrentMap<Integer, Pair<RemoteOperation, RemoteOperationResult>>
     private ConcurrentMap<Integer, Pair<RemoteOperation, RemoteOperationResult>>
-            mUndispatchedFinishedOperations = new ConcurrentHashMap<>();
+        mUndispatchedFinishedOperations = new ConcurrentHashMap<>();
 
 
     @Inject UserAccountManager accountManager;
     @Inject UserAccountManager accountManager;
 
 
@@ -155,7 +159,7 @@ public class OperationsService extends Service {
 
 
         // First worker thread for most of operations
         // First worker thread for most of operations
         HandlerThread thread = new HandlerThread("Operations thread",
         HandlerThread thread = new HandlerThread("Operations thread",
-                Process.THREAD_PRIORITY_BACKGROUND);
+                                                 Process.THREAD_PRIORITY_BACKGROUND);
         thread.start();
         thread.start();
         mOperationsHandler = new ServiceHandler(thread.getLooper(), this);
         mOperationsHandler = new ServiceHandler(thread.getLooper(), this);
         mOperationsBinder = new OperationsServiceBinder(mOperationsHandler);
         mOperationsBinder = new OperationsServiceBinder(mOperationsHandler);
@@ -169,8 +173,8 @@ public class OperationsService extends Service {
     /**
     /**
      * Entry point to add a new operation to the queue of operations.
      * Entry point to add a new operation to the queue of operations.
      * <p/>
      * <p/>
-     * New operations are added calling to startService(), resulting in a call to this method.
-     * This ensures the service will keep on working although the caller activity goes away.
+     * New operations are added calling to startService(), resulting in a call to this method. This ensures the service
+     * will keep on working although the caller activity goes away.
      */
      */
     @Override
     @Override
     public int onStartCommand(Intent intent, int flags, int startId) {
     public int onStartCommand(Intent intent, int flags, int startId) {
@@ -191,8 +195,9 @@ public class OperationsService extends Service {
 
 
             Pair<Target, RemoteOperation> itemToQueue = newOperation(intent);
             Pair<Target, RemoteOperation> itemToQueue = newOperation(intent);
             if (itemToQueue != null) {
             if (itemToQueue != null) {
-                mSyncFolderHandler.add(account, remotePath,
-                        (SynchronizeFolderOperation)itemToQueue.second);
+                mSyncFolderHandler.add(account,
+                                       remotePath,
+                                       (SynchronizeFolderOperation) itemToQueue.second);
                 Message msg = mSyncFolderHandler.obtainMessage();
                 Message msg = mSyncFolderHandler.obtainMessage();
                 msg.arg1 = startId;
                 msg.arg1 = startId;
                 msg.obj = itemSyncKey;
                 msg.obj = itemSyncKey;
@@ -210,7 +215,7 @@ public class OperationsService extends Service {
 
 
     @Override
     @Override
     public void onDestroy() {
     public void onDestroy() {
-        Log_OC.v(TAG, "Destroying service" );
+        Log_OC.v(TAG, "Destroying service");
         // Saving cookies
         // Saving cookies
         OwnCloudClientManagerFactory.getDefaultSingleton()
         OwnCloudClientManagerFactory.getDefaultSingleton()
             .saveAllClients(this, MainApp.getAccountType(getApplicationContext()));
             .saveAllClients(this, MainApp.getAccountType(getApplicationContext()));
@@ -229,8 +234,8 @@ public class OperationsService extends Service {
     }
     }
 
 
     /**
     /**
-     * Provides a binder object that clients can use to perform actions on the queue of operations,
-     * except the addition of new operations.
+     * Provides a binder object that clients can use to perform actions on the queue of operations, except the addition
+     * of new operations.
      */
      */
     @Override
     @Override
     public IBinder onBind(Intent intent) {
     public IBinder onBind(Intent intent) {
@@ -256,8 +261,8 @@ public class OperationsService extends Service {
     public class OperationsServiceBinder extends Binder /* implements OnRemoteOperationListener */ {
     public class OperationsServiceBinder extends Binder /* implements OnRemoteOperationListener */ {
 
 
         /**
         /**
-         * Map of listeners that will be reported about the end of operations from a
-         * {@link OperationsServiceBinder} instance
+         * Map of listeners that will be reported about the end of operations from a {@link OperationsServiceBinder}
+         * instance
          */
          */
         private final ConcurrentMap<OnRemoteOperationListener, Handler> mBoundListeners = new ConcurrentHashMap<>();
         private final ConcurrentMap<OnRemoteOperationListener, Handler> mBoundListeners = new ConcurrentHashMap<>();
 
 
@@ -271,8 +276,8 @@ public class OperationsService extends Service {
         /**
         /**
          * Cancels a pending or current synchronization.
          * Cancels a pending or current synchronization.
          *
          *
-         * @param account       ownCloud account where the remote folder is stored.
-         * @param file          A folder in the queue of pending synchronizations
+         * @param account ownCloud account where the remote folder is stored.
+         * @param file    A folder in the queue of pending synchronizations
          */
          */
         public void cancel(Account account, OCFile file) {
         public void cancel(Account account, OCFile file) {
             mSyncFolderHandler.cancel(account, file);
             mSyncFolderHandler.cancel(account, file);
@@ -288,12 +293,11 @@ public class OperationsService extends Service {
         /**
         /**
          * Adds a listener interested in being reported about the end of operations.
          * Adds a listener interested in being reported about the end of operations.
          *
          *
-         * @param listener          Object to notify about the end of operations.
-         * @param callbackHandler   {@link Handler} to access the listener without
-         *                                         breaking Android threading protection.
+         * @param listener        Object to notify about the end of operations.
+         * @param callbackHandler {@link Handler} to access the listener without breaking Android threading protection.
          */
          */
-        public void addOperationListener (OnRemoteOperationListener listener,
-                                          Handler callbackHandler) {
+        public void addOperationListener(OnRemoteOperationListener listener,
+                                         Handler callbackHandler) {
             synchronized (mBoundListeners) {
             synchronized (mBoundListeners) {
                 mBoundListeners.put(listener, callbackHandler);
                 mBoundListeners.put(listener, callbackHandler);
             }
             }
@@ -301,10 +305,9 @@ public class OperationsService extends Service {
 
 
 
 
         /**
         /**
-         * Removes a listener from the list of objects interested in the being reported about
-         * the end of operations.
+         * Removes a listener from the list of objects interested in the being reported about the end of operations.
          *
          *
-         * @param listener      Object to notify about progress of transfer.
+         * @param listener Object to notify about progress of transfer.
          */
          */
         public void removeOperationListener(OnRemoteOperationListener listener) {
         public void removeOperationListener(OnRemoteOperationListener listener) {
             synchronized (mBoundListeners) {
             synchronized (mBoundListeners) {
@@ -316,8 +319,7 @@ public class OperationsService extends Service {
         /**
         /**
          * TODO - IMPORTANT: update implementation when more operations are moved into the service
          * TODO - IMPORTANT: update implementation when more operations are moved into the service
          *
          *
-         * @return  'True' when an operation that enforces the user to wait for completion is
-         *          in process.
+         * @return 'True' when an operation that enforces the user to wait for completion is in process.
          */
          */
         public boolean isPerformingBlockingOperation() {
         public boolean isPerformingBlockingOperation() {
             return !mServiceHandler.mPendingOperations.isEmpty();
             return !mServiceHandler.mPendingOperations.isEmpty();
@@ -326,11 +328,11 @@ public class OperationsService extends Service {
 
 
         /**
         /**
          * Creates and adds to the queue a new operation, as described by operationIntent.
          * Creates and adds to the queue a new operation, as described by operationIntent.
-         *
+         * <p>
          * Calls startService to make the operation is processed by the ServiceHandler.
          * Calls startService to make the operation is processed by the ServiceHandler.
          *
          *
-         * @param operationIntent       Intent describing a new operation to queue and execute.
-         * @return                      Identifier of the operation created, or null if failed.
+         * @param operationIntent Intent describing a new operation to queue and execute.
+         * @return Identifier of the operation created, or null if failed.
          */
          */
         public long queueNewOperation(Intent operationIntent) {
         public long queueNewOperation(Intent operationIntent) {
             Pair<Target, RemoteOperation> itemToQueue = newOperation(operationIntent);
             Pair<Target, RemoteOperation> itemToQueue = newOperation(operationIntent);
@@ -347,7 +349,7 @@ public class OperationsService extends Service {
         public boolean dispatchResultIfFinished(int operationId,
         public boolean dispatchResultIfFinished(int operationId,
                                                 OnRemoteOperationListener listener) {
                                                 OnRemoteOperationListener listener) {
             Pair<RemoteOperation, RemoteOperationResult> undispatched =
             Pair<RemoteOperation, RemoteOperationResult> undispatched =
-                    mUndispatchedFinishedOperations.remove(operationId);
+                mUndispatchedFinishedOperations.remove(operationId);
             if (undispatched != null) {
             if (undispatched != null) {
                 listener.onRemoteOperationFinish(undispatched.first, undispatched.second);
                 listener.onRemoteOperationFinish(undispatched.first, undispatched.second);
                 return true;
                 return true;
@@ -357,15 +359,14 @@ public class OperationsService extends Service {
         }
         }
 
 
         /**
         /**
-         * Returns True when the file described by 'file' in the ownCloud account 'account' is
-         * downloading or waiting to download.
-         *
-         * If 'file' is a directory, returns 'true' if some of its descendant files is downloading
-         * or waiting to download.
+         * Returns True when the file described by 'file' in the ownCloud account 'account' is downloading or waiting to
+         * download.
+         * <p>
+         * If 'file' is a directory, returns 'true' if some of its descendant files is downloading or waiting to
+         * download.
          *
          *
-         * @param user          user where the remote file is stored.
-         * @param file          File to check if something is synchronizing
-         *                      / downloading / uploading inside.
+         * @param user user where the remote file is stored.
+         * @param file File to check if something is synchronizing / downloading / uploading inside.
          */
          */
         public boolean isSynchronizing(User user, OCFile file) {
         public boolean isSynchronizing(User user, OCFile file) {
             return mSyncFolderHandler.isSynchronizing(user, file.getRemotePath());
             return mSyncFolderHandler.isSynchronizing(user, file.getRemotePath());
@@ -376,7 +377,7 @@ public class OperationsService extends Service {
 
 
     /**
     /**
      * Operations worker. Performs the pending operations in the order they were requested.
      * Operations worker. Performs the pending operations in the order they were requested.
-     *
+     * <p>
      * Created with the Looper of a new thread, started in {@link OperationsService#onCreate()}.
      * Created with the Looper of a new thread, started in {@link OperationsService#onCreate()}.
      */
      */
     private static class ServiceHandler extends Handler {
     private static class ServiceHandler extends Handler {
@@ -386,7 +387,7 @@ public class OperationsService extends Service {
 
 
 
 
         private ConcurrentLinkedQueue<Pair<Target, RemoteOperation>> mPendingOperations =
         private ConcurrentLinkedQueue<Pair<Target, RemoteOperation>> mPendingOperations =
-                new ConcurrentLinkedQueue<>();
+            new ConcurrentLinkedQueue<>();
         private RemoteOperation mCurrentOperation;
         private RemoteOperation mCurrentOperation;
         private Target mLastTarget;
         private Target mLastTarget;
         private OwnCloudClient mOwnCloudClient;
         private OwnCloudClient mOwnCloudClient;
@@ -414,7 +415,7 @@ public class OperationsService extends Service {
             //Log_OC.e(TAG, "nextOperation init" );
             //Log_OC.e(TAG, "nextOperation init" );
 
 
             Pair<Target, RemoteOperation> next;
             Pair<Target, RemoteOperation> next;
-            synchronized(mPendingOperations) {
+            synchronized (mPendingOperations) {
                 next = mPendingOperations.peek();
                 next = mPendingOperations.peek();
             }
             }
 
 
@@ -440,20 +441,20 @@ public class OperationsService extends Service {
                 } catch (AccountsException e) {
                 } catch (AccountsException e) {
                     if (mLastTarget.mAccount == null) {
                     if (mLastTarget.mAccount == null) {
                         Log_OC.e(TAG, "Error while trying to get authorization for a NULL account",
                         Log_OC.e(TAG, "Error while trying to get authorization for a NULL account",
-                                e);
+                                 e);
                     } else {
                     } else {
                         Log_OC.e(TAG, "Error while trying to get authorization for " +
                         Log_OC.e(TAG, "Error while trying to get authorization for " +
-                                mLastTarget.mAccount.name, e);
+                            mLastTarget.mAccount.name, e);
                     }
                     }
                     result = new RemoteOperationResult(e);
                     result = new RemoteOperationResult(e);
 
 
                 } catch (IOException e) {
                 } catch (IOException e) {
                     if (mLastTarget.mAccount == null) {
                     if (mLastTarget.mAccount == null) {
                         Log_OC.e(TAG, "Error while trying to get authorization for a NULL account",
                         Log_OC.e(TAG, "Error while trying to get authorization for a NULL account",
-                                e);
+                                 e);
                     } else {
                     } else {
                         Log_OC.e(TAG, "Error while trying to get authorization for " +
                         Log_OC.e(TAG, "Error while trying to get authorization for " +
-                                mLastTarget.mAccount.name, e);
+                            mLastTarget.mAccount.name, e);
                     }
                     }
                     result = new RemoteOperationResult(e);
                     result = new RemoteOperationResult(e);
                 } catch (Exception e) {
                 } catch (Exception e) {
@@ -465,7 +466,7 @@ public class OperationsService extends Service {
                     result = new RemoteOperationResult(e);
                     result = new RemoteOperationResult(e);
 
 
                 } finally {
                 } finally {
-                    synchronized(mPendingOperations) {
+                    synchronized (mPendingOperations) {
                         mPendingOperations.poll();
                         mPendingOperations.poll();
                     }
                     }
                 }
                 }
@@ -479,19 +480,18 @@ public class OperationsService extends Service {
 
 
     /**
     /**
      * Creates a new operation, as described by operationIntent.
      * Creates a new operation, as described by operationIntent.
-     *
+     * <p>
      * TODO - move to ServiceHandler (probably)
      * TODO - move to ServiceHandler (probably)
      *
      *
-     * @param operationIntent       Intent describing a new operation to queue and execute.
-     * @return                      Pair with the new operation object and the information about its
-     *                              target server.
+     * @param operationIntent Intent describing a new operation to queue and execute.
+     * @return Pair with the new operation object and the information about its target server.
      */
      */
     private Pair<Target, RemoteOperation> newOperation(Intent operationIntent) {
     private Pair<Target, RemoteOperation> newOperation(Intent operationIntent) {
         RemoteOperation operation = null;
         RemoteOperation operation = null;
         Target target = null;
         Target target = null;
         try {
         try {
             if (!operationIntent.hasExtra(EXTRA_ACCOUNT) &&
             if (!operationIntent.hasExtra(EXTRA_ACCOUNT) &&
-                    !operationIntent.hasExtra(EXTRA_SERVER_URL)) {
+                !operationIntent.hasExtra(EXTRA_SERVER_URL)) {
                 Log_OC.e(TAG, "Not enough information provided in intent");
                 Log_OC.e(TAG, "Not enough information provided in intent");
 
 
             } else {
             } else {
@@ -583,12 +583,58 @@ public class OperationsService extends Service {
                         String shareeName = operationIntent.getStringExtra(EXTRA_SHARE_WITH);
                         String shareeName = operationIntent.getStringExtra(EXTRA_SHARE_WITH);
                         shareType = (ShareType) operationIntent.getSerializableExtra(EXTRA_SHARE_TYPE);
                         shareType = (ShareType) operationIntent.getSerializableExtra(EXTRA_SHARE_TYPE);
                         int permissions = operationIntent.getIntExtra(EXTRA_SHARE_PERMISSIONS, -1);
                         int permissions = operationIntent.getIntExtra(EXTRA_SHARE_PERMISSIONS, -1);
+                        String noteMessage = operationIntent.getStringExtra(EXTRA_SHARE_NOTE);
+                        String sharePassword = operationIntent.getStringExtra(EXTRA_SHARE_PASSWORD);
+                        long expirationDateInMillis = operationIntent
+                            .getLongExtra(EXTRA_SHARE_EXPIRATION_DATE_IN_MILLIS, 0L);
+                        boolean hideFileDownload = operationIntent.getBooleanExtra(EXTRA_SHARE_HIDE_FILE_DOWNLOAD,
+                                                                                   false);
                         if (!TextUtils.isEmpty(remotePath)) {
                         if (!TextUtils.isEmpty(remotePath)) {
-                            operation = new CreateShareWithShareeOperation(remotePath,
-                                                                           shareeName,
-                                                                           shareType,
-                                                                           permissions,
-                                                                           fileDataStorageManager);
+                            CreateShareWithShareeOperation createShareWithShareeOperation =
+                                new CreateShareWithShareeOperation(remotePath,
+                                                                   shareeName,
+                                                                   shareType,
+                                                                   permissions,
+                                                                   noteMessage,
+                                                                   sharePassword,
+                                                                   expirationDateInMillis,
+                                                                   hideFileDownload,
+                                                                   fileDataStorageManager);
+
+                            if (operationIntent.hasExtra(EXTRA_SHARE_PUBLIC_LABEL)) {
+                                createShareWithShareeOperation.setLabel(operationIntent.getStringExtra(EXTRA_SHARE_PUBLIC_LABEL));
+                            }
+                            operation = createShareWithShareeOperation;
+                        }
+                        break;
+
+                    case ACTION_UPDATE_SHARE_INFO:
+                        shareId = operationIntent.getLongExtra(EXTRA_SHARE_ID, -1);
+
+                        if (shareId > 0) {
+                            UpdateShareInfoOperation updateShare = new UpdateShareInfoOperation(shareId,
+                                                                                                fileDataStorageManager);
+
+                            int permissionsToChange = operationIntent.getIntExtra(EXTRA_SHARE_PERMISSIONS, -1);
+                            updateShare.setPermissions(permissionsToChange);
+
+                            long expirationDateInMills = operationIntent
+                                .getLongExtra(EXTRA_SHARE_EXPIRATION_DATE_IN_MILLIS, 0L);
+                            updateShare.setExpirationDateInMillis(expirationDateInMills);
+
+                            password = operationIntent.getStringExtra(EXTRA_SHARE_PASSWORD);
+                            updateShare.setPassword(password);
+
+                            boolean fileDownloadHide = operationIntent.getBooleanExtra(EXTRA_SHARE_HIDE_FILE_DOWNLOAD
+                                , false);
+
+                            updateShare.setHideFileDownload(fileDownloadHide);
+
+                            if (operationIntent.hasExtra(EXTRA_SHARE_PUBLIC_LABEL)) {
+                                updateShare.setLabel(operationIntent.getStringExtra(EXTRA_SHARE_PUBLIC_LABEL));
+                            }
+
+                            operation = updateShare;
                         }
                         }
                         break;
                         break;
 
 
@@ -717,12 +763,12 @@ public class OperationsService extends Service {
     /**
     /**
      * Notifies the currently subscribed listeners about the end of an operation.
      * Notifies the currently subscribed listeners about the end of an operation.
      *
      *
-     * @param operation         Finished operation.
-     * @param result            Result of the operation.
+     * @param operation Finished operation.
+     * @param result    Result of the operation.
      */
      */
     protected void dispatchResultToOperationListeners(
     protected void dispatchResultToOperationListeners(
-            final RemoteOperation operation, final RemoteOperationResult result
-    ) {
+        final RemoteOperation operation, final RemoteOperationResult result
+                                                     ) {
         int count = 0;
         int count = 0;
         Iterator<OnRemoteOperationListener> listeners = mOperationsBinder.mBoundListeners.keySet().iterator();
         Iterator<OnRemoteOperationListener> listeners = mOperationsBinder.mBoundListeners.keySet().iterator();
         while (listeners.hasNext()) {
         while (listeners.hasNext()) {

+ 11 - 1
src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java

@@ -4,12 +4,14 @@
  * @author Andy Scherzinger
  * @author Andy Scherzinger
  * @author Tobias Kaminsky
  * @author Tobias Kaminsky
  * @author Chris Narkiewicz  <hello@ezaquarii.com>
  * @author Chris Narkiewicz  <hello@ezaquarii.com>
+ * @author TSI-mc
  * Copyright (C) 2016 Andy Scherzinger
  * Copyright (C) 2016 Andy Scherzinger
  * Copyright (C) 2017 Tobias Kaminsky
  * Copyright (C) 2017 Tobias Kaminsky
  * Copyright (C) 2016 Nextcloud
  * Copyright (C) 2016 Nextcloud
  * Copyright (C) 2016 ownCloud Inc.
  * Copyright (C) 2016 ownCloud Inc.
  * Copyright (C) 2020 Chris Narkiewicz <hello@ezaquarii.com>
  * Copyright (C) 2020 Chris Narkiewicz <hello@ezaquarii.com>
  * Copyright (C) 2020 Infomaniak Network SA
  * Copyright (C) 2020 Infomaniak Network SA
+ * Copyright (C) 2021 TSI-mc
  *
  *
  * This program is free software; you can redistribute it and/or
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
  * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
@@ -98,6 +100,7 @@ import com.owncloud.android.ui.events.AccountRemovedEvent;
 import com.owncloud.android.ui.events.ChangeMenuEvent;
 import com.owncloud.android.ui.events.ChangeMenuEvent;
 import com.owncloud.android.ui.events.DummyDrawerEvent;
 import com.owncloud.android.ui.events.DummyDrawerEvent;
 import com.owncloud.android.ui.events.SearchEvent;
 import com.owncloud.android.ui.events.SearchEvent;
+import com.owncloud.android.ui.fragment.FileDetailsSharingProcessFragment;
 import com.owncloud.android.ui.fragment.GalleryFragment;
 import com.owncloud.android.ui.fragment.GalleryFragment;
 import com.owncloud.android.ui.fragment.OCFileListFragment;
 import com.owncloud.android.ui.fragment.OCFileListFragment;
 import com.owncloud.android.ui.preview.PreviewTextStringFragment;
 import com.owncloud.android.ui.preview.PreviewTextStringFragment;
@@ -132,6 +135,7 @@ import androidx.core.content.ContextCompat;
 import androidx.core.content.res.ResourcesCompat;
 import androidx.core.content.res.ResourcesCompat;
 import androidx.core.view.GravityCompat;
 import androidx.core.view.GravityCompat;
 import androidx.drawerlayout.widget.DrawerLayout;
 import androidx.drawerlayout.widget.DrawerLayout;
+import androidx.fragment.app.Fragment;
 
 
 /**
 /**
  * Base class to handle setup of the drawer implementation including user switching and avatar fetching and fallback
  * Base class to handle setup of the drawer implementation including user switching and avatar fetching and fallback
@@ -967,7 +971,13 @@ public abstract class DrawerActivity extends ToolbarActivity
             closeDrawer();
             closeDrawer();
             return;
             return;
         }
         }
-        super.onBackPressed();
+        Fragment fileDetailsSharingProcessFragment =
+            getSupportFragmentManager().findFragmentByTag(FileDetailsSharingProcessFragment.TAG);
+        if (fileDetailsSharingProcessFragment != null) {
+            ((FileDetailsSharingProcessFragment)fileDetailsSharingProcessFragment).onBackPressed();
+        } else {
+            super.onBackPressed();
+        }
     }
     }
 
 
     @Override
     @Override

+ 40 - 17
src/main/java/com/owncloud/android/ui/activity/FileActivity.java

@@ -3,9 +3,11 @@
  *
  *
  *   @author David A. Velasco
  *   @author David A. Velasco
  *   @author Chris Narkiewicz
  *   @author Chris Narkiewicz
+ *   @author TSI-mc
  *   Copyright (C) 2011  Bartek Przybylski
  *   Copyright (C) 2011  Bartek Przybylski
  *   Copyright (C) 2016 ownCloud Inc.
  *   Copyright (C) 2016 ownCloud Inc.
  *   Copyright (C) 2019 Chris Narkiewicz <hello@ezaquarii.com>
  *   Copyright (C) 2019 Chris Narkiewicz <hello@ezaquarii.com>
+ *   Copyright (C) 2021 TSI-mc
  *
  *
  *   This program is free software: you can redistribute it and/or modify
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License version 2,
  *   it under the terms of the GNU General Public License version 2,
@@ -70,6 +72,7 @@ import com.owncloud.android.operations.SynchronizeFileOperation;
 import com.owncloud.android.operations.SynchronizeFolderOperation;
 import com.owncloud.android.operations.SynchronizeFolderOperation;
 import com.owncloud.android.operations.UnshareOperation;
 import com.owncloud.android.operations.UnshareOperation;
 import com.owncloud.android.operations.UpdateNoteForShareOperation;
 import com.owncloud.android.operations.UpdateNoteForShareOperation;
+import com.owncloud.android.operations.UpdateShareInfoOperation;
 import com.owncloud.android.operations.UpdateSharePermissionsOperation;
 import com.owncloud.android.operations.UpdateSharePermissionsOperation;
 import com.owncloud.android.operations.UpdateShareViaLinkOperation;
 import com.owncloud.android.operations.UpdateShareViaLinkOperation;
 import com.owncloud.android.providers.UsersAndGroupsSearchProvider;
 import com.owncloud.android.providers.UsersAndGroupsSearchProvider;
@@ -112,7 +115,7 @@ import static com.owncloud.android.ui.activity.FileDisplayActivity.TAG_PUBLIC_LI
  */
  */
 public abstract class FileActivity extends DrawerActivity
 public abstract class FileActivity extends DrawerActivity
         implements OnRemoteOperationListener, ComponentsGetter, SslUntrustedCertDialog.OnSslUntrustedCertListener,
         implements OnRemoteOperationListener, ComponentsGetter, SslUntrustedCertDialog.OnSslUntrustedCertListener,
-        LoadingVersionNumberTask.VersionDevInterface {
+        LoadingVersionNumberTask.VersionDevInterface, FileDetailSharingFragment.OnEditShareListener {
 
 
     public static final String EXTRA_FILE = "com.owncloud.android.ui.activity.FILE";
     public static final String EXTRA_FILE = "com.owncloud.android.ui.activity.FILE";
     public static final String EXTRA_ACCOUNT = "com.owncloud.android.ui.activity.ACCOUNT";
     public static final String EXTRA_ACCOUNT = "com.owncloud.android.ui.activity.ACCOUNT";
@@ -406,7 +409,7 @@ public abstract class FileActivity extends DrawerActivity
             onCreateShareViaLinkOperationFinish((CreateShareViaLinkOperation) operation, result);
             onCreateShareViaLinkOperationFinish((CreateShareViaLinkOperation) operation, result);
         } else if (operation instanceof CreateShareWithShareeOperation) {
         } else if (operation instanceof CreateShareWithShareeOperation) {
             onUpdateShareInformation(result, R.string.sharee_add_failed);
             onUpdateShareInformation(result, R.string.sharee_add_failed);
-        } else if (operation instanceof UpdateShareViaLinkOperation) {
+        } else if (operation instanceof UpdateShareViaLinkOperation || operation instanceof UpdateShareInfoOperation) {
             onUpdateShareInformation(result, R.string.updating_share_failed);
             onUpdateShareInformation(result, R.string.updating_share_failed);
         } else if (operation instanceof UpdateSharePermissionsOperation) {
         } else if (operation instanceof UpdateSharePermissionsOperation) {
             onUpdateShareInformation(result, R.string.updating_share_failed);
             onUpdateShareInformation(result, R.string.updating_share_failed);
@@ -883,22 +886,42 @@ public abstract class FileActivity extends DrawerActivity
         }
         }
     }
     }
 
 
+    /**
+     * open the new sharing process fragment to create the share
+     * @param shareeName
+     * @param shareType
+     */
     private void doShareWith(String shareeName, ShareType shareType) {
     private void doShareWith(String shareeName, ShareType shareType) {
-        getFileOperationsHelper().shareFileWithSharee(getFile(),
-                                                      shareeName,
-                                                      shareType,
-                                                      getAppropriatePermissions(shareType));
-    }
-
-    private int getAppropriatePermissions(ShareType shareType) {
-        if (getFile().isSharedWithMe()) {
-            return OCShare.READ_PERMISSION_FLAG;    // minimum permissions
-        } else if (ShareType.FEDERATED.equals(shareType)) {
-            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;
+        Fragment fragment  = getSupportFragmentManager().findFragmentByTag(FileDisplayActivity.TAG_LIST_OF_FILES);
+        if (fragment!=null){
+            ((FileDetailFragment)fragment).initiateSharingProcess(shareeName, shareType);
+        }
+    }
+
+    /**
+     * open the new sharing process to modify the created share
+     * @param share
+     * @param screenTypePermission
+     * @param isReshareShown
+     * @param isExpiryDateShown
+     */
+    @Override
+    public void editExistingShare(OCShare share, int screenTypePermission, boolean isReshareShown,
+                                  boolean isExpiryDateShown) {
+        Fragment fragment  = getSupportFragmentManager().findFragmentByTag(FileDisplayActivity.TAG_LIST_OF_FILES);
+        if (fragment!=null){
+            ((FileDetailFragment)fragment).editExistingShare(share, screenTypePermission, isReshareShown, isExpiryDateShown);
+        }
+    }
+
+    /**
+     * callback triggered on closing/finishing the sharing process
+     */
+    @Override
+    public void onShareProcessClosed() {
+        Fragment fragment  = getSupportFragmentManager().findFragmentByTag(FileDisplayActivity.TAG_LIST_OF_FILES);
+        if (fragment!=null){
+            ((FileDetailFragment)fragment).showHideFragmentView(false);
         }
         }
     }
     }
 }
 }

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

@@ -3,8 +3,11 @@
  * Nextcloud Android client application
  * Nextcloud Android client application
  *
  *
  * @author Tobias Kaminsky
  * @author Tobias Kaminsky
+ * @author TSI-mc
+ *
  * Copyright (C) 2020 Tobias Kaminsky
  * Copyright (C) 2020 Tobias Kaminsky
  * Copyright (C) 2020 Nextcloud GmbH
  * Copyright (C) 2020 Nextcloud GmbH
+ * Copyright (C) 2021 TSI-mc
  *
  *
  * This program is free software: you can redistribute it and/or modify
  * 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
  * it under the terms of the GNU Affero General Public License as published by
@@ -31,6 +34,7 @@ import com.owncloud.android.R;
 import com.owncloud.android.databinding.FileDetailsShareLinkShareItemBinding;
 import com.owncloud.android.databinding.FileDetailsShareLinkShareItemBinding;
 import com.owncloud.android.lib.resources.shares.OCShare;
 import com.owncloud.android.lib.resources.shares.OCShare;
 import com.owncloud.android.lib.resources.shares.ShareType;
 import com.owncloud.android.lib.resources.shares.ShareType;
+import com.owncloud.android.ui.fragment.util.SharingMenuHelper;
 import com.owncloud.android.utils.theme.ThemeAvatarUtils;
 import com.owncloud.android.utils.theme.ThemeAvatarUtils;
 
 
 import androidx.annotation.NonNull;
 import androidx.annotation.NonNull;
@@ -74,7 +78,20 @@ class LinkShareViewHolder extends RecyclerView.ViewHolder {
             ThemeAvatarUtils.colorIconImageViewWithBackground(binding.icon, context);
             ThemeAvatarUtils.colorIconImageViewWithBackground(binding.icon, context);
         }
         }
 
 
+        String permissionName = SharingMenuHelper.getPermissionName(context, publicShare);
+        setPermissionName(permissionName);
+
         binding.copyLink.setOnClickListener(v -> listener.copyLink(publicShare));
         binding.copyLink.setOnClickListener(v -> listener.copyLink(publicShare));
-        binding.overflowMenu.setOnClickListener(v -> listener.showLinkOverflowMenu(publicShare, binding.overflowMenu));
+        binding.overflowMenu.setOnClickListener(v -> listener.showSharingMenuActionSheet(publicShare));
+        binding.shareByLinkContainer.setOnClickListener(v -> listener.showPermissionsDialog(publicShare));
+    }
+
+    private void setPermissionName(String permissionName) {
+        if (!TextUtils.isEmpty(permissionName)) {
+            binding.permissionName.setText(permissionName);
+            binding.permissionName.setVisibility(View.VISIBLE);
+        } else {
+            binding.permissionName.setVisibility(View.GONE);
+        }
     }
     }
 }
 }

+ 84 - 0
src/main/java/com/owncloud/android/ui/adapter/QuickSharingPermissionsAdapter.kt

@@ -0,0 +1,84 @@
+/*
+ * Nextcloud Android client application
+ *
+ * @author TSI-mc
+ * Copyright (C) 2021 TSI-mc
+ * Copyright (C) 2021 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.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import com.owncloud.android.databinding.ItemQuickSharePermissionsBinding
+import com.owncloud.android.datamodel.QuickPermissionModel
+
+class QuickSharingPermissionsAdapter(
+    private val quickPermissionList: MutableList<QuickPermissionModel>,
+    private val onPermissionChangeListener: QuickSharingPermissionViewHolder.OnPermissionChangeListener
+) :
+    RecyclerView.Adapter<RecyclerView.ViewHolder>() {
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
+        val binding = ItemQuickSharePermissionsBinding.inflate(LayoutInflater.from(parent.context), parent, false)
+        return QuickSharingPermissionViewHolder(binding, binding.root, onPermissionChangeListener)
+    }
+
+    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
+        if (holder is QuickSharingPermissionViewHolder) {
+            holder.bindData(quickPermissionList[position])
+        }
+    }
+
+    override fun getItemCount(): Int {
+        return quickPermissionList.size
+    }
+
+    class QuickSharingPermissionViewHolder(
+        val binding: ItemQuickSharePermissionsBinding,
+        itemView: View,
+        val onPermissionChangeListener: OnPermissionChangeListener
+    ) :
+        RecyclerView
+        .ViewHolder(itemView) {
+
+        fun bindData(quickPermissionModel: QuickPermissionModel) {
+            binding.tvQuickShareName.text = quickPermissionModel.permissionName
+            if (quickPermissionModel.isSelected) {
+                binding.tvQuickShareCheckIcon.visibility = View.VISIBLE
+            } else {
+                binding.tvQuickShareCheckIcon.visibility = View.INVISIBLE
+            }
+
+            itemView.setOnClickListener {
+
+                // if user select different options then only update the permission
+                if (!quickPermissionModel.isSelected) {
+                    onPermissionChangeListener.onPermissionChanged(adapterPosition)
+                } else {
+                    // dismiss sheet on selection of same permission
+                    onPermissionChangeListener.onDismissSheet()
+                }
+            }
+        }
+
+        interface OnPermissionChangeListener {
+            fun onPermissionChanged(position: Int)
+            fun onDismissSheet()
+        }
+    }
+}

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

@@ -3,8 +3,10 @@
  * Nextcloud Android client application
  * Nextcloud Android client application
  *
  *
  * @author Tobias Kaminsky
  * @author Tobias Kaminsky
+ * @author TSI-mc
  * Copyright (C) 2020 Tobias Kaminsky
  * Copyright (C) 2020 Tobias Kaminsky
  * Copyright (C) 2020 Nextcloud GmbH
  * Copyright (C) 2020 Nextcloud GmbH
+ * Copyright (C) 2021 TSI-mc
  *
  *
  * This program is free software: you can redistribute it and/or modify
  * 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
  * it under the terms of the GNU Affero General Public License as published by
@@ -23,6 +25,8 @@
 package com.owncloud.android.ui.adapter;
 package com.owncloud.android.ui.adapter;
 
 
 import android.content.Context;
 import android.content.Context;
+import android.text.TextUtils;
+import android.util.Log;
 import android.view.View;
 import android.view.View;
 import android.widget.ImageView;
 import android.widget.ImageView;
 
 
@@ -31,6 +35,7 @@ import com.owncloud.android.R;
 import com.owncloud.android.databinding.FileDetailsShareShareItemBinding;
 import com.owncloud.android.databinding.FileDetailsShareShareItemBinding;
 import com.owncloud.android.lib.resources.shares.OCShare;
 import com.owncloud.android.lib.resources.shares.OCShare;
 import com.owncloud.android.ui.TextDrawable;
 import com.owncloud.android.ui.TextDrawable;
+import com.owncloud.android.ui.fragment.util.SharingMenuHelper;
 import com.owncloud.android.utils.DisplayUtils;
 import com.owncloud.android.utils.DisplayUtils;
 import com.owncloud.android.utils.theme.ThemeAvatarUtils;
 import com.owncloud.android.utils.theme.ThemeAvatarUtils;
 
 
@@ -105,13 +110,26 @@ class ShareViewHolder extends RecyclerView.ViewHolder {
         if (share.getShareWith().equalsIgnoreCase(userId) || share.getUserId().equalsIgnoreCase(userId)) {
         if (share.getShareWith().equalsIgnoreCase(userId) || share.getUserId().equalsIgnoreCase(userId)) {
             binding.overflowMenu.setVisibility(View.VISIBLE);
             binding.overflowMenu.setVisibility(View.VISIBLE);
 
 
+            String permissionName = SharingMenuHelper.getPermissionName(context, share);
+            setPermissionName(permissionName);
+
             // bind listener to edit privileges
             // bind listener to edit privileges
-            binding.overflowMenu.setOnClickListener(v -> listener.showUserOverflowMenu(share, binding.overflowMenu));
+            binding.overflowMenu.setOnClickListener(v -> listener.showSharingMenuActionSheet(share));
+            binding.shareNameLayout.setOnClickListener(v -> listener.showPermissionsDialog(share));
         } else {
         } else {
             binding.overflowMenu.setVisibility(View.GONE);
             binding.overflowMenu.setVisibility(View.GONE);
         }
         }
     }
     }
 
 
+    private void setPermissionName(String permissionName) {
+        if (!TextUtils.isEmpty(permissionName)) {
+            binding.permissionName.setText(permissionName);
+            binding.permissionName.setVisibility(View.VISIBLE);
+        } else {
+            binding.permissionName.setVisibility(View.GONE);
+        }
+    }
+
     private void setImage(ImageView avatar, String name, @DrawableRes int fallback) {
     private void setImage(ImageView avatar, String name, @DrawableRes int fallback) {
         try {
         try {
             avatar.setImageDrawable(TextDrawable.createNamedAvatar(name, avatarRadiusDimension));
             avatar.setImageDrawable(TextDrawable.createNamedAvatar(name, avatarRadiusDimension));

+ 5 - 5
src/main/java/com/owncloud/android/ui/adapter/ShareeListAdapterListener.java

@@ -3,8 +3,10 @@
  * Nextcloud Android client application
  * Nextcloud Android client application
  *
  *
  * @author Tobias Kaminsky
  * @author Tobias Kaminsky
+ * @author TSI-mc
  * Copyright (C) 2020 Tobias Kaminsky
  * Copyright (C) 2020 Tobias Kaminsky
  * Copyright (C) 2020 Nextcloud GmbH
  * Copyright (C) 2020 Nextcloud GmbH
+ * Copyright (C) 2021 TSI-mc
  *
  *
  * This program is free software: you can redistribute it and/or modify
  * 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
  * it under the terms of the GNU Affero General Public License as published by
@@ -22,17 +24,13 @@
 
 
 package com.owncloud.android.ui.adapter;
 package com.owncloud.android.ui.adapter;
 
 
-import android.widget.ImageView;
-
 import com.nextcloud.client.account.User;
 import com.nextcloud.client.account.User;
 import com.owncloud.android.lib.resources.shares.OCShare;
 import com.owncloud.android.lib.resources.shares.OCShare;
 
 
 public interface ShareeListAdapterListener {
 public interface ShareeListAdapterListener {
     void copyLink(OCShare share);
     void copyLink(OCShare share);
 
 
-    void showLinkOverflowMenu(OCShare publicShare, ImageView overflowMenuShareLink);
-
-    void showUserOverflowMenu(OCShare share, ImageView overflowMenu);
+    void showSharingMenuActionSheet(OCShare share);
 
 
     void copyInternalLink();
     void copyInternalLink();
 
 
@@ -40,5 +38,7 @@ public interface ShareeListAdapterListener {
 
 
     void requestPasswordForShare(OCShare share, boolean askForPassword);
     void requestPasswordForShare(OCShare share, boolean askForPassword);
 
 
+    void showPermissionsDialog(OCShare share);
+
     void showProfileBottomSheet(User user, String shareWith);
     void showProfileBottomSheet(User user, String shareWith);
 }
 }

+ 23 - 19
src/main/java/com/owncloud/android/ui/dialog/ExpirationDatePickerDialogFragment.java

@@ -3,8 +3,10 @@
  *
  *
  *   @author David A. Velasco
  *   @author David A. Velasco
  *   @author Andy Scherzinger
  *   @author Andy Scherzinger
+ *   @author TSI-mc
  *   Copyright (C) 2015 ownCloud Inc.
  *   Copyright (C) 2015 ownCloud Inc.
  *   Copyright (C) 2018 Andy Scherzinger
  *   Copyright (C) 2018 Andy Scherzinger
+ *   Copyright (C) 2018 TSI-mc
  *
  *
  *   This program is free software: you can redistribute it and/or modify
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License version 2,
  *   it under the terms of the GNU General Public License version 2,
@@ -29,9 +31,6 @@ import android.text.format.DateUtils;
 import android.widget.DatePicker;
 import android.widget.DatePicker;
 
 
 import com.owncloud.android.R;
 import com.owncloud.android.R;
-import com.owncloud.android.lib.resources.shares.OCShare;
-import com.owncloud.android.ui.activity.FileActivity;
-import com.owncloud.android.ui.helpers.FileOperationsHelper;
 import com.owncloud.android.utils.theme.ThemeColorUtils;
 import com.owncloud.android.utils.theme.ThemeColorUtils;
 
 
 import java.util.Calendar;
 import java.util.Calendar;
@@ -49,25 +48,19 @@ public class ExpirationDatePickerDialogFragment
     /** Tag for FragmentsManager */
     /** Tag for FragmentsManager */
     public static final String DATE_PICKER_DIALOG = "DATE_PICKER_DIALOG";
     public static final String DATE_PICKER_DIALOG = "DATE_PICKER_DIALOG";
 
 
-    /** Parameter constant for {@link OCShare} instance to set the expiration date */
-    private static final String ARG_SHARE = "SHARE";
-
     /** Parameter constant for date chosen initially */
     /** Parameter constant for date chosen initially */
     private static final String ARG_CHOSEN_DATE_IN_MILLIS = "CHOSEN_DATE_IN_MILLIS";
     private static final String ARG_CHOSEN_DATE_IN_MILLIS = "CHOSEN_DATE_IN_MILLIS";
 
 
-    /** Share to bind an expiration date */
-    private OCShare share;
+    private OnExpiryDateListener onExpiryDateListener;
 
 
     /**
     /**
      * Factory method to create new instances
      * Factory method to create new instances
      *
      *
-     * @param share              share to bind an expiration date
      * @param chosenDateInMillis Date chosen when the dialog appears
      * @param chosenDateInMillis Date chosen when the dialog appears
      * @return New dialog instance
      * @return New dialog instance
      */
      */
-    public static ExpirationDatePickerDialogFragment newInstance(@NonNull OCShare share, long chosenDateInMillis) {
+    public static ExpirationDatePickerDialogFragment newInstance(long chosenDateInMillis) {
         Bundle arguments = new Bundle();
         Bundle arguments = new Bundle();
-        arguments.putParcelable(ARG_SHARE, share);
         arguments.putLong(ARG_CHOSEN_DATE_IN_MILLIS, chosenDateInMillis);
         arguments.putLong(ARG_CHOSEN_DATE_IN_MILLIS, chosenDateInMillis);
 
 
         ExpirationDatePickerDialogFragment dialog = new ExpirationDatePickerDialogFragment();
         ExpirationDatePickerDialogFragment dialog = new ExpirationDatePickerDialogFragment();
@@ -75,6 +68,10 @@ public class ExpirationDatePickerDialogFragment
         return dialog;
         return dialog;
     }
     }
 
 
+    public void setOnExpiryDateListener(OnExpiryDateListener onExpiryDateListener) {
+        this.onExpiryDateListener = onExpiryDateListener;
+    }
+
     /**
     /**
      * {@inheritDoc}
      * {@inheritDoc}
      *
      *
@@ -83,8 +80,6 @@ public class ExpirationDatePickerDialogFragment
     @Override
     @Override
     @NonNull
     @NonNull
     public Dialog onCreateDialog(Bundle savedInstanceState) {
     public Dialog onCreateDialog(Bundle savedInstanceState) {
-        // Load arguments
-        share = requireArguments().getParcelable(ARG_SHARE);
 
 
         // Chosen date received as an argument must be later than tomorrow ; default to tomorrow in other case
         // Chosen date received as an argument must be later than tomorrow ; default to tomorrow in other case
         final Calendar chosenDate = Calendar.getInstance();
         final Calendar chosenDate = Calendar.getInstance();
@@ -101,14 +96,18 @@ public class ExpirationDatePickerDialogFragment
             chosenDate.get(Calendar.MONTH),
             chosenDate.get(Calendar.MONTH),
             chosenDate.get(Calendar.DAY_OF_MONTH)
             chosenDate.get(Calendar.DAY_OF_MONTH)
         );
         );
-        dialog.setButton(
+
+        //show unset button only when date is already selected
+        if (chosenDateInMillis > 0) {
+            dialog.setButton(
                 Dialog.BUTTON_NEUTRAL,
                 Dialog.BUTTON_NEUTRAL,
                 getText(R.string.share_via_link_unset_password),
                 getText(R.string.share_via_link_unset_password),
                 (dialog1, which) -> {
                 (dialog1, which) -> {
-                    if (share != null) {
-                        ((FileActivity) requireActivity()).getFileOperationsHelper().setExpirationDateToShare(share, -1);
+                    if (onExpiryDateListener != null) {
+                        onExpiryDateListener.onDateUnSet();
                     }
                     }
                 });
                 });
+        }
 
 
         dialog.show();
         dialog.show();
         dialog.getButton(DatePickerDialog.BUTTON_NEUTRAL).setTextColor(ThemeColorUtils.primaryColor(getContext(), true));
         dialog.getButton(DatePickerDialog.BUTTON_NEUTRAL).setTextColor(ThemeColorUtils.primaryColor(getContext(), true));
@@ -144,9 +143,14 @@ public class ExpirationDatePickerDialogFragment
         chosenDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
         chosenDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
         long chosenDateInMillis = chosenDate.getTimeInMillis();
         long chosenDateInMillis = chosenDate.getTimeInMillis();
 
 
-        FileOperationsHelper operationsHelper = ((FileActivity) requireActivity()).getFileOperationsHelper();
-        if (share != null) {
-            operationsHelper.setExpirationDateToShare(share, chosenDateInMillis);
+        if (onExpiryDateListener != null) {
+            onExpiryDateListener.onDateSet(year, monthOfYear, dayOfMonth, chosenDateInMillis);
         }
         }
     }
     }
+
+    public interface OnExpiryDateListener {
+        void onDateSet(int year, int monthOfYear, int dayOfMonth, long chosenDateInMillis);
+
+        void onDateUnSet();
+    }
 }
 }

+ 54 - 0
src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java

@@ -5,11 +5,13 @@
  *   @author David A. Velasco
  *   @author David A. Velasco
  *   @author Andy Scherzinger
  *   @author Andy Scherzinger
  *   @author Chris Narkiewicz
  *   @author Chris Narkiewicz
+ *   @author TSI-mc
  *
  *
  *   Copyright (C) 2011 Bartek Przybylski
  *   Copyright (C) 2011 Bartek Przybylski
  *   Copyright (C) 2016 ownCloud Inc.
  *   Copyright (C) 2016 ownCloud Inc.
  *   Copyright (C) 2018 Andy Scherzinger
  *   Copyright (C) 2018 Andy Scherzinger
  *   Copyright (C) 2019 Chris Narkiewicz <hello@ezaquarii.com>
  *   Copyright (C) 2019 Chris Narkiewicz <hello@ezaquarii.com>
+ *   Copyright (C) 2021 TSI-mc
  *
  *
  *   This program is free software: you can redistribute it and/or modify
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License version 2,
  *   it under the terms of the GNU General Public License version 2,
@@ -37,6 +39,7 @@ import android.view.ViewGroup;
 import android.widget.PopupMenu;
 import android.widget.PopupMenu;
 import android.widget.ProgressBar;
 import android.widget.ProgressBar;
 
 
+import com.google.android.material.floatingactionbutton.FloatingActionButton;
 import com.google.android.material.tabs.TabLayout;
 import com.google.android.material.tabs.TabLayout;
 import com.nextcloud.client.account.User;
 import com.nextcloud.client.account.User;
 import com.nextcloud.client.account.UserAccountManager;
 import com.nextcloud.client.account.UserAccountManager;
@@ -54,6 +57,8 @@ import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
 import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
 import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
 import com.owncloud.android.lib.common.network.OnDatatransferProgressListener;
 import com.owncloud.android.lib.common.network.OnDatatransferProgressListener;
 import com.owncloud.android.lib.common.utils.Log_OC;
 import com.owncloud.android.lib.common.utils.Log_OC;
+import com.owncloud.android.lib.resources.shares.OCShare;
+import com.owncloud.android.lib.resources.shares.ShareType;
 import com.owncloud.android.ui.activity.FileDisplayActivity;
 import com.owncloud.android.ui.activity.FileDisplayActivity;
 import com.owncloud.android.ui.activity.ToolbarActivity;
 import com.owncloud.android.ui.activity.ToolbarActivity;
 import com.owncloud.android.ui.adapter.FileDetailTabAdapter;
 import com.owncloud.android.ui.adapter.FileDetailTabAdapter;
@@ -665,6 +670,55 @@ public class FileDetailFragment extends FileFragment implements OnClickListener,
         binding.emptyList.emptyListIcon.setVisibility(View.VISIBLE);
         binding.emptyList.emptyListIcon.setVisibility(View.VISIBLE);
     }
     }
 
 
+    /**
+     * open the sharing process fragment for creating new share
+     * @param shareeName
+     * @param shareType
+     */
+    public void initiateSharingProcess(String shareeName, ShareType shareType) {
+        requireActivity().getSupportFragmentManager().beginTransaction().add(R.id.sharing_frame_container,
+                                                                             FileDetailsSharingProcessFragment.newInstance(getFile(),
+                                                                                                                           shareeName,
+                                                                                                                           shareType),
+                                                                             FileDetailsSharingProcessFragment.TAG)
+            .commit();
+
+        showHideFragmentView(true);
+    }
+
+    /**
+     * method will handle the views need to be hidden when sharing process fragment shows
+     * @param isFragmentReplaced
+     */
+    public void showHideFragmentView(boolean isFragmentReplaced) {
+        binding.tabLayout.setVisibility(isFragmentReplaced ? View.GONE : View.VISIBLE);
+        binding.pager.setVisibility(isFragmentReplaced ? View.GONE : View.VISIBLE);
+        binding.sharingFrameContainer.setVisibility(isFragmentReplaced ? View.VISIBLE : View.GONE);
+        FloatingActionButton mFabMain = requireActivity().findViewById(R.id.fab_main);
+        if (isFragmentReplaced) {
+            mFabMain.hide();
+        } else {
+            mFabMain.show();
+        }
+    }
+
+    /**
+     * open the new sharing screen process to modify the created share
+     * @param share
+     * @param screenTypePermission
+     * @param isReshareShown
+     * @param isExpiryDateShown
+     */
+    public void editExistingShare(OCShare share, int screenTypePermission, boolean isReshareShown,
+                                  boolean isExpiryDateShown) {
+        requireActivity().getSupportFragmentManager().beginTransaction().add(R.id.sharing_frame_container,
+                                                                             FileDetailsSharingProcessFragment.newInstance(share, screenTypePermission, isReshareShown,
+                                                                                                                           isExpiryDateShown),
+                                                                             FileDetailsSharingProcessFragment.TAG)
+            .commit();
+        showHideFragmentView(true);
+    }
+
     /**
     /**
      * Helper class responsible for updating the progress bar shown for file downloading.
      * Helper class responsible for updating the progress bar shown for file downloading.
      */
      */

+ 81 - 286
src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java

@@ -3,9 +3,11 @@
  *
  *
  * @author Andy Scherzinger
  * @author Andy Scherzinger
  * @author Chris Narkiewicz <hello@ezaquarii.com>
  * @author Chris Narkiewicz <hello@ezaquarii.com>
+ * @author TSI-mc
  *
  *
  * Copyright (C) 2018 Andy Scherzinger
  * Copyright (C) 2018 Andy Scherzinger
  * Copyright (C) 2020 Chris Narkiewicz <hello@ezaquarii.com>
  * Copyright (C) 2020 Chris Narkiewicz <hello@ezaquarii.com>
+ * Copyright (C) 2020 TSI-mc
  *
  *
  * This program is free software; you can redistribute it and/or
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
  * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
@@ -24,19 +26,16 @@
 package com.owncloud.android.ui.fragment;
 package com.owncloud.android.ui.fragment;
 
 
 import android.accounts.AccountManager;
 import android.accounts.AccountManager;
+import android.app.AlertDialog;
 import android.app.SearchManager;
 import android.app.SearchManager;
 import android.content.Context;
 import android.content.Context;
-import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.os.Bundle;
 import android.text.InputType;
 import android.text.InputType;
 import android.text.TextUtils;
 import android.text.TextUtils;
 import android.view.LayoutInflater;
 import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuItem;
 import android.view.View;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewGroup;
-import android.widget.ImageView;
 
 
 import com.nextcloud.client.account.User;
 import com.nextcloud.client.account.User;
 import com.nextcloud.client.account.UserAccountManager;
 import com.nextcloud.client.account.UserAccountManager;
@@ -49,7 +48,6 @@ import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.lib.common.OwnCloudAccount;
 import com.owncloud.android.lib.common.OwnCloudAccount;
 import com.owncloud.android.lib.common.operations.RemoteOperationResult;
 import com.owncloud.android.lib.common.operations.RemoteOperationResult;
 import com.owncloud.android.lib.resources.shares.OCShare;
 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.shares.ShareType;
 import com.owncloud.android.lib.resources.status.NextcloudVersion;
 import com.owncloud.android.lib.resources.status.NextcloudVersion;
 import com.owncloud.android.lib.resources.status.OCCapability;
 import com.owncloud.android.lib.resources.status.OCCapability;
@@ -59,17 +57,12 @@ import com.owncloud.android.ui.activity.FileDisplayActivity;
 import com.owncloud.android.ui.adapter.ShareeListAdapter;
 import com.owncloud.android.ui.adapter.ShareeListAdapter;
 import com.owncloud.android.ui.adapter.ShareeListAdapterListener;
 import com.owncloud.android.ui.adapter.ShareeListAdapterListener;
 import com.owncloud.android.ui.asynctasks.RetrieveHoverCardAsyncTask;
 import com.owncloud.android.ui.asynctasks.RetrieveHoverCardAsyncTask;
-import com.owncloud.android.ui.dialog.ExpirationDatePickerDialogFragment;
-import com.owncloud.android.ui.dialog.NoteDialogFragment;
-import com.owncloud.android.ui.dialog.RenamePublicShareDialogFragment;
 import com.owncloud.android.ui.dialog.SharePasswordDialogFragment;
 import com.owncloud.android.ui.dialog.SharePasswordDialogFragment;
 import com.owncloud.android.ui.fragment.util.FileDetailSharingFragmentHelper;
 import com.owncloud.android.ui.fragment.util.FileDetailSharingFragmentHelper;
-import com.owncloud.android.ui.fragment.util.SharingMenuHelper;
 import com.owncloud.android.ui.helpers.FileOperationsHelper;
 import com.owncloud.android.ui.helpers.FileOperationsHelper;
 import com.owncloud.android.utils.ClipboardUtil;
 import com.owncloud.android.utils.ClipboardUtil;
 import com.owncloud.android.utils.DisplayUtils;
 import com.owncloud.android.utils.DisplayUtils;
 import com.owncloud.android.utils.theme.ThemeToolbarUtils;
 import com.owncloud.android.utils.theme.ThemeToolbarUtils;
-import com.owncloud.android.utils.theme.ThemeUtils;
 
 
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.List;
@@ -79,23 +72,13 @@ import javax.inject.Inject;
 import androidx.annotation.NonNull;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 import androidx.annotation.VisibleForTesting;
-import androidx.appcompat.widget.PopupMenu;
 import androidx.appcompat.widget.SearchView;
 import androidx.appcompat.widget.SearchView;
 import androidx.fragment.app.Fragment;
 import androidx.fragment.app.Fragment;
 import androidx.recyclerview.widget.LinearLayoutManager;
 import androidx.recyclerview.widget.LinearLayoutManager;
 
 
-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,
 public class FileDetailSharingFragment extends Fragment implements ShareeListAdapterListener,
     DisplayUtils.AvatarGenerationListener,
     DisplayUtils.AvatarGenerationListener,
-    Injectable {
+    Injectable, FileDetailsSharingMenuBottomSheetActions, QuickSharingPermissionsBottomSheetDialog.QuickPermissionSharingBottomSheetActions {
 
 
     private static final String ARG_FILE = "FILE";
     private static final String ARG_FILE = "FILE";
     private static final String ARG_USER = "USER";
     private static final String ARG_USER = "USER";
@@ -111,6 +94,8 @@ public class FileDetailSharingFragment extends Fragment implements ShareeListAda
 
 
     private FileDetailsSharingFragmentBinding binding;
     private FileDetailsSharingFragmentBinding binding;
 
 
+    private OnEditShareListener onEditShareListener;
+
     @Inject UserAccountManager accountManager;
     @Inject UserAccountManager accountManager;
 
 
     @Inject ClientFactory clientFactory;
     @Inject ClientFactory clientFactory;
@@ -198,6 +183,11 @@ public class FileDetailSharingFragment extends Fragment implements ShareeListAda
         if (!(getActivity() instanceof FileActivity)) {
         if (!(getActivity() instanceof FileActivity)) {
             throw new IllegalArgumentException("Calling activity must be of type FileActivity");
             throw new IllegalArgumentException("Calling activity must be of type FileActivity");
         }
         }
+        try {
+            onEditShareListener = (OnEditShareListener) context;
+        } catch (Exception ignored) {
+            throw new IllegalArgumentException("Calling activity must implement the interface", ignored);
+        }
     }
     }
 
 
     private void setupView() {
     private void setupView() {
@@ -307,239 +297,24 @@ public class FileDetailSharingFragment extends Fragment implements ShareeListAda
         }
         }
     }
     }
 
 
-    @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}
+     * show share action bottom sheet
      *
      *
-     * @param menu  the menu of the sharee/shared file
-     * @param share the shared file
+     * @param share
      */
      */
+    @Override
     @VisibleForTesting
     @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);
-
-        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());
-    }
-
-    public void showLinkOverflowMenu(OCShare publicShare, ImageView overflowMenuShareLink) {
-        if (ThemeUtils.themingEnabled(requireContext())) {
-            // use grey as fallback for elements where custom theming is not available
-            requireContext().getTheme().applyStyle(R.style.FallbackThemingTheme, true);
-        }
-
-        PopupMenu popup = new PopupMenu(requireContext(), overflowMenuShareLink);
-        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();
-    }
-
-    @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));
-
-        SharingMenuHelper.setupPasswordMenuItem(menu.findItem(R.id.action_password),
-                                                publicShare.isPasswordProtected());
-
-        SharingMenuHelper.setupExpirationDateMenuItem(menu.findItem(R.id.action_share_expiration_date),
-                                                      publicShare.getExpirationDate(),
-                                                      res);
-    }
-
-    @VisibleForTesting
-    public boolean isUploadAndEditingAllowed(OCShare share) {
-        if (share.getPermissions() == NO_PERMISSION) {
-            return false;
-        }
-
-        return (share.getPermissions() & MAXIMUM_PERMISSIONS_FOR_FOLDER) == MAXIMUM_PERMISSIONS_FOR_FOLDER;
-    }
-
-    @VisibleForTesting
-    public boolean isReadOnly(OCShare share) {
-        if (share.getPermissions() == NO_PERMISSION) {
-            return false;
-        }
-
-        return (share.getPermissions() & ~SHARE_PERMISSION_FLAG) == READ_PERMISSION_FLAG;
+    public void showSharingMenuActionSheet(OCShare share) {
+        new FileDetailSharingMenuBottomSheetDialog(fileActivity, this, share).show();
     }
     }
 
 
-    @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) {
-        int itemId = item.getItemId();
-
-        if (itemId == R.id.allow_editing || itemId == R.id.allow_creating || itemId == R.id.allow_deleting || itemId == 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;
-        } else if (itemId == 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;
-        } else if (itemId == R.id.action_expiration_date) {
-            ExpirationDatePickerDialogFragment dialog = ExpirationDatePickerDialogFragment
-                .newInstance(share, share.getExpirationDate());
-            dialog.show(fileActivity.getSupportFragmentManager(),
-                        ExpirationDatePickerDialogFragment.DATE_PICKER_DIALOG);
-            return true;
-        } else if (itemId == R.id.action_share_send_note) {
-            NoteDialogFragment dialog = NoteDialogFragment.newInstance(share);
-            dialog.show(fileActivity.getSupportFragmentManager(), NoteDialogFragment.NOTE_FRAGMENT);
-            return true;
-        }
-
-        return true;
-    }
-
-    public boolean linkOptionsItemSelected(MenuItem item, OCShare publicShare) {
-        int itemId = item.getItemId();
-
-        if (itemId == R.id.link_share_read_only) {
-            item.setChecked(true);
-            fileOperationsHelper.setPermissionsToShare(publicShare, READ_PERMISSION_FLAG);
-            return true;
-        } else if (itemId == 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;
-        } else if (itemId == R.id.link_share_file_drop) {
-            item.setChecked(true);
-            fileOperationsHelper.setPermissionsToShare(publicShare, CREATE_PERMISSION_FLAG);
-            return true;
-        } else if (itemId == R.id.allow_editing) {
-            if (file.isSharedViaLink()) {
-                item.setChecked(!item.isChecked());
-                fileOperationsHelper.setUploadPermissionsToPublicShare(publicShare, item.isChecked());
-            }
-            return true;
-        } else if (itemId == R.id.action_hide_file_download) {
-            item.setChecked(!item.isChecked());
-            fileOperationsHelper.setHideFileDownloadPermissionsToPublicShare(publicShare, item.isChecked());
-            return true;
-        } else if (itemId == R.id.action_password) {
-            requestPasswordForShare(publicShare,
-                                    capabilities.getFilesSharingPublicAskForOptionalPassword().isTrue());
-            return true;
-        } else if (itemId == R.id.action_share_expiration_date) {
-            ExpirationDatePickerDialogFragment expirationDialog = ExpirationDatePickerDialogFragment
-                .newInstance(publicShare, publicShare.getExpirationDate());
-            expirationDialog.show(fileActivity.getSupportFragmentManager(),
-                                  ExpirationDatePickerDialogFragment.DATE_PICKER_DIALOG);
-            return true;
-        } else if (itemId == R.id.action_share_send_link) {
-            if (file.isSharedViaLink() && !TextUtils.isEmpty(publicShare.getShareLink())) {
-                FileDisplayActivity.showShareLinkDialog(fileActivity, file, publicShare.getShareLink());
-            } else {
-                showSendLinkTo(publicShare);
-            }
-            return true;
-        } else if (itemId == R.id.action_share_send_note) {
-            NoteDialogFragment noteDialog = NoteDialogFragment.newInstance(publicShare);
-            noteDialog.show(fileActivity.getSupportFragmentManager(), NoteDialogFragment.NOTE_FRAGMENT);
-            return true;
-        } else if (itemId == R.id.action_edit_label) {
-            RenamePublicShareDialogFragment renameDialog = RenamePublicShareDialogFragment.newInstance(publicShare);
-            renameDialog.show(fileActivity.getSupportFragmentManager(),
-                              RenamePublicShareDialogFragment.RENAME_PUBLIC_SHARE_FRAGMENT);
-            return true;
-        } else if (itemId == R.id.action_unshare) {
-            fileOperationsHelper.unshareShare(file, publicShare);
-            return true;
-        } else if (itemId == R.id.action_add_another_public_share_link) {
-            createPublicShareLink();
-            return true;
-        }
-
-        return super.onOptionsItemSelected(item);
+    /**
+     * show quick sharing permission dialog
+     * @param share
+     */
+    @Override
+    public void showPermissionsDialog(OCShare share) {
+        new QuickSharingPermissionsBottomSheetDialog(fileActivity, this, share).show();
     }
     }
 
 
     /**
     /**
@@ -571,27 +346,6 @@ public class FileDetailSharingFragment extends Fragment implements ShareeListAda
         fileOperationsHelper.unshareShare(file, share);
         fileOperationsHelper.unshareShare(file, share);
     }
     }
 
 
-    private int updatePermissionsToShare(OCShare share,
-                                         boolean canReshare,
-                                         boolean canEdit,
-                                         boolean canEditCreate,
-                                         boolean canEditDelete) {
-        SharePermissionsBuilder spb = new SharePermissionsBuilder();
-        spb.setSharePermission(canReshare);
-
-        if (file.isFolder()) {
-            spb.setCreatePermission(canEditCreate)
-                .setDeletePermission(canEditDelete);
-        } else {
-            spb.setUpdatePermission(canEdit);
-        }
-        int permissions = spb.build();
-
-        fileOperationsHelper.setPermissionsToShare(share, permissions);
-
-        return permissions;
-    }
-
     /**
     /**
      * Starts a dialog that requests a password to the user to protect a share link.
      * Starts a dialog that requests a password to the user to protect a share link.
      *
      *
@@ -627,8 +381,8 @@ 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.
+     * 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 refreshSharesFromDB() {
     public void refreshSharesFromDB() {
         ShareeListAdapter adapter = (ShareeListAdapter) binding.sharesList.getAdapter();
         ShareeListAdapter adapter = (ShareeListAdapter) binding.sharesList.getAdapter();
@@ -698,30 +452,71 @@ public class FileDetailSharingFragment extends Fragment implements ShareeListAda
             capabilities != null && capabilities.getFilesSharingResharing().isFalse();
             capabilities != null && capabilities.getFilesSharingResharing().isFalse();
     }
     }
 
 
-    private boolean canEdit(OCShare share) {
-        return (share.getPermissions() &
-            (CREATE_PERMISSION_FLAG | UPDATE_PERMISSION_FLAG | DELETE_PERMISSION_FLAG)) > 0;
+    @VisibleForTesting
+    public void search(String query) {
+        SearchView searchView = getView().findViewById(R.id.searchView);
+        searchView.setQuery(query, true);
     }
     }
 
 
-    private boolean canCreate(OCShare share) {
-        return (share.getPermissions() & CREATE_PERMISSION_FLAG) > 0;
+    public OCFile getFile() {
+        return file;
     }
     }
 
 
-    private boolean canDelete(OCShare share) {
-        return (share.getPermissions() & DELETE_PERMISSION_FLAG) > 0;
+    @Override
+    public void openIn(OCShare share) {
+        fileOperationsHelper.sendShareFile(file);
     }
     }
 
 
-    private boolean canReshare(OCShare share) {
-        return (share.getPermissions() & SHARE_PERMISSION_FLAG) > 0;
+    @Override
+    public void advancedPermissions(OCShare share) {
+        modifyExistingShare(share, FileDetailsSharingProcessFragment.SCREEN_TYPE_PERMISSION);
     }
     }
 
 
-    @VisibleForTesting
-    public void search(String query) {
-        SearchView searchView = getView().findViewById(R.id.searchView);
-        searchView.setQuery(query, true);
+
+    @Override
+    public void sendNewEmail(OCShare share) {
+        modifyExistingShare(share, FileDetailsSharingProcessFragment.SCREEN_TYPE_NOTE);
     }
     }
 
 
-    public OCFile getFile() {
-        return file;
+    @Override
+    public void unShare(OCShare share) {
+        unshareWith(share);
+        ShareeListAdapter adapter = (ShareeListAdapter) binding.sharesList.getAdapter();
+        if (adapter == null) {
+            DisplayUtils.showSnackMessage(getView(), getString(R.string.failed_update_ui));
+            return;
+        }
+        adapter.remove(share);
+    }
+
+    @Override
+    public void sendLink(OCShare share) {
+        if (file.isSharedViaLink() && !TextUtils.isEmpty(share.getShareLink())) {
+            FileDisplayActivity.showShareLinkDialog(fileActivity, file, share.getShareLink());
+        } else {
+            showSendLinkTo(share);
+        }
+    }
+
+    @Override
+    public void addAnotherLink(OCShare share) {
+        createPublicShareLink();
+    }
+
+    private void modifyExistingShare(OCShare share, int screenTypePermission) {
+        onEditShareListener.editExistingShare(share, screenTypePermission, !isReshareForbidden(share),
+                                              capabilities.getVersion().isNewerOrEqual(OwnCloudVersion.nextcloud_18));
+    }
+
+    @Override
+    public void onQuickPermissionChanged(OCShare share, int permission) {
+        fileOperationsHelper.setPermissionsToShare(share, permission);
+    }
+
+    public interface OnEditShareListener {
+        void editExistingShare(OCShare share, int screenTypePermission, boolean isReshareShown,
+                               boolean isExpiryDateShown);
+
+        void onShareProcessClosed();
     }
     }
 }
 }

+ 124 - 0
src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingMenuBottomSheetDialog.java

@@ -0,0 +1,124 @@
+/*
+ * Nextcloud Android client application
+ *
+ * @author TSI-mc
+ * Copyright (C) 2021 TSI-mc
+ * Copyright (C) 2021 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.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.google.android.material.bottomsheet.BottomSheetBehavior;
+import com.google.android.material.bottomsheet.BottomSheetDialog;
+import com.owncloud.android.databinding.FileDetailsSharingMenuBottomSheetFragmentBinding;
+import com.owncloud.android.lib.resources.shares.OCShare;
+import com.owncloud.android.lib.resources.shares.ShareType;
+import com.owncloud.android.ui.activity.FileActivity;
+
+/**
+ * File Details Sharing option menus {@link android.app.Dialog} styled as a bottom sheet for main actions.
+ */
+public class FileDetailSharingMenuBottomSheetDialog extends BottomSheetDialog {
+    private FileDetailsSharingMenuBottomSheetFragmentBinding binding;
+    private final FileDetailsSharingMenuBottomSheetActions actions;
+    private final OCShare ocShare;
+
+    public FileDetailSharingMenuBottomSheetDialog(FileActivity fileActivity,
+                                                  FileDetailsSharingMenuBottomSheetActions actions,
+                                                  OCShare ocShare) {
+        super(fileActivity);
+        this.actions = actions;
+        this.ocShare = ocShare;
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        binding = FileDetailsSharingMenuBottomSheetFragmentBinding.inflate(getLayoutInflater());
+        setContentView(binding.getRoot());
+
+        if (getWindow() != null) {
+            getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+        }
+
+        updateUI();
+
+        setupClickListener();
+
+        setOnShowListener(d ->
+                              BottomSheetBehavior.from((View) binding.getRoot().getParent())
+                                  .setPeekHeight(binding.getRoot().getMeasuredHeight())
+                         );
+    }
+
+    private void updateUI() {
+        if (ocShare.isFolder()) {
+            binding.menuShareOpenIn.setVisibility(View.GONE);
+        } else {
+            binding.menuShareOpenIn.setVisibility(View.VISIBLE);
+        }
+
+        if (ocShare.getShareType() == ShareType.PUBLIC_LINK) {
+            binding.menuShareAddAnotherLink.setVisibility(View.VISIBLE);
+            binding.menuShareSendLink.setVisibility(View.VISIBLE);
+        } else {
+            binding.menuShareAddAnotherLink.setVisibility(View.GONE);
+            binding.menuShareSendLink.setVisibility(View.GONE);
+        }
+    }
+
+    private void setupClickListener() {
+        binding.menuShareOpenIn.setOnClickListener(v -> {
+            actions.openIn(ocShare);
+            dismiss();
+        });
+
+        binding.menuShareAdvancedPermissions.setOnClickListener(v -> {
+            actions.advancedPermissions(ocShare);
+            dismiss();
+        });
+
+        binding.menuShareSendNewEmail.setOnClickListener(v -> {
+            actions.sendNewEmail(ocShare);
+            dismiss();
+        });
+
+        binding.menuShareUnshare.setOnClickListener(v -> {
+            actions.unShare(ocShare);
+            dismiss();
+        });
+
+        binding.menuShareSendLink.setOnClickListener(v -> {
+            actions.sendLink(ocShare);
+            dismiss();
+        });
+
+        binding.menuShareAddAnotherLink.setOnClickListener(v -> {
+            actions.addAnotherLink(ocShare);
+            dismiss();
+        });
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        binding = null;
+    }
+}

+ 61 - 0
src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingMenuBottomSheetActions.java

@@ -0,0 +1,61 @@
+/*
+ * Nextcloud Android client application
+ *
+ * @author TSI-mc
+ * Copyright (C) 2021 TSI-mc
+ * Copyright (C) 2021 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 com.owncloud.android.lib.resources.shares.OCShare;
+import com.owncloud.android.lib.resources.shares.ShareType;
+
+/**
+ * Actions interface to be implemented by any class that makes use of {@link FileDetailSharingMenuBottomSheetDialog}.
+ */
+public interface FileDetailsSharingMenuBottomSheetActions {
+    /**
+     * open sharing options only applicable for files
+     */
+    void openIn(OCShare share);
+
+    /**
+     * open advanced permission for selected share
+     */
+    void advancedPermissions(OCShare share);
+
+    /**
+     * open note screen to send new email
+     */
+    void sendNewEmail(OCShare share);
+
+    /**
+     * unshare the current share
+     */
+    void unShare(OCShare share);
+
+    /**
+     * send created link only valid for {@link ShareType#PUBLIC_LINK}
+     */
+    void sendLink(OCShare share);
+
+    /**
+     * create another link only valid for {@link ShareType#PUBLIC_LINK}
+     */
+    void addAnotherLink(OCShare share);
+}

+ 538 - 0
src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingProcessFragment.kt

@@ -0,0 +1,538 @@
+/*
+ * Nextcloud Android client application
+ *
+ * @author TSI-mc
+ * Copyright (C) 2021 TSI-mc
+ * Copyright (C) 2021 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.content.Context
+import android.os.Bundle
+import android.text.TextUtils
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import com.owncloud.android.R
+import com.owncloud.android.databinding.FileDetailsSharingProcessFragmentBinding
+import com.owncloud.android.datamodel.OCFile
+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.ui.activity.FileActivity
+import com.owncloud.android.ui.dialog.ExpirationDatePickerDialogFragment
+import com.owncloud.android.ui.fragment.util.SharingMenuHelper
+import com.owncloud.android.ui.helpers.FileOperationsHelper
+import com.owncloud.android.utils.ClipboardUtil
+import com.owncloud.android.utils.DisplayUtils
+import java.text.SimpleDateFormat
+import java.util.Date
+
+/**
+ * Fragment class to show share permission options, set expiration date, change label, set password, send note
+ *
+ * This fragment handles following:
+ * 1. This will be shown while creating new internal and external share. So that user can set every share
+ * configuration at one time.
+ * 2. This will handle both Advanced Permissions and Send New Email functionality for existing shares to modify them.
+ */
+@Suppress("TooManyFunctions")
+class FileDetailsSharingProcessFragment : Fragment(), ExpirationDatePickerDialogFragment.OnExpiryDateListener {
+
+    companion object {
+        const val TAG = "FileDetailsSharingProcessFragment"
+        private const val ARG_OCFILE = "arg_sharing_oc_file"
+        private const val ARG_SHAREE_NAME = "arg_sharee_name"
+        private const val ARG_SHARE_TYPE = "arg_share_type"
+        private const val ARG_OCSHARE = "arg_ocshare"
+        private const val ARG_SCREEN_TYPE = "arg_screen_type"
+        private const val ARG_RESHARE_SHOWN = "arg_reshare_shown"
+        private const val ARG_EXP_DATE_SHOWN = "arg_exp_date_shown"
+
+        // types of screens to be displayed
+        const val SCREEN_TYPE_PERMISSION = 1 // permissions screen
+        const val SCREEN_TYPE_NOTE = 2 // note screen
+
+        /**
+         * fragment instance to be called while creating new share for internal and external share
+         */
+        @JvmStatic
+        fun newInstance(file: OCFile, shareeName: String, shareType: ShareType): FileDetailsSharingProcessFragment {
+            val args = Bundle()
+            args.putParcelable(ARG_OCFILE, file)
+            args.putSerializable(ARG_SHARE_TYPE, shareType)
+            args.putString(ARG_SHAREE_NAME, shareeName)
+            val fragment = FileDetailsSharingProcessFragment()
+            fragment.arguments = args
+            return fragment
+        }
+
+        /**
+         * fragment instance to be called while modifying existing share information
+         */
+        @JvmStatic
+        fun newInstance(share: OCShare, screenType: Int, isReshareShown: Boolean, isExpirationDateShown: Boolean):
+            FileDetailsSharingProcessFragment {
+            val args = Bundle()
+            args.putParcelable(ARG_OCSHARE, share)
+            args.putInt(ARG_SCREEN_TYPE, screenType)
+            args.putBoolean(ARG_RESHARE_SHOWN, isReshareShown)
+            args.putBoolean(ARG_EXP_DATE_SHOWN, isExpirationDateShown)
+            val fragment = FileDetailsSharingProcessFragment()
+            fragment.arguments = args
+            return fragment
+        }
+    }
+
+    private lateinit var onEditShareListener: FileDetailSharingFragment.OnEditShareListener
+
+    private lateinit var binding: FileDetailsSharingProcessFragmentBinding
+    private var fileOperationsHelper: FileOperationsHelper? = null
+    private var fileActivity: FileActivity? = null
+
+    private var file: OCFile? = null // file to be share
+    private var shareeName: String? = null
+    private lateinit var shareType: ShareType
+    private var shareProcessStep = SCREEN_TYPE_PERMISSION // default screen type
+    private var permission = OCShare.NO_PERMISSION // no permission
+    private var chosenExpDateInMills: Long = -1 // for no expiry date
+
+    private var share: OCShare? = null
+    private var isReshareShown: Boolean = true // show or hide reshare option
+    private var isExpDateShown: Boolean = true // show or hide expiry date option
+
+    override fun onAttach(context: Context) {
+        super.onAttach(context)
+        try {
+            onEditShareListener = context as FileDetailSharingFragment.OnEditShareListener
+        } catch (e: ClassCastException) {
+            throw IllegalStateException("Calling activity must implement the interface")
+        }
+    }
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        arguments?.let {
+            file = it.getParcelable(ARG_OCFILE)
+            shareeName = it.getString(ARG_SHAREE_NAME)
+            if (it.containsKey(ARG_SHARE_TYPE)) {
+                shareType = it.getSerializable(ARG_SHARE_TYPE) as ShareType
+            }
+            share = it.getParcelable(ARG_OCSHARE)
+            shareProcessStep = it.getInt(ARG_SCREEN_TYPE, SCREEN_TYPE_PERMISSION)
+            isReshareShown = it.getBoolean(ARG_RESHARE_SHOWN, true)
+            isExpDateShown = it.getBoolean(ARG_EXP_DATE_SHOWN, true)
+        }
+
+        fileActivity = activity as FileActivity?
+
+        requireNotNull(fileActivity) { "FileActivity may not be null" }
+    }
+
+    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+        binding = FileDetailsSharingProcessFragmentBinding.inflate(inflater, container, false)
+        fileOperationsHelper = fileActivity?.fileOperationsHelper
+        return binding.root
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+        if (shareProcessStep == SCREEN_TYPE_PERMISSION) {
+            showShareProcessFirst()
+        } else {
+            showShareProcessSecond()
+        }
+        implementClickEvents()
+    }
+
+    private fun showShareProcessFirst() {
+        binding.shareProcessGroupOne.visibility = View.VISIBLE
+        binding.shareProcessGroupTwo.visibility = View.GONE
+
+        // set up UI for modifying share
+        if (share != null) {
+            if (share?.isFolder == true) {
+                updateViewForFolder()
+            } else {
+                updateViewForFile()
+            }
+
+            // read only / allow upload and editing / file drop
+            if (SharingMenuHelper.isUploadAndEditingAllowed(share)) {
+                binding.shareProcessPermissionUploadEditing.isChecked = true
+            } else if (SharingMenuHelper.isFileDrop(share) && share?.isFolder == true) {
+                binding.shareProcessPermissionFileDrop.isChecked = true
+            } else if (SharingMenuHelper.isReadOnly(share)) {
+                binding.shareProcessPermissionReadOnly.isChecked = true
+            }
+
+            shareType = share?.shareType ?: ShareType.NO_SHARED
+            // show different text for link share and other shares
+            // because we have link to share in Public Link
+            if (shareType == ShareType.PUBLIC_LINK) {
+                binding.shareProcessBtnNext.text = requireContext().resources.getString(R.string.share_copy_link)
+            } else {
+                binding.shareProcessBtnNext.text = requireContext().resources.getString(R.string.common_confirm)
+            }
+            updateViewForShareType()
+            binding.shareProcessSetPasswordSwitch.isChecked = share?.isPasswordProtected == true
+            showPasswordInput(binding.shareProcessSetPasswordSwitch.isChecked)
+            updateExpirationDateView()
+            showExpirationDateInput(binding.shareProcessSetExpDateSwitch.isChecked)
+        }
+
+        // update UI for creating new share
+        else {
+            binding.shareProcessBtnNext.text = requireContext().resources.getString(R.string.common_next)
+            file.let {
+                if (file?.isFolder == true) {
+                    updateViewForFolder()
+                } else {
+                    updateViewForFile()
+                }
+                updateViewForShareType()
+            }
+            showPasswordInput(binding.shareProcessSetPasswordSwitch.isChecked)
+            showExpirationDateInput(binding.shareProcessSetExpDateSwitch.isChecked)
+        }
+
+        // show or hide expiry date
+        if (isExpDateShown) {
+            binding.shareProcessSetExpDateSwitch.visibility = View.VISIBLE
+        } else {
+            binding.shareProcessSetExpDateSwitch.visibility = View.GONE
+        }
+        shareProcessStep = SCREEN_TYPE_PERMISSION
+    }
+
+    /**
+     * method to update views on the basis of Share type
+     */
+    private fun updateViewForShareType() {
+        // external share
+        if (shareType == ShareType.EMAIL) {
+            binding.shareProcessChangeNameSwitch.visibility = View.GONE
+            binding.shareProcessChangeNameEt.visibility = View.GONE
+            updateViewForExternalAndLinkShare()
+        }
+        // link share
+        else if (shareType == ShareType.PUBLIC_LINK) {
+            updateViewForExternalAndLinkShare()
+            binding.shareProcessChangeNameSwitch.visibility = View.VISIBLE
+            if (share != null) {
+                binding.shareProcessChangeNameEt.setText(share?.label)
+                binding.shareProcessChangeNameSwitch.isChecked = !TextUtils.isEmpty(share?.label)
+            }
+            showChangeNameInput(binding.shareProcessChangeNameSwitch.isChecked)
+        }
+        // internal share
+        else {
+            binding.shareProcessChangeNameSwitch.visibility = View.GONE
+            binding.shareProcessChangeNameEt.visibility = View.GONE
+            binding.shareProcessHideDownloadCheckbox.visibility = View.GONE
+            binding.shareProcessAllowResharingCheckbox.visibility = View.VISIBLE
+            binding.shareProcessSetPasswordSwitch.visibility = View.GONE
+            if (share != null) {
+                if (!isReshareShown) {
+                    binding.shareProcessAllowResharingCheckbox.visibility = View.GONE
+                }
+                binding.shareProcessAllowResharingCheckbox.isChecked = SharingMenuHelper.canReshare(share)
+            }
+        }
+    }
+
+    /**
+     * update views where share type external or link share
+     */
+    private fun updateViewForExternalAndLinkShare() {
+        binding.shareProcessHideDownloadCheckbox.visibility = View.VISIBLE
+        binding.shareProcessAllowResharingCheckbox.visibility = View.GONE
+        binding.shareProcessSetPasswordSwitch.visibility = View.VISIBLE
+
+        if (share != null) {
+            if (SharingMenuHelper.isFileDrop(share)) {
+                binding.shareProcessHideDownloadCheckbox.visibility = View.GONE
+            } else {
+                binding.shareProcessHideDownloadCheckbox.visibility = View.VISIBLE
+                binding.shareProcessHideDownloadCheckbox.isChecked = share?.isHideFileDownload == true
+            }
+        }
+    }
+
+    /**
+     * update expiration date view while modifying the share
+     */
+    private fun updateExpirationDateView() {
+        if (share != null) {
+            if (share?.expirationDate ?: 0 > 0) {
+                chosenExpDateInMills = share?.expirationDate ?: -1
+                binding.shareProcessSetExpDateSwitch.isChecked = true
+                binding.shareProcessSelectExpDate.text = (
+                    resources.getString(
+                        R.string.share_expiration_date_format,
+                        SimpleDateFormat.getDateInstance().format(Date(share?.expirationDate ?: 0))
+                    )
+                    )
+            }
+        }
+    }
+
+    private fun updateViewForFile() {
+        binding.shareProcessPermissionUploadEditing.text =
+            requireContext().resources.getString(R.string.link_share_editing)
+        binding.shareProcessPermissionFileDrop.visibility = View.GONE
+    }
+
+    private fun updateViewForFolder() {
+        binding.shareProcessPermissionUploadEditing.text =
+            requireContext().resources.getString(R.string.link_share_allow_upload_and_editing)
+        binding.shareProcessPermissionFileDrop.visibility = View.VISIBLE
+    }
+
+    /**
+     * update views for screen type Note
+     */
+    private fun showShareProcessSecond() {
+        binding.shareProcessGroupOne.visibility = View.GONE
+        binding.shareProcessGroupTwo.visibility = View.VISIBLE
+        if (share != null) {
+            binding.shareProcessBtnNext.text = requireContext().resources.getString(R.string.set_note)
+            binding.noteText.setText(share?.note)
+        } else {
+            binding.shareProcessBtnNext.text = requireContext().resources.getString(R.string.send_share)
+            binding.noteText.setText("")
+        }
+        shareProcessStep = SCREEN_TYPE_NOTE
+    }
+
+    private fun implementClickEvents() {
+        binding.shareProcessBtnCancel.setOnClickListener {
+            onCancelClick()
+        }
+        binding.shareProcessBtnNext.setOnClickListener {
+            if (shareProcessStep == SCREEN_TYPE_PERMISSION) {
+                validateShareProcessFirst()
+            } else {
+                validateShareProcessSecond()
+            }
+        }
+        binding.shareProcessSetPasswordSwitch.setOnCheckedChangeListener { _, isChecked ->
+            showPasswordInput(isChecked)
+        }
+        binding.shareProcessSetExpDateSwitch.setOnCheckedChangeListener { _, isChecked ->
+            showExpirationDateInput(isChecked)
+        }
+        binding.shareProcessChangeNameSwitch.setOnCheckedChangeListener { _, isChecked ->
+            showChangeNameInput(isChecked)
+        }
+        binding.shareProcessSelectExpDate.setOnClickListener {
+            showExpirationDateDialog()
+        }
+    }
+
+    private fun showExpirationDateDialog() {
+        val dialog = ExpirationDatePickerDialogFragment.newInstance(chosenExpDateInMills)
+        dialog.setOnExpiryDateListener(this)
+        fileActivity?.let { it1 ->
+            dialog.show(
+                it1.supportFragmentManager,
+                ExpirationDatePickerDialogFragment.DATE_PICKER_DIALOG
+            )
+        }
+    }
+
+    private fun showChangeNameInput(isChecked: Boolean) {
+        binding.shareProcessChangeNameEt.visibility = if (isChecked) View.VISIBLE else View.GONE
+        if (!isChecked) {
+            binding.shareProcessChangeNameEt.setText("")
+        }
+    }
+
+    private fun onCancelClick() {
+        // if modifying the existing share then on back press remove the current fragment
+        if (share != null) {
+            removeCurrentFragment()
+        }
+        // else we have to check if user is in step 2(note screen) then show step 1 (permission screen)
+        // and if user is in step 1 (permission screen) then remove the fragment
+        else {
+            if (shareProcessStep == SCREEN_TYPE_NOTE) {
+                showShareProcessFirst()
+            } else {
+                removeCurrentFragment()
+            }
+        }
+    }
+
+    private fun showExpirationDateInput(isChecked: Boolean) {
+        binding.shareProcessSelectExpDate.visibility = if (isChecked) View.VISIBLE else View.GONE
+        binding.shareProcessExpDateDivider.visibility = if (isChecked) View.VISIBLE else View.GONE
+
+        // reset the expiration date if switch is unchecked
+        if (!isChecked) {
+            chosenExpDateInMills = -1
+            binding.shareProcessSelectExpDate.text = ""
+        }
+    }
+
+    private fun showPasswordInput(isChecked: Boolean) {
+        binding.shareProcessEnterPassword.visibility = if (isChecked) View.VISIBLE else View.GONE
+
+        // reset the password if switch is unchecked
+        if (!isChecked) {
+            binding.shareProcessEnterPassword.setText("")
+        }
+    }
+
+    private fun removeCurrentFragment() {
+        onEditShareListener.onShareProcessClosed()
+        fileActivity?.supportFragmentManager?.beginTransaction()?.remove(this)?.commit()
+    }
+
+    private fun getResharePermission(): Int {
+        val spb = SharePermissionsBuilder()
+        spb.setSharePermission(true)
+        return spb.build()
+    }
+
+    /**
+     * method to validate the step 1 screen information
+     */
+    @Suppress("ReturnCount")
+    private fun validateShareProcessFirst() {
+        // get the permissions on the basis of selection
+        when {
+            binding.shareProcessPermissionReadOnly.isChecked -> {
+                permission = OCShare.READ_PERMISSION_FLAG
+            }
+            binding.shareProcessPermissionUploadEditing.isChecked -> {
+                permission = if (file?.isFolder == true || share?.isFolder == true) {
+                    OCShare.MAXIMUM_PERMISSIONS_FOR_FOLDER
+                } else {
+                    OCShare.MAXIMUM_PERMISSIONS_FOR_FILE
+                }
+            }
+            binding.shareProcessPermissionFileDrop.isChecked -> {
+                permission = OCShare.CREATE_PERMISSION_FLAG
+            }
+        }
+
+        if (binding.shareProcessAllowResharingCheckbox.isChecked) {
+            permission = getResharePermission()
+        }
+
+        if (permission == OCShare.NO_PERMISSION) {
+            DisplayUtils.showSnackMessage(binding.root, R.string.no_share_permission_selected)
+            return
+        }
+
+        if (binding.shareProcessSetPasswordSwitch.isChecked && TextUtils.isEmpty(
+                binding.shareProcessEnterPassword
+                    .text.toString().trim()
+            )
+        ) {
+            DisplayUtils.showSnackMessage(binding.root, R.string.share_link_empty_password)
+            return
+        }
+
+        if (binding.shareProcessSetExpDateSwitch.isChecked && TextUtils.isEmpty(
+                binding.shareProcessSelectExpDate
+                    .text.toString().trim()
+            )
+        ) {
+            showExpirationDateDialog()
+            return
+        }
+
+        if (binding.shareProcessChangeNameSwitch.isChecked && TextUtils.isEmpty(
+                binding.shareProcessChangeNameEt
+                    .text.toString().trim()
+            )
+        ) {
+            DisplayUtils.showSnackMessage(binding.root, R.string.label_empty)
+            return
+        }
+
+        // if modifying existing share information then execute the process
+        if (share != null) {
+            fileOperationsHelper?.updateShareInformation(
+                share, permission,
+                binding.shareProcessHideDownloadCheckbox.isChecked,
+                binding.shareProcessEnterPassword.text.toString().trim(),
+                chosenExpDateInMills, binding.shareProcessChangeNameEt.text.toString().trim()
+            )
+            // copy the share link if available
+            if (!TextUtils.isEmpty(share?.shareLink)) {
+                ClipboardUtil.copyToClipboard(activity, share?.shareLink)
+            }
+            removeCurrentFragment()
+        } else {
+            // else show step 2 (note screen)
+            showShareProcessSecond()
+        }
+    }
+
+    /**
+     * method to validate step 2 (note screen) information
+     */
+    private fun validateShareProcessSecond() {
+        if (TextUtils.isEmpty(binding.noteText.text.toString().trim())) {
+            DisplayUtils.showSnackMessage(binding.root, R.string.share_link_empty_note_message)
+            return
+        }
+        // if modifying existing share then directly update the note and send email
+        if (share != null) {
+            fileOperationsHelper?.updateNoteToShare(share, binding.noteText.text.toString().trim())
+        } else {
+            // else create new share
+            fileOperationsHelper?.shareFileWithSharee(
+                file,
+                shareeName,
+                shareType,
+                permission,
+                binding
+                    .shareProcessHideDownloadCheckbox.isChecked,
+                binding.shareProcessEnterPassword.text.toString().trim(),
+                chosenExpDateInMills,
+                binding.noteText.text.toString().trim(), binding.shareProcessChangeNameEt.text.toString().trim()
+            )
+        }
+        removeCurrentFragment()
+    }
+
+    /**
+     * method will be called from DrawerActivity on back press to handle screen backstack
+     */
+    fun onBackPressed() {
+        onCancelClick()
+    }
+
+    override fun onDateSet(year: Int, monthOfYear: Int, dayOfMonth: Int, chosenDateInMillis: Long) {
+        binding.shareProcessSelectExpDate.text = (
+            resources.getString(
+                R.string.share_expiration_date_format,
+                SimpleDateFormat.getDateInstance().format(Date(chosenDateInMillis))
+            )
+            )
+        this.chosenExpDateInMills = chosenDateInMillis
+    }
+
+    override fun onDateUnSet() {
+        binding.shareProcessSetExpDateSwitch.isChecked = false
+    }
+}

+ 165 - 0
src/main/java/com/owncloud/android/ui/fragment/QuickSharingPermissionsBottomSheetDialog.java

@@ -0,0 +1,165 @@
+/*
+ * Nextcloud Android client application
+ *
+ * @author TSI-mc
+ * Copyright (C) 2021 TSI-mc
+ * Copyright (C) 2021 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.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.google.android.material.bottomsheet.BottomSheetBehavior;
+import com.google.android.material.bottomsheet.BottomSheetDialog;
+import com.owncloud.android.R;
+import com.owncloud.android.databinding.QuickSharingPermissionsBottomSheetFragmentBinding;
+import com.owncloud.android.datamodel.QuickPermissionModel;
+import com.owncloud.android.lib.resources.shares.OCShare;
+import com.owncloud.android.ui.activity.FileActivity;
+import com.owncloud.android.ui.adapter.QuickSharingPermissionsAdapter;
+import com.owncloud.android.ui.fragment.util.SharingMenuHelper;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import androidx.recyclerview.widget.LinearLayoutManager;
+
+import static com.owncloud.android.lib.resources.shares.OCShare.CREATE_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.READ_PERMISSION_FLAG;
+
+/**
+ * File Details Quick Sharing permissions options {@link android.app.Dialog} styled as a bottom sheet for main actions.
+ */
+public class QuickSharingPermissionsBottomSheetDialog extends BottomSheetDialog {
+    private QuickSharingPermissionsBottomSheetFragmentBinding binding;
+    private final QuickPermissionSharingBottomSheetActions actions;
+    private final FileActivity fileActivity;
+    private final OCShare ocShare;
+
+    public QuickSharingPermissionsBottomSheetDialog(FileActivity fileActivity,
+                                                    QuickPermissionSharingBottomSheetActions actions,
+                                                    OCShare ocShare) {
+        super(fileActivity);
+        this.actions = actions;
+        this.ocShare = ocShare;
+        this.fileActivity = fileActivity;
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        binding = QuickSharingPermissionsBottomSheetFragmentBinding.inflate(getLayoutInflater());
+        setContentView(binding.getRoot());
+
+        if (getWindow() != null) {
+            getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+        }
+
+        setUpRecyclerView();
+        setOnShowListener(d ->
+                              BottomSheetBehavior.from((View) binding.getRoot().getParent())
+                                  .setPeekHeight(binding.getRoot().getMeasuredHeight())
+                         );
+    }
+
+    private void setUpRecyclerView() {
+        List<QuickPermissionModel> quickPermissionModelList = getQuickPermissionList();
+        QuickSharingPermissionsAdapter adapter = new QuickSharingPermissionsAdapter(quickPermissionModelList, new QuickSharingPermissionsAdapter.QuickSharingPermissionViewHolder.OnPermissionChangeListener() {
+            @Override
+            public void onPermissionChanged(int position) {
+                handlePermissionChanged(quickPermissionModelList, position);
+            }
+
+            @Override
+            public void onDismissSheet() {
+                dismiss();
+            }
+        });
+        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(fileActivity);
+        binding.rvQuickSharePermissions.setLayoutManager(linearLayoutManager);
+        binding.rvQuickSharePermissions.setAdapter(adapter);
+    }
+
+    /**
+     * handle permission changed on click of selected permission
+     * @param quickPermissionModelList
+     * @param position
+     */
+    private void handlePermissionChanged(List<QuickPermissionModel> quickPermissionModelList, int position) {
+        if (quickPermissionModelList.get(position).getPermissionName().equalsIgnoreCase(fileActivity.getResources().getString(R.string.link_share_allow_upload_and_editing))
+            || quickPermissionModelList.get(position).getPermissionName().equalsIgnoreCase(fileActivity.getResources().getString(R.string.link_share_editing))) {
+            if (ocShare.isFolder()) {
+                actions.onQuickPermissionChanged(ocShare,
+                                                 MAXIMUM_PERMISSIONS_FOR_FOLDER);
+            } else {
+                actions.onQuickPermissionChanged(ocShare,
+                                                 MAXIMUM_PERMISSIONS_FOR_FILE);
+            }
+        } else if (quickPermissionModelList.get(position).getPermissionName().equalsIgnoreCase(fileActivity.getResources().getString(R.string
+                                                                                                                                         .link_share_read_only))) {
+            actions.onQuickPermissionChanged(ocShare,
+                                             READ_PERMISSION_FLAG);
+
+        } else if (quickPermissionModelList.get(position).getPermissionName().equalsIgnoreCase(fileActivity.getResources().getString(R.string
+                                                                                                                                         .link_share_file_drop))) {
+            actions.onQuickPermissionChanged(ocShare,
+                                             CREATE_PERMISSION_FLAG);
+        }
+        dismiss();
+    }
+
+    /**
+     * prepare the list of permissions needs to be displayed on recyclerview
+     * @return
+     */
+    private List<QuickPermissionModel> getQuickPermissionList() {
+
+        String[] permissionArray;
+        if (ocShare.isFolder()) {
+            permissionArray =
+                fileActivity.getResources().getStringArray(R.array.folder_share_permission_dialog_values);
+        } else {
+            permissionArray =
+                fileActivity.getResources().getStringArray(R.array.file_share_permission_dialog_values);
+        }
+        //get the checked item position
+        int checkedItem = SharingMenuHelper.getPermissionCheckedItem(fileActivity, ocShare, permissionArray);
+
+
+        final List<QuickPermissionModel> quickPermissionModelList = new ArrayList<>(permissionArray.length);
+        for (int i = 0; i < permissionArray.length; i++) {
+            QuickPermissionModel quickPermissionModel = new QuickPermissionModel(permissionArray[i], checkedItem == i);
+            quickPermissionModelList.add(quickPermissionModel);
+        }
+        return quickPermissionModelList;
+    }
+
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        binding = null;
+    }
+
+    public interface QuickPermissionSharingBottomSheetActions {
+        void onQuickPermissionChanged(OCShare share, int permission);
+    }
+}

+ 82 - 27
src/main/java/com/owncloud/android/ui/fragment/util/SharingMenuHelper.java

@@ -2,7 +2,9 @@
  * Nextcloud Android client application
  * Nextcloud Android client application
  *
  *
  * @author Andy Scherzinger
  * @author Andy Scherzinger
+ * @author TSI-mc
  * Copyright (C) 2018 Andy Scherzinger
  * Copyright (C) 2018 Andy Scherzinger
+ * Copyright (C) 2021 TSI-mc
  *
  *
  * This program is free software; you can redistribute it and/or
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
  * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
@@ -20,6 +22,7 @@
 
 
 package com.owncloud.android.ui.fragment.util;
 package com.owncloud.android.ui.fragment.util;
 
 
+import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.Resources;
 import android.view.MenuItem;
 import android.view.MenuItem;
 
 
@@ -29,6 +32,13 @@ import com.owncloud.android.lib.resources.shares.OCShare;
 import java.text.SimpleDateFormat;
 import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.Date;
 
 
+import static com.owncloud.android.lib.resources.shares.OCShare.CREATE_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;
+
 /**
 /**
  * Helper calls for visibility logic of the sharing menu.
  * Helper calls for visibility logic of the sharing menu.
  */
  */
@@ -38,30 +48,6 @@ public final class SharingMenuHelper {
         // utility class -> private constructor
         // utility class -> private constructor
     }
     }
 
 
-    /**
-     * Sets checked/visiblity state on the given {@link MenuItem} based on the given criteria.
-     *
-     * @param fileListing            the {@link MenuItem} to be setup
-     * @param isFolder               flag if it is a folder
-     * @param isEditingAllowed       flag if editing is allowed
-     * @param publicSharePermissions share permissions of the link
-     */
-    public static void setupHideFileListingMenuItem(MenuItem fileListing,
-                                                    boolean isFolder,
-                                                    boolean isEditingAllowed,
-                                                    int publicSharePermissions) {
-        if (!isFolder) {
-            fileListing.setVisible(false);
-        } else {
-            if (isEditingAllowed) {
-                boolean readOnly = (publicSharePermissions & OCShare.READ_PERMISSION_FLAG) != 0;
-                fileListing.setChecked(!readOnly);
-            } else {
-                fileListing.setVisible(false);
-            }
-        }
-    }
-
     /**
     /**
      * Sets checked/visibility state on the given {@link MenuItem} based on the given criteria.
      * Sets checked/visibility state on the given {@link MenuItem} based on the given criteria.
      *
      *
@@ -102,11 +88,80 @@ public final class SharingMenuHelper {
     public static void setupExpirationDateMenuItem(MenuItem expirationDate, long expirationDateValue, Resources res) {
     public static void setupExpirationDateMenuItem(MenuItem expirationDate, long expirationDateValue, Resources res) {
         if (expirationDateValue > 0) {
         if (expirationDateValue > 0) {
             expirationDate.setTitle(res.getString(
             expirationDate.setTitle(res.getString(
-                    R.string.share_expiration_date_label,
-                    SimpleDateFormat.getDateInstance().format(new Date(expirationDateValue))
-            ));
+                R.string.share_expiration_date_label,
+                SimpleDateFormat.getDateInstance().format(new Date(expirationDateValue))
+                                                 ));
         } else {
         } else {
             expirationDate.setTitle(R.string.share_no_expiration_date_label);
             expirationDate.setTitle(R.string.share_no_expiration_date_label);
         }
         }
     }
     }
+
+    public static boolean isUploadAndEditingAllowed(OCShare share) {
+        if (share.getPermissions() == NO_PERMISSION) {
+            return false;
+        }
+
+        return (share.getPermissions() & (share.isFolder() ? MAXIMUM_PERMISSIONS_FOR_FOLDER :
+            MAXIMUM_PERMISSIONS_FOR_FILE)) == (share.isFolder() ? MAXIMUM_PERMISSIONS_FOR_FOLDER :
+            MAXIMUM_PERMISSIONS_FOR_FILE);
+    }
+
+    public static boolean isReadOnly(OCShare share) {
+        if (share.getPermissions() == NO_PERMISSION) {
+            return false;
+        }
+
+        return (share.getPermissions() & ~SHARE_PERMISSION_FLAG) == READ_PERMISSION_FLAG;
+    }
+
+    public static boolean isFileDrop(OCShare share) {
+        if (share.getPermissions() == NO_PERMISSION) {
+            return false;
+        }
+
+        return (share.getPermissions() & ~SHARE_PERMISSION_FLAG) == CREATE_PERMISSION_FLAG;
+    }
+
+    public static String getPermissionName(Context context, OCShare share) {
+        if (SharingMenuHelper.isUploadAndEditingAllowed(share)) {
+            return context.getResources().getString(R.string.share_permission_can_edit);
+        } else if (SharingMenuHelper.isReadOnly(share)) {
+            return context.getResources().getString(R.string.share_permission_view_only);
+        } else if (SharingMenuHelper.isFileDrop(share)) {
+            return context.getResources().getString(R.string.share_permission_file_drop);
+        }
+        return null;
+    }
+
+    /**
+     * method to get the current checked index from the list of permissions
+     *
+     */
+    public static int getPermissionCheckedItem(Context context, OCShare share, String[] permissionArray) {
+        if (SharingMenuHelper.isUploadAndEditingAllowed(share)) {
+            if (share.isFolder()) {
+                return getPermissionIndexFromArray(context, permissionArray, R.string.link_share_allow_upload_and_editing);
+            } else {
+                return getPermissionIndexFromArray(context, permissionArray, R.string.link_share_editing);
+            }
+        } else if (SharingMenuHelper.isReadOnly(share)) {
+            return getPermissionIndexFromArray(context, permissionArray, R.string.link_share_read_only);
+        } else if (SharingMenuHelper.isFileDrop(share)) {
+            return getPermissionIndexFromArray(context, permissionArray, R.string.link_share_file_drop);
+        }
+        return 0;//default first item selected
+    }
+
+    private static int getPermissionIndexFromArray(Context context, String[] permissionArray, int permissionName) {
+        for (int i = 0; i < permissionArray.length; i++) {
+            if (permissionArray[i].equalsIgnoreCase(context.getResources().getString(permissionName))) {
+                return i;
+            }
+        }
+        return 0;
+    }
+
+    public static boolean canReshare(OCShare share) {
+        return (share.getPermissions() & SHARE_PERMISSION_FLAG) > 0;
+    }
 }
 }

+ 75 - 1
src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java

@@ -6,10 +6,12 @@
  * @author Juan Carlos González Cabrero
  * @author Juan Carlos González Cabrero
  * @author Andy Scherzinger
  * @author Andy Scherzinger
  * @author Chris Narkiewicz
  * @author Chris Narkiewicz
+ * @author TSI-mc
  *
  *
  * Copyright (C) 2015 ownCloud Inc.
  * Copyright (C) 2015 ownCloud Inc.
  * Copyright (C) 2018 Andy Scherzinger
  * Copyright (C) 2018 Andy Scherzinger
  * Copyright (C) 2020 Chris Narkiewicz <hello@ezaquarii.com>
  * Copyright (C) 2020 Chris Narkiewicz <hello@ezaquarii.com>
+ * Copyright (C) 2021 TSI-mc
  *
  *
  * This program is free software: you can redistribute it and/or modify
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2,
  * it under the terms of the GNU General Public License version 2,
@@ -244,7 +246,7 @@ public class FileOperationsHelper {
 
 
     private void syncFile(OCFile file, User user, FileDataStorageManager storageManager) {
     private void syncFile(OCFile file, User user, FileDataStorageManager storageManager) {
         fileActivity.runOnUiThread(() -> fileActivity.showLoadingDialog(fileActivity.getResources()
         fileActivity.runOnUiThread(() -> fileActivity.showLoadingDialog(fileActivity.getResources()
-                                                                            .getString(R.string.sync_in_progress)));
+                .getString(R.string.sync_in_progress)));
 
 
         SynchronizeFileOperation sfo = new SynchronizeFileOperation(file,
         SynchronizeFileOperation sfo = new SynchronizeFileOperation(file,
                                                                     null,
                                                                     null,
@@ -530,6 +532,50 @@ public class FileOperationsHelper {
         }
         }
     }
     }
 
 
+
+    /**
+     * Helper method to share a file with a known sharee. Starts a request to do it in {@link OperationsService}
+     *
+     * @param file                   The file to share.
+     * @param shareeName             Name (user name or group name) of the target sharee.
+     * @param shareType              The share type determines the sharee type.
+     * @param permissions            Permissions to grant to sharee on the shared file.
+     * @param hideFileDownload       true/false to hide file download
+     * @param password               Password to set for the public link; null or empty string to clear the current
+     *                               password
+     * @param expirationTimeInMillis Expiration date to set. A negative value clears the current expiration date,
+     *                               leaving the link unrestricted. Zero makes no change.
+     * @param note                   note message for the receiver. Null or empty for no message
+     * @param label                  new label
+     */
+    public void shareFileWithSharee(OCFile file, String shareeName, ShareType shareType, int permissions,
+                                    boolean hideFileDownload, String password, long expirationTimeInMillis,
+                                    String note, String label) {
+        if (file != null) {
+            // TODO check capability?
+            fileActivity.showLoadingDialog(fileActivity.getApplicationContext().
+                getString(R.string.wait_a_moment));
+
+            Intent service = new Intent(fileActivity, OperationsService.class);
+            service.setAction(OperationsService.ACTION_CREATE_SHARE_WITH_SHAREE);
+            service.putExtra(OperationsService.EXTRA_ACCOUNT, fileActivity.getAccount());
+            service.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath());
+            service.putExtra(OperationsService.EXTRA_SHARE_WITH, shareeName);
+            service.putExtra(OperationsService.EXTRA_SHARE_TYPE, shareType);
+            service.putExtra(OperationsService.EXTRA_SHARE_PERMISSIONS, permissions);
+            service.putExtra(OperationsService.EXTRA_SHARE_HIDE_FILE_DOWNLOAD, hideFileDownload);
+            service.putExtra(OperationsService.EXTRA_SHARE_PASSWORD, (password == null) ? "" : password);
+            service.putExtra(OperationsService.EXTRA_SHARE_EXPIRATION_DATE_IN_MILLIS, expirationTimeInMillis);
+            service.putExtra(OperationsService.EXTRA_SHARE_NOTE, note);
+            service.putExtra(OperationsService.EXTRA_SHARE_PUBLIC_LABEL, (label == null) ? "" : label);
+
+            mWaitingForOpId = fileActivity.getOperationsServiceBinder().queueNewOperation(service);
+
+        } else {
+            Log_OC.e(TAG, "Trying to share a NULL OCFile");
+        }
+    }
+
     /**
     /**
      * Helper method to revert to a file version. Starts a request to do it in {@link OperationsService}
      * Helper method to revert to a file version. Starts a request to do it in {@link OperationsService}
      *
      *
@@ -699,6 +745,34 @@ public class FileOperationsHelper {
         queueShareIntent(updateShareIntent);
         queueShareIntent(updateShareIntent);
     }
     }
 
 
+    /**
+     * Helper method to update the share information
+     *
+     * @param share                  {@link OCShare} instance which information will be updated.
+     * @param permissions            Permissions to grant to sharee on the shared file.
+     * @param hideFileDownload       true/false to hide file download
+     * @param password               Password to set for the public link; null or empty string to clear the current
+     *                               password
+     * @param expirationTimeInMillis Expiration date to set. A negative value clears the current expiration date,
+     *                               leaving the link unrestricted. Zero makes no change.
+     * @param label                  new label
+     */
+    public void updateShareInformation(OCShare share, int permissions,
+                                       boolean hideFileDownload, String password, long expirationTimeInMillis,
+                                       String label) {
+        Intent updateShareIntent = new Intent(fileActivity, OperationsService.class);
+        updateShareIntent.setAction(OperationsService.ACTION_UPDATE_SHARE_INFO);
+        updateShareIntent.putExtra(OperationsService.EXTRA_ACCOUNT, fileActivity.getAccount());
+        updateShareIntent.putExtra(OperationsService.EXTRA_SHARE_ID, share.getId());
+        updateShareIntent.putExtra(OperationsService.EXTRA_SHARE_PERMISSIONS, permissions);
+        updateShareIntent.putExtra(OperationsService.EXTRA_SHARE_HIDE_FILE_DOWNLOAD, hideFileDownload);
+        updateShareIntent.putExtra(OperationsService.EXTRA_SHARE_PASSWORD, (password == null) ? "" : password);
+        updateShareIntent.putExtra(OperationsService.EXTRA_SHARE_EXPIRATION_DATE_IN_MILLIS, expirationTimeInMillis);
+        updateShareIntent.putExtra(OperationsService.EXTRA_SHARE_PUBLIC_LABEL, (label == null) ? "" : label);
+        queueShareIntent(updateShareIntent);
+    }
+
+
     public void sendShareFile(OCFile file, boolean hideNcSharingOptions) {
     public void sendShareFile(OCFile file, boolean hideNcSharingOptions) {
         // Show dialog
         // Show dialog
         FragmentManager fm = fileActivity.getSupportFragmentManager();
         FragmentManager fm = fileActivity.getSupportFragmentManager();

+ 30 - 0
src/main/res/drawable/ic_baseline_arrow_drop_down_24.xml

@@ -0,0 +1,30 @@
+<!--
+ Nextcloud Android client application
+
+ @author TSI-mc
+ Copyright (C) 2021 TSI-mc
+ Copyright (C) 2021 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/>.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="?attr/colorControlNormal">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M7,10l5,5 5,-5z"/>
+</vector>

+ 30 - 0
src/main/res/drawable/ic_baseline_check_24.xml

@@ -0,0 +1,30 @@
+<!--
+ Nextcloud Android client application
+
+ @author TSI-mc
+ Copyright (C) 2021 TSI-mc
+ Copyright (C) 2021 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/>.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="?attr/colorControlNormal">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
+</vector>

+ 9 - 0
src/main/res/layout/file_details_fragment.xml

@@ -1,9 +1,12 @@
 <?xml version="1.0" encoding="utf-8"?><!--
 <?xml version="1.0" encoding="utf-8"?><!--
   ownCloud Android client application
   ownCloud Android client application
 
 
+  @author TSI-mc
+
   Copyright (C) 2012 Bartek Przybylski
   Copyright (C) 2012 Bartek Przybylski
   Copyright (C) 2017 ownCloud GmbH.
   Copyright (C) 2017 ownCloud GmbH.
   Copyright (C) 2018 Andy Scherzinger
   Copyright (C) 2018 Andy Scherzinger
+  Copyright (C) 2021 TSI-mc
 
 
   This program is free software: you can redistribute it and/or modify
   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License version 2,
   it under the terms of the GNU General Public License version 2,
@@ -191,6 +194,12 @@
         android:layout_width="match_parent"
         android:layout_width="match_parent"
         android:layout_height="match_parent" />
         android:layout_height="match_parent" />
 
 
+    <FrameLayout
+        android:layout_width="match_parent"
+        android:visibility="gone"
+        android:id="@+id/sharing_frame_container"
+        android:layout_height="match_parent"/>
+
     <include
     <include
         android:id="@+id/empty_list"
         android:id="@+id/empty_list"
         layout="@layout/empty_list" />
         layout="@layout/empty_list" />

+ 36 - 12
src/main/res/layout/file_details_share_link_share_item.xml

@@ -1,12 +1,14 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
   Nextcloud Android client application
   Nextcloud Android client application
 
 
   @author Tobias Kaminsky
   @author Tobias Kaminsky
   @author Andy Scherzinger
   @author Andy Scherzinger
+  @author TSI-mc
+
   Copyright (C) 2020 Andy Scherzinger
   Copyright (C) 2020 Andy Scherzinger
   Copyright (C) 2020 Tobias Kaminsky
   Copyright (C) 2020 Tobias Kaminsky
   Copyright (C) 2020 Nextcloud GmbH
   Copyright (C) 2020 Nextcloud GmbH
+  Copyright (C) 2021 TSI-mc
 
 
   This program is free software; you can redistribute it and/or
   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
   modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
@@ -23,6 +25,8 @@
 -->
 -->
 
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/share_by_link_container"
     android:id="@+id/share_by_link_container"
     android:layout_width="match_parent"
     android:layout_width="match_parent"
     android:layout_height="@dimen/sharee_list_item_size"
     android:layout_height="@dimen/sharee_list_item_size"
@@ -40,18 +44,38 @@
         android:padding="@dimen/standard_half_padding"
         android:padding="@dimen/standard_half_padding"
         android:src="@drawable/shared_via_link" />
         android:src="@drawable/shared_via_link" />
 
 
-    <TextView
-        android:id="@+id/name"
+    <LinearLayout
+        android:id="@+id/share_name_layout"
         android:layout_width="0dp"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="center_vertical"
         android:layout_gravity="center_vertical"
-        android:textColor="@color/text_color"
-        android:ellipsize="end"
-        android:singleLine="true"
         android:layout_weight="1"
         android:layout_weight="1"
-        android:gravity="center_vertical"
-        android:textSize="@dimen/two_line_primary_text_size"
-        android:text="@string/share_via_link_section_title" />
+        android:orientation="vertical">
+
+        <TextView
+            android:id="@+id/name"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:ellipsize="end"
+            android:gravity="center_vertical"
+            android:singleLine="true"
+            android:text="@string/share_via_link_section_title"
+            android:textColor="@color/text_color"
+            android:textSize="@dimen/two_line_primary_text_size" />
+
+        <androidx.appcompat.widget.AppCompatTextView
+            android:id="@+id/permission_name"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:gravity="center_vertical"
+            android:singleLine="true"
+            tools:text="View only"
+            app:drawableEndCompat="@drawable/ic_baseline_arrow_drop_down_24"
+            app:drawableTint="@color/primary"
+            app:drawableRightCompat="@drawable/ic_baseline_arrow_drop_down_24"
+            android:textColor="@color/primary"
+            android:textSize="@dimen/two_line_secondary_text_size" />
+    </LinearLayout>
 
 
     <ImageView
     <ImageView
         android:id="@+id/copy_link"
         android:id="@+id/copy_link"
@@ -59,11 +83,11 @@
         android:layout_height="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="center_vertical"
         android:layout_gravity="center_vertical"
         android:contentDescription="@string/copy_link"
         android:contentDescription="@string/copy_link"
+        android:paddingStart="@dimen/standard_padding"
         android:paddingTop="@dimen/standard_quarter_margin"
         android:paddingTop="@dimen/standard_quarter_margin"
+        android:paddingEnd="@dimen/standard_padding"
         android:paddingBottom="@dimen/standard_quarter_margin"
         android:paddingBottom="@dimen/standard_quarter_margin"
         android:scaleType="fitCenter"
         android:scaleType="fitCenter"
-        android:paddingStart="@dimen/standard_padding"
-        android:paddingEnd="@dimen/standard_padding"
         android:src="@drawable/ic_content_copy" />
         android:src="@drawable/ic_content_copy" />
 
 
     <ImageView
     <ImageView

+ 48 - 25
src/main/res/layout/file_details_share_share_item.xml

@@ -1,9 +1,11 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
   ownCloud Android client application
   ownCloud Android client application
 
 
+  @author TSI-mc
+
   Copyright (C) 2015 ownCloud Inc.
   Copyright (C) 2015 ownCloud Inc.
   Copyright (C) 2018 Andy Scherzinger
   Copyright (C) 2018 Andy Scherzinger
+  Copyright (C) 2021 TSI-mc
 
 
   This program is free software: you can redistribute it and/or modify
   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License version 2,
   it under the terms of the GNU General Public License version 2,
@@ -18,39 +20,60 @@
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 -->
 -->
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 <LinearLayout 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"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_width="match_parent"
-    android:orientation="horizontal"
     android:layout_height="@dimen/sharee_list_item_size"
     android:layout_height="@dimen/sharee_list_item_size"
+    android:orientation="horizontal"
     android:weightSum="1"
     android:weightSum="1"
     tools:ignore="UseCompoundDrawables">
     tools:ignore="UseCompoundDrawables">
 
 
-        <ImageView
-            android:id="@+id/icon"
-            android:layout_width="@dimen/user_icon_size"
-            android:layout_height="@dimen/user_icon_size"
-            android:layout_gravity="center_vertical"
-            android:layout_marginBottom="@dimen/standard_half_margin"
-            android:layout_marginEnd="@dimen/standard_margin"
-            android:layout_marginLeft="@dimen/standard_margin"
-            android:layout_marginRight="@dimen/standard_margin"
-            android:layout_marginStart="@dimen/standard_margin"
-            android:layout_marginTop="@dimen/standard_half_margin"
-            android:contentDescription="@string/user_icon"
-            android:src="@drawable/ic_user" />
-
-    <TextView
-        android:id="@+id/name"
+    <ImageView
+        android:id="@+id/icon"
+        android:layout_width="@dimen/user_icon_size"
+        android:layout_height="@dimen/user_icon_size"
+        android:layout_gravity="center_vertical"
+        android:layout_marginStart="@dimen/standard_margin"
+        android:layout_marginLeft="@dimen/standard_margin"
+        android:layout_marginTop="@dimen/standard_half_margin"
+        android:layout_marginEnd="@dimen/standard_margin"
+        android:layout_marginRight="@dimen/standard_margin"
+        android:layout_marginBottom="@dimen/standard_half_margin"
+        android:contentDescription="@string/user_icon"
+        android:src="@drawable/ic_user" />
+
+    <LinearLayout
+        android:id="@+id/share_name_layout"
         android:layout_width="0dp"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="center_vertical"
         android:layout_gravity="center_vertical"
-        android:textColor="@color/text_color"
-        android:ellipsize="end"
-        android:singleLine="true"
         android:layout_weight="1"
         android:layout_weight="1"
-        android:gravity="center_vertical"
-        android:textSize="@dimen/two_line_primary_text_size"
-        android:text="@string/username" />
+        android:orientation="vertical">
+
+        <TextView
+            android:id="@+id/name"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:ellipsize="end"
+            android:gravity="center_vertical"
+            android:singleLine="true"
+            android:text="@string/username"
+            android:textColor="@color/text_color"
+            android:textSize="@dimen/two_line_primary_text_size" />
+
+        <androidx.appcompat.widget.AppCompatTextView
+            android:id="@+id/permission_name"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:gravity="center_vertical"
+            android:singleLine="true"
+            android:textColor="@color/primary"
+            android:textSize="@dimen/two_line_secondary_text_size"
+            app:drawableEndCompat="@drawable/ic_baseline_arrow_drop_down_24"
+            app:drawableRightCompat="@drawable/ic_baseline_arrow_drop_down_24"
+            app:drawableTint="@color/primary"
+            tools:text="View only" />
+    </LinearLayout>
 
 
     <ImageView
     <ImageView
         android:id="@+id/overflow_menu"
         android:id="@+id/overflow_menu"

+ 233 - 0
src/main/res/layout/file_details_sharing_menu_bottom_sheet_fragment.xml

@@ -0,0 +1,233 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Nextcloud Android client application
+
+ @author TSI-mc
+ Copyright (C) 2021 TSI-mc
+ Copyright (C) 2021 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 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"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:paddingTop="@dimen/standard_padding">
+
+    <LinearLayout
+        android:id="@+id/menu_share_open_in"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:minHeight="@dimen/minimum_size_for_touchable_area"
+        android:background="?android:attr/selectableItemBackground"
+        android:orientation="horizontal"
+        android:paddingLeft="@dimen/standard_padding"
+        android:paddingTop="@dimen/standard_half_padding"
+        android:paddingRight="@dimen/standard_padding"
+        android:paddingBottom="@dimen/standard_half_padding"
+        tools:ignore="UseCompoundDrawables">
+
+        <ImageView
+            android:id="@+id/menu_icon_open_in"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:contentDescription="@null"
+            android:src="@drawable/ic_content_copy"
+            app:tint="@color/primary" />
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:layout_marginStart="@dimen/standard_margin"
+            android:text="@string/share_open_in"
+            android:textColor="@color/text_color"
+            android:textSize="@dimen/bottom_sheet_text_size" />
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/menu_share_advanced_permissions"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:minHeight="@dimen/minimum_size_for_touchable_area"
+        android:background="?android:attr/selectableItemBackground"
+        android:orientation="horizontal"
+        android:paddingLeft="@dimen/standard_padding"
+        android:paddingTop="@dimen/standard_half_padding"
+        android:paddingRight="@dimen/standard_padding"
+        android:paddingBottom="@dimen/standard_half_padding"
+        tools:ignore="UseCompoundDrawables">
+
+        <ImageView
+            android:id="@+id/menu_icon_advanced_permissions"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:contentDescription="@null"
+            android:src="@drawable/ic_edit"
+            app:tint="@color/primary" />
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:layout_marginStart="@dimen/standard_margin"
+            android:text="@string/share_settings"
+            android:textColor="@color/text_color"
+            android:textSize="@dimen/bottom_sheet_text_size" />
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/menu_share_send_new_email"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:minHeight="@dimen/minimum_size_for_touchable_area"
+        android:background="?android:attr/selectableItemBackground"
+        android:orientation="horizontal"
+        android:paddingLeft="@dimen/standard_padding"
+        android:paddingTop="@dimen/standard_half_padding"
+        android:paddingRight="@dimen/standard_padding"
+        android:paddingBottom="@dimen/standard_half_padding"
+        tools:ignore="UseCompoundDrawables">
+
+        <ImageView
+            android:id="@+id/menu_icon_send_new_email"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:contentDescription="@null"
+            android:src="@drawable/ic_email"
+            app:tint="@color/primary" />
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:layout_marginStart="@dimen/standard_margin"
+            android:text="@string/share_send_new_email"
+            android:textColor="@color/text_color"
+            android:textSize="@dimen/bottom_sheet_text_size" />
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/menu_share_send_link"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:minHeight="@dimen/minimum_size_for_touchable_area"
+        android:background="?android:attr/selectableItemBackground"
+        android:orientation="horizontal"
+        android:paddingLeft="@dimen/standard_padding"
+        android:paddingTop="@dimen/standard_half_padding"
+        android:paddingRight="@dimen/standard_padding"
+        android:paddingBottom="@dimen/standard_half_padding"
+        android:visibility="gone"
+        tools:ignore="UseCompoundDrawables"
+        tools:visibility="visible">
+
+        <ImageView
+            android:id="@+id/menu_icon_send_link"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:contentDescription="@null"
+            android:src="@drawable/ic_link"
+            app:tint="@color/primary" />
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:layout_marginStart="@dimen/standard_margin"
+            android:text="@string/share_via_link_send_link_label"
+            android:textColor="@color/text_color"
+            android:textSize="@dimen/bottom_sheet_text_size" />
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/menu_share_unshare"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:minHeight="@dimen/minimum_size_for_touchable_area"
+        android:background="?android:attr/selectableItemBackground"
+        android:orientation="horizontal"
+        android:paddingLeft="@dimen/standard_padding"
+        android:paddingTop="@dimen/standard_half_padding"
+        android:paddingRight="@dimen/standard_padding"
+        android:paddingBottom="@dimen/standard_half_padding"
+        tools:ignore="UseCompoundDrawables">
+
+        <ImageView
+            android:id="@+id/menu_icon_unshare"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:contentDescription="@null"
+            android:src="@drawable/ic_delete"
+            app:tint="@color/primary" />
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:layout_marginStart="@dimen/standard_margin"
+            android:text="@string/delete_link"
+            android:textColor="@color/text_color"
+            android:textSize="@dimen/bottom_sheet_text_size" />
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/menu_share_add_another_link"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:minHeight="@dimen/minimum_size_for_touchable_area"
+        android:background="?android:attr/selectableItemBackground"
+        android:orientation="horizontal"
+        android:paddingLeft="@dimen/standard_padding"
+        android:paddingTop="@dimen/standard_half_padding"
+        android:paddingRight="@dimen/standard_padding"
+        android:paddingBottom="@dimen/standard_half_padding"
+        android:visibility="gone"
+        tools:ignore="UseCompoundDrawables"
+        tools:visibility="visible">
+
+        <ImageView
+            android:id="@+id/menu_icon_add_another_link"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:contentDescription="@null"
+            android:src="@drawable/ic_plus"
+            app:tint="@color/primary" />
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:layout_marginStart="@dimen/standard_margin"
+            android:text="@string/add_another_public_share_link"
+            android:textColor="@color/text_color"
+            android:textSize="@dimen/bottom_sheet_text_size" />
+
+    </LinearLayout>
+
+
+</LinearLayout>

+ 278 - 0
src/main/res/layout/file_details_sharing_process_fragment.xml

@@ -0,0 +1,278 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Nextcloud Android client application
+
+ @author TSI-mc
+ Copyright (C) 2021 TSI-mc
+ Copyright (C) 2021 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/>.
+-->
+<androidx.constraintlayout.widget.ConstraintLayout 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"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:focusable="true"
+    android:focusableInTouchMode="true">
+
+    <androidx.core.widget.NestedScrollView
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:padding="@dimen/standard_padding"
+        app:layout_constraintBottom_toTopOf="@+id/share_process_btn_cancel"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent">
+
+        <androidx.constraintlayout.widget.ConstraintLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content">
+
+            <androidx.appcompat.widget.AppCompatTextView
+                android:id="@+id/share_process_edit_share_link"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="@dimen/standard_margin"
+                android:text="@string/share_permissions"
+                android:textColor="@color/secondary_text_color"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toTopOf="parent" />
+
+            <RadioGroup
+                android:id="@+id/share_process_permission_radio_group"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@+id/share_process_edit_share_link">
+
+                <androidx.appcompat.widget.AppCompatRadioButton
+                    android:id="@+id/share_process_permission_read_only"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/link_share_read_only" />
+
+                <androidx.appcompat.widget.AppCompatRadioButton
+                    android:id="@+id/share_process_permission_upload_editing"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/link_share_allow_upload_and_editing" />
+
+                <androidx.appcompat.widget.AppCompatRadioButton
+                    android:id="@+id/share_process_permission_file_drop"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/link_share_file_drop" />
+
+            </RadioGroup>
+
+            <androidx.appcompat.widget.AppCompatTextView
+                android:id="@+id/share_process_advance_permission_title"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="@dimen/standard_margin"
+                android:text="@string/advanced_settings"
+                android:textColor="@color/secondary_text_color"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@+id/share_process_permission_radio_group" />
+
+            <androidx.appcompat.widget.AppCompatCheckBox
+                android:id="@+id/share_process_allow_resharing_checkbox"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:text="@string/allow_resharing"
+                android:visibility="gone"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@+id/share_process_advance_permission_title"
+                tools:visibility="visible" />
+
+            <androidx.appcompat.widget.SwitchCompat
+                android:id="@+id/share_process_set_password_switch"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/share_no_password_title"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@+id/share_process_allow_resharing_checkbox" />
+
+            <androidx.appcompat.widget.AppCompatEditText
+                android:id="@+id/share_process_enter_password"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:autofillHints="password"
+                android:hint="@string/hint_password"
+                android:inputType="textPassword"
+                android:visibility="gone"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@+id/share_process_set_password_switch"
+                tools:visibility="visible" />
+
+            <androidx.appcompat.widget.SwitchCompat
+                android:id="@+id/share_process_set_exp_date_switch"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="@dimen/standard_half_margin"
+                android:text="@string/share_no_expiration_date_label"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@+id/share_process_enter_password" />
+
+            <androidx.appcompat.widget.AppCompatTextView
+                android:id="@+id/share_process_select_exp_date"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:gravity="center_vertical"
+                android:padding="@dimen/standard_half_padding"
+                android:visibility="gone"
+                app:drawableEndCompat="@drawable/file_calendar"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@+id/share_process_set_exp_date_switch"
+                tools:visibility="visible" />
+
+            <View
+                android:id="@+id/share_process_exp_date_divider"
+                android:layout_width="match_parent"
+                android:layout_height="0.5dp"
+                android:background="@color/black"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@+id/share_process_select_exp_date" />
+
+            <androidx.appcompat.widget.SwitchCompat
+                android:id="@+id/share_process_hide_download_checkbox"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="@dimen/standard_half_margin"
+                android:text="@string/share_via_link_hide_download"
+                android:visibility="gone"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@+id/share_process_exp_date_divider"
+                tools:visibility="visible" />
+
+            <androidx.appcompat.widget.SwitchCompat
+                android:id="@+id/share_process_change_name_switch"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="@dimen/standard_half_margin"
+                android:text="@string/link_name"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@+id/share_process_hide_download_checkbox" />
+
+            <androidx.appcompat.widget.AppCompatEditText
+                android:id="@+id/share_process_change_name_et"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:hint="@string/hint_name"
+                android:importantForAutofill="no"
+                android:inputType="textNoSuggestions|textCapSentences"
+                android:visibility="gone"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@+id/share_process_change_name_switch"
+                tools:visibility="visible" />
+
+            <androidx.constraintlayout.widget.Group
+                android:id="@+id/share_process_group_one"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:visibility="visible"
+                app:constraint_referenced_ids="share_process_exp_date_divider,
+                share_process_permission_radio_group,
+                share_process_advance_permission_title, share_process_hide_download_checkbox,
+                share_process_allow_resharing_checkbox, share_process_set_password_switch,
+                share_process_set_exp_date_switch, share_process_enter_password,
+                share_process_select_exp_date, share_process_change_name_switch,
+                share_process_change_name_et" />
+
+            <androidx.appcompat.widget.AppCompatTextView
+                android:id="@+id/share_process_message_title"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="@dimen/standard_margin"
+                android:text="@string/share_send_note"
+                android:textColor="@color/secondary_text_color"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@+id/share_process_edit_share_link"/>
+
+            <com.google.android.material.textfield.TextInputLayout
+                android:id="@+id/note_container"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="@dimen/standard_half_margin"
+                android:hint="@string/hint_note"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@+id/share_process_message_title">
+
+                <com.google.android.material.textfield.TextInputEditText
+                    android:id="@+id/note_text"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:gravity="top"
+                    android:importantForAutofill="no"
+                    android:inputType="textCapSentences|textMultiLine|textNoSuggestions"
+                    android:minLines="10"
+                    android:scrollbars="vertical" />
+            </com.google.android.material.textfield.TextInputLayout>
+
+            <androidx.constraintlayout.widget.Group
+                android:id="@+id/share_process_group_two"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:visibility="gone"
+                app:constraint_referenced_ids="share_process_message_title, note_container" />
+
+        </androidx.constraintlayout.widget.ConstraintLayout>
+
+    </androidx.core.widget.NestedScrollView>
+
+    <com.google.android.material.button.MaterialButton
+        android:id="@+id/share_process_btn_cancel"
+        style="@style/OutlinedButton"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="@dimen/standard_margin"
+        android:layout_marginEnd="@dimen/standard_half_margin"
+        android:layout_marginBottom="@dimen/standard_margin"
+        android:text="@string/common_cancel"
+        app:cornerRadius="@dimen/button_corner_radius"
+        app:layout_constraintTop_toTopOf="@+id/share_process_btn_next"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toStartOf="@+id/share_process_btn_next"
+        app:layout_constraintStart_toStartOf="parent" />
+
+    <com.google.android.material.button.MaterialButton
+        android:id="@+id/share_process_btn_next"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="@dimen/standard_half_margin"
+        android:layout_marginEnd="@dimen/standard_margin"
+        android:layout_marginBottom="@dimen/standard_margin"
+        android:text="@string/common_next"
+        android:theme="@style/Button.Primary"
+        app:cornerRadius="@dimen/button_corner_radius"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toEndOf="@+id/share_process_btn_cancel" />
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 52 - 0
src/main/res/layout/item_quick_share_permissions.xml

@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Nextcloud Android client application
+
+ @author TSI-mc
+ Copyright (C) 2021 TSI-mc
+ Copyright (C) 2021 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/>.
+-->
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:padding="@dimen/standard_padding">
+
+    <ImageView
+        android:id="@+id/tv_quick_share_check_icon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:contentDescription="@null"
+        android:src="@drawable/ic_baseline_check_24"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:tint="@color/primary" />
+
+    <TextView
+        android:id="@+id/tv_quick_share_name"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:layout_marginStart="@dimen/standard_margin"
+        android:text="@string/link_share_read_only"
+        android:textColor="@color/text_color"
+        android:textSize="@dimen/bottom_sheet_text_size"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toEndOf="@+id/tv_quick_share_check_icon"
+        app:layout_constraintTop_toTopOf="parent" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 35 - 0
src/main/res/layout/quick_sharing_permissions_bottom_sheet_fragment.xml

@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Nextcloud Android client application
+
+ @author TSI-mc
+ Copyright (C) 2021 TSI-mc
+ Copyright (C) 2021 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 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:paddingTop="@dimen/standard_padding"
+    android:paddingBottom="@dimen/standard_padding"
+    android:orientation="vertical">
+
+    <androidx.recyclerview.widget.RecyclerView
+        android:id="@+id/rv_quick_share_permissions"
+        android:layout_width="match_parent"
+        tools:listitem="@layout/item_quick_share_permissions"
+        android:layout_height="wrap_content"/>
+
+</LinearLayout>

+ 13 - 3
src/main/res/layout/test_layout.xml

@@ -17,11 +17,21 @@
   You should have received a copy of the GNU Affero General Public
   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/>.
   License along with this program. If not, see <http://www.gnu.org/licenses/>.
 -->
 -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/root"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:layout_height="match_parent"
     android:fitsSystemWindows="true"
     android:fitsSystemWindows="true"
+    android:id="@+id/root_layout"
     android:orientation="vertical">
     android:orientation="vertical">
 
 
-</LinearLayout>
+    <androidx.fragment.app.FragmentContainerView
+        android:id="@+id/secondary_fragment"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+    <androidx.fragment.app.FragmentContainerView
+        android:id="@+id/main_fragment"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+</FrameLayout>

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

@@ -1,68 +0,0 @@
-<?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>

+ 0 - 83
src/main/res/menu/fragment_file_detail_sharing_public_link.xml

@@ -1,83 +0,0 @@
-<?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_link"
-        android:showAsAction="never"
-        android:title="@string/share_via_link_send_link_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_edit_label"
-        android:showAsAction="never"
-        android:title="@string/edit_label"
-        app:showAsAction="never" />
-    <item
-        android:id="@+id/action_unshare"
-        android:showAsAction="never"
-        android:title="@string/unshare"
-        app:showAsAction="never" />
-    <item
-        android:id="@+id/action_add_another_public_share_link"
-        android:showAsAction="never"
-        android:title="@string/add_another_public_share_link"
-        app:showAsAction="never" />
-</menu>

+ 0 - 64
src/main/res/menu/item_user_sharing_settings.xml

@@ -1,64 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Nesxtcloud 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">
-    <item
-        android:id="@+id/allow_editing"
-        android:checkable="true"
-        android:showAsAction="never"
-        android:title="@string/allow_editing"
-        app:showAsAction="never" />
-
-    <item
-        android:id="@+id/allow_creating"
-        android:checkable="true"
-        android:showAsAction="never"
-        android:title="@string/allow_creating"
-        app:showAsAction="never" />
-    <item
-        android:id="@+id/allow_deleting"
-        android:checkable="true"
-        android:showAsAction="never"
-        android:title="@string/allow_deleting"
-        app:showAsAction="never" />
-    <item
-        android:id="@+id/allow_resharing"
-        android:checkable="true"
-        android:showAsAction="never"
-        android:title="@string/allow_resharing"
-        app:showAsAction="never" />
-    <item
-        android:id="@+id/action_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/share_privilege_unshare"
-        app:showAsAction="never" />
-</menu>

+ 20 - 0
src/main/res/values-b+en+001/strings.xml

@@ -383,6 +383,7 @@
     <string name="link_share_allow_upload_and_editing">Allow upload and editing</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="link_share_file_drop">File drop (upload only)</string>
     <string name="link_share_read_only">Read only</string>
     <string name="link_share_read_only">Read only</string>
+    <string name="link_share_editing">Editing</string>
     <string name="list_layout">Listed layout</string>
     <string name="list_layout">Listed layout</string>
     <string name="local_file_list_empty">There are no files in this folder.</string>
     <string name="local_file_list_empty">There are no files in this folder.</string>
     <string name="local_file_not_found_message">File not found in local file system</string>
     <string name="local_file_not_found_message">File not found in local file system</string>
@@ -831,6 +832,25 @@
     <string name="whats_your_status">What is your status?</string>
     <string name="whats_your_status">What is your status?</string>
     <string name="wrong_storage_path">Data storage folder does not exist!</string>
     <string name="wrong_storage_path">Data storage folder does not exist!</string>
     <string name="wrong_storage_path_desc">This might be due to a backup restore on another device. Falling back to default. Please check settings to adjust data storage folder.</string>
     <string name="wrong_storage_path_desc">This might be due to a backup restore on another device. Falling back to default. Please check settings to adjust data storage folder.</string>
+    <string name="share_permission_view_only">View only</string>
+    <string name="share_permission_can_edit">Can edit</string>
+    <string name="share_permission_file_drop">File drop</string>
+    <string name="share_permissions">Share Permissions</string>
+    <string name="advanced_settings">Advanced Settings</string>
+    <string name="common_next">Next</string>
+    <string name="send_share">Send Share</string>
+    <string name="no_share_permission_selected">Please select at least one permission to share.</string>
+    <string name="share_link_empty_exp_date">You must select expiration date.</string>
+    <string name="share_link_empty_note_message">Please enter note.</string>
+    <string name="share_copy_link">Share &amp; Copy Link</string>
+    <string name="set_note">Set Note</string>
+    <string name="share_open_in">Open in...</string>
+    <string name="share_advanced_permissions">Advanced permissions</string>
+    <string name="share_send_new_email">Send new email</string>
+    <string name="link_name">Link Name</string>
+    <string name="delete_link">Delete Link</string>
+    <string name="share_settings">Settings</string>
+    <string name="common_confirm">Confirm</string>
     <plurals name="sync_fail_in_favourites_content">
     <plurals name="sync_fail_in_favourites_content">
         <item quantity="one">Could not sync %1$d file (conflicts: %2$d)</item>
         <item quantity="one">Could not sync %1$d file (conflicts: %2$d)</item>
         <item quantity="other">Could not sync %1$d files (conflicts: %2$d)</item>
         <item quantity="other">Could not sync %1$d files (conflicts: %2$d)</item>

+ 23 - 3
src/main/res/values-de/strings.xml

@@ -383,9 +383,10 @@
     <string name="invisible">Unsichtbar</string>
     <string name="invisible">Unsichtbar</string>
     <string name="label_empty">Bezeichnung darf nicht leer sein</string>
     <string name="label_empty">Bezeichnung darf nicht leer sein</string>
     <string name="link">Link</string>
     <string name="link">Link</string>
-    <string name="link_share_allow_upload_and_editing">Hochladen und Bearbeiten erlauben</string>
-    <string name="link_share_file_drop">Dateien ablegen (nur Hochladen)</string>
-    <string name="link_share_read_only">Schreibgeschützt</string>
+    <string name="link_share_allow_upload_and_editing">Hochladen &amp; Bearbeiten</string>
+    <string name="link_share_file_drop">Sammelbox</string>
+    <string name="link_share_read_only">Nur Lesen</string>
+    <string name="link_share_editing">Bearbeiten</string>
     <string name="list_layout">Layout der Liste</string>
     <string name="list_layout">Layout der Liste</string>
     <string name="local_file_list_empty">Es befinden sich keine Dateien in diesem Ordner.</string>
     <string name="local_file_list_empty">Es befinden sich keine Dateien in diesem Ordner.</string>
     <string name="local_file_not_found_message">Die Datei wurde im lokalen Dateisystem nicht gefunden</string>
     <string name="local_file_not_found_message">Die Datei wurde im lokalen Dateisystem nicht gefunden</string>
@@ -837,6 +838,25 @@
     <string name="whats_your_status">Wie ist Ihr Status?</string>
     <string name="whats_your_status">Wie ist Ihr Status?</string>
     <string name="wrong_storage_path">Speicherordner existiert nicht!</string>
     <string name="wrong_storage_path">Speicherordner existiert nicht!</string>
     <string name="wrong_storage_path_desc">Ursache könnte die Wiederherstellung einer Sicherungskopie auf einem anderen Gerät sein. Der Standard-Ordner wird jetzt wieder verwendet. Bitte überprüfen Sie die Einstellungen bezüglich des Speicherortes.</string>
     <string name="wrong_storage_path_desc">Ursache könnte die Wiederherstellung einer Sicherungskopie auf einem anderen Gerät sein. Der Standard-Ordner wird jetzt wieder verwendet. Bitte überprüfen Sie die Einstellungen bezüglich des Speicherortes.</string>
+    <string name="share_permission_view_only">Nur anschauen</string>
+    <string name="share_permission_can_edit">Kann bearbeiten</string>
+    <string name="share_permission_file_drop">Dateiablegen</string>
+    <string name="share_permissions">Berechtigungen teilen</string>
+    <string name="advanced_settings">Erweiterte Einstellungen</string>
+    <string name="common_next">Nächster</string>
+    <string name="send_share">Senden Teilen</string>
+    <string name="no_share_permission_selected">Bitte wählen Sie mindestens eine Berechtigung zum Teilen aus.</string>
+    <string name="share_link_empty_exp_date">Sie müssen das Ablaufdatum auswählen.</string>
+    <string name="share_link_empty_note_message">Bitte Anmerkung eingeben.</string>
+    <string name="share_copy_link">Link teilen &amp; kopieren</string>
+    <string name="set_note">Notiz setzen</string>
+    <string name="share_open_in">Öffnen mit...</string>
+    <string name="share_advanced_permissions">Erweiterte Berechtigungen</string>
+    <string name="share_send_new_email">Neue Email versenden</string>
+    <string name="link_name">Linkname</string>
+    <string name="delete_link">Link löschen</string>
+    <string name="share_settings">Einstellungen</string>
+    <string name="common_confirm">Bestätigen</string>
     <plurals name="sync_fail_in_favourites_content">
     <plurals name="sync_fail_in_favourites_content">
         <item quantity="one">Inhalte von %1$d Datei konnten nicht synchronisiert werden (Konflikte: %2$d)</item>
         <item quantity="one">Inhalte von %1$d Datei konnten nicht synchronisiert werden (Konflikte: %2$d)</item>
         <item quantity="other">Inhalte von %1$d Dateien konnten nicht synchronisiert werden (Konflikte: %2$d)</item>
         <item quantity="other">Inhalte von %1$d Dateien konnten nicht synchronisiert werden (Konflikte: %2$d)</item>

+ 11 - 0
src/main/res/values/attrs.xml

@@ -18,4 +18,15 @@
         <item>@string/pref_instant_name_collision_policy_entries_rename</item>
         <item>@string/pref_instant_name_collision_policy_entries_rename</item>
         <item>@string/pref_instant_name_collision_policy_entries_cancel</item>
         <item>@string/pref_instant_name_collision_policy_entries_cancel</item>
     </string-array>
     </string-array>
+
+    <string-array name="folder_share_permission_dialog_values" translatable="false">
+        <item>@string/link_share_read_only</item>
+        <item>@string/link_share_allow_upload_and_editing</item>
+        <item>@string/link_share_file_drop</item>
+    </string-array>
+
+    <string-array name="file_share_permission_dialog_values" translatable="false">
+        <item>@string/link_share_read_only</item>
+        <item>@string/link_share_editing</item>
+    </string-array>
 </resources>
 </resources>

+ 19 - 8
src/main/res/values/strings.xml

@@ -469,8 +469,8 @@
 
 
     <string name="share_dialog_title">Sharing</string>
     <string name="share_dialog_title">Sharing</string>
     <string name="share_file">Share %1$s</string>
     <string name="share_file">Share %1$s</string>
-    <string name="share_via_link_menu_password_label">Password protect (%1$s)</string>
     <string name="share_expiration_date_label">Expires %1$s</string>
     <string name="share_expiration_date_label">Expires %1$s</string>
+    <string name="share_expiration_date_format">%1$s</string>
     <string name="share_no_expiration_date_label">Set expiration date</string>
     <string name="share_no_expiration_date_label">Set expiration date</string>
     <string name="share_via_link_section_title">Share link</string>
     <string name="share_via_link_section_title">Share link</string>
     <string name="share_via_link_send_link_label">Send link</string>
     <string name="share_via_link_send_link_label">Send link</string>
@@ -486,8 +486,6 @@
     <string name="share_room_clarification">%1$s (conversation)</string>
     <string name="share_room_clarification">%1$s (conversation)</string>
     <string name="share_known_remote_on_clarification">on %1$s</string>
     <string name="share_known_remote_on_clarification">on %1$s</string>
 
 
-    <string name="share_privilege_unshare">Unshare</string>
-
     <string name="action_clear_failed_uploads">Clear failed uploads</string>
     <string name="action_clear_failed_uploads">Clear failed uploads</string>
 
 
     <string name="action_switch_grid_view">Grid view</string>
     <string name="action_switch_grid_view">Grid view</string>
@@ -921,17 +919,13 @@
     <string name="qr_could_not_be_read">QR code could not be read!</string>
     <string name="qr_could_not_be_read">QR code could not be read!</string>
     <string name="note_icon_hint">Note icon</string>
     <string name="note_icon_hint">Note icon</string>
     <string name="add_another_public_share_link">Add another link</string>
     <string name="add_another_public_share_link">Add another link</string>
-    <string name="unshare">Unshare</string>
-    <string name="allow_editing">Allow editing</string>
     <string name="add_new_public_share">Add new public share link</string>
     <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="public_share_name">New name</string>
     <string name="share_link_with_label">Share link (%1$s)</string>
     <string name="share_link_with_label">Share link (%1$s)</string>
     <string name="share_link">Share link</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="allow_resharing">Allow resharing</string>
     <string name="link_share_read_only">Read only</string>
     <string name="link_share_read_only">Read only</string>
+    <string name="link_share_editing">Editing</string>
     <string name="link_share_allow_upload_and_editing">Allow upload and editing</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="link_share_file_drop">File drop (upload only)</string>
     <string name="could_not_retrieve_shares">Could not retrieve shares</string>
     <string name="could_not_retrieve_shares">Could not retrieve shares</string>
@@ -970,6 +964,23 @@
     <string name="create">Create</string>
     <string name="create">Create</string>
     <string name="select_one_template">Please select one template</string>
     <string name="select_one_template">Please select one template</string>
     <string name="choose_template_helper_text">Please choose a template and enter a file name.</string>
     <string name="choose_template_helper_text">Please choose a template and enter a file name.</string>
+    <string name="share_permission_view_only">View only</string>
+    <string name="share_permission_can_edit">Can edit</string>
+    <string name="share_permission_file_drop">File drop</string>
+    <string name="share_permissions">Share Permissions</string>
+    <string name="advanced_settings">Advanced Settings</string>
+    <string name="common_next">Next</string>
+    <string name="send_share">Send Share</string>
+    <string name="no_share_permission_selected">Please select at least one permission to share.</string>
+    <string name="share_link_empty_note_message">Please enter note.</string>
+    <string name="share_copy_link">Share &amp; Copy Link</string>
+    <string name="set_note">Set Note</string>
+    <string name="share_open_in">Open in…</string>
+    <string name="share_send_new_email">Send new email</string>
+    <string name="link_name">Link Name</string>
+    <string name="delete_link">Delete Link</string>
+    <string name="share_settings">Settings</string>
+    <string name="common_confirm">Confirm</string>
     <string name="strict_mode">Strict mode: no HTTP connection allowed!</string>
     <string name="strict_mode">Strict mode: no HTTP connection allowed!</string>
     <string name="fullscreen">Fullscreen</string>
     <string name="fullscreen">Fullscreen</string>
     <string name="destination_filename">Destination filename</string>
     <string name="destination_filename">Destination filename</string>

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

@@ -42,6 +42,7 @@ class ShareeListAdapterTest {
     private val fileActivity: FileActivity? = null
     private val fileActivity: FileActivity? = null
 
 
     @Test
     @Test
+    @Suppress("LongMethod")
     fun testSorting() {
     fun testSorting() {
         MockitoAnnotations.openMocks(this)
         MockitoAnnotations.openMocks(this)
         val resources = Mockito.mock(Resources::class.java)
         val resources = Mockito.mock(Resources::class.java)