diff --git a/.github/workflows/autoApproveDependabot.yml b/.github/workflows/autoApproveDependabot.yml new file mode 100644 index 000000000000..72f564f8bb71 --- /dev/null +++ b/.github/workflows/autoApproveDependabot.yml @@ -0,0 +1,11 @@ +name: Auto approve +on: pull_request + +jobs: + auto-approve: + runs-on: ubuntu-latest + steps: + - uses: hmarr/auto-approve-action@v2.0.0 + if: github.actor == 'dependabot[bot]' || github.actor == 'dependabot-preview[bot]' + with: + github-token: "${{ secrets.DEPENDABOT_AUTOMERGE_TOKEN }}" diff --git a/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsSharing.png b/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsSharing.png new file mode 100644 index 000000000000..cfa849fe3c0b Binary files /dev/null and b/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsSharing.png differ diff --git a/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailSharingFragment.png b/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailSharingFragment.png index f3f1f6315e71..7423f57ced0b 100644 Binary files a/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailSharingFragment.png and b/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailSharingFragment.png differ diff --git a/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileAllShareTypes.png b/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileAllShareTypes.png new file mode 100644 index 000000000000..c9634d7fc223 Binary files /dev/null and b/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileAllShareTypes.png differ diff --git a/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileNone.png b/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileNone.png new file mode 100644 index 000000000000..eed7aa4f5556 Binary files /dev/null and b/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileNone.png differ diff --git a/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileResharingNotAllowed.png b/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileResharingNotAllowed.png new file mode 100644 index 000000000000..7423f57ced0b Binary files /dev/null and b/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileResharingNotAllowed.png differ diff --git a/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listShares_file_allShareTypes.png b/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listShares_file_allShareTypes.png new file mode 100644 index 000000000000..c9634d7fc223 Binary files /dev/null and b/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listShares_file_allShareTypes.png differ diff --git a/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listShares_file_none.png b/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listShares_file_none.png new file mode 100644 index 000000000000..eed7aa4f5556 Binary files /dev/null and b/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listShares_file_none.png differ diff --git a/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listShares_file_resharing_not_allowed.png b/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listShares_file_resharing_not_allowed.png new file mode 100644 index 000000000000..7423f57ced0b Binary files /dev/null and b/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listShares_file_resharing_not_allowed.png differ diff --git a/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_publicLink_optionMenu.png b/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_publicLink_optionMenu.png new file mode 100644 index 000000000000..eed7aa4f5556 Binary files /dev/null and b/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_publicLink_optionMenu.png differ diff --git a/scripts/analysis/findbugs-results.txt b/scripts/analysis/findbugs-results.txt index 18eed1357e5c..ab760c939afd 100644 --- a/scripts/analysis/findbugs-results.txt +++ b/scripts/analysis/findbugs-results.txt @@ -1 +1 @@ -320 +319 \ No newline at end of file diff --git a/scripts/analysis/lint-results.txt b/scripts/analysis/lint-results.txt index 89df7437f06f..31824795b1a7 100644 --- a/scripts/analysis/lint-results.txt +++ b/scripts/analysis/lint-results.txt @@ -1,2 +1,2 @@ DO NOT TOUCH; GENERATED BY DRONE - Lint Report: 81 warnings + Lint Report: 80 warnings diff --git a/src/androidTest/java/com/nextcloud/client/FileDisplayActivityIT.java b/src/androidTest/java/com/nextcloud/client/FileDisplayActivityIT.java index 353d2daa1f0b..186cfb78461c 100644 --- a/src/androidTest/java/com/nextcloud/client/FileDisplayActivityIT.java +++ b/src/androidTest/java/com/nextcloud/client/FileDisplayActivityIT.java @@ -97,7 +97,7 @@ public void showShares() { "users", false, "", - OCShare.DEFAULT_PERMISSION) + OCShare.NO_PERMISSION) .execute(client).isSuccess()); // share folder to circle diff --git a/src/androidTest/java/com/owncloud/android/AbstractIT.java b/src/androidTest/java/com/owncloud/android/AbstractIT.java index 5569a3555365..8796e0c39662 100644 --- a/src/androidTest/java/com/owncloud/android/AbstractIT.java +++ b/src/androidTest/java/com/owncloud/android/AbstractIT.java @@ -58,6 +58,7 @@ import static androidx.test.InstrumentationRegistry.getInstrumentation; import static androidx.test.espresso.Espresso.onView; import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static com.owncloud.android.lib.common.accounts.AccountUtils.Constants.KEY_USER_ID; import static org.junit.Assert.assertTrue; @@ -96,7 +97,7 @@ public static void beforeAll() { Account temp = new Account("test@https://server.com", MainApp.getAccountType(targetContext)); platformAccountManager.addAccountExplicitly(temp, "password", null); platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_OC_BASE_URL, "https://server.com"); - platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_USER_ID, "test"); + platformAccountManager.setUserData(temp, KEY_USER_ID, "test"); final UserAccountManager userAccountManager = UserAccountManagerImpl.fromContext(targetContext); account = userAccountManager.getAccountByName("test@https://server.com"); @@ -381,4 +382,8 @@ private String createName() { return name; } + + public static String getUserId(User user) { + return AccountManager.get(targetContext).getUserData(user.toPlatformAccount(), KEY_USER_ID); + } } diff --git a/src/androidTest/java/com/owncloud/android/authentication/AuthenticatorActivityIT.kt b/src/androidTest/java/com/owncloud/android/authentication/AuthenticatorActivityIT.kt index 63c9721f4c7f..c12d8ed10d2f 100644 --- a/src/androidTest/java/com/owncloud/android/authentication/AuthenticatorActivityIT.kt +++ b/src/androidTest/java/com/owncloud/android/authentication/AuthenticatorActivityIT.kt @@ -32,6 +32,7 @@ class AuthenticatorActivityIT { } @Test + @Suppress("TooGenericExceptionCaught") fun tryCatch() { val color = try { Color.parseColor("1") @@ -43,6 +44,7 @@ class AuthenticatorActivityIT { } @Test + @Suppress("TooGenericExceptionCaught") fun tryCatch2() { val color = try { Color.parseColor("") @@ -54,6 +56,7 @@ class AuthenticatorActivityIT { } @Test + @Suppress("TooGenericExceptionCaught") fun tryCatch3() { val color = try { Color.parseColor(null) @@ -65,6 +68,7 @@ class AuthenticatorActivityIT { } @Test + @Suppress("TooGenericExceptionCaught") fun tryCatch4() { val color = try { Color.parseColor("abc") diff --git a/src/androidTest/java/com/owncloud/android/datamodel/OCFileUnitTest.java b/src/androidTest/java/com/owncloud/android/datamodel/OCFileUnitTest.java index 4f7f6e194570..5965906ab4d0 100644 --- a/src/androidTest/java/com/owncloud/android/datamodel/OCFileUnitTest.java +++ b/src/androidTest/java/com/owncloud/android/datamodel/OCFileUnitTest.java @@ -88,7 +88,6 @@ public void writeThenReadAsParcelable() { mFile.setEtag(ETAG); mFile.setSharedViaLink(true); mFile.setSharedWithSharee(true); - mFile.setPublicLink(PUBLIC_LINK); mFile.setPermissions(PERMISSIONS); mFile.setRemoteId(REMOTE_ID); mFile.setUpdateThumbnailNeeded(true); @@ -122,7 +121,6 @@ public void writeThenReadAsParcelable() { assertThat(fileReadFromParcel.getEtag(), is(ETAG)); assertThat(fileReadFromParcel.isSharedViaLink(), is(true)); assertThat(fileReadFromParcel.isSharedWithSharee(), is(true)); - assertThat(fileReadFromParcel.getPublicLink(), is(PUBLIC_LINK)); assertThat(fileReadFromParcel.getPermissions(), is(PERMISSIONS)); assertThat(fileReadFromParcel.getRemoteId(), is(REMOTE_ID)); assertThat(fileReadFromParcel.isUpdateThumbnailNeeded(), is(true)); diff --git a/src/androidTest/java/com/owncloud/android/ui/fragment/AvatarIT.kt b/src/androidTest/java/com/owncloud/android/ui/fragment/AvatarIT.kt index 82a34689f3a2..8ec894730ab5 100644 --- a/src/androidTest/java/com/owncloud/android/ui/fragment/AvatarIT.kt +++ b/src/androidTest/java/com/owncloud/android/ui/fragment/AvatarIT.kt @@ -40,7 +40,7 @@ class AvatarIT : AbstractIT() { @ScreenshotTest fun showAvatars() { val avatarRadius = targetContext.resources.getDimension(R.dimen.list_item_avatar_icon_radius) - val width = DisplayUtils.convertDpToPixel(40f, targetContext) + val width = DisplayUtils.convertDpToPixel(2 * avatarRadius, targetContext) val sut = testActivityRule.launchActivity(null) val fragment = AvatarTestFragment() @@ -56,6 +56,7 @@ class AvatarIT : AbstractIT() { fragment.addAvatar("email@server.com", avatarRadius, width, targetContext) } + shortSleep() waitForIdleSync() screenshot(sut) } diff --git a/src/androidTest/java/com/owncloud/android/ui/fragment/AvatarTestFragment.kt b/src/androidTest/java/com/owncloud/android/ui/fragment/AvatarTestFragment.kt index 70e14687c6bd..b6913983b766 100644 --- a/src/androidTest/java/com/owncloud/android/ui/fragment/AvatarTestFragment.kt +++ b/src/androidTest/java/com/owncloud/android/ui/fragment/AvatarTestFragment.kt @@ -45,7 +45,7 @@ internal class AvatarTestFragment : Fragment() { } fun addAvatar(name: String, avatarRadius: Float, width: Int, targetContext: Context) { - val margin = 10 + val margin = padding val imageView = ImageView(targetContext) imageView.setImageDrawable(TextDrawable.createNamedAvatar(name, avatarRadius)) @@ -56,4 +56,8 @@ internal class AvatarTestFragment : Fragment() { list.addView(imageView) } + + companion object { + private const val padding = 10 + } } diff --git a/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailFragmentStaticServerIT.kt b/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailFragmentStaticServerIT.kt index 671fa6631454..0d5757d3de9e 100644 --- a/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailFragmentStaticServerIT.kt +++ b/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailFragmentStaticServerIT.kt @@ -172,7 +172,7 @@ class FileDetailFragmentStaticServerIT : AbstractIT() { @Test @ScreenshotTest - fun showDetails_Sharing() { + fun showDetailsSharing() { val sut = testActivityRule.launchActivity(null) sut.addFragment(FileDetailFragment.newInstance(file, user, 1)) diff --git a/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailSharingFragmentIT.kt b/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailSharingFragmentIT.kt new file mode 100644 index 000000000000..c644ed8bfe85 --- /dev/null +++ b/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailSharingFragmentIT.kt @@ -0,0 +1,630 @@ +/* + * + * Nextcloud Android client application + * + * @author Tobias Kaminsky + * Copyright (C) 2020 Tobias Kaminsky + * Copyright (C) 2020 Nextcloud GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package com.owncloud.android.ui.fragment + +import android.Manifest +import android.widget.ImageView +import androidx.appcompat.widget.PopupMenu +import androidx.test.espresso.intent.rule.IntentsTestRule +import androidx.test.rule.GrantPermissionRule +import com.nextcloud.client.TestActivity +import com.owncloud.android.AbstractIT +import com.owncloud.android.R +import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.lib.resources.shares.OCShare +import com.owncloud.android.lib.resources.shares.OCShare.CREATE_PERMISSION_FLAG +import com.owncloud.android.lib.resources.shares.OCShare.DELETE_PERMISSION_FLAG +import com.owncloud.android.lib.resources.shares.OCShare.MAXIMUM_PERMISSIONS_FOR_FILE +import com.owncloud.android.lib.resources.shares.OCShare.MAXIMUM_PERMISSIONS_FOR_FOLDER +import com.owncloud.android.lib.resources.shares.OCShare.NO_PERMISSION +import com.owncloud.android.lib.resources.shares.OCShare.READ_PERMISSION_FLAG +import com.owncloud.android.lib.resources.shares.OCShare.SHARE_PERMISSION_FLAG +import com.owncloud.android.lib.resources.shares.ShareType +import com.owncloud.android.utils.ScreenshotTest +import org.junit.After +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Rule +import org.junit.Test + +class FileDetailSharingFragmentIT : AbstractIT() { + @get:Rule + val testActivityRule = IntentsTestRule(TestActivity::class.java, true, false) + + @get:Rule + val permissionRule: GrantPermissionRule = GrantPermissionRule.grant(Manifest.permission.WRITE_EXTERNAL_STORAGE) + + lateinit var file: OCFile + lateinit var folder: OCFile + lateinit var activity: TestActivity + + @Before + fun before() { + activity = testActivityRule.launchActivity(null) + file = OCFile("/test.md").apply { + parentId = activity.storageManager.getFileByEncryptedRemotePath("/").fileId + permissions = OCFile.PERMISSION_CAN_RESHARE + } + + folder = OCFile("/test").apply { + setFolder() + parentId = activity.storageManager.getFileByEncryptedRemotePath("/").fileId + permissions = OCFile.PERMISSION_CAN_RESHARE + } + } + + @Test + @ScreenshotTest + fun listSharesFileNone() { + show(file) + } + + @Test + @ScreenshotTest + fun listSharesFileResharingNotAllowed() { + file.permissions = "" + + show(file) + } + + @Test + @ScreenshotTest + @Suppress("MagicNumber") + fun listSharesFileAllShareTypes() { + OCShare(file.decryptedRemotePath).apply { + remoteId = 1 + shareType = ShareType.USER + sharedWithDisplayName = "Admin" + permissions = MAXIMUM_PERMISSIONS_FOR_FILE + userId = getUserId(user) + activity.storageManager.saveShare(this) + } + + OCShare(file.decryptedRemotePath).apply { + remoteId = 2 + shareType = ShareType.GROUP + sharedWithDisplayName = "Group" + permissions = MAXIMUM_PERMISSIONS_FOR_FILE + userId = getUserId(user) + activity.storageManager.saveShare(this) + } + + OCShare(file.decryptedRemotePath).apply { + remoteId = 3 + shareType = ShareType.EMAIL + sharedWithDisplayName = "admin@nextcloud.server.com" + userId = getUserId(user) + activity.storageManager.saveShare(this) + } + + OCShare(file.decryptedRemotePath).apply { + remoteId = 4 + shareType = ShareType.PUBLIC_LINK + sharedWithDisplayName = "Customer" + activity.storageManager.saveShare(this) + } + + OCShare(file.decryptedRemotePath).apply { + remoteId = 5 + shareType = ShareType.PUBLIC_LINK + sharedWithDisplayName = "Colleagues" + activity.storageManager.saveShare(this) + } + + OCShare(file.decryptedRemotePath).apply { + remoteId = 6 + shareType = ShareType.FEDERATED + sharedWithDisplayName = "admin@nextcloud.remoteserver.com" + permissions = OCShare.FEDERATED_PERMISSIONS_FOR_FILE + userId = getUserId(user) + activity.storageManager.saveShare(this) + } + + OCShare(file.decryptedRemotePath).apply { + remoteId = 7 + shareType = ShareType.CIRCLE + sharedWithDisplayName = "Private circle" + permissions = SHARE_PERMISSION_FLAG + userId = getUserId(user) + activity.storageManager.saveShare(this) + } + + OCShare(file.decryptedRemotePath).apply { + remoteId = 8 + shareType = ShareType.ROOM + sharedWithDisplayName = "Meeting" + permissions = SHARE_PERMISSION_FLAG + userId = getUserId(user) + activity.storageManager.saveShare(this) + } + + show(file) + } + + private fun show(file: OCFile) { + val fragment = FileDetailSharingFragment.newInstance(file, user) + + activity.addFragment(fragment) + + waitForIdleSync() + + screenshot(activity) + } + + @Test + @Suppress("MagicNumber") + // public link and email are handled the same way + fun publicLinkOptionMenuFolder() { + val sut = FileDetailSharingFragment.newInstance(file, user) + activity.addFragment(sut) + shortSleep() + sut.refreshCapabilitiesFromDB() + + val overflowMenuShareLink = ImageView(targetContext) + val popup = PopupMenu(targetContext, overflowMenuShareLink) + popup.inflate(R.menu.fragment_file_detail_sharing_public_link) + val publicShare = OCShare().apply { + isFolder = true + shareType = ShareType.PUBLIC_LINK + permissions = 17 + } + + sut.prepareLinkOptionsMenu(popup.menu, publicShare) + + // check if items are visible + assertTrue(popup.menu.findItem(R.id.action_hide_file_download).isVisible) + assertTrue(popup.menu.findItem(R.id.action_password).isVisible) + assertTrue(popup.menu.findItem(R.id.action_share_expiration_date).isVisible) + assertTrue(popup.menu.findItem(R.id.action_share_send_link).isVisible) + assertTrue(popup.menu.findItem(R.id.action_share_send_note).isVisible) + assertTrue(popup.menu.findItem(R.id.action_edit_label).isVisible) + assertTrue(popup.menu.findItem(R.id.action_unshare).isVisible) + assertTrue(popup.menu.findItem(R.id.action_add_another_public_share_link).isVisible) + + // read-only + assertTrue(popup.menu.findItem(R.id.link_share_read_only).isChecked) + assertFalse(popup.menu.findItem(R.id.link_share_allow_upload_and_editing).isChecked) + assertFalse(popup.menu.findItem(R.id.link_share_file_drop).isChecked) + + // upload and editing + publicShare.permissions = MAXIMUM_PERMISSIONS_FOR_FOLDER + sut.prepareLinkOptionsMenu(popup.menu, publicShare) + assertFalse(popup.menu.findItem(R.id.link_share_read_only).isChecked) + assertTrue(popup.menu.findItem(R.id.link_share_allow_upload_and_editing).isChecked) + assertFalse(popup.menu.findItem(R.id.link_share_file_drop).isChecked) + + // file drop + publicShare.permissions = 4 + sut.prepareLinkOptionsMenu(popup.menu, publicShare) + assertFalse(popup.menu.findItem(R.id.link_share_read_only).isChecked) + assertFalse(popup.menu.findItem(R.id.link_share_allow_upload_and_editing).isChecked) + assertTrue(popup.menu.findItem(R.id.link_share_file_drop).isChecked) + + // password protection + publicShare.shareWith = "someValue" + sut.prepareLinkOptionsMenu(popup.menu, publicShare) + assertTrue( + popup.menu.findItem(R.id.action_password).title == targetContext.getString(R.string.share_password_title) + ) + + publicShare.shareWith = "" + sut.prepareLinkOptionsMenu(popup.menu, publicShare) + assertTrue( + popup.menu.findItem(R.id.action_password).title == targetContext.getString(R.string.share_no_password_title) + ) + + // hide download + publicShare.isHideFileDownload = true + publicShare.permissions = MAXIMUM_PERMISSIONS_FOR_FOLDER + sut.prepareLinkOptionsMenu(popup.menu, publicShare) + assertTrue(popup.menu.findItem(R.id.action_hide_file_download).isChecked) + + publicShare.isHideFileDownload = false + sut.prepareLinkOptionsMenu(popup.menu, publicShare) + assertFalse(popup.menu.findItem(R.id.action_hide_file_download).isChecked) + + publicShare.expirationDate = 1582019340000 + sut.prepareLinkOptionsMenu(popup.menu, publicShare) + assertTrue( + popup.menu.findItem(R.id.action_share_expiration_date).title + .startsWith(targetContext.getString(R.string.share_expiration_date_label).split(" ")[0]) + ) + + publicShare.expirationDate = 0 + sut.prepareLinkOptionsMenu(popup.menu, publicShare) + assertTrue( + popup.menu.findItem(R.id.action_share_expiration_date).title == + targetContext.getString(R.string.share_no_expiration_date_label) + ) + + // file + publicShare.isFolder = false + publicShare.permissions = READ_PERMISSION_FLAG + sut.prepareLinkOptionsMenu(popup.menu, publicShare) + // check if items are visible + assertTrue(popup.menu.findItem(R.id.action_hide_file_download).isVisible) + assertTrue(popup.menu.findItem(R.id.action_password).isVisible) + assertTrue(popup.menu.findItem(R.id.action_share_expiration_date).isVisible) + assertTrue(popup.menu.findItem(R.id.action_share_send_link).isVisible) + assertTrue(popup.menu.findItem(R.id.action_share_send_note).isVisible) + assertTrue(popup.menu.findItem(R.id.action_edit_label).isVisible) + assertTrue(popup.menu.findItem(R.id.action_unshare).isVisible) + assertTrue(popup.menu.findItem(R.id.action_add_another_public_share_link).isVisible) + + assertFalse(popup.menu.findItem(R.id.link_share_read_only).isVisible) + assertFalse(popup.menu.findItem(R.id.link_share_allow_upload_and_editing).isVisible) + assertFalse(popup.menu.findItem(R.id.link_share_file_drop).isVisible) + assertTrue(popup.menu.findItem(R.id.allow_editing).isVisible) + + // allow editing + publicShare.permissions = 17 // from server + sut.prepareLinkOptionsMenu(popup.menu, publicShare) + assertFalse(popup.menu.findItem(R.id.allow_editing).isChecked) + + publicShare.permissions = 19 // from server + sut.prepareLinkOptionsMenu(popup.menu, publicShare) + assertTrue(popup.menu.findItem(R.id.allow_editing).isChecked) + + // hide download + publicShare.isHideFileDownload = true + sut.prepareLinkOptionsMenu(popup.menu, publicShare) + assertTrue(popup.menu.findItem(R.id.action_hide_file_download).isChecked) + + publicShare.isHideFileDownload = false + sut.prepareLinkOptionsMenu(popup.menu, publicShare) + assertFalse(popup.menu.findItem(R.id.action_hide_file_download).isChecked) + + // password protection + publicShare.isPasswordProtected = true + publicShare.shareWith = "someValue" + sut.prepareLinkOptionsMenu(popup.menu, publicShare) + assertTrue( + popup.menu.findItem(R.id.action_password).title == targetContext.getString(R.string.share_password_title) + ) + + publicShare.isPasswordProtected = false + publicShare.shareWith = "" + sut.prepareLinkOptionsMenu(popup.menu, publicShare) + assertTrue( + popup.menu.findItem(R.id.action_password).title == targetContext.getString(R.string.share_no_password_title) + ) + + // expires + publicShare.expirationDate = 1582019340 + sut.prepareLinkOptionsMenu(popup.menu, publicShare) + assertTrue( + popup.menu.findItem(R.id.action_share_expiration_date).title + .startsWith(targetContext.getString(R.string.share_expiration_date_label).split(" ")[0]) + ) + + publicShare.expirationDate = 0 + sut.prepareLinkOptionsMenu(popup.menu, publicShare) + assertTrue( + popup.menu.findItem(R.id.action_share_expiration_date).title == + targetContext.getString(R.string.share_no_expiration_date_label) + ) + } + + @Test + @Suppress("MagicNumber") + // public link and email are handled the same way + fun publicLinkOptionMenuFile() { + val sut = FileDetailSharingFragment.newInstance(file, user) + activity.addFragment(sut) + shortSleep() + sut.refreshCapabilitiesFromDB() + + val overflowMenuShareLink = ImageView(targetContext) + val popup = PopupMenu(targetContext, overflowMenuShareLink) + popup.inflate(R.menu.fragment_file_detail_sharing_public_link) + val publicShare = OCShare().apply { + isFolder = false + shareType = ShareType.PUBLIC_LINK + permissions = 17 + } + + sut.prepareLinkOptionsMenu(popup.menu, publicShare) + + // check if items are visible + assertTrue(popup.menu.findItem(R.id.action_hide_file_download).isVisible) + assertTrue(popup.menu.findItem(R.id.action_password).isVisible) + assertTrue(popup.menu.findItem(R.id.action_share_expiration_date).isVisible) + assertTrue(popup.menu.findItem(R.id.action_share_send_link).isVisible) + assertTrue(popup.menu.findItem(R.id.action_share_send_note).isVisible) + assertTrue(popup.menu.findItem(R.id.action_edit_label).isVisible) + assertTrue(popup.menu.findItem(R.id.action_unshare).isVisible) + assertTrue(popup.menu.findItem(R.id.action_add_another_public_share_link).isVisible) + + assertFalse(popup.menu.findItem(R.id.link_share_read_only).isVisible) + assertFalse(popup.menu.findItem(R.id.link_share_allow_upload_and_editing).isVisible) + assertFalse(popup.menu.findItem(R.id.link_share_file_drop).isVisible) + assertTrue(popup.menu.findItem(R.id.allow_editing).isVisible) + + // password protection + publicShare.shareWith = "someValue" + sut.prepareLinkOptionsMenu(popup.menu, publicShare) + assertTrue( + popup.menu.findItem(R.id.action_password).title == targetContext.getString(R.string.share_password_title) + ) + + publicShare.shareWith = "" + sut.prepareLinkOptionsMenu(popup.menu, publicShare) + assertTrue( + popup.menu.findItem(R.id.action_password).title == targetContext.getString(R.string.share_no_password_title) + ) + + // hide download + publicShare.isHideFileDownload = true + sut.prepareLinkOptionsMenu(popup.menu, publicShare) + assertTrue(popup.menu.findItem(R.id.action_hide_file_download).isChecked) + + publicShare.isHideFileDownload = false + sut.prepareLinkOptionsMenu(popup.menu, publicShare) + assertFalse(popup.menu.findItem(R.id.action_hide_file_download).isChecked) + + // expiration date + publicShare.expirationDate = 1582019340000 + sut.prepareLinkOptionsMenu(popup.menu, publicShare) + assertTrue( + popup.menu.findItem(R.id.action_share_expiration_date).title + .startsWith(targetContext.getString(R.string.share_expiration_date_label).split(" ")[0]) + ) + + publicShare.expirationDate = 0 + sut.prepareLinkOptionsMenu(popup.menu, publicShare) + assertTrue( + popup.menu.findItem(R.id.action_share_expiration_date).title == + targetContext.getString(R.string.share_no_expiration_date_label) + ) + + publicShare.isFolder = false + publicShare.permissions = READ_PERMISSION_FLAG + sut.prepareLinkOptionsMenu(popup.menu, publicShare) + + // allow editing + publicShare.permissions = 17 // from server + assertFalse(popup.menu.findItem(R.id.allow_editing).isChecked) + + publicShare.permissions = 19 // from server + sut.prepareLinkOptionsMenu(popup.menu, publicShare) + assertTrue(popup.menu.findItem(R.id.allow_editing).isChecked) + } + + @Test + @Suppress("MagicNumber") + // also applies for + // group + // conversation + // circle + // federated share + fun userOptionMenuFile() { + val sut = FileDetailSharingFragment.newInstance(file, user) + activity.addFragment(sut) + shortSleep() + sut.refreshCapabilitiesFromDB() + + val overflowMenuShareLink = ImageView(targetContext) + val popup = PopupMenu(targetContext, overflowMenuShareLink) + popup.inflate(R.menu.item_user_sharing_settings) + val userShare = OCShare().apply { + isFolder = false + shareType = ShareType.USER + permissions = 17 + } + + sut.prepareUserOptionsMenu(popup.menu, userShare) + assertFalse(popup.menu.findItem(R.id.allow_creating).isVisible) + assertFalse(popup.menu.findItem(R.id.allow_deleting).isVisible) + + // allow editing + userShare.permissions = 17 // from server + assertFalse(popup.menu.findItem(R.id.allow_editing).isChecked) + + userShare.permissions = 19 // from server + sut.prepareUserOptionsMenu(popup.menu, userShare) + assertTrue(popup.menu.findItem(R.id.allow_editing).isChecked) + + // allow reshare + userShare.permissions = 1 // from server + sut.prepareUserOptionsMenu(popup.menu, userShare) + assertFalse(popup.menu.findItem(R.id.allow_resharing).isChecked) + + userShare.permissions = 17 // from server + sut.prepareUserOptionsMenu(popup.menu, userShare) + assertTrue(popup.menu.findItem(R.id.allow_resharing).isChecked) + + // set expiration date + userShare.expirationDate = 1582019340000 + sut.prepareUserOptionsMenu(popup.menu, userShare) + assertTrue( + popup.menu.findItem(R.id.action_expiration_date).title + .startsWith(targetContext.getString(R.string.share_expiration_date_label).split(" ")[0]) + ) + + userShare.expirationDate = 0 + sut.prepareUserOptionsMenu(popup.menu, userShare) + assertTrue( + popup.menu.findItem(R.id.action_expiration_date).title == + targetContext.getString(R.string.share_no_expiration_date_label) + ) + + // note + assertTrue(popup.menu.findItem(R.id.action_share_send_note).isVisible) + } + + @Test + @Suppress("MagicNumber") + // also applies for + // group + // conversation + // circle + // federated share + fun userOptionMenuFolder() { + val sut = FileDetailSharingFragment.newInstance(file, user) + activity.addFragment(sut) + shortSleep() + sut.refreshCapabilitiesFromDB() + + val overflowMenuShareLink = ImageView(targetContext) + val popup = PopupMenu(targetContext, overflowMenuShareLink) + popup.inflate(R.menu.item_user_sharing_settings) + val userShare = OCShare().apply { + isFolder = true + shareType = ShareType.USER + permissions = 17 + } + + sut.prepareUserOptionsMenu(popup.menu, userShare) + assertTrue(popup.menu.findItem(R.id.allow_creating).isVisible) + assertTrue(popup.menu.findItem(R.id.allow_deleting).isVisible) + + // allow editing + userShare.permissions = 17 // from server + assertFalse(popup.menu.findItem(R.id.allow_editing).isChecked) + + userShare.permissions = 19 // from server + sut.prepareUserOptionsMenu(popup.menu, userShare) + assertTrue(popup.menu.findItem(R.id.allow_editing).isChecked) + + // allow reshare + userShare.permissions = 1 // from server + sut.prepareUserOptionsMenu(popup.menu, userShare) + assertFalse(popup.menu.findItem(R.id.allow_resharing).isChecked) + + userShare.permissions = 17 // from server + sut.prepareUserOptionsMenu(popup.menu, userShare) + assertTrue(popup.menu.findItem(R.id.allow_resharing).isChecked) + + // set expiration date + userShare.expirationDate = 1582019340000 + sut.prepareUserOptionsMenu(popup.menu, userShare) + assertTrue( + popup.menu.findItem(R.id.action_expiration_date).title + .startsWith(targetContext.getString(R.string.share_expiration_date_label).split(" ")[0]) + ) + + userShare.expirationDate = 0 + sut.prepareUserOptionsMenu(popup.menu, userShare) + assertTrue( + popup.menu.findItem(R.id.action_expiration_date).title == + targetContext.getString(R.string.share_no_expiration_date_label) + ) + + // note + assertTrue(popup.menu.findItem(R.id.action_share_send_note).isVisible) + } + + @Test + fun testUploadAndEditingSharePermissions() { + val sut = FileDetailSharingFragment() + + val share = OCShare().apply { + permissions = MAXIMUM_PERMISSIONS_FOR_FOLDER + } + assertTrue(sut.isUploadAndEditingAllowed(share)) + + share.permissions = NO_PERMISSION + assertFalse(sut.isUploadAndEditingAllowed(share)) + + share.permissions = READ_PERMISSION_FLAG + assertFalse(sut.isUploadAndEditingAllowed(share)) + + share.permissions = CREATE_PERMISSION_FLAG + assertFalse(sut.isUploadAndEditingAllowed(share)) + + share.permissions = DELETE_PERMISSION_FLAG + assertFalse(sut.isUploadAndEditingAllowed(share)) + + share.permissions = SHARE_PERMISSION_FLAG + assertFalse(sut.isUploadAndEditingAllowed(share)) + } + + @Test + @Suppress("MagicNumber") + fun testReadOnlySharePermissions() { + val sut = FileDetailSharingFragment() + + val share = OCShare().apply { + permissions = 17 + } + assertTrue(sut.isReadOnly(share)) + + share.permissions = NO_PERMISSION + assertFalse(sut.isReadOnly(share)) + + share.permissions = READ_PERMISSION_FLAG + assertTrue(sut.isReadOnly(share)) + + share.permissions = CREATE_PERMISSION_FLAG + assertFalse(sut.isReadOnly(share)) + + share.permissions = DELETE_PERMISSION_FLAG + assertFalse(sut.isReadOnly(share)) + + share.permissions = SHARE_PERMISSION_FLAG + assertFalse(sut.isReadOnly(share)) + + share.permissions = MAXIMUM_PERMISSIONS_FOR_FOLDER + assertFalse(sut.isReadOnly(share)) + + share.permissions = MAXIMUM_PERMISSIONS_FOR_FILE + assertFalse(sut.isReadOnly(share)) + } + + @Test + @Suppress("MagicNumber") + fun testFileDropSharePermissions() { + val sut = FileDetailSharingFragment() + + val share = OCShare().apply { + permissions = 4 + } + assertTrue(sut.isFileDrop(share)) + + share.permissions = NO_PERMISSION + assertFalse(sut.isFileDrop(share)) + + share.permissions = READ_PERMISSION_FLAG + assertFalse(sut.isFileDrop(share)) + + share.permissions = CREATE_PERMISSION_FLAG + assertTrue(sut.isFileDrop(share)) + + share.permissions = DELETE_PERMISSION_FLAG + assertFalse(sut.isFileDrop(share)) + + share.permissions = SHARE_PERMISSION_FLAG + assertFalse(sut.isFileDrop(share)) + + share.permissions = MAXIMUM_PERMISSIONS_FOR_FOLDER + assertFalse(sut.isFileDrop(share)) + + share.permissions = MAXIMUM_PERMISSIONS_FOR_FILE + assertFalse(sut.isFileDrop(share)) + } + + @After + fun after() { + activity.storageManager.cleanShares() + } +} diff --git a/src/debug/java/com/nextcloud/client/TestActivity.kt b/src/debug/java/com/nextcloud/client/TestActivity.kt index 4d0bfa1d630d..7a054d3eba83 100644 --- a/src/debug/java/com/nextcloud/client/TestActivity.kt +++ b/src/debug/java/com/nextcloud/client/TestActivity.kt @@ -32,6 +32,8 @@ import com.owncloud.android.datamodel.FileDataStorageManager import com.owncloud.android.datamodel.OCFile import com.owncloud.android.files.services.FileDownloader import com.owncloud.android.files.services.FileUploader +import com.owncloud.android.lib.resources.status.OCCapability +import com.owncloud.android.lib.resources.status.OwnCloudVersion import com.owncloud.android.services.OperationsService import com.owncloud.android.ui.activity.FileActivity import com.owncloud.android.ui.activity.OnEnforceableRefreshListener @@ -101,6 +103,10 @@ class TestActivity : override fun getStorageManager(): FileDataStorageManager { if (!this::storage.isInitialized) { storage = FileDataStorageManager(account, contentResolver) + + val ocCapability = OCCapability() + ocCapability.versionMayor = OwnCloudVersion.nextcloud_20.majorVersionNumber + storage.saveCapabilities(ocCapability) } return storage diff --git a/src/main/java/com/nextcloud/client/jobs/AccountRemovalWork.kt b/src/main/java/com/nextcloud/client/jobs/AccountRemovalWork.kt index eb4cdd7482a4..6b9d2c4fd6a1 100644 --- a/src/main/java/com/nextcloud/client/jobs/AccountRemovalWork.kt +++ b/src/main/java/com/nextcloud/client/jobs/AccountRemovalWork.kt @@ -25,8 +25,6 @@ package com.nextcloud.client.jobs import android.accounts.Account import android.content.Context -import android.os.Build -import android.provider.DocumentsContract import android.text.TextUtils import androidx.work.Worker import androidx.work.WorkerParameters @@ -48,6 +46,7 @@ import com.owncloud.android.lib.common.OwnCloudClient import com.owncloud.android.lib.common.OwnCloudClientManagerFactory import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.lib.resources.users.RemoteWipeSuccessRemoteOperation +import com.owncloud.android.providers.DocumentsStorageProvider import com.owncloud.android.ui.activity.ContactsPreferenceActivity import com.owncloud.android.ui.activity.ManageAccountsActivity import com.owncloud.android.ui.events.AccountRemovedEvent @@ -127,11 +126,7 @@ class AccountRemovalWork( } } // notify Document Provider - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - val authority = context.resources.getString(R.string.document_provider_authority) - val rootsUri = DocumentsContract.buildRootsUri(authority) - context.contentResolver.notifyChange(rootsUri, null) - } + DocumentsStorageProvider.notifyRootsChanged(context) return Result.success() } diff --git a/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java b/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java index a20830cd1d4c..6f77d05f6a4e 100644 --- a/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java +++ b/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java @@ -61,7 +61,6 @@ import android.os.Handler; import android.os.IBinder; import android.preference.PreferenceManager; -import android.provider.DocumentsContract; import android.text.TextUtils; import android.util.AndroidRuntimeException; import android.view.KeyEvent; @@ -114,6 +113,7 @@ import com.owncloud.android.lib.resources.users.GetUserInfoRemoteOperation; import com.owncloud.android.operations.DetectAuthenticationMethodOperation.AuthenticationMethod; import com.owncloud.android.operations.GetServerInfoOperation; +import com.owncloud.android.providers.DocumentsStorageProvider; import com.owncloud.android.services.OperationsService; import com.owncloud.android.services.OperationsService.OperationsServiceBinder; import com.owncloud.android.ui.activity.FileDisplayActivity; @@ -1287,11 +1287,7 @@ protected boolean createAccount(RemoteOperationResult authResult) { setResult(RESULT_OK, intent); // notify Document Provider - if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - String authority = getResources().getString(R.string.document_provider_authority); - Uri rootsUri = DocumentsContract.buildRootsUri(authority); - getContentResolver().notifyChange(rootsUri, null); - } + DocumentsStorageProvider.notifyRootsChanged(this); return true; } diff --git a/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java b/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java index bbfa32c2cb65..2f265e6cd267 100644 --- a/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java +++ b/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java @@ -68,6 +68,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; public class FileDataStorageManager { @@ -230,7 +231,6 @@ public boolean saveFile(OCFile ocFile) { cv.put(ProviderTableMeta.FILE_ETAG_ON_SERVER, ocFile.getEtagOnServer()); cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, ocFile.isSharedViaLink() ? 1 : 0); cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, ocFile.isSharedWithSharee() ? 1 : 0); - cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, ocFile.getPublicLink()); cv.put(ProviderTableMeta.FILE_PERMISSIONS, ocFile.getPermissions()); cv.put(ProviderTableMeta.FILE_REMOTE_ID, ocFile.getRemoteId()); cv.put(ProviderTableMeta.FILE_UPDATE_THUMBNAIL, ocFile.isUpdateThumbnailNeeded()); @@ -485,7 +485,6 @@ private ContentValues createContentValueForFile(OCFile folder) { cv.put(ProviderTableMeta.FILE_ETAG_ON_SERVER, folder.getEtagOnServer()); cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, folder.isSharedViaLink() ? 1 : 0); cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, folder.isSharedWithSharee() ? 1 : 0); - cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, folder.getPublicLink()); cv.put(ProviderTableMeta.FILE_PERMISSIONS, folder.getPermissions()); cv.put(ProviderTableMeta.FILE_REMOTE_ID, folder.getRemoteId()); cv.put(ProviderTableMeta.FILE_FAVORITE, folder.isFavorite()); @@ -520,7 +519,6 @@ private ContentValues createContentValueForFile(OCFile file, OCFile folder) { cv.put(ProviderTableMeta.FILE_ETAG_ON_SERVER, file.getEtagOnServer()); cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, file.isSharedViaLink() ? 1 : 0); cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, file.isSharedWithSharee() ? 1 : 0); - cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, file.getPublicLink()); cv.put(ProviderTableMeta.FILE_PERMISSIONS, file.getPermissions()); cv.put(ProviderTableMeta.FILE_REMOTE_ID, file.getRemoteId()); cv.put(ProviderTableMeta.FILE_UPDATE_THUMBNAIL, file.isUpdateThumbnailNeeded()); @@ -1006,7 +1004,6 @@ private OCFile createFileInstance(Cursor cursor) { ocFile.setEtagOnServer(cursor.getString(cursor.getColumnIndex(ProviderTableMeta.FILE_ETAG_ON_SERVER))); ocFile.setSharedViaLink(cursor.getInt(cursor.getColumnIndex(ProviderTableMeta.FILE_SHARED_VIA_LINK)) == 1); ocFile.setSharedWithSharee(cursor.getInt(cursor.getColumnIndex(ProviderTableMeta.FILE_SHARED_WITH_SHAREE)) == 1); - ocFile.setPublicLink(cursor.getString(cursor.getColumnIndex(ProviderTableMeta.FILE_PUBLIC_LINK))); ocFile.setPermissions(cursor.getString(cursor.getColumnIndex(ProviderTableMeta.FILE_PERMISSIONS))); ocFile.setRemoteId(cursor.getString(cursor.getColumnIndex(ProviderTableMeta.FILE_REMOTE_ID))); ocFile.setUpdateThumbnailNeeded(cursor.getInt(cursor.getColumnIndex(ProviderTableMeta.FILE_UPDATE_THUMBNAIL)) == 1); @@ -1296,7 +1293,6 @@ private void resetShareFlagsInAllFiles() { ContentValues cv = new ContentValues(); cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, Boolean.FALSE); cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, Boolean.FALSE); - cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, ""); String where = ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?"; String[] whereArgs = new String[]{account.name}; @@ -1316,7 +1312,6 @@ private void resetShareFlagsInFolder(OCFile folder) { ContentValues contentValues = new ContentValues(); contentValues.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, Boolean.FALSE); contentValues.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, Boolean.FALSE); - contentValues.put(ProviderTableMeta.FILE_PUBLIC_LINK, ""); String where = ProviderTableMeta.FILE_ACCOUNT_OWNER + AND + ProviderTableMeta.FILE_PARENT + " = ?"; String[] whereArgs = new String[]{account.name, String.valueOf(folder.getFileId())}; @@ -1336,7 +1331,6 @@ private void resetShareFlagInAFile(String filePath) { ContentValues contentValues = new ContentValues(); contentValues.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, Boolean.FALSE); contentValues.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, Boolean.FALSE); - contentValues.put(ProviderTableMeta.FILE_PUBLIC_LINK, ""); String where = ProviderTableMeta.FILE_ACCOUNT_OWNER + AND + ProviderTableMeta.FILE_PATH + " = ?"; String[] whereArgs = new String[]{account.name, filePath}; @@ -1352,7 +1346,8 @@ private void resetShareFlagInAFile(String filePath) { } } - private void cleanShares() { + @VisibleForTesting + public void cleanShares() { String where = ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?"; String[] whereArgs = new String[]{account.name}; @@ -1447,7 +1442,6 @@ public void updateSharedFiles(Collection sharedFiles) { cv.put(ProviderTableMeta.FILE_ETAG_ON_SERVER, file.getEtagOnServer()); cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, file.isSharedViaLink() ? 1 : 0); cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, file.isSharedWithSharee() ? 1 : 0); - cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, file.getPublicLink()); cv.put(ProviderTableMeta.FILE_PERMISSIONS, file.getPermissions()); cv.put(ProviderTableMeta.FILE_REMOTE_ID, file.getRemoteId()); cv.put(ProviderTableMeta.FILE_FAVORITE, file.isFavorite()); diff --git a/src/main/java/com/owncloud/android/datamodel/OCFile.java b/src/main/java/com/owncloud/android/datamodel/OCFile.java index 2bea653ddbf0..721d409033df 100644 --- a/src/main/java/com/owncloud/android/datamodel/OCFile.java +++ b/src/main/java/com/owncloud/android/datamodel/OCFile.java @@ -47,7 +47,8 @@ public class OCFile implements Parcelable, Comparable, ServerFileInterface { private final static String PERMISSION_SHARED_WITH_ME = "S"; - private final static String PERMISSION_CAN_RESHARE = "R"; + @VisibleForTesting + public final static String PERMISSION_CAN_RESHARE = "R"; private final static String PERMISSION_CAN_WRITE = "CK"; public static final String PATH_SEPARATOR = "/"; @@ -75,7 +76,6 @@ public class OCFile implements Parcelable, Comparable, ServerFileInterfa private String etag; private String etagOnServer; private boolean sharedViaLink; - private String publicLink; private String permissions; private String remoteId; // The fileid namespaced by the instance fileId, globally unique private boolean updateThumbnailNeeded; @@ -150,7 +150,6 @@ private OCFile(Parcel source) { etag = source.readString(); etagOnServer = source.readString(); sharedViaLink = source.readInt() == 1; - publicLink = source.readString(); permissions = source.readString(); remoteId = source.readString(); updateThumbnailNeeded = source.readInt() == 1; @@ -184,7 +183,6 @@ public void writeToParcel(Parcel dest, int flags) { dest.writeString(etag); dest.writeString(etagOnServer); dest.writeInt(sharedViaLink ? 1 : 0); - dest.writeString(publicLink); dest.writeString(permissions); dest.writeString(remoteId); dest.writeInt(updateThumbnailNeeded ? 1 : 0); @@ -450,7 +448,6 @@ private void resetData() { etag = null; etagOnServer = null; sharedViaLink = false; - publicLink = null; permissions = null; remoteId = null; updateThumbnailNeeded = false; @@ -646,10 +643,6 @@ public boolean isSharedViaLink() { return this.sharedViaLink; } - public String getPublicLink() { - return this.publicLink; - } - public String getPermissions() { return this.permissions; } @@ -758,10 +751,6 @@ public void setSharedViaLink(boolean sharedViaLink) { this.sharedViaLink = sharedViaLink; } - public void setPublicLink(String publicLink) { - this.publicLink = publicLink; - } - public void setPermissions(String permissions) { this.permissions = permissions; } diff --git a/src/main/java/com/owncloud/android/db/ProviderMeta.java b/src/main/java/com/owncloud/android/db/ProviderMeta.java index 591157abb9f6..a184f148015c 100644 --- a/src/main/java/com/owncloud/android/db/ProviderMeta.java +++ b/src/main/java/com/owncloud/android/db/ProviderMeta.java @@ -102,7 +102,6 @@ static public class ProviderTableMeta implements BaseColumns { public static final String FILE_ETAG_ON_SERVER = "etag_on_server"; public static final String FILE_SHARED_VIA_LINK = "share_by_link"; public static final String FILE_SHARED_WITH_SHAREE = "shared_via_users"; - public static final String FILE_PUBLIC_LINK = "public_link"; public static final String FILE_PERMISSIONS = "permissions"; public static final String FILE_REMOTE_ID = "remote_id"; public static final String FILE_UPDATE_THUMBNAIL = "update_thumbnail"; @@ -137,7 +136,6 @@ static public class ProviderTableMeta implements BaseColumns { FILE_ETAG_ON_SERVER, FILE_SHARED_VIA_LINK, FILE_SHARED_WITH_SHAREE, - FILE_PUBLIC_LINK, FILE_PERMISSIONS, FILE_REMOTE_ID, FILE_UPDATE_THUMBNAIL, diff --git a/src/main/java/com/owncloud/android/files/services/FileUploader.java b/src/main/java/com/owncloud/android/files/services/FileUploader.java index 6993c6ccc0c7..bfc8b1c22ea6 100644 --- a/src/main/java/com/owncloud/android/files/services/FileUploader.java +++ b/src/main/java/com/owncloud/android/files/services/FileUploader.java @@ -426,7 +426,7 @@ private Integer gatherAndStartNewUploads( createdBy, file, disableRetries - ); + ); } } catch (IllegalArgumentException e) { Log_OC.e(TAG, "Not enough information provided in intent: " + e.getMessage()); @@ -455,7 +455,7 @@ private void startNewUpload( int createdBy, OCFile file, boolean disableRetries - ) { + ) { OCUpload ocUpload = new OCUpload(file, user.toPlatformAccount()); ocUpload.setFileSize(file.getFileLength()); ocUpload.setNameCollisionPolicy(nameCollisionPolicy); @@ -975,7 +975,7 @@ public static void uploadUpdateFile( OCFile existingFile, Integer behaviour, NameCollisionPolicy nameCollisionPolicy - ) { + ) { uploadUpdateFile(context, account, new OCFile[]{existingFile}, behaviour, nameCollisionPolicy, true); } @@ -989,7 +989,7 @@ public static void uploadUpdateFile( Integer behaviour, NameCollisionPolicy nameCollisionPolicy, boolean disableRetries - ) { + ) { uploadUpdateFile(context, account, new OCFile[]{existingFile}, behaviour, nameCollisionPolicy, disableRetries); } @@ -1003,7 +1003,7 @@ public static void uploadUpdateFile( Integer behaviour, NameCollisionPolicy nameCollisionPolicy, boolean disableRetries - ) { + ) { Intent intent = new Intent(context, FileUploader.class); intent.putExtra(FileUploader.KEY_ACCOUNT, account); diff --git a/src/main/java/com/owncloud/android/operations/CreateShareViaLinkOperation.java b/src/main/java/com/owncloud/android/operations/CreateShareViaLinkOperation.java index adf837063077..30a49f4f3955 100644 --- a/src/main/java/com/owncloud/android/operations/CreateShareViaLinkOperation.java +++ b/src/main/java/com/owncloud/android/operations/CreateShareViaLinkOperation.java @@ -52,7 +52,7 @@ protected RemoteOperationResult run(OwnCloudClient client) { "", false, password, - OCShare.DEFAULT_PERMISSION); + OCShare.NO_PERMISSION); createOp.setGetShareDetails(true); RemoteOperationResult result = createOp.execute(client); @@ -88,7 +88,6 @@ private void updateData(OCShare share) { // Update OCFile with data from share: ShareByLink and publicLink OCFile file = getStorageManager().getFileByEncryptedRemotePath(path); if (file != null) { - file.setPublicLink(share.getShareLink()); file.setSharedViaLink(true); getStorageManager().saveFile(file); } diff --git a/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java b/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java index 89d32aac5be7..0b6264f2528b 100644 --- a/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java +++ b/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java @@ -576,7 +576,6 @@ private void setLocalFileDataOnUpdatedFile(OCFile remoteFile, OCFile localFile, Log.d(TAG, "Image " + remoteFile.getFileName() + " updated on the server"); } - updatedFile.setPublicLink(localFile.getPublicLink()); updatedFile.setSharedViaLink(localFile.isSharedViaLink()); updatedFile.setSharedWithSharee(localFile.isSharedWithSharee()); } else { diff --git a/src/main/java/com/owncloud/android/operations/SynchronizeFolderOperation.java b/src/main/java/com/owncloud/android/operations/SynchronizeFolderOperation.java index b8cd2377c040..b05c955e72e6 100644 --- a/src/main/java/com/owncloud/android/operations/SynchronizeFolderOperation.java +++ b/src/main/java/com/owncloud/android/operations/SynchronizeFolderOperation.java @@ -360,7 +360,6 @@ private void updateLocalStateData(OCFile remoteFile, OCFile localFile, OCFile up updatedFile.setUpdateThumbnailNeeded(true); Log.d(TAG, "Image " + remoteFile.getFileName() + " updated on the server"); } - updatedFile.setPublicLink(localFile.getPublicLink()); updatedFile.setSharedViaLink(localFile.isSharedViaLink()); updatedFile.setSharedWithSharee(localFile.isSharedWithSharee()); updatedFile.setEtagInConflict(localFile.getEtagInConflict()); diff --git a/src/main/java/com/owncloud/android/operations/UnshareOperation.java b/src/main/java/com/owncloud/android/operations/UnshareOperation.java index 2d066dced580..3988c920c2e5 100644 --- a/src/main/java/com/owncloud/android/operations/UnshareOperation.java +++ b/src/main/java/com/owncloud/android/operations/UnshareOperation.java @@ -68,7 +68,6 @@ protected RemoteOperationResult run(OwnCloudClient client) { if (ShareType.PUBLIC_LINK.equals(share.getShareType())) { file.setSharedViaLink(false); - file.setPublicLink(""); } else if (ShareType.USER.equals(share.getShareType()) || ShareType.GROUP.equals(share.getShareType()) || ShareType.FEDERATED.equals(share.getShareType())) { // Check if it is the last share diff --git a/src/main/java/com/owncloud/android/operations/UpdateShareViaLinkOperation.java b/src/main/java/com/owncloud/android/operations/UpdateShareViaLinkOperation.java index 90229523827e..29f697961e3e 100644 --- a/src/main/java/com/owncloud/android/operations/UpdateShareViaLinkOperation.java +++ b/src/main/java/com/owncloud/android/operations/UpdateShareViaLinkOperation.java @@ -34,10 +34,6 @@ */ public class UpdateShareViaLinkOperation extends SyncOperation { private String password; - /** - * Enable upload permissions to update in Share resource. - */ - private boolean publicUpload; private Boolean hideFileDownload; private long expirationDateInMillis; private long shareId; @@ -58,12 +54,6 @@ protected RemoteOperationResult run(OwnCloudClient client) { updateOp.setHideFileDownload(hideFileDownload); updateOp.setLabel(label); - if (publicShare.isFolder()) { - updateOp.setPublicUploadOnFolder(publicUpload); - } else { - updateOp.setPublicUploadOnFile(publicUpload); - } - RemoteOperationResult result = updateOp.execute(client); if (result.isSuccess()) { @@ -87,10 +77,6 @@ public void setPassword(String password) { this.password = password; } - public void setPublicUpload(boolean publicUpload) { - this.publicUpload = publicUpload; - } - public void setHideFileDownload(Boolean hideFileDownload) { this.hideFileDownload = hideFileDownload; } diff --git a/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index 5e23ba0a9011..f1bbb9ae222e 100644 --- a/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -207,8 +207,8 @@ public UploadFileOperation(UploadsStorageManager uploadsStorageManager, } if (TextUtils.isEmpty(upload.getLocalPath())) { throw new IllegalArgumentException( - "Illegal file in UploadFileOperation; storage path invalid: " - + upload.getLocalPath()); + "Illegal file in UploadFileOperation; storage path invalid: " + + upload.getLocalPath()); } this.uploadsStorageManager = uploadsStorageManager; diff --git a/src/main/java/com/owncloud/android/providers/DocumentsStorageProvider.java b/src/main/java/com/owncloud/android/providers/DocumentsStorageProvider.java index d049e5b713d9..07aa16e6a3e5 100644 --- a/src/main/java/com/owncloud/android/providers/DocumentsStorageProvider.java +++ b/src/main/java/com/owncloud/android/providers/DocumentsStorageProvider.java @@ -135,6 +135,14 @@ public Cursor queryRoots(String[] projection) { return result; } + public static void notifyRootsChanged(Context context) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + String authority = context.getString(R.string.document_provider_authority); + Uri rootsUri = DocumentsContract.buildRootsUri(authority); + context.getContentResolver().notifyChange(rootsUri, null); + } + } + @Override public Cursor queryDocument(String documentId, String[] projection) throws FileNotFoundException { Log.d(TAG, "queryDocument(), id=" + documentId); diff --git a/src/main/java/com/owncloud/android/providers/FileContentProvider.java b/src/main/java/com/owncloud/android/providers/FileContentProvider.java index e8f132d7e51b..8ecc26bd1260 100644 --- a/src/main/java/com/owncloud/android/providers/FileContentProvider.java +++ b/src/main/java/com/owncloud/android/providers/FileContentProvider.java @@ -708,7 +708,6 @@ private void createFilesTable(SQLiteDatabase db) { + ProviderTableMeta.FILE_ETAG + TEXT + ProviderTableMeta.FILE_ETAG_ON_SERVER + TEXT + ProviderTableMeta.FILE_SHARED_VIA_LINK + INTEGER - + ProviderTableMeta.FILE_PUBLIC_LINK + TEXT + ProviderTableMeta.FILE_PERMISSIONS + " TEXT null," + ProviderTableMeta.FILE_REMOTE_ID + " TEXT null," + ProviderTableMeta.FILE_UPDATE_THUMBNAIL + INTEGER //boolean @@ -1148,10 +1147,6 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { ADD_COLUMN + ProviderTableMeta.FILE_SHARED_VIA_LINK + " INTEGER " + " DEFAULT 0"); - db.execSQL(ALTER_TABLE + ProviderTableMeta.FILE_TABLE_NAME + - ADD_COLUMN + ProviderTableMeta.FILE_PUBLIC_LINK + " TEXT " + - " DEFAULT NULL"); - // Create table OCShares createOCSharesTable(db); diff --git a/src/main/java/com/owncloud/android/services/OperationsService.java b/src/main/java/com/owncloud/android/services/OperationsService.java index c615f9c22bf5..0a16e17b7f48 100644 --- a/src/main/java/com/owncloud/android/services/OperationsService.java +++ b/src/main/java/com/owncloud/android/services/OperationsService.java @@ -89,12 +89,10 @@ public class OperationsService extends Service { public static final String EXTRA_ACCOUNT = "ACCOUNT"; public static final String EXTRA_SERVER_URL = "SERVER_URL"; - public static final String EXTRA_OAUTH2_QUERY_PARAMETERS = "OAUTH2_QUERY_PARAMETERS"; public static final String EXTRA_REMOTE_PATH = "REMOTE_PATH"; public static final String EXTRA_NEWNAME = "NEWNAME"; public static final String EXTRA_REMOVE_ONLY_LOCAL = "REMOVE_LOCAL_COPY"; public static final String EXTRA_SYNC_FILE_CONTENTS = "SYNC_FILE_CONTENTS"; - public static final String EXTRA_RESULT = "RESULT"; public static final String EXTRA_NEW_PARENT_PATH = "NEW_PARENT_PATH"; public static final String EXTRA_FILE = "FILE"; public static final String EXTRA_FILE_VERSION = "FILE_VERSION"; @@ -103,7 +101,6 @@ public class OperationsService extends Service { public static final String EXTRA_SHARE_WITH = "SHARE_WITH"; public static final String EXTRA_SHARE_EXPIRATION_DATE_IN_MILLIS = "SHARE_EXPIRATION_YEAR"; public static final String EXTRA_SHARE_PERMISSIONS = "SHARE_PERMISSIONS"; - public static final String EXTRA_SHARE_PUBLIC_UPLOAD = "SHARE_PUBLIC_UPLOAD"; public static final String EXTRA_SHARE_PUBLIC_LABEL = "SHARE_PUBLIC_LABEL"; public static final String EXTRA_SHARE_HIDE_FILE_DOWNLOAD = "HIDE_FILE_DOWNLOAD"; public static final String EXTRA_SHARE_ID = "SHARE_ID"; @@ -384,9 +381,7 @@ public boolean isSynchronizing(User user, OCFile file) { * Created with the Looper of a new thread, started in {@link OperationsService#onCreate()}. */ private static class ServiceHandler extends Handler { - // don't make it a final class, and don't remove the static ; lint will warn about a p - // ossible memory leak - + // don't make it a final class, and don't remove the static ; lint will warn about a possible memory leak OperationsService mService; @@ -428,7 +423,7 @@ private void nextOperation() { if (next != null) { mCurrentOperation = next.second; - RemoteOperationResult result = null; + RemoteOperationResult result; try { /// prepare client object to send the request to the ownCloud server if (mLastTarget == null || !mLastTarget.equals(next.first)) { @@ -553,9 +548,9 @@ private Pair newOperation(Intent operationIntent) { false); updateLinkOperation.setHideFileDownload(hideFileDownload); - if (operationIntent.hasExtra(EXTRA_SHARE_PUBLIC_UPLOAD)) { - updateLinkOperation.setPublicUpload(true); - } +// if (operationIntent.hasExtra(EXTRA_SHARE_PUBLIC_UPLOAD)) { +// updateLinkOperation.setPublicUpload(true); +// } if (operationIntent.hasExtra(EXTRA_SHARE_PUBLIC_LABEL)) { updateLinkOperation.setLabel(operationIntent.getStringExtra(EXTRA_SHARE_PUBLIC_LABEL)); @@ -698,7 +693,7 @@ private Pair newOperation(Intent operationIntent) { } if (operation != null) { - return new Pair(target, operation); + return new Pair<>(target, operation); } else { return null; } diff --git a/src/main/java/com/owncloud/android/ui/activity/FileActivity.java b/src/main/java/com/owncloud/android/ui/activity/FileActivity.java index 2e6d5330c9c6..347a720cdc17 100644 --- a/src/main/java/com/owncloud/android/ui/activity/FileActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/FileActivity.java @@ -746,7 +746,6 @@ private void onUpdateNoteForShareOperationFinish(RemoteOperationResult result) { if (result.isSuccess()) { if (sharingFragment != null) { - sharingFragment.refreshPublicShareFromDB(); sharingFragment.onUpdateShareInformation(result, getFile()); } } else { @@ -761,7 +760,6 @@ private void onUpdateShareInformation(RemoteOperationResult result, @StringRes i if (result.isSuccess()) { updateFileFromDB(); if (sharingFragment != null) { - sharingFragment.refreshPublicShareFromDB(); sharingFragment.onUpdateShareInformation(result, getFile()); } } else if (sharingFragment != null && sharingFragment.getView() != null) { @@ -807,7 +805,6 @@ private void onCreateShareViaLinkOperationFinish(CreateShareViaLinkOperation ope copyAndShareFileLink(this, file, link); if (sharingFragment != null) { - sharingFragment.refreshPublicShareFromDB(); sharingFragment.onUpdateShareInformation(result, getFile()); } } else { @@ -828,7 +825,7 @@ private void onCreateShareViaLinkOperationFinish(CreateShareViaLinkOperation ope } else { if (sharingFragment != null) { - sharingFragment.refreshPublicShareFromDB(); + sharingFragment.refreshSharesFromDB(); } Snackbar snackbar = Snackbar.make(findViewById(android.R.id.content), ErrorMessageAdapter.getErrorCauseMessage(result, @@ -899,8 +896,8 @@ 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_AFTER_OC9 : - OCShare.FEDERATED_PERMISSIONS_FOR_FILE_AFTER_OC9; + return getFile().isFolder() ? OCShare.FEDERATED_PERMISSIONS_FOR_FOLDER : + OCShare.FEDERATED_PERMISSIONS_FOR_FILE; } else { return getFile().isFolder() ? OCShare.MAXIMUM_PERMISSIONS_FOR_FOLDER : OCShare.MAXIMUM_PERMISSIONS_FOR_FILE; diff --git a/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java b/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java index 16530fabdbed..8f43e24feeed 100644 --- a/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java @@ -70,6 +70,7 @@ import com.owncloud.android.lib.common.ExternalLink; import com.owncloud.android.lib.common.ExternalLinkType; import com.owncloud.android.lib.common.utils.Log_OC; +import com.owncloud.android.providers.DocumentsStorageProvider; import com.owncloud.android.ui.asynctasks.LoadingVersionNumberTask; import com.owncloud.android.utils.DeviceCredentialUtils; import com.owncloud.android.utils.DisplayUtils; @@ -641,12 +642,17 @@ private void enableLock(String lock) { DisplayUtils.showSnackMessage(this, R.string.prefs_lock_device_credentials_not_setup); } else { DisplayUtils.showSnackMessage(this, R.string.prefs_lock_device_credentials_enabled); - this.lock.setValue(LOCK_DEVICE_CREDENTIALS); - this.lock.setSummary(this.lock.getEntry()); + changeLockSetting(LOCK_DEVICE_CREDENTIALS); } } } + private void changeLockSetting(String value) { + lock.setValue(value); + lock.setSummary(lock.getEntry()); + DocumentsStorageProvider.notifyRootsChanged(this); + } + private void disableLock(String lock) { if (LOCK_PASSCODE.equals(lock)) { Intent i = new Intent(getApplicationContext(), PassCodeActivity.class); @@ -819,14 +825,12 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { appPrefs.putString(PassCodeActivity.PREFERENCE_PASSCODE_D + i, passcode.substring(i - 1, i)); } appPrefs.apply(); - lock.setValue(LOCK_PASSCODE); - lock.setSummary(lock.getEntry()); + changeLockSetting(LOCK_PASSCODE); DisplayUtils.showSnackMessage(this, R.string.pass_code_stored); } } else if (requestCode == ACTION_CONFIRM_PASSCODE && resultCode == RESULT_OK) { if (data.getBooleanExtra(PassCodeActivity.KEY_CHECK_RESULT, false)) { - lock.setValue(LOCK_NONE); - lock.setSummary(lock.getEntry()); + changeLockSetting(LOCK_NONE); DisplayUtils.showSnackMessage(this, R.string.pass_code_removed); if (!LOCK_NONE.equals(pendingLock)) { @@ -840,8 +844,7 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { data.getIntExtra(RequestCredentialsActivity.KEY_CHECK_RESULT, RequestCredentialsActivity.KEY_CHECK_RESULT_FALSE) == RequestCredentialsActivity.KEY_CHECK_RESULT_TRUE) { - lock.setValue(LOCK_NONE); - lock.setSummary(lock.getEntry()); + changeLockSetting(LOCK_NONE); DisplayUtils.showSnackMessage(this, R.string.credentials_disabled); if (!LOCK_NONE.equals(pendingLock)) { enableLock(pendingLock); diff --git a/src/main/java/com/owncloud/android/ui/activity/ShareActivity.java b/src/main/java/com/owncloud/android/ui/activity/ShareActivity.java index 7e506f58c8b3..4ab713bcce71 100644 --- a/src/main/java/com/owncloud/android/ui/activity/ShareActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/ShareActivity.java @@ -145,8 +145,7 @@ private void refreshSharesFromStorageManager() { if (shareFileFragment != null && shareFileFragment.isAdded()) { // only if added to the view hierarchy!! shareFileFragment.refreshCapabilitiesFromDB(); - //shareFileFragment.refrefreshUsersOrGroupsListFromDB(); - shareFileFragment.refreshPublicShareFromDB(); + shareFileFragment.refreshSharesFromDB(); } } diff --git a/src/main/java/com/owncloud/android/ui/adapter/InternalShareViewHolder.java b/src/main/java/com/owncloud/android/ui/adapter/InternalShareViewHolder.java new file mode 100644 index 000000000000..81b77a9d9ee8 --- /dev/null +++ b/src/main/java/com/owncloud/android/ui/adapter/InternalShareViewHolder.java @@ -0,0 +1,74 @@ +/* + * + * Nextcloud Android client application + * + * @author Tobias Kaminsky + * Copyright (C) 2020 Tobias Kaminsky + * Copyright (C) 2020 Nextcloud GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.owncloud.android.ui.adapter; + +import android.content.Context; +import android.graphics.PorterDuff; +import android.view.View; + +import com.owncloud.android.R; +import com.owncloud.android.databinding.FileDetailsShareInternalShareLinkBinding; +import com.owncloud.android.lib.resources.shares.OCShare; + +import androidx.annotation.NonNull; +import androidx.core.content.res.ResourcesCompat; +import androidx.recyclerview.widget.RecyclerView; + +class InternalShareViewHolder extends RecyclerView.ViewHolder { + private FileDetailsShareInternalShareLinkBinding binding; + private Context context; + + public InternalShareViewHolder(@NonNull View itemView) { + super(itemView); + } + + public InternalShareViewHolder(FileDetailsShareInternalShareLinkBinding binding, Context context) { + this(binding.getRoot()); + this.binding = binding; + this.context = context; + } + + public void bind(OCShare share, ShareeListAdapterListener listener) { + binding.copyInternalLinkIcon + .getBackground() + .setColorFilter(ResourcesCompat.getColor(context.getResources(), + R.color.grey_db, + null), + PorterDuff.Mode.SRC_IN); + binding.copyInternalLinkIcon + .getDrawable() + .mutate() + .setColorFilter(ResourcesCompat.getColor(context.getResources(), + R.color.black, + null), + PorterDuff.Mode.SRC_IN); + + if (share.isFolder()) { + binding.shareInternalLinkText.setText(context.getString(R.string.share_internal_link_to_folder_text)); + } else { + binding.shareInternalLinkText.setText(context.getString(R.string.share_internal_link_to_file_text)); + } + + binding.copyInternalContainer.setOnClickListener(l -> listener.copyInternalLink()); + } +} diff --git a/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.java b/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.java new file mode 100644 index 000000000000..790ffb43f4a6 --- /dev/null +++ b/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.java @@ -0,0 +1,74 @@ +/* + * + * Nextcloud Android client application + * + * @author Tobias Kaminsky + * Copyright (C) 2020 Tobias Kaminsky + * Copyright (C) 2020 Nextcloud GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.owncloud.android.ui.adapter; + +import android.content.Context; +import android.text.TextUtils; +import android.view.View; + +import com.owncloud.android.R; +import com.owncloud.android.databinding.FileDetailsShareLinkShareItemBinding; +import com.owncloud.android.lib.resources.shares.OCShare; +import com.owncloud.android.lib.resources.shares.ShareType; +import com.owncloud.android.utils.ThemeUtils; + +import androidx.annotation.NonNull; +import androidx.core.content.res.ResourcesCompat; +import androidx.recyclerview.widget.RecyclerView; + +class LinkShareViewHolder extends RecyclerView.ViewHolder { + private FileDetailsShareLinkShareItemBinding binding; + private Context context; + + public LinkShareViewHolder(@NonNull View itemView) { + super(itemView); + } + + public LinkShareViewHolder(FileDetailsShareLinkShareItemBinding binding, Context context) { + this(binding.getRoot()); + this.binding = binding; + this.context = context; + } + + public void bind(OCShare publicShare, ShareeListAdapterListener listener) { + if (ShareType.EMAIL == publicShare.getShareType()) { + binding.name.setText(publicShare.getSharedWithDisplayName()); + binding.icon.setImageDrawable(ResourcesCompat.getDrawable(context.getResources(), + R.drawable.ic_email, + null)); + binding.copyLink.setVisibility(View.GONE); + } else { + if (!TextUtils.isEmpty(publicShare.getLabel())) { + String text = String.format(context.getString(R.string.share_link_with_label), publicShare.getLabel()); + binding.name.setText(text); + } else { + binding.name.setText(R.string.share_link); + } + } + + ThemeUtils.colorIconImageViewWithBackground(binding.icon, context); + + binding.copyLink.setOnClickListener(v -> listener.copyLink(publicShare)); + binding.overflowMenu.setOnClickListener(v -> listener.showLinkOverflowMenu(publicShare, binding.overflowMenu)); + } +} diff --git a/src/main/java/com/owncloud/android/ui/adapter/PublicShareViewHolder.java b/src/main/java/com/owncloud/android/ui/adapter/NewLinkShareViewHolder.java similarity index 51% rename from src/main/java/com/owncloud/android/ui/adapter/PublicShareViewHolder.java rename to src/main/java/com/owncloud/android/ui/adapter/NewLinkShareViewHolder.java index 98e274a45b0e..0da1388fdb70 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/PublicShareViewHolder.java +++ b/src/main/java/com/owncloud/android/ui/adapter/NewLinkShareViewHolder.java @@ -22,41 +22,26 @@ package com.owncloud.android.ui.adapter; -import android.content.Context; -import android.text.TextUtils; import android.view.View; -import com.owncloud.android.databinding.FileDetailsSharePublicLinkItemBinding; -import com.owncloud.android.lib.resources.shares.OCShare; -import com.owncloud.android.utils.ThemeUtils; +import com.owncloud.android.databinding.FileDetailsSharePublicLinkAddNewItemBinding; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; -class PublicShareViewHolder extends RecyclerView.ViewHolder { - private FileDetailsSharePublicLinkItemBinding binding; - private Context context; +class NewLinkShareViewHolder extends RecyclerView.ViewHolder { + private FileDetailsSharePublicLinkAddNewItemBinding binding; - public PublicShareViewHolder(@NonNull View itemView) { + public NewLinkShareViewHolder(@NonNull View itemView) { super(itemView); } - public PublicShareViewHolder(FileDetailsSharePublicLinkItemBinding binding, Context context) { + public NewLinkShareViewHolder(FileDetailsSharePublicLinkAddNewItemBinding binding) { this(binding.getRoot()); this.binding = binding; - this.context = context; } - public void bind(OCShare publicShare, PublicShareInterface listener) { - if (!TextUtils.isEmpty(publicShare.getLabel())) { - binding.publicShareLabel.setText(publicShare.getLabel()); - } - - ThemeUtils.colorIconImageViewWithBackground(binding.copyInternalLinkIcon, context); - - binding.shareLinkCopyIcon.setOnClickListener(v -> listener.copyLink(publicShare)); - - binding.overflowMenuShareLink.setOnClickListener( - v -> listener.showLinkOverflowMenu(publicShare, binding.overflowMenuShareLink)); + public void bind(ShareeListAdapterListener listener) { + binding.addNewPublicShareLink.setOnClickListener(v -> listener.createPublicShareLink()); } } diff --git a/src/main/java/com/owncloud/android/ui/adapter/PublicShareListAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/PublicShareListAdapter.java deleted file mode 100644 index abc11ecdea24..000000000000 --- a/src/main/java/com/owncloud/android/ui/adapter/PublicShareListAdapter.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * - * Nextcloud Android client application - * - * @author Tobias Kaminsky - * Copyright (C) 2020 Tobias Kaminsky - * Copyright (C) 2020 Nextcloud GmbH - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.owncloud.android.ui.adapter; - -import android.content.Context; -import android.view.LayoutInflater; -import android.view.ViewGroup; - -import com.owncloud.android.databinding.FileDetailsSharePublicLinkItemBinding; -import com.owncloud.android.lib.resources.shares.OCShare; - -import java.util.List; - -import androidx.annotation.NonNull; -import androidx.recyclerview.widget.RecyclerView; - -public class PublicShareListAdapter extends RecyclerView.Adapter { - private Context context; - private List shares; - private PublicShareInterface listener; - - public PublicShareListAdapter(Context context, List shares, PublicShareInterface listener) { - this.context = context; - this.shares = shares; - this.listener = listener; - } - - @NonNull - @Override - public PublicShareViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - FileDetailsSharePublicLinkItemBinding binding = - FileDetailsSharePublicLinkItemBinding.inflate(LayoutInflater.from(context), parent, false); - - return new PublicShareViewHolder(binding, context); - } - - @Override - public void onBindViewHolder(@NonNull PublicShareViewHolder holder, int position) { - OCShare share = shares.get(position); - - holder.bind(share, listener); - } - - @Override - public int getItemCount() { - return shares.size(); - } -} diff --git a/src/main/java/com/owncloud/android/ui/adapter/ShareViewHolder.java b/src/main/java/com/owncloud/android/ui/adapter/ShareViewHolder.java new file mode 100644 index 000000000000..c48b42277b82 --- /dev/null +++ b/src/main/java/com/owncloud/android/ui/adapter/ShareViewHolder.java @@ -0,0 +1,102 @@ +/* + * + * Nextcloud Android client application + * + * @author Tobias Kaminsky + * Copyright (C) 2020 Tobias Kaminsky + * Copyright (C) 2020 Nextcloud GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.owncloud.android.ui.adapter; + +import android.content.Context; +import android.view.View; +import android.widget.ImageView; + +import com.owncloud.android.R; +import com.owncloud.android.databinding.FileDetailsShareShareItemBinding; +import com.owncloud.android.lib.resources.shares.OCShare; +import com.owncloud.android.ui.TextDrawable; + +import java.security.NoSuchAlgorithmException; + +import androidx.annotation.DrawableRes; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +class ShareViewHolder extends RecyclerView.ViewHolder { + private FileDetailsShareShareItemBinding binding; + private float avatarRadiusDimension; + private Context context; + + public ShareViewHolder(@NonNull View itemView) { + super(itemView); + } + + public ShareViewHolder(FileDetailsShareShareItemBinding binding, Context context) { + this(binding.getRoot()); + this.binding = binding; + this.context = context; + } + + public void bind(OCShare share, + ShareeListAdapterListener listener, + String userId, + float avatarRadiusDimension) { + this.avatarRadiusDimension = avatarRadiusDimension; + String name = share.getSharedWithDisplayName(); + + switch (share.getShareType()) { + case GROUP: + name = context.getString(R.string.share_group_clarification, name); + setImage(binding.icon, share.getSharedWithDisplayName(), R.drawable.ic_group); + break; + case ROOM: + name = context.getString(R.string.share_room_clarification, name); + setImage(binding.icon, share.getSharedWithDisplayName(), R.drawable.ic_chat_bubble); + break; + case CIRCLE: + binding.icon.setImageResource(R.drawable.ic_circles); + break; + case FEDERATED: + name = context.getString(R.string.share_remote_clarification, name); + setImage(binding.icon, share.getSharedWithDisplayName(), R.drawable.ic_user); + break; + default: + setImage(binding.icon, name, R.drawable.ic_user); + break; + } + + binding.name.setText(name); + + if (share.getShareWith().equalsIgnoreCase(userId) || share.getUserId().equalsIgnoreCase(userId)) { + binding.overflowMenu.setVisibility(View.VISIBLE); + + // bind listener to edit privileges + binding.overflowMenu.setOnClickListener(v -> listener.showUserOverflowMenu(share, binding.overflowMenu)); + } else { + binding.overflowMenu.setVisibility(View.GONE); + } + } + + private void setImage(ImageView avatar, String name, @DrawableRes int fallback) { + try { + avatar.setImageDrawable(TextDrawable.createNamedAvatar(name, avatarRadiusDimension)); + } catch (NoSuchAlgorithmException | StringIndexOutOfBoundsException e) { + avatar.setImageResource(fallback); + } + } +} diff --git a/src/main/java/com/owncloud/android/ui/adapter/ShareeListAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/ShareeListAdapter.java index 3faaee5dbd3e..2bde1fcd151b 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/ShareeListAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/ShareeListAdapter.java @@ -25,132 +25,108 @@ package com.owncloud.android.ui.adapter; -import android.accounts.Account; import android.content.Context; import android.graphics.drawable.Drawable; import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; -import android.widget.PopupMenu; -import android.widget.TextView; import com.owncloud.android.R; -import com.owncloud.android.datamodel.FileDataStorageManager; -import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.databinding.FileDetailsShareInternalShareLinkBinding; +import com.owncloud.android.databinding.FileDetailsShareLinkShareItemBinding; +import com.owncloud.android.databinding.FileDetailsSharePublicLinkAddNewItemBinding; +import com.owncloud.android.databinding.FileDetailsShareShareItemBinding; import com.owncloud.android.lib.resources.shares.OCShare; import com.owncloud.android.lib.resources.shares.ShareType; -import com.owncloud.android.lib.resources.status.OCCapability; -import com.owncloud.android.lib.resources.status.OwnCloudVersion; -import com.owncloud.android.ui.TextDrawable; -import com.owncloud.android.ui.dialog.ExpirationDatePickerDialogFragment; -import com.owncloud.android.ui.dialog.NoteDialogFragment; -import com.owncloud.android.ui.fragment.util.SharingMenuHelper; import com.owncloud.android.utils.DisplayUtils; -import com.owncloud.android.utils.ThemeUtils; -import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; -import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; -import androidx.appcompat.widget.AppCompatCheckBox; -import androidx.fragment.app.FragmentManager; import androidx.recyclerview.widget.RecyclerView; -import butterknife.BindView; -import butterknife.ButterKnife; /** * Adapter to show a user/group/email/remote in Sharing list in file details view. */ -public class ShareeListAdapter extends RecyclerView.Adapter - implements DisplayUtils.AvatarGenerationListener { +public class ShareeListAdapter extends RecyclerView.Adapter + implements DisplayUtils.AvatarGenerationListener { private ShareeListAdapterListener listener; - private OCCapability capabilities; - private FragmentManager fragmentManager; private Context context; - private int accentColor; private List shares; private float avatarRadiusDimension; - private OCFile file; private String userId; - public ShareeListAdapter(FragmentManager fragmentManager, Context context, List shares, Account account, - OCFile file, ShareeListAdapterListener listener, String userId) { + public ShareeListAdapter(Context context, + List shares, + ShareeListAdapterListener listener, + String userId) { this.context = context; - this.fragmentManager = fragmentManager; this.shares = shares; this.listener = listener; - this.file = file; this.userId = userId; - accentColor = ThemeUtils.primaryAccentColor(context); - capabilities = new FileDataStorageManager(account, context.getContentResolver()).getCapability(account.name); avatarRadiusDimension = context.getResources().getDimension(R.dimen.user_icon_radius); + + sortShares(); } - @NonNull @Override - public UserViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.file_details_share_user_item, parent, false); - return new UserViewHolder(v); + public int getItemViewType(int position) { + return shares.get(position).getShareType().getValue(); } + @NonNull @Override - public void onBindViewHolder(@NonNull UserViewHolder holder, int position) { - if (shares != null && shares.size() > position) { - final OCShare share = shares.get(position); - - String name = share.getSharedWithDisplayName(); - - switch (share.getShareType()) { - case GROUP: - name = context.getString(R.string.share_group_clarification, name); - setImage(holder, name, R.drawable.ic_group); - break; - case EMAIL: - name = context.getString(R.string.share_email_clarification, name); - setImage(holder, name, R.drawable.ic_email); - break; - case ROOM: - name = context.getString(R.string.share_room_clarification, name); - setImage(holder, name, R.drawable.ic_chat_bubble); - break; - case CIRCLE: - holder.avatar.setImageResource(R.drawable.ic_circles); - break; - default: - setImage(holder, name, R.drawable.ic_user); - break; - } - - holder.name.setText(name); - - if (share.getShareWith().equalsIgnoreCase(userId) || share.getUserId().equalsIgnoreCase(userId)) { - holder.allowEditing.setVisibility(View.VISIBLE); - holder.editShareButton.setVisibility(View.VISIBLE); - - ThemeUtils.tintCheckbox(holder.allowEditing, accentColor); - holder.allowEditing.setChecked(canEdit(share)); - holder.allowEditing.setOnClickListener(v -> allowEditClick(holder.allowEditing, share)); - - // bind listener to edit privileges - holder.editShareButton.setOnClickListener(v -> onOverflowIconClicked(v, holder.allowEditing, share)); - } else { - holder.allowEditing.setVisibility(View.GONE); - holder.editShareButton.setVisibility(View.GONE); - } + public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + switch (ShareType.fromValue(viewType)) { + case PUBLIC_LINK: + case EMAIL: + return new LinkShareViewHolder( + FileDetailsShareLinkShareItemBinding.inflate(LayoutInflater.from(context), + parent, + false), + context); + case NEW_PUBLIC_LINK: + return new NewLinkShareViewHolder( + FileDetailsSharePublicLinkAddNewItemBinding.inflate(LayoutInflater.from(context), + parent, + false) + ); + case INTERNAL: + return new InternalShareViewHolder( + FileDetailsShareInternalShareLinkBinding.inflate(LayoutInflater.from(context), parent, false), + context); + default: + return new ShareViewHolder(FileDetailsShareShareItemBinding.inflate(LayoutInflater.from(context), + parent, + false), + context); } } - private void setImage(UserViewHolder holder, String name, @DrawableRes int fallback) { - try { - holder.avatar.setImageDrawable(TextDrawable.createNamedAvatar(name, avatarRadiusDimension)); - } catch (NoSuchAlgorithmException e) { - holder.avatar.setImageResource(fallback); + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { + if (shares == null || shares.size() <= position) { + return; + } + + final OCShare share = shares.get(position); + + if (holder instanceof LinkShareViewHolder) { + LinkShareViewHolder publicShareViewHolder = (LinkShareViewHolder) holder; + publicShareViewHolder.bind(share, listener); + } else if (holder instanceof InternalShareViewHolder) { + InternalShareViewHolder internalShareViewHolder = (InternalShareViewHolder) holder; + internalShareViewHolder.bind(share, listener); + } else if (holder instanceof NewLinkShareViewHolder) { + NewLinkShareViewHolder newLinkShareViewHolder = (NewLinkShareViewHolder) holder; + newLinkShareViewHolder.bind(listener); + } else { + ShareViewHolder userViewHolder = (ShareViewHolder) holder; + userViewHolder.bind(share, listener, userId, avatarRadiusDimension); } } @@ -164,204 +140,10 @@ public int getItemCount() { return shares.size(); } - private void allowEditClick(AppCompatCheckBox checkBox, @NonNull OCShare share) { - if (!share.isFolder()) { - share.setPermissions(listener.updatePermissionsToShare( - share, - canReshare(share), - checkBox.isChecked(), - false, - false, - false - )); - } else { - share.setPermissions(listener.updatePermissionsToShare( - share, - canReshare(share), - checkBox.isChecked(), - checkBox.isChecked(), - checkBox.isChecked(), - checkBox.isChecked() - )); - } - } - - private void onOverflowIconClicked(View view, AppCompatCheckBox allowEditsCheckBox, OCShare share) { - // use grey as fallback for elements where custom theming is not available - if (ThemeUtils.themingEnabled(context)) { - context.getTheme().applyStyle(R.style.FallbackThemingTheme, true); - } - PopupMenu popup = new PopupMenu(context, view); - popup.inflate(R.menu.item_user_sharing_settings); - - prepareOptionsMenu(popup.getMenu(), share); - - popup.setOnMenuItemClickListener(item -> optionsItemSelected(popup.getMenu(), item, allowEditsCheckBox, share)); - popup.show(); - } - - /** - * Updates the sharee's menu with the current permissions of the {@link OCShare} - * - * @param menu the menu of the sharee/shared file - * @param share the shared file - */ - private void prepareOptionsMenu(Menu menu, OCShare share) { - - MenuItem editCreateItem = menu.findItem(R.id.action_can_edit_create); - MenuItem editChangeItem = menu.findItem(R.id.action_can_edit_change); - MenuItem editDeleteItem = menu.findItem(R.id.action_can_edit_delete); - - MenuItem hideFileListingItem = menu.findItem(R.id.action_hide_file_listing); - MenuItem passwordItem = menu.findItem(R.id.action_password); - MenuItem expirationDateItem = menu.findItem(R.id.action_expiration_date); - - MenuItem reshareItem = menu.findItem(R.id.action_can_reshare); - - MenuItem sendNoteItem = menu.findItem(R.id.action_share_send_note); - - if (isReshareForbidden(share)) { - reshareItem.setVisible(false); - } - reshareItem.setChecked(canReshare(share)); - - if (share.getShareType() == ShareType.EMAIL) { - SharingMenuHelper.setupHideFileListingMenuItem( - hideFileListingItem, - file.isFolder(), - canEdit(share), - share.getPermissions() - ); - SharingMenuHelper.setupPasswordMenuItem(passwordItem, share.isPasswordProtected()); - - reshareItem.setVisible(false); - editCreateItem.setVisible(false); - editChangeItem.setVisible(false); - editDeleteItem.setVisible(false); - } else { - if (file.isFolder() && isEditOptionsAvailable(share)) { - /// TODO change areEditOptionsAvailable in order to delete !isFederated - editCreateItem.setChecked(canCreate(share)); - editChangeItem.setChecked(canUpdate(share)); - editDeleteItem.setChecked(canDelete(share)); - } else { - editCreateItem.setVisible(false); - editChangeItem.setVisible(false); - editDeleteItem.setVisible(false); - } - - hideFileListingItem.setVisible(false); - passwordItem.setVisible(false); - - if (!capabilities.getVersion().isNewerOrEqual(OwnCloudVersion.nextcloud_18)) { - expirationDateItem.setVisible(false); - } - } - - SharingMenuHelper.setupExpirationDateMenuItem( - menu.findItem(R.id.action_expiration_date), share.getExpirationDate(), context.getResources()); - - sendNoteItem.setVisible(capabilities.getVersion().isNoteOnShareSupported()); - } - - private boolean isEditOptionsAvailable(OCShare share) { - return !ShareType.FEDERATED.equals(share.getShareType()); - } - - private boolean isReshareForbidden(OCShare share) { - return ShareType.FEDERATED.equals(share.getShareType()) || - (capabilities != null && capabilities.getFilesSharingResharing().isFalse()); - } - - private boolean canEdit(OCShare share) { - return (share.getPermissions() & - (OCShare.CREATE_PERMISSION_FLAG | OCShare.UPDATE_PERMISSION_FLAG | OCShare.DELETE_PERMISSION_FLAG)) > 0; - } - - private boolean canCreate(OCShare share) { - return (share.getPermissions() & OCShare.CREATE_PERMISSION_FLAG) > 0; - } - - private boolean canUpdate(OCShare share) { - return (share.getPermissions() & OCShare.UPDATE_PERMISSION_FLAG) > 0; - } - - private boolean canDelete(OCShare share) { - return (share.getPermissions() & OCShare.DELETE_PERMISSION_FLAG) > 0; - } - - private boolean canReshare(OCShare share) { - return (share.getPermissions() & OCShare.SHARE_PERMISSION_FLAG) > 0; - } - - private boolean optionsItemSelected(Menu menu, MenuItem item, AppCompatCheckBox allowEditsCheckBox, OCShare share) { - switch (item.getItemId()) { - case R.id.action_can_edit_create: - case R.id.action_can_edit_change: - case R.id.action_can_edit_delete: { - item.setChecked(!item.isChecked()); - if (item.isChecked() && !allowEditsCheckBox.isChecked()) { - allowEditsCheckBox.setChecked(true); - } - share.setPermissions( - updatePermissionsToShare( - share, - menu.findItem(R.id.action_can_reshare).isChecked(), - allowEditsCheckBox.isChecked(), - menu.findItem(R.id.action_can_edit_create).isChecked(), - menu.findItem(R.id.action_can_edit_change).isChecked(), - menu.findItem(R.id.action_can_edit_delete).isChecked()) - ); - return true; - } - case R.id.action_can_reshare: { - item.setChecked(!item.isChecked()); - share.setPermissions( - updatePermissionsToShare( - share, - menu.findItem(R.id.action_can_reshare).isChecked(), - allowEditsCheckBox.isChecked(), - menu.findItem(R.id.action_can_edit_create).isChecked(), - menu.findItem(R.id.action_can_edit_change).isChecked(), - menu.findItem(R.id.action_can_edit_delete).isChecked()) - ); - return true; - } - case R.id.action_unshare: { - listener.unshareWith(share); - shares.remove(share); - notifyDataSetChanged(); - return true; - } - case R.id.action_password: { - listener.requestPasswordForShare(share, false); - return true; - } - case R.id.action_expiration_date: { - ExpirationDatePickerDialogFragment dialog = ExpirationDatePickerDialogFragment - .newInstance(share, share.getExpirationDate()); - dialog.show(fragmentManager, ExpirationDatePickerDialogFragment.DATE_PICKER_DIALOG); - return true; - } - case R.id.action_share_send_note: - NoteDialogFragment dialog = NoteDialogFragment.newInstance(share); - dialog.show(fragmentManager, NoteDialogFragment.NOTE_FRAGMENT); - return true; - default: - return true; - } - } - - private int updatePermissionsToShare(OCShare share, boolean canReshare, boolean canEdit, boolean canEditCreate, - boolean canEditChange, boolean canEditDelete) { - return listener.updatePermissionsToShare( - share, - canReshare, - canEdit, - canEditCreate, - canEditChange, - canEditDelete - ); + public void addShares(List sharesToAdd) { + shares.addAll(sharesToAdd); + sortShares(); + notifyDataSetChanged(); } @Override @@ -381,53 +163,46 @@ public boolean shouldCallGeneratedCallback(String tag, Object callContext) { return false; } - class UserViewHolder extends RecyclerView.ViewHolder { - @BindView(R.id.avatar) - ImageView avatar; - @BindView(R.id.name) - TextView name; - @BindView(R.id.allowEditing) - AppCompatCheckBox allowEditing; - @BindView(R.id.editShareButton) - ImageView editShareButton; + public void remove(OCShare share) { + shares.remove(share); + notifyDataSetChanged(); + } - UserViewHolder(View itemView) { - super(itemView); - ButterKnife.bind(this, itemView); + /** + * sort all by creation time, then email/link shares on top + */ + protected final void sortShares() { + List links = new ArrayList<>(); + List users = new ArrayList<>(); + + for (OCShare share : shares) { + if (ShareType.PUBLIC_LINK == share.getShareType() || ShareType.EMAIL == share.getShareType()) { + links.add(share); + } else if (share.getShareType() != ShareType.INTERNAL) { + users.add(share); + } } - } - public interface ShareeListAdapterListener { - /** - * unshare with given sharee {@link OCShare}. - * - * @param share the share - */ - void unshareWith(OCShare share); + Collections.sort(links, (o1, o2) -> Long.compare(o2.getSharedDate(), o1.getSharedDate())); + Collections.sort(users, (o1, o2) -> Long.compare(o2.getSharedDate(), o1.getSharedDate())); + + shares = links; + shares.addAll(users); + + // add internal share link at end + shares.add(new OCShare().setShareType(ShareType.INTERNAL)); + } - /** - * Updates the permissions of the {@link OCShare}. - * - * @param share the share to be updated - * @param canReshare reshare permission - * @param canEdit edit permission - * @param canEditCreate create permission (folders only) - * @param canEditChange change permission (folders only) - * @param canEditDelete delete permission (folders only) - * @return permissions value set - */ - int updatePermissionsToShare(OCShare share, - boolean canReshare, - boolean canEdit, - boolean canEditCreate, - boolean canEditChange, - boolean canEditDelete); + public List getShares() { + return shares; + } - /** - * Starts a dialog that requests a password to the user to protect a share. - * - * @param share the share for which a password shall be configured/removed - */ - void requestPasswordForShare(OCShare share, boolean askForPassword); + public void removeNewPublicShare() { + for (OCShare share : shares) { + if (share.getShareType() == ShareType.NEW_PUBLIC_LINK) { + shares.remove(share); + break; + } + } } } diff --git a/src/main/java/com/owncloud/android/ui/adapter/PublicShareInterface.java b/src/main/java/com/owncloud/android/ui/adapter/ShareeListAdapterListener.java similarity index 80% rename from src/main/java/com/owncloud/android/ui/adapter/PublicShareInterface.java rename to src/main/java/com/owncloud/android/ui/adapter/ShareeListAdapterListener.java index d34d5bb90405..c1e4926cb95c 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/PublicShareInterface.java +++ b/src/main/java/com/owncloud/android/ui/adapter/ShareeListAdapterListener.java @@ -26,8 +26,16 @@ import com.owncloud.android.lib.resources.shares.OCShare; -public interface PublicShareInterface { +public interface ShareeListAdapterListener { void copyLink(OCShare share); void showLinkOverflowMenu(OCShare publicShare, ImageView overflowMenuShareLink); + + void showUserOverflowMenu(OCShare share, ImageView overflowMenu); + + void copyInternalLink(); + + void createPublicShareLink(); + + void requestPasswordForShare(OCShare share, boolean askForPassword); } diff --git a/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java index 8c61baa30f5e..8c41e97b880f 100755 --- a/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java @@ -716,7 +716,7 @@ public SectionedViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int vie /** * Load upload items from {@link UploadsStorageManager}. */ - public void loadUploadItemsFromDb() { + public final void loadUploadItemsFromDb() { Log_OC.d(TAG, "loadUploadItemsFromDb"); for (UploadGroup group : uploadGroups) { diff --git a/src/main/java/com/owncloud/android/ui/dialog/SharePasswordDialogFragment.java b/src/main/java/com/owncloud/android/ui/dialog/SharePasswordDialogFragment.java index 359424b56e5a..04816c21fab0 100644 --- a/src/main/java/com/owncloud/android/ui/dialog/SharePasswordDialogFragment.java +++ b/src/main/java/com/owncloud/android/ui/dialog/SharePasswordDialogFragment.java @@ -27,13 +27,12 @@ import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; -import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; import android.widget.EditText; -import android.widget.TextView; import com.owncloud.android.R; +import com.owncloud.android.databinding.PasswordDialogBinding; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.lib.resources.shares.OCShare; import com.owncloud.android.ui.activity.FileActivity; @@ -41,7 +40,6 @@ import com.owncloud.android.utils.ThemeUtils; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.DialogFragment; @@ -58,6 +56,7 @@ public class SharePasswordDialogFragment extends DialogFragment implements Dialo private static final String ARG_ASK_FOR_PASSWORD = "ASK_FOR_PASSWORD"; public static final String PASSWORD_FRAGMENT = "PASSWORD_FRAGMENT"; + private PasswordDialogBinding binding; private OCFile file; private OCShare share; private boolean createShare; @@ -122,12 +121,6 @@ public static SharePasswordDialogFragment newInstance(OCShare share) { return frag; } - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - return super.onCreateView(inflater, container, savedInstanceState); - } - @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { @@ -137,17 +130,18 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { askForPassword = getArguments().getBoolean(ARG_ASK_FOR_PASSWORD, false); // Inflate the layout for the dialog - LayoutInflater inflater = getActivity().getLayoutInflater(); - View v = inflater.inflate(R.layout.password_dialog, null); + LayoutInflater inflater = requireActivity().getLayoutInflater(); + binding = PasswordDialogBinding.inflate(inflater, null, false); + View view = binding.getRoot(); // Setup layout - EditText inputText = v.findViewById(R.id.share_password); - inputText.getBackground().setColorFilter( - ThemeUtils.primaryAccentColor(getContext()), - PorterDuff.Mode.SRC_ATOP - ); + EditText inputText = binding.sharePassword; + inputText.setHighlightColor(ThemeUtils.primaryColor(getActivity())); inputText.setText(""); + ThemeUtils.themeEditText(getContext(), inputText, false); inputText.requestFocus(); + inputText.getBackground().setColorFilter(ThemeUtils.primaryAccentColor(getContext()), + PorterDuff.Mode.SRC_ATOP); int title; if (askForPassword) { @@ -157,10 +151,9 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { } // Build the dialog - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), - R.style.Theme_ownCloud_Dialog_NoButtonBarStyle); + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); - builder.setView(v) + builder.setView(view) .setPositiveButton(R.string.common_ok, this) .setNegativeButton(R.string.common_cancel, this) .setNeutralButton(R.string.common_delete, this) @@ -178,11 +171,10 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { @Override public void onClick(DialogInterface dialog, int which) { if (which == AlertDialog.BUTTON_POSITIVE) { - String password = ((TextView) (getDialog().findViewById(R.id.share_password))).getText().toString(); + String password = binding.sharePassword.getText().toString(); if (!askForPassword && TextUtils.isEmpty(password)) { - DisplayUtils.showSnackMessage(getActivity().findViewById(android.R.id.content), - R.string.share_link_empty_password); + DisplayUtils.showSnackMessage(binding.getRoot(), R.string.share_link_empty_password); return; } @@ -217,4 +209,10 @@ private void setPassword(boolean createShare, OCFile file, String password) { private void setPassword(OCShare share, String password) { ((FileActivity) getActivity()).getFileOperationsHelper().setPasswordToShare(share, password); } + + @Override + public void onDestroyView() { + super.onDestroyView(); + binding = null; + } } diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java index 556b0bf1b7c0..81efaea17a4a 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java @@ -27,7 +27,6 @@ import android.app.SearchManager; import android.content.Context; import android.content.res.Resources; -import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.text.InputType; @@ -38,13 +37,12 @@ import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManager; import com.nextcloud.client.di.Injectable; import com.owncloud.android.R; +import com.owncloud.android.databinding.FileDetailsSharingFragmentBinding; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.lib.common.OwnCloudAccount; @@ -53,11 +51,11 @@ import com.owncloud.android.lib.resources.shares.SharePermissionsBuilder; import com.owncloud.android.lib.resources.shares.ShareType; import com.owncloud.android.lib.resources.status.OCCapability; +import com.owncloud.android.lib.resources.status.OwnCloudVersion; import com.owncloud.android.ui.activity.FileActivity; import com.owncloud.android.ui.activity.FileDisplayActivity; -import com.owncloud.android.ui.adapter.PublicShareInterface; -import com.owncloud.android.ui.adapter.PublicShareListAdapter; import com.owncloud.android.ui.adapter.ShareeListAdapter; +import com.owncloud.android.ui.adapter.ShareeListAdapterListener; import com.owncloud.android.ui.decoration.SimpleListItemDividerDecoration; import com.owncloud.android.ui.dialog.ExpirationDatePickerDialogFragment; import com.owncloud.android.ui.dialog.NoteDialogFragment; @@ -70,25 +68,29 @@ import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.ThemeUtils; +import java.util.ArrayList; import java.util.List; import javax.inject.Inject; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; import androidx.appcompat.widget.PopupMenu; -import androidx.appcompat.widget.SearchView; import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; -import butterknife.Unbinder; -public class FileDetailSharingFragment extends Fragment implements ShareeListAdapter.ShareeListAdapterListener, +import static com.owncloud.android.lib.resources.shares.OCShare.CREATE_PERMISSION_FLAG; +import static com.owncloud.android.lib.resources.shares.OCShare.DELETE_PERMISSION_FLAG; +import static com.owncloud.android.lib.resources.shares.OCShare.MAXIMUM_PERMISSIONS_FOR_FILE; +import static com.owncloud.android.lib.resources.shares.OCShare.MAXIMUM_PERMISSIONS_FOR_FOLDER; +import static com.owncloud.android.lib.resources.shares.OCShare.NO_PERMISSION; +import static com.owncloud.android.lib.resources.shares.OCShare.READ_PERMISSION_FLAG; +import static com.owncloud.android.lib.resources.shares.OCShare.SHARE_PERMISSION_FLAG; +import static com.owncloud.android.lib.resources.shares.OCShare.UPDATE_PERMISSION_FLAG; + +public class FileDetailSharingFragment extends Fragment implements ShareeListAdapterListener, DisplayUtils.AvatarGenerationListener, - PublicShareInterface, Injectable { private static final String ARG_FILE = "FILE"; @@ -103,40 +105,7 @@ public class FileDetailSharingFragment extends Fragment implements ShareeListAda private FileActivity fileActivity; private FileDataStorageManager fileDataStorageManager; - private Unbinder unbinder; - - @BindView(R.id.searchView) - SearchView searchView; - - @BindView(R.id.shareUsersList) - RecyclerView usersList; - - @BindView(R.id.publicShareList) - RecyclerView publicShareList; - - @BindView(R.id.new_public_share) - View addPublicShare; - - @BindView(R.id.shared_with_you_container) - LinearLayout sharedWithYouContainer; - - @BindView(R.id.shared_with_you_avatar) - ImageView sharedWithYouAvatar; - - @BindView(R.id.shared_with_you_username) - TextView sharedWithYouUsername; - - @BindView(R.id.shared_with_you_note_container) - View sharedWithYouNoteContainer; - - @BindView(R.id.shared_with_you_note) - TextView sharedWithYouNote; - - @BindView(R.id.copy_internal_link_icon) - ImageView internalLinkIcon; - - @BindView(R.id.shareInternalLinkText) - TextView internalLinkText; + private FileDetailsSharingFragmentBinding binding; @Inject UserAccountManager accountManager; @@ -184,30 +153,29 @@ public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); refreshCapabilitiesFromDB(); - refreshPublicShareFromDB(); + refreshSharesFromDB(); } @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.file_details_sharing_fragment, container, false); - unbinder = ButterKnife.bind(this, view); + binding = FileDetailsSharingFragmentBinding.inflate(inflater, container, false); + View view = binding.getRoot(); fileOperationsHelper = fileActivity.getFileOperationsHelper(); fileDataStorageManager = fileActivity.getStorageManager(); - setupView(); + AccountManager accountManager = AccountManager.get(getContext()); + String userId = accountManager.getUserData(user.toPlatformAccount(), + com.owncloud.android.lib.common.accounts.AccountUtils.Constants.KEY_USER_ID); - // todo extract - internalLinkIcon.getBackground().setColorFilter(getResources().getColor(R.color.grey_db), - PorterDuff.Mode.SRC_IN); - internalLinkIcon.getDrawable().mutate().setColorFilter(getResources().getColor(R.color.black), - PorterDuff.Mode.SRC_IN); + binding.sharesList.setAdapter(new ShareeListAdapter(fileActivity, + new ArrayList<>(), + this, + userId)); + binding.sharesList.setLayoutManager(new LinearLayoutManager(getContext())); + binding.sharesList.addItemDecoration(new SimpleListItemDividerDecoration(getContext())); - if (file.isFolder()) { - internalLinkText.setText(getString(R.string.share_internal_link_to_folder_text)); - } else { - internalLinkText.setText(getString(R.string.share_internal_link_to_file_text)); - } + setupView(); return view; } @@ -215,7 +183,7 @@ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, @Override public void onDestroy() { super.onDestroy(); - unbinder.unbind(); + binding = null; } @Override @@ -230,16 +198,17 @@ private void setupView() { setShareWithYou(); FileDetailSharingFragmentHelper.setupSearchView( - (SearchManager) fileActivity.getSystemService(Context.SEARCH_SERVICE), searchView, + (SearchManager) fileActivity.getSystemService(Context.SEARCH_SERVICE), + binding.searchView, fileActivity.getComponentName()); - ThemeUtils.themeSearchView(searchView, requireContext()); + ThemeUtils.themeSearchView(binding.searchView, requireContext()); if (file.canReshare()) { - setShareWithUserInfo(); + binding.searchView.setQueryHint(getResources().getString(R.string.share_search)); } else { - searchView.setQueryHint(getResources().getString(R.string.reshare_not_allowed)); - searchView.setInputType(InputType.TYPE_NULL); - disableSearchView(searchView); + binding.searchView.setQueryHint(getResources().getString(R.string.reshare_not_allowed)); + binding.searchView.setInputType(InputType.TYPE_NULL); + disableSearchView(binding.searchView); } } @@ -257,52 +226,32 @@ private void disableSearchView(View view) { private void setShareWithYou() { if (accountManager.userOwnsFile(file, user)) { - sharedWithYouContainer.setVisibility(View.GONE); + binding.sharedWithYouContainer.setVisibility(View.GONE); } else { - sharedWithYouUsername.setText( + binding.sharedWithYouUsername.setText( String.format(getString(R.string.shared_with_you_by), file.getOwnerDisplayName())); - DisplayUtils.setAvatar(user, file.getOwnerId(), this, getResources().getDimension( - R.dimen.file_list_item_avatar_icon_radius), getResources(), sharedWithYouAvatar, - getContext()); - sharedWithYouAvatar.setVisibility(View.VISIBLE); + DisplayUtils.setAvatar(user, + file.getOwnerId(), + this, + getResources().getDimension( + R.dimen.file_list_item_avatar_icon_radius), + getResources(), + binding.sharedWithYouAvatar, + getContext()); + binding.sharedWithYouAvatar.setVisibility(View.VISIBLE); String note = file.getNote(); if (!TextUtils.isEmpty(note)) { - sharedWithYouNote.setText(file.getNote()); - sharedWithYouNoteContainer.setVisibility(View.VISIBLE); + binding.sharedWithYouNote.setText(file.getNote()); + binding.sharedWithYouNoteContainer.setVisibility(View.VISIBLE); } else { - sharedWithYouNoteContainer.setVisibility(View.GONE); + binding.sharedWithYouNoteContainer.setVisibility(View.GONE); } } } - private void setShareWithUserInfo() { - // TODO Refactoring: create a new {@link ShareUserListAdapter} instance with every call should not be needed - // to show share with users/groups info - List shares = fileDataStorageManager.getSharesWithForAFile(file.getRemotePath(), - user.toPlatformAccount().name); - if (shares.size() > 0) { - AccountManager accountManager = AccountManager.get(getContext()); - String userId = accountManager.getUserData(user.toPlatformAccount(), - com.owncloud.android.lib.common.accounts.AccountUtils.Constants.KEY_USER_ID); - - usersList.setVisibility(View.VISIBLE); - usersList.setAdapter(new ShareeListAdapter(fileActivity.getSupportFragmentManager(), - fileActivity, - shares, - user.toPlatformAccount(), - file, - this, - userId)); - usersList.setLayoutManager(new LinearLayoutManager(getContext())); - usersList.addItemDecoration(new SimpleListItemDividerDecoration(getContext())); - } else { - usersList.setVisibility(View.GONE); - } - } - - @OnClick(R.id.copy_internal_container) + @Override public void copyInternalLink() { OwnCloudAccount account = accountManager.getCurrentOwnCloudAccount(); @@ -318,7 +267,8 @@ private String createInternalLink(OwnCloudAccount account, OCFile file) { return account.getBaseUri() + "/index.php/f/" + file.getLocalId(); } - private void createShareLink() { + @Override + public void createPublicShareLink() { if (capabilities != null && (capabilities.getFilesSharingPublicPasswordEnforced().isTrue() || capabilities.getFilesSharingPublicAskForOptionalPassword().isTrue())) { // password enforced by server, request to the user before trying to create @@ -331,26 +281,80 @@ private void createShareLink() { } } - private void showSendLinkTo() { + private void showSendLinkTo(OCShare publicShare) { if (file.isSharedViaLink()) { - if (TextUtils.isEmpty(file.getPublicLink())) { + if (TextUtils.isEmpty(publicShare.getShareLink())) { fileOperationsHelper.getFileWithLink(file); } else { - FileDisplayActivity.showShareLinkDialog(fileActivity, file, file.getPublicLink()); + FileDisplayActivity.showShareLinkDialog(fileActivity, file, publicShare.getShareLink()); } } } public void copyLink(OCShare share) { if (file.isSharedViaLink()) { - if (TextUtils.isEmpty(file.getPublicLink())) { + if (TextUtils.isEmpty(share.getShareLink())) { fileOperationsHelper.getFileWithLink(file); } else { - ClipboardUtil.copyToClipboard(getActivity(), file.getPublicLink()); + ClipboardUtil.copyToClipboard(getActivity(), share.getShareLink()); } } } + @Override + public void showUserOverflowMenu(OCShare share, ImageView overflowMenu) { + // use grey as fallback for elements where custom theming is not available + if (ThemeUtils.themingEnabled(requireContext())) { + requireContext().getTheme().applyStyle(R.style.FallbackThemingTheme, true); + } + PopupMenu popup = new PopupMenu(requireContext(), overflowMenu); + popup.inflate(R.menu.item_user_sharing_settings); + prepareUserOptionsMenu(popup.getMenu(), share); + popup.setOnMenuItemClickListener(item -> userOptionsItemSelected(popup.getMenu(), item, share)); + popup.show(); + } + + /** + * Updates the sharee's menu with the current permissions of the {@link OCShare} + * + * @param menu the menu of the sharee/shared file + * @param share the shared file + */ + @VisibleForTesting + public void prepareUserOptionsMenu(Menu menu, OCShare share) { + MenuItem allowEditingItem = menu.findItem(R.id.allow_editing); + MenuItem allowCreatingItem = menu.findItem(R.id.allow_creating); + MenuItem allowDeletingItem = menu.findItem(R.id.allow_deleting); + MenuItem expirationDateItem = menu.findItem(R.id.action_expiration_date); + MenuItem reshareItem = menu.findItem(R.id.allow_resharing); + MenuItem sendNoteItem = menu.findItem(R.id.action_share_send_note); + + allowEditingItem.setChecked(canEdit(share)); + + if (isReshareForbidden(share)) { + reshareItem.setVisible(false); + } + reshareItem.setChecked(canReshare(share)); + + if (file.isFolder() || share.isFolder()) { + allowCreatingItem.setChecked(canCreate(share)); + allowDeletingItem.setChecked(canDelete(share)); + } else { + allowCreatingItem.setVisible(false); + allowDeletingItem.setVisible(false); + } + + if (!capabilities.getVersion().isNewerOrEqual(OwnCloudVersion.nextcloud_18)) { + expirationDateItem.setVisible(false); + } + + SharingMenuHelper.setupExpirationDateMenuItem(menu.findItem(R.id.action_expiration_date), + share.getExpirationDate(), + getResources()); + + sendNoteItem.setVisible(capabilities.getVersion().isNoteOnShareSupported()); + } + public void showLinkOverflowMenu(OCShare publicShare, ImageView overflowMenuShareLink) { if (ThemeUtils.themingEnabled(requireContext())) { // use grey as fallback for elements where custom theming is not available @@ -358,21 +362,45 @@ public void showLinkOverflowMenu(OCShare publicShare, ImageView overflowMenuShar } PopupMenu popup = new PopupMenu(requireContext(), overflowMenuShareLink); - popup.inflate(R.menu.fragment_file_detail_sharing_link); - prepareOptionsMenu(popup.getMenu(), publicShare); - popup.setOnMenuItemClickListener(menuItem -> optionsItemSelected(menuItem, publicShare)); + if (ShareType.EMAIL == publicShare.getShareType()) { + popup.inflate(R.menu.fragment_file_detail_sharing_email_link); + } else { + popup.inflate(R.menu.fragment_file_detail_sharing_public_link); + } + prepareLinkOptionsMenu(popup.getMenu(), publicShare); + popup.setOnMenuItemClickListener(menuItem -> linkOptionsItemSelected(menuItem, publicShare)); popup.show(); } - private void prepareOptionsMenu(Menu menu, OCShare publicShare) { - Resources res = requireContext().getResources(); - SharingMenuHelper.setupHideFileListingMenuItem(menu.findItem(R.id.action_hide_file_listing), - file.isFolder(), - menu.findItem(R.id.action_allow_editing).isChecked(), - publicShare.getPermissions()); + @VisibleForTesting + public void prepareLinkOptionsMenu(Menu menu, OCShare publicShare) { + if (publicShare.isFolder()) { + menu.setGroupVisible(R.id.folder_permission, true); + menu.findItem(R.id.allow_editing).setVisible(false); + + // read only / allow upload and editing / file drop + if (isUploadAndEditingAllowed(publicShare)) { + menu.findItem(R.id.link_share_allow_upload_and_editing).setChecked(true); + } else if (isFileDrop(publicShare)) { + menu.findItem(R.id.link_share_file_drop).setChecked(true); + } else if (isReadOnly(publicShare)) { + menu.findItem(R.id.link_share_read_only).setChecked(true); + } + } else { + menu.setGroupVisible(R.id.folder_permission, false); + menu.findItem(R.id.allow_editing).setVisible(true); + + if (publicShare.getPermissions() > PERMISSION_EDITING_ALLOWED) { + menu.findItem(R.id.allow_editing).setChecked(true); + } else { + menu.findItem(R.id.allow_editing).setChecked(false); + } + } + Resources res = requireContext().getResources(); SharingMenuHelper.setupHideFileDownload(menu.findItem(R.id.action_hide_file_download), publicShare.isHideFileDownload(), + isFileDrop(publicShare), capabilities); SharingMenuHelper.setupPasswordMenuItem(menu.findItem(R.id.action_password), @@ -383,36 +411,104 @@ private void prepareOptionsMenu(Menu menu, OCShare publicShare) { res); menu.findItem(R.id.action_share_send_note).setVisible(capabilities.getVersion().isNoteOnShareSupported()); + } - if (publicShare.getPermissions() > PERMISSION_EDITING_ALLOWED) { - menu.findItem(R.id.action_allow_editing).setChecked(true); - } else { - menu.findItem(R.id.action_allow_editing).setChecked(false); + @VisibleForTesting + public boolean isUploadAndEditingAllowed(OCShare share) { + if (share.getPermissions() == NO_PERMISSION) { + return false; } + + return (share.getPermissions() & MAXIMUM_PERMISSIONS_FOR_FOLDER) == MAXIMUM_PERMISSIONS_FOR_FOLDER; } - public boolean optionsItemSelected(MenuItem item, OCShare publicShare) { + @VisibleForTesting + public boolean isReadOnly(OCShare share) { + if (share.getPermissions() == NO_PERMISSION) { + return false; + } + + return (share.getPermissions() & ~SHARE_PERMISSION_FLAG) == READ_PERMISSION_FLAG; + } + + @VisibleForTesting + public boolean isFileDrop(OCShare share) { + if (share.getPermissions() == NO_PERMISSION) { + return false; + } + + return (share.getPermissions() & ~SHARE_PERMISSION_FLAG) == CREATE_PERMISSION_FLAG; + } + + private boolean userOptionsItemSelected(Menu menu, MenuItem item, OCShare share) { switch (item.getItemId()) { - case R.id.action_allow_editing: + case R.id.allow_editing: + case R.id.allow_creating: + case R.id.allow_deleting: + case R.id.allow_resharing: { + item.setChecked(!item.isChecked()); + share.setPermissions(updatePermissionsToShare(share, + menu.findItem(R.id.allow_resharing).isChecked(), + menu.findItem(R.id.allow_editing).isChecked(), + menu.findItem(R.id.allow_creating).isChecked(), + menu.findItem(R.id.allow_deleting).isChecked())); + return true; + } + case R.id.action_unshare: { + unshareWith(share); + ShareeListAdapter adapter = (ShareeListAdapter) binding.sharesList.getAdapter(); + if (adapter == null) { + DisplayUtils.showSnackMessage(getView(), getString(R.string.failed_update_ui)); + return true; + } + adapter.remove(share); + + return true; + } + case R.id.action_expiration_date: { + ExpirationDatePickerDialogFragment dialog = ExpirationDatePickerDialogFragment + .newInstance(share, share.getExpirationDate()); + dialog.show(fileActivity.getSupportFragmentManager(), + ExpirationDatePickerDialogFragment.DATE_PICKER_DIALOG); + return true; + } + case R.id.action_share_send_note: + NoteDialogFragment dialog = NoteDialogFragment.newInstance(share); + dialog.show(fileActivity.getSupportFragmentManager(), NoteDialogFragment.NOTE_FRAGMENT); + return true; + default: + return true; + } + } + + public boolean linkOptionsItemSelected(MenuItem item, OCShare publicShare) { + switch (item.getItemId()) { + case R.id.link_share_read_only: + item.setChecked(true); + fileOperationsHelper.setPermissionsToShare(publicShare, READ_PERMISSION_FLAG); + return true; + case R.id.link_share_allow_upload_and_editing: + item.setChecked(true); + if (publicShare.isFolder()) { + fileOperationsHelper.setPermissionsToShare(publicShare, MAXIMUM_PERMISSIONS_FOR_FOLDER); + } else { + fileOperationsHelper.setPermissionsToShare(publicShare, MAXIMUM_PERMISSIONS_FOR_FILE); + } + return true; + case R.id.link_share_file_drop: { + item.setChecked(true); + fileOperationsHelper.setPermissionsToShare(publicShare, CREATE_PERMISSION_FLAG); + return true; + } + case R.id.allow_editing: if (file.isSharedViaLink()) { item.setChecked(!item.isChecked()); fileOperationsHelper.setUploadPermissionsToPublicShare(publicShare, item.isChecked()); } return true; - case R.id.action_hide_file_listing: { - item.setChecked(!item.isChecked()); - fileOperationsHelper.setHideFileListingPermissionsToPublicShare(publicShare, item.isChecked()); - return true; - } case R.id.action_hide_file_download: item.setChecked(!item.isChecked()); fileOperationsHelper.setHideFileDownloadPermissionsToPublicShare(publicShare, item.isChecked()); - - return true; - case R.id.action_edit_label: - RenamePublicShareDialogFragment renameDialog = RenamePublicShareDialogFragment.newInstance(publicShare); - renameDialog.show(fileActivity.getSupportFragmentManager(), - RenamePublicShareDialogFragment.RENAME_PUBLIC_SHARE_FRAGMENT); return true; case R.id.action_password: { requestPasswordForShare(publicShare, @@ -427,10 +523,10 @@ public boolean optionsItemSelected(MenuItem item, OCShare publicShare) { return true; } case R.id.action_share_send_link: { - if (file.isSharedViaLink() && !TextUtils.isEmpty(file.getPublicLink())) { - FileDisplayActivity.showShareLinkDialog(fileActivity, file, file.getPublicLink()); + if (file.isSharedViaLink() && !TextUtils.isEmpty(publicShare.getShareLink())) { + FileDisplayActivity.showShareLinkDialog(fileActivity, file, publicShare.getShareLink()); } else { - showSendLinkTo(); + showSendLinkTo(publicShare); } return true; } @@ -438,12 +534,17 @@ public boolean optionsItemSelected(MenuItem item, OCShare publicShare) { NoteDialogFragment noteDialog = NoteDialogFragment.newInstance(publicShare); noteDialog.show(fileActivity.getSupportFragmentManager(), NoteDialogFragment.NOTE_FRAGMENT); return true; - case R.id.action_add_another_public_share_link: - createShareLink(); + case R.id.action_edit_label: + RenamePublicShareDialogFragment renameDialog = RenamePublicShareDialogFragment.newInstance(publicShare); + renameDialog.show(fileActivity.getSupportFragmentManager(), + RenamePublicShareDialogFragment.RENAME_PUBLIC_SHARE_FRAGMENT); return true; case R.id.action_unshare: fileOperationsHelper.unshareShare(file, publicShare); return true; + case R.id.action_add_another_public_share_link: + createPublicShareLink(); + return true; default: return super.onOptionsItemSelected(item); } @@ -469,28 +570,25 @@ public void onUpdateShareInformation(RemoteOperationResult result, OCFile file) * Get {@link OCShare} instance from DB and updates the UI. */ private void refreshUiFromDB() { + refreshSharesFromDB(); // Updates UI with new state setupView(); } - @Override - public void unshareWith(OCShare share) { + private void unshareWith(OCShare share) { fileOperationsHelper.unshareShare(file, share); } - @Override - public int updatePermissionsToShare(OCShare share, - boolean canReshare, - boolean canEdit, - boolean canEditCreate, - boolean canEditChange, - boolean canEditDelete) { + private int updatePermissionsToShare(OCShare share, + boolean canReshare, + boolean canEdit, + boolean canEditCreate, + boolean canEditDelete) { SharePermissionsBuilder spb = new SharePermissionsBuilder(); spb.setSharePermission(canReshare); if (file.isFolder()) { - spb.setUpdatePermission(canEditChange) - .setCreatePermission(canEditCreate) + spb.setCreatePermission(canEditCreate) .setDeletePermission(canEditDelete); } else { spb.setUpdatePermission(canEdit); @@ -531,38 +629,51 @@ public void refreshCapabilitiesFromDB() { /** * Get public link from the DB to fill in the "Share link" section in the UI. - * * Takes into account server capabilities before reading database. */ - public void refreshPublicShareFromDB() { + public void refreshSharesFromDB() { + ShareeListAdapter adapter = (ShareeListAdapter) binding.sharesList.getAdapter(); + + if (adapter == null) { + DisplayUtils.showSnackMessage(getView(), getString(R.string.could_not_retrieve_shares)); + return; + } + adapter.getShares().clear(); + + // to show share with users/groups info + List shares = fileDataStorageManager.getSharesWithForAFile(file.getRemotePath(), + user.toPlatformAccount().name); + + adapter.addShares(shares); + if (FileDetailSharingFragmentHelper.isPublicShareDisabled(capabilities) || !file.canReshare()) { - publicShareList.setVisibility(View.GONE); return; } // Get public share - List shares = fileDataStorageManager.getSharesByPathAndType(file.getRemotePath(), - ShareType.PUBLIC_LINK, - ""); - - if (shares.isEmpty()) { - addPublicShare.setVisibility(View.VISIBLE); - publicShareList.setVisibility(View.GONE); - ImageView icon = requireView().findViewById(R.id.copy_internal_link_icon); - icon.getBackground().setColorFilter(requireContext() - .getResources() - .getColor(R.color.primary_button_background_color), - PorterDuff.Mode.SRC_IN); - icon.getDrawable().mutate().setColorFilter(requireContext().getResources().getColor(R.color.black), - PorterDuff.Mode.SRC_IN); - requireView().findViewById(R.id.add_new_public_share_link).setOnClickListener(v -> createShareLink()); + List publicShares = fileDataStorageManager.getSharesByPathAndType(file.getRemotePath(), + ShareType.PUBLIC_LINK, + ""); + + + if (publicShares.isEmpty() && containsNoNewPublicShare(adapter.getShares())) { + publicShares.add(new OCShare().setShareType(ShareType.NEW_PUBLIC_LINK)); } else { - addPublicShare.setVisibility(View.GONE); - publicShareList.setVisibility(View.VISIBLE); - publicShareList.setAdapter(new PublicShareListAdapter(getContext(), shares, this)); - publicShareList.setLayoutManager(new LinearLayoutManager(getContext())); - publicShareList.addItemDecoration(new SimpleListItemDividerDecoration(getContext())); + adapter.removeNewPublicShare(); } + + adapter.addShares(publicShares); + } + + + private boolean containsNoNewPublicShare(List shares) { + for (OCShare share : shares) { + if (share.getShareType() == ShareType.NEW_PUBLIC_LINK) { + return false; + } + } + + return true; } @Override @@ -575,11 +686,33 @@ public void onSaveInstanceState(@NonNull Bundle outState) { @Override public void avatarGenerated(Drawable avatarDrawable, Object callContext) { - sharedWithYouAvatar.setImageDrawable(avatarDrawable); + binding.sharedWithYouAvatar.setImageDrawable(avatarDrawable); } @Override public boolean shouldCallGeneratedCallback(String tag, Object callContext) { return false; } + + private boolean isReshareForbidden(OCShare share) { + return ShareType.FEDERATED.equals(share.getShareType()) || + capabilities != null && capabilities.getFilesSharingResharing().isFalse(); + } + + private boolean canEdit(OCShare share) { + return (share.getPermissions() & + (CREATE_PERMISSION_FLAG | UPDATE_PERMISSION_FLAG | DELETE_PERMISSION_FLAG)) > 0; + } + + private boolean canCreate(OCShare share) { + return (share.getPermissions() & CREATE_PERMISSION_FLAG) > 0; + } + + private boolean canDelete(OCShare share) { + return (share.getPermissions() & DELETE_PERMISSION_FLAG) > 0; + } + + private boolean canReshare(OCShare share) { + return (share.getPermissions() & SHARE_PERMISSION_FLAG) > 0; + } } diff --git a/src/main/java/com/owncloud/android/ui/fragment/util/SharingMenuHelper.java b/src/main/java/com/owncloud/android/ui/fragment/util/SharingMenuHelper.java index b322511e26ab..9d53ccc52a2d 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/util/SharingMenuHelper.java +++ b/src/main/java/com/owncloud/android/ui/fragment/util/SharingMenuHelper.java @@ -65,11 +65,15 @@ public static void setupHideFileListingMenuItem(MenuItem fileListing, /** * Sets checked/visibility state on the given {@link MenuItem} based on the given criteria. - * @param menuItem the {@link MenuItem} to be setup + * + * @param menuItem the {@link MenuItem} to be setup * @param capabilities Capabilities of server to check if hide download is supported */ - public static void setupHideFileDownload(MenuItem menuItem, boolean hideFileDownload, OCCapability capabilities) { - if (!capabilities.getVersion().isHideFileDownloadSupported()) { + public static void setupHideFileDownload(MenuItem menuItem, + boolean hideFileDownload, + boolean isFileDrop, + OCCapability capabilities) { + if (!capabilities.getVersion().isHideFileDownloadSupported() || isFileDrop) { menuItem.setVisible(false); } else { menuItem.setVisible(true); diff --git a/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java b/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java index c61e7e3fe125..e81843e81c51 100755 --- a/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java +++ b/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java @@ -578,23 +578,6 @@ public void showShareFile(OCFile file) { fileActivity.startActivity(intent); } - - /** - * Updates a public share on a file to set its password. Starts a request to do it in {@link OperationsService} - * - * @param password Password to set for the public link; null or empty string to clear the current password - */ - public void setPasswordToPublicShare(OCShare share, String password) { - // Set password updating share - Intent updateShareIntent = new Intent(fileActivity, OperationsService.class); - updateShareIntent.setAction(OperationsService.ACTION_UPDATE_PUBLIC_SHARE); - updateShareIntent.putExtra(OperationsService.EXTRA_ACCOUNT, fileActivity.getAccount()); - updateShareIntent.putExtra(OperationsService.EXTRA_SHARE_ID, share.getId()); - updateShareIntent.putExtra(OperationsService.EXTRA_SHARE_PASSWORD, (password == null) ? "" : password); - - queueShareIntent(updateShareIntent); - } - /** * Updates a public share on a file to set its label. Starts a request to do it in {@link OperationsService} * @@ -633,23 +616,6 @@ public void setPasswordToShare(OCShare share, String password) { } - /** - * Updates a public share on a file to set its expiration date. Starts a request to do it in {@link - * OperationsService} - * - * @param share {@link OCShare} instance which permissions will be updated. - * @param expirationTimeInMillis Expiration date to set. A negative value clears the current expiration date, - * leaving the link unrestricted. Zero makes no change. - */ - public void setExpirationDateToPublicShare(OCShare share, long expirationTimeInMillis) { - Intent updateShareIntent = new Intent(fileActivity, OperationsService.class); - updateShareIntent.setAction(OperationsService.ACTION_UPDATE_PUBLIC_SHARE); - updateShareIntent.putExtra(OperationsService.EXTRA_ACCOUNT, fileActivity.getAccount()); - updateShareIntent.putExtra(OperationsService.EXTRA_SHARE_ID, share.getId()); - updateShareIntent.putExtra(OperationsService.EXTRA_SHARE_EXPIRATION_DATE_IN_MILLIS, expirationTimeInMillis); - queueShareIntent(updateShareIntent); - } - /** * Updates a public share on a file to set its expiration date. * Starts a request to do it in {@link OperationsService} @@ -693,31 +659,13 @@ public void setPermissionsToShare(OCShare share, int permissions) { */ public void setUploadPermissionsToPublicShare(OCShare share, boolean uploadPermission) { Intent updateShareIntent = new Intent(fileActivity, OperationsService.class); - updateShareIntent.setAction(OperationsService.ACTION_UPDATE_PUBLIC_SHARE); - updateShareIntent.putExtra(OperationsService.EXTRA_ACCOUNT, fileActivity.getAccount()); - updateShareIntent.putExtra(OperationsService.EXTRA_SHARE_ID, share.getId()); - updateShareIntent.putExtra(OperationsService.EXTRA_SHARE_PUBLIC_UPLOAD, uploadPermission); - queueShareIntent(updateShareIntent); - } - - /** - * Updates a public share on a folder to set its hide file listing permission. - * Starts a request to do it in {@link OperationsService} - * - * @param share {@link OCShare} instance which permissions will be updated. - * @param hideFileListing New state of the permission for editing the folder shared via link. - */ - public void setHideFileListingPermissionsToPublicShare(OCShare share, boolean hideFileListing) { - Intent updateShareIntent = new Intent(fileActivity, OperationsService.class); - updateShareIntent.setAction(OperationsService.ACTION_UPDATE_PUBLIC_SHARE); + updateShareIntent.setAction(OperationsService.ACTION_UPDATE_USER_SHARE); updateShareIntent.putExtra(OperationsService.EXTRA_ACCOUNT, fileActivity.getAccount()); updateShareIntent.putExtra(OperationsService.EXTRA_SHARE_ID, share.getId()); - - if (hideFileListing) { - updateShareIntent.putExtra(OperationsService.EXTRA_SHARE_PERMISSIONS, OCShare.CREATE_PERMISSION_FLAG); + if (uploadPermission) { + updateShareIntent.putExtra(OperationsService.EXTRA_SHARE_PERMISSIONS, 3); } else { - updateShareIntent.putExtra(OperationsService.EXTRA_SHARE_PERMISSIONS, - OCShare.FEDERATED_PERMISSIONS_FOR_FOLDER_AFTER_OC9); + updateShareIntent.putExtra(OperationsService.EXTRA_SHARE_PERMISSIONS, 1); } queueShareIntent(updateShareIntent); diff --git a/src/main/res/drawable/ic_hide.xml b/src/main/res/drawable/ic_hide.xml deleted file mode 100644 index dec6ba13172a..000000000000 --- a/src/main/res/drawable/ic_hide.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - diff --git a/src/main/res/drawable/ic_show.xml b/src/main/res/drawable/ic_show.xml deleted file mode 100644 index a0ead7e544cf..000000000000 --- a/src/main/res/drawable/ic_show.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - diff --git a/src/main/res/drawable/password_visibility_selector.xml b/src/main/res/drawable/password_visibility_selector.xml deleted file mode 100644 index 71e5a6ace792..000000000000 --- a/src/main/res/drawable/password_visibility_selector.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/main/res/layout/file_details_fragment.xml b/src/main/res/layout/file_details_fragment.xml index f69a0abd22b1..e5c3ceb45ccf 100644 --- a/src/main/res/layout/file_details_fragment.xml +++ b/src/main/res/layout/file_details_fragment.xml @@ -23,7 +23,6 @@ android:id="@+id/detail_container" android:layout_width="match_parent" android:layout_height="match_parent" - android:fillViewport="true" android:orientation="vertical"> + android:layout_height="match_parent" /> diff --git a/src/main/res/layout/file_details_share_internal_share_link.xml b/src/main/res/layout/file_details_share_internal_share_link.xml new file mode 100644 index 000000000000..6dfe2501f4f9 --- /dev/null +++ b/src/main/res/layout/file_details_share_internal_share_link.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + diff --git a/src/main/res/layout/file_details_share_public_link_item.xml b/src/main/res/layout/file_details_share_link_share_item.xml similarity index 82% rename from src/main/res/layout/file_details_share_public_link_item.xml rename to src/main/res/layout/file_details_share_link_share_item.xml index f473fc1244ef..a355a45fdeda 100644 --- a/src/main/res/layout/file_details_share_public_link_item.xml +++ b/src/main/res/layout/file_details_share_link_share_item.xml @@ -25,11 +25,11 @@ + android:layout_height="@dimen/sharee_list_item_size" + android:orientation="horizontal"> - - - - - + + + diff --git a/src/main/res/layout/file_details_sharing_fragment.xml b/src/main/res/layout/file_details_sharing_fragment.xml index 6778c9ecfd7e..26f93511dcde 100644 --- a/src/main/res/layout/file_details_sharing_fragment.xml +++ b/src/main/res/layout/file_details_sharing_fragment.xml @@ -1,5 +1,4 @@ - - - + android:layout_width="match_parent" + android:orientation="vertical" + android:paddingTop="@dimen/standard_eight_padding" + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools"> + + - - + android:layout_marginBottom="@dimen/standard_half_margin" + android:layout_width="match_parent" + android:orientation="horizontal" + android:paddingLeft="@dimen/standard_padding" + android:paddingRight="@dimen/standard_padding" + android:paddingTop="@dimen/standard_padding"> + + - - + android:paddingRight="@dimen/standard_padding" + android:paddingTop="@dimen/standard_half_padding"> - - - - - - - - - - - - - - - - - - - - - - - + android:layout_width="match_parent" + android:text="@string/shared_with_you_by" + android:textSize="16sp" /> + android:orientation="horizontal" + android:paddingTop="@dimen/standard_half_padding" + tools:ignore="UseCompoundDrawables"> - + + android:layout_weight="1" + android:layout_width="0dp" + android:paddingEnd="@dimen/standard_half_padding" + android:paddingStart="@dimen/standard_half_padding" + android:textSize="16sp" /> - - + + + diff --git a/src/main/res/layout/password_dialog.xml b/src/main/res/layout/password_dialog.xml index 4f927a5d87db..2b5638208486 100644 --- a/src/main/res/layout/password_dialog.xml +++ b/src/main/res/layout/password_dialog.xml @@ -18,32 +18,20 @@ --> + android:layout_height="match_parent" + android:gravity="clip_horizontal" + android:orientation="vertical" + android:padding="@dimen/standard_padding"> - - - - - - + android:ems="10" + android:hint="@string/hint_password" + android:inputType="textPassword" + android:autofillHints="password" + android:textColorHint="@color/bg_fallback_highlight"> diff --git a/src/main/res/layout/share_user_item.xml b/src/main/res/layout/share_user_item.xml deleted file mode 100644 index befa156e9c52..000000000000 --- a/src/main/res/layout/share_user_item.xml +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/src/main/res/layout/toolbar_standard.xml b/src/main/res/layout/toolbar_standard.xml index 066082ba4645..481f22579b4d 100644 --- a/src/main/res/layout/toolbar_standard.xml +++ b/src/main/res/layout/toolbar_standard.xml @@ -39,7 +39,7 @@ android:layout_height="wrap_content" android:layout_marginTop="?attr/actionBarSize" android:background="@color/bg_default" - android:paddingTop="@dimen/standard_eigth_padding" + android:paddingTop="@dimen/standard_eight_padding" android:visibility="gone" tools:visibility="visible"> diff --git a/src/main/res/menu/fragment_file_detail_sharing_email_link.xml b/src/main/res/menu/fragment_file_detail_sharing_email_link.xml new file mode 100644 index 000000000000..6fdd4d40f2c9 --- /dev/null +++ b/src/main/res/menu/fragment_file_detail_sharing_email_link.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + diff --git a/src/main/res/menu/fragment_file_detail_sharing_link.xml b/src/main/res/menu/fragment_file_detail_sharing_public_link.xml similarity index 80% rename from src/main/res/menu/fragment_file_detail_sharing_link.xml rename to src/main/res/menu/fragment_file_detail_sharing_public_link.xml index e7f7df85f3bd..5aa14cae89a2 100644 --- a/src/main/res/menu/fragment_file_detail_sharing_link.xml +++ b/src/main/res/menu/fragment_file_detail_sharing_public_link.xml @@ -1,6 +1,5 @@ - -