From ead3a744e0e9a307a37486a1cc335adfda4e65b4 Mon Sep 17 00:00:00 2001 From: A117870935 Date: Fri, 28 Apr 2023 01:25:43 +0530 Subject: [PATCH 1/3] Customizations done for Sharing with test cases. --- .../java/com/nmc/android/ui/FileSharingIT.kt | 392 ++++++++++++++++++ .../nmc/android/ui/RecyclerViewAssertions.kt | 81 ++++ app/src/main/AndroidManifest.xml | 3 +- .../nmc/android/utils/CheckableThemeUtils.kt | 86 ++++ .../com/nmc/android/utils/DisplayUtils.kt | 18 + .../com/nmc/android/utils/KeyboardUtils.java | 21 + .../nmc/android/utils/SearchViewThemeUtils.kt | 22 + .../CreateShareWithShareeOperation.java | 4 +- .../UsersAndGroupsSearchProvider.java | 33 +- .../android/ui/activity/FileActivity.java | 32 +- .../android/ui/activity/ShareActivity.java | 6 +- .../android/ui/activity/ToolbarActivity.java | 48 +++ .../ui/adapter/LinkShareViewHolder.java | 45 +- .../ui/adapter/ListGridImageViewHolder.kt | 1 + .../android/ui/adapter/OCFileListAdapter.java | 3 +- .../android/ui/adapter/OCFileListDelegate.kt | 68 ++- .../adapter/OCFileListGridImageViewHolder.kt | 2 + .../adapter/OCFileListGridItemViewHolder.kt | 2 + .../ui/adapter/OCFileListItemViewHolder.kt | 2 + .../adapter/QuickSharingPermissionsAdapter.kt | 1 - .../android/ui/adapter/ShareViewHolder.java | 15 +- .../android/ui/adapter/ShareeListAdapter.java | 15 +- .../ui/events/ShareSearchViewFocusEvent.kt | 28 ++ .../ui/fragment/FileDetailFragment.java | 322 +++++++------- .../fragment/FileDetailSharingFragment.java | 199 +++++++-- ...ileDetailSharingMenuBottomSheetDialog.java | 29 +- ...eDetailsSharingMenuBottomSheetActions.java | 8 +- .../ui/fragment/OCFileListFragment.java | 8 +- ...ckSharingPermissionsBottomSheetDialog.java | 26 +- .../ui/fragment/util/SharingMenuHelper.java | 72 +++- .../res/drawable-night/ic_internal_share.xml | 12 + app/src/main/res/drawable/ic_calendar.xml | 12 + app/src/main/res/drawable/ic_clipboard.xml | 12 + .../main/res/drawable/ic_external_share.xml | 12 + .../main/res/drawable/ic_internal_share.xml | 12 + app/src/main/res/drawable/ic_open_in.xml | 12 + app/src/main/res/drawable/ic_pencil_edit.xml | 12 + app/src/main/res/drawable/ic_shared.xml | 12 + .../main/res/drawable/ic_shared_with_me.xml | 13 + app/src/main/res/drawable/share_et_bg.xml | 5 + .../res/drawable/share_search_background.xml | 17 + .../res/drawable/sharing_email_warning_bg.xml | 6 + .../res/drawable/toolbar_back_arrow_bg.xml | 5 + .../main/res/layout/file_details_fragment.xml | 40 +- .../file_details_share_link_share_item.xml | 30 +- ...details_share_public_link_add_new_item.xml | 4 +- .../layout/file_details_share_share_item.xml | 17 +- .../layout/file_details_sharing_fragment.xml | 241 +++++++---- ...ils_sharing_menu_bottom_sheet_fragment.xml | 79 +++- .../layout/item_quick_share_permissions.xml | 14 +- app/src/main/res/layout/list_item.xml | 18 +- app/src/main/res/layout/toolbar_standard.xml | 9 + app/src/main/res/values-de/strings.xml | 77 +++- app/src/main/res/values-night/colors.xml | 66 +++ app/src/main/res/values-sw480dp/bool.xml | 4 + app/src/main/res/values/attrs.xml | 15 +- app/src/main/res/values/bool.xml | 4 + app/src/main/res/values/colors.xml | 91 ++++ app/src/main/res/values/dimens.xml | 31 ++ app/src/main/res/values/strings.xml | 54 ++- app/src/main/res/values/styles.xml | 11 + 61 files changed, 2032 insertions(+), 507 deletions(-) create mode 100644 app/src/androidTest/java/com/nmc/android/ui/FileSharingIT.kt create mode 100644 app/src/androidTest/java/com/nmc/android/ui/RecyclerViewAssertions.kt create mode 100644 app/src/main/java/com/nmc/android/utils/CheckableThemeUtils.kt create mode 100644 app/src/main/java/com/nmc/android/utils/DisplayUtils.kt create mode 100644 app/src/main/java/com/nmc/android/utils/KeyboardUtils.java create mode 100644 app/src/main/java/com/nmc/android/utils/SearchViewThemeUtils.kt create mode 100644 app/src/main/java/com/owncloud/android/ui/events/ShareSearchViewFocusEvent.kt create mode 100644 app/src/main/res/drawable-night/ic_internal_share.xml create mode 100644 app/src/main/res/drawable/ic_calendar.xml create mode 100644 app/src/main/res/drawable/ic_clipboard.xml create mode 100644 app/src/main/res/drawable/ic_external_share.xml create mode 100644 app/src/main/res/drawable/ic_internal_share.xml create mode 100644 app/src/main/res/drawable/ic_open_in.xml create mode 100644 app/src/main/res/drawable/ic_pencil_edit.xml create mode 100644 app/src/main/res/drawable/ic_shared.xml create mode 100644 app/src/main/res/drawable/ic_shared_with_me.xml create mode 100644 app/src/main/res/drawable/share_et_bg.xml create mode 100644 app/src/main/res/drawable/share_search_background.xml create mode 100644 app/src/main/res/drawable/sharing_email_warning_bg.xml create mode 100644 app/src/main/res/drawable/toolbar_back_arrow_bg.xml create mode 100644 app/src/main/res/values-sw480dp/bool.xml create mode 100644 app/src/main/res/values/bool.xml create mode 100644 app/src/main/res/values/dimens.xml diff --git a/app/src/androidTest/java/com/nmc/android/ui/FileSharingIT.kt b/app/src/androidTest/java/com/nmc/android/ui/FileSharingIT.kt new file mode 100644 index 000000000000..27f64df78d40 --- /dev/null +++ b/app/src/androidTest/java/com/nmc/android/ui/FileSharingIT.kt @@ -0,0 +1,392 @@ +package com.nmc.android.ui + +import androidx.recyclerview.widget.RecyclerView +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.Espresso.pressBack +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.contrib.RecyclerViewActions +import androidx.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition +import androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.matcher.ViewMatchers.withText +import androidx.test.ext.junit.rules.ActivityScenarioRule +import com.nextcloud.test.RetryTestRule +import com.nextcloud.test.TestActivity +import com.nmc.android.ui.RecyclerViewAssertions.clickChildViewWithId +import com.nmc.android.ui.RecyclerViewAssertions.withRecyclerView +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.ShareType +import com.owncloud.android.ui.fragment.FileDetailFragment +import com.owncloud.android.ui.fragment.FileDetailSharingFragment +import com.owncloud.android.ui.fragment.util.SharingMenuHelper +import org.hamcrest.Matchers.not +import org.junit.After +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Rule +import org.junit.Test + +class FileSharingIT : AbstractIT() { + @get:Rule + val activityScenarioRule = ActivityScenarioRule(TestActivity::class.java) + + @get:Rule + val retryRule = RetryTestRule() + + lateinit var file: OCFile + lateinit var folder: OCFile + + @Before + fun before() { + activityScenarioRule.scenario.onActivity { + file = OCFile("/test.md").apply { + remoteId = "00000001" + parentId = it.storageManager.getFileByEncryptedRemotePath("/").fileId + permissions = OCFile.PERMISSION_CAN_RESHARE + } + + folder = OCFile("/test").apply { + setFolder() + remoteId = "00000002" + parentId = it.storageManager.getFileByEncryptedRemotePath("/").fileId + permissions = OCFile.PERMISSION_CAN_RESHARE + } + } + } + + private fun show(file: OCFile) { + val fragment = FileDetailFragment.newInstance(file, user, 0) + + activityScenarioRule.scenario.onActivity { + it.addFragment(fragment) + } + + waitForIdleSync() + + shortSleep() + } + + private fun getFragment(): FileDetailSharingFragment? { + var fragment: FileDetailSharingFragment? = null + activityScenarioRule.scenario.onActivity { + fragment = + it.supportFragmentManager.findFragmentByTag("SHARING_DETAILS_FRAGMENT") as FileDetailSharingFragment + } + return fragment + } + + @Test + fun validateUiOfFileDetailFragment() { + show(file) + + onView(withId(R.id.filename)).check(matches(isCompletelyDisplayed())) + onView(withId(R.id.filename)).check(matches(withText("test.md"))) + onView(withId(R.id.favorite)).check(matches(isCompletelyDisplayed())) + onView(withId(R.id.size)).check(matches(isCompletelyDisplayed())) + onView(withId(R.id.file_separator)).check(matches(isCompletelyDisplayed())) + onView(withId(R.id.last_modification_timestamp)).check(matches(isCompletelyDisplayed())) + } + + @Test + fun validateUiForEmptyShares() { + show(file) + + onView(withId(R.id.shared_with_you_container)).check(matches(not(isCompletelyDisplayed()))) + onView(withId(R.id.tv_sharing_details_message)).check(matches(isCompletelyDisplayed())) + onView(withId(R.id.tv_sharing_details_message)).check(matches(withText("You can create links or send shares by mail. If you invite MagentaCLOUD users, you have more opportunities for collaboration."))) + onView(withId(R.id.searchView)).check(matches(isCompletelyDisplayed())) + onView(withId(R.id.label_personal_share)).check(matches(isCompletelyDisplayed())) + onView(withId(R.id.label_personal_share)).check(matches(withText("Personal share by mail"))) + + onView(withId(R.id.share_create_new_link)).check(matches(isCompletelyDisplayed())) + onView(withId(R.id.share_create_new_link)).check(matches(withText("Add link"))) + + onView(withId(R.id.tv_your_shares)).check(matches(not(isDisplayed()))) + onView(withId(R.id.sharesList)).check(matches(not(isDisplayed()))) + onView(withId(R.id.tv_empty_shares)).check(matches(isCompletelyDisplayed())) + onView(withId(R.id.tv_empty_shares)).check(matches(withText("No shares created yet."))) + } + + @Test + fun validateUiForFileWithShares() { + activityScenarioRule.scenario.onActivity { + OCShare(file.decryptedRemotePath).apply { + remoteId = 1 + shareType = ShareType.USER + sharedWithDisplayName = "Admin" + permissions = OCShare.READ_PERMISSION_FLAG + userId = getUserId(user) + it.storageManager.saveShare(this) + } + + OCShare(file.decryptedRemotePath).apply { + remoteId = 3 + shareType = ShareType.EMAIL + permissions = SharingMenuHelper.CAN_EDIT_PERMISSIONS_FOR_FILE + sharedWithDisplayName = "johndoe@gmail.com" + userId = getUserId(user) + it.storageManager.saveShare(this) + } + + OCShare(file.decryptedRemotePath).apply { + remoteId = 4 + shareType = ShareType.PUBLIC_LINK + permissions = OCShare.READ_PERMISSION_FLAG + label = "Customer" + it.storageManager.saveShare(this) + } + + OCShare(file.decryptedRemotePath).apply { + remoteId = 5 + shareType = ShareType.PUBLIC_LINK + permissions = SharingMenuHelper.CAN_EDIT_PERMISSIONS_FOR_FILE + label = "Colleagues" + it.storageManager.saveShare(this) + } + + } + show(file) + + onView(withId(R.id.shared_with_you_container)).check(matches(not(isCompletelyDisplayed()))) + onView(withId(R.id.tv_sharing_details_message)).check(matches(isCompletelyDisplayed())) + onView(withId(R.id.tv_sharing_details_message)).check(matches(withText("You can create links or send shares by mail. If you invite MagentaCLOUD users, you have more opportunities for collaboration."))) + onView(withId(R.id.searchView)).check(matches(isCompletelyDisplayed())) + onView(withId(R.id.label_personal_share)).check(matches(isCompletelyDisplayed())) + onView(withId(R.id.label_personal_share)).check(matches(withText("Personal share by mail"))) + + onView(withId(R.id.share_create_new_link)).check(matches(isCompletelyDisplayed())) + onView(withId(R.id.share_create_new_link)).check(matches(withText("Add link"))) + + onView(withId(R.id.tv_your_shares)).check(matches(isCompletelyDisplayed())) + onView(withId(R.id.tv_your_shares)).check(matches(withText("Your Shares"))) + onView(withId(R.id.sharesList)).check(matches(isCompletelyDisplayed())) + onView(withId(R.id.tv_empty_shares)).check(matches(not(isDisplayed()))) + } + + @Test + fun validateUiWithResharingNotAllowed() { + file = file.apply { + permissions = "" + ownerDisplayName = "John Doe" + ownerId = "JohnDoe" + note = "Shared for testing purpose." + } + show(file) + + onView(withId(R.id.shared_with_you_container)).check(matches(isCompletelyDisplayed())) + onView(withId(R.id.tv_sharing_details_message)).check(matches(isCompletelyDisplayed())) + onView(withId(R.id.tv_sharing_details_message)).check(matches(withText("Resharing is not allowed."))) + onView(withId(R.id.shared_with_you_username)).check(matches(withText("Shared with you by John Doe"))) + onView(withId(R.id.shared_with_you_note)).check(matches(withText("Shared for testing purpose."))) + onView(withId(R.id.shared_with_you_avatar)).check(matches(isCompletelyDisplayed())) + + onView(withId(R.id.searchView)).check(matches(not(isDisplayed()))) + onView(withId(R.id.label_personal_share)).check(matches(not(isDisplayed()))) + onView(withId(R.id.share_create_new_link)).check(matches(not(isDisplayed()))) + onView(withId(R.id.tv_your_shares)).check(matches(not(isDisplayed()))) + onView(withId(R.id.sharesList)).check(matches(not(isDisplayed()))) + onView(withId(R.id.tv_empty_shares)).check(matches(not(isDisplayed()))) + } + + @Test + fun validateUiWithResharingAllowed() { + file = file.apply { + ownerDisplayName = "John Doe" + ownerId = "JohnDoe" + } + show(file) + + onView(withId(R.id.shared_with_you_container)).check(matches(isCompletelyDisplayed())) + onView(withId(R.id.tv_sharing_details_message)).check(matches(isCompletelyDisplayed())) + onView(withId(R.id.tv_sharing_details_message)).check(matches(withText("Resharing is allowed. You can create links or send shares by mail. If you invite MagentaCLOUD users, you have more opportunities for collaboration."))) + onView(withId(R.id.shared_with_you_username)).check(matches(withText("Shared with you by John Doe"))) + onView(withId(R.id.shared_with_you_avatar)).check(matches(isCompletelyDisplayed())) + onView(withId(R.id.shared_with_you_note_container)).check(matches(not(isDisplayed()))) + + onView(withId(R.id.searchView)).check(matches(isCompletelyDisplayed())) + onView(withId(R.id.label_personal_share)).check(matches(isCompletelyDisplayed())) + onView(withId(R.id.share_create_new_link)).check(matches(isCompletelyDisplayed())) + onView(withId(R.id.tv_your_shares)).check(matches(not(isDisplayed()))) + onView(withId(R.id.sharesList)).check(matches(not(isDisplayed()))) + onView(withId(R.id.tv_empty_shares)).check(matches(isCompletelyDisplayed())) + onView(withId(R.id.tv_empty_shares)).check(matches(withText("No shares created yet."))) + } + + @Test + fun validateQuickPermissionDialogForFiles() { + val sharesList: MutableList = mutableListOf() + + activityScenarioRule.scenario.onActivity { it -> + OCShare(file.decryptedRemotePath).apply { + remoteId = 1 + shareType = ShareType.USER + sharedWithDisplayName = "Admin" + permissions = OCShare.READ_PERMISSION_FLAG + userId = getUserId(user) + isFolder = false + it.storageManager.saveShare(this) + } + + OCShare(file.decryptedRemotePath).apply { + remoteId = 3 + shareType = ShareType.EMAIL + sharedWithDisplayName = "johndoe@gmail.com" + permissions = SharingMenuHelper.CAN_EDIT_PERMISSIONS_FOR_FILE + userId = getUserId(user) + isFolder = false + it.storageManager.saveShare(this) + } + + OCShare(file.decryptedRemotePath).apply { + remoteId = 4 + shareType = ShareType.PUBLIC_LINK + permissions = OCShare.READ_PERMISSION_FLAG + userId = getUserId(user) + label = "Customer" + isFolder = false + it.storageManager.saveShare(this) + } + + //get other shares + sharesList.addAll(it.storageManager.getSharesWithForAFile(file.remotePath, user.accountName)) + + //get public link shares + sharesList.addAll(it.storageManager.getSharesByPathAndType(file.remotePath, ShareType.PUBLIC_LINK, "")) + + sharesList.sortByDescending { share -> share.shareType } + } + + + assertEquals(3, sharesList.size) + + show(file) + + for (i in sharesList.indices) { + val share = sharesList[i] + //since for public link the quick permission button is disabled + if (share.shareType == ShareType.PUBLIC_LINK) { + continue + } + showQuickPermissionDialogAndValidate(i, file.isFolder, share) + pressBack() + } + } + + @Test + fun validateQuickPermissionDialogForFolder() { + val sharesList: MutableList = mutableListOf() + + activityScenarioRule.scenario.onActivity { it -> + OCShare(folder.decryptedRemotePath).apply { + remoteId = 1 + shareType = ShareType.USER + sharedWithDisplayName = "Admin" + permissions = OCShare.CREATE_PERMISSION_FLAG + userId = getUserId(user) + isFolder = true + it.storageManager.saveShare(this) + } + + OCShare(folder.decryptedRemotePath).apply { + remoteId = 3 + shareType = ShareType.EMAIL + sharedWithDisplayName = "johndoe@gmail.com" + permissions = SharingMenuHelper.CAN_EDIT_PERMISSIONS_FOR_FOLDER + userId = getUserId(user) + isFolder = true + it.storageManager.saveShare(this) + } + + OCShare(folder.decryptedRemotePath).apply { + remoteId = 4 + shareType = ShareType.PUBLIC_LINK + permissions = OCShare.READ_PERMISSION_FLAG + userId = getUserId(user) + label = "Customer" + isFolder = true + it.storageManager.saveShare(this) + } + + //get other shares + sharesList.addAll(it.storageManager.getSharesWithForAFile(folder.remotePath, user.accountName)) + + //get public link shares + sharesList.addAll(it.storageManager.getSharesByPathAndType(folder.remotePath, ShareType.PUBLIC_LINK, "")) + + sharesList.sortByDescending { share -> share.shareType } + } + + + assertEquals(3, sharesList.size) + + show(folder) + + for (i in sharesList.indices) { + val share = sharesList[i] + showQuickPermissionDialogAndValidate(i, folder.isFolder, share) + pressBack() + } + } + + private fun showQuickPermissionDialogAndValidate(index: Int, isFolder: Boolean, ocShare: OCShare) { + onView(withId(R.id.sharesList)).perform( + actionOnItemAtPosition( + index, + clickChildViewWithId(if (ocShare.shareType == ShareType.USER) R.id.share_name_layout else R.id.share_by_link_container) + ) + ) + + val permissionList = permissionList(isFolder, ocShare.shareType) + + for (i in permissionList.indices) { + // Scroll to the item at position i + onView(withId(R.id.rv_quick_share_permissions)).perform( + RecyclerViewActions.scrollToPosition( + i + ) + ) + + val permissionTextView = onView( + withRecyclerView(R.id.rv_quick_share_permissions) + .atPositionOnView(i, R.id.tv_quick_share_name) + ) + permissionTextView.check(matches(withText(permissionList[i]))) + + val permissionCheckView = onView( + withRecyclerView(R.id.rv_quick_share_permissions) + .atPositionOnView(i, R.id.tv_quick_share_check_icon) + ) + if ((permissionList[i] == "Read only" && SharingMenuHelper.isReadOnly(ocShare)) + || (permissionList[i] == "Can edit" && SharingMenuHelper.isUploadAndEditingAllowed(ocShare)) + || (permissionList[i] == "Filedrop only" && SharingMenuHelper.isFileDrop(ocShare)) + ) { + permissionCheckView.check(matches(isDisplayed())) + } + } + } + + @After + override fun after() { + activityScenarioRule.scenario.onActivity { + it.storageManager.cleanShares() + it.finish() + } + super.after() + } + + companion object { + private val filePermissionList = listOf("Read only", "Can edit") + private val folderExternalAndLinkSharePermissionList = listOf("Read only", "Can edit", "Filedrop only") + private val folderOtherSharePermissionList = listOf("Read only", "Can edit") + + fun permissionList(isFolder: Boolean, shareType: ShareType): List = + if (isFolder) { + if (shareType == ShareType.PUBLIC_LINK || shareType == ShareType.EMAIL) folderExternalAndLinkSharePermissionList + else folderOtherSharePermissionList + } else filePermissionList + } +} diff --git a/app/src/androidTest/java/com/nmc/android/ui/RecyclerViewAssertions.kt b/app/src/androidTest/java/com/nmc/android/ui/RecyclerViewAssertions.kt new file mode 100644 index 000000000000..040b165f9e82 --- /dev/null +++ b/app/src/androidTest/java/com/nmc/android/ui/RecyclerViewAssertions.kt @@ -0,0 +1,81 @@ +package com.nmc.android.ui + +import android.content.res.Resources +import android.view.View +import androidx.recyclerview.widget.RecyclerView +import androidx.test.espresso.UiController +import androidx.test.espresso.ViewAction +import org.hamcrest.Description +import org.hamcrest.Matcher +import org.hamcrest.TypeSafeMatcher + +object RecyclerViewAssertions { + + fun clickChildViewWithId(id: Int): ViewAction { + return object : ViewAction { + override fun getConstraints(): Matcher? { + return null + } + + override fun getDescription(): String { + return "Click on a child view with specified id." + } + + override fun perform(uiController: UiController?, view: View) { + val v: View = view.findViewById(id) + v.performClick() + } + } + } + + fun withRecyclerView(recyclerViewId: Int): RecyclerViewMatcher { + return RecyclerViewMatcher(recyclerViewId) + } + + class RecyclerViewMatcher(private val recyclerViewId: Int) { + fun atPosition(position: Int): Matcher { + return atPositionOnView(position, -1) + } + + fun atPositionOnView(position: Int, targetViewId: Int): Matcher { + return object : TypeSafeMatcher() { + var resources: Resources? = null + var childView: View? = null + + override fun describeTo(description: Description?) { + var idDescription = recyclerViewId.toString() + resources?.let { + idDescription = try { + resources!!.getResourceName(recyclerViewId) + } catch (exception: Resources.NotFoundException) { + "$recyclerViewId (resource name not found)" + } + } + + description?.appendText("with id: $idDescription") + } + + override fun matchesSafely(view: View?): Boolean { + resources = view?.resources + + if (childView == null) { + val recyclerView = view?.rootView?.findViewById(recyclerViewId) + + if (recyclerView != null && recyclerView.id == recyclerViewId) { + childView = recyclerView.findViewHolderForAdapterPosition(position)?.itemView + } else { + return false + } + } + + return if (targetViewId == -1) { + view == childView + } else { + val targetView = childView?.findViewById(targetViewId) + view == targetView + } + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f83f59ef32fd..e603992c6669 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -478,7 +478,8 @@ android:label="@string/share_dialog_title" android:launchMode="singleTop" android:theme="@style/Theme.ownCloud.Dialog.NoTitle" - android:windowSoftInputMode="adjustResize"> + android:configChanges="orientation|screenSize" + android:windowSoftInputMode="adjustPan"> diff --git a/app/src/main/java/com/nmc/android/utils/CheckableThemeUtils.kt b/app/src/main/java/com/nmc/android/utils/CheckableThemeUtils.kt new file mode 100644 index 000000000000..7c86042db7cb --- /dev/null +++ b/app/src/main/java/com/nmc/android/utils/CheckableThemeUtils.kt @@ -0,0 +1,86 @@ +package com.nmc.android.utils + +import android.content.res.ColorStateList +import androidx.appcompat.widget.AppCompatCheckBox +import androidx.appcompat.widget.SwitchCompat +import androidx.core.graphics.drawable.DrawableCompat +import androidx.core.widget.CompoundButtonCompat +import com.owncloud.android.MainApp +import com.owncloud.android.R + +object CheckableThemeUtils { + @JvmStatic + fun tintCheckbox(vararg checkBoxes: AppCompatCheckBox) { + for (checkBox in checkBoxes) { + val checkEnabled = MainApp.getAppContext().resources.getColor(R.color.checkbox_checked_enabled) + val checkDisabled = MainApp.getAppContext().resources.getColor(R.color.checkbox_checked_disabled) + val uncheckEnabled = MainApp.getAppContext().resources.getColor(R.color.checkbox_unchecked_enabled) + val uncheckDisabled = MainApp.getAppContext().resources.getColor(R.color.checkbox_unchecked_disabled) + + val states = arrayOf( + intArrayOf(android.R.attr.state_enabled, android.R.attr.state_checked), + intArrayOf(-android.R.attr.state_enabled, android.R.attr.state_checked), + intArrayOf(android.R.attr.state_enabled, -android.R.attr.state_checked), + intArrayOf(-android.R.attr.state_enabled, -android.R.attr.state_checked) + ) + val colors = intArrayOf( + checkEnabled, + checkDisabled, + uncheckEnabled, + uncheckDisabled + ) + val checkColorStateList = ColorStateList(states, colors) + CompoundButtonCompat.setButtonTintList(checkBox, checkColorStateList) + } + } + + @JvmStatic + @JvmOverloads + fun tintSwitch(switchView: SwitchCompat, color: Int = 0, colorText: Boolean = false) { + if (colorText) { + switchView.setTextColor(color) + } + val thumbColorCheckedEnabled = MainApp.getAppContext().resources.getColor(R.color.switch_thumb_checked_enabled) + val thumbColorUncheckedEnabled = + MainApp.getAppContext().resources.getColor(R.color.switch_thumb_unchecked_enabled) + val thumbColorCheckedDisabled = + MainApp.getAppContext().resources.getColor(R.color.switch_thumb_checked_disabled) + val thumbColorUncheckedDisabled = + MainApp.getAppContext().resources.getColor(R.color.switch_thumb_unchecked_disabled) + + val states = arrayOf( + intArrayOf(android.R.attr.state_enabled, android.R.attr.state_checked), + intArrayOf(-android.R.attr.state_enabled, android.R.attr.state_checked), + intArrayOf(android.R.attr.state_enabled, -android.R.attr.state_checked), + intArrayOf(-android.R.attr.state_enabled, -android.R.attr.state_checked) + ) + val thumbColors = intArrayOf( + thumbColorCheckedEnabled, + thumbColorCheckedDisabled, + thumbColorUncheckedEnabled, + thumbColorUncheckedDisabled + ) + val thumbColorStateList = ColorStateList(states, thumbColors) + val trackColorCheckedEnabled = MainApp.getAppContext().resources.getColor(R.color.switch_track_checked_enabled) + val trackColorUncheckedEnabled = + MainApp.getAppContext().resources.getColor(R.color.switch_track_unchecked_enabled) + val trackColorCheckedDisabled = + MainApp.getAppContext().resources.getColor(R.color.switch_track_checked_disabled) + val trackColorUncheckedDisabled = + MainApp.getAppContext().resources.getColor(R.color.switch_track_unchecked_disabled) + + val trackColors = intArrayOf( + trackColorCheckedEnabled, + trackColorCheckedDisabled, + trackColorUncheckedEnabled, + trackColorUncheckedDisabled + ) + val trackColorStateList = ColorStateList(states, trackColors) + + // setting the thumb color + DrawableCompat.setTintList(switchView.thumbDrawable, thumbColorStateList) + + // setting the track color + DrawableCompat.setTintList(switchView.trackDrawable, trackColorStateList) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/nmc/android/utils/DisplayUtils.kt b/app/src/main/java/com/nmc/android/utils/DisplayUtils.kt new file mode 100644 index 000000000000..f58e93c4252a --- /dev/null +++ b/app/src/main/java/com/nmc/android/utils/DisplayUtils.kt @@ -0,0 +1,18 @@ +package com.nmc.android.utils + +import android.content.res.Configuration +import com.owncloud.android.MainApp +import com.owncloud.android.R + +object DisplayUtils { + + @JvmStatic + fun isShowDividerForList(): Boolean = isTablet() || isLandscapeOrientation() + + @JvmStatic + fun isTablet(): Boolean = MainApp.getAppContext().resources.getBoolean(R.bool.isTablet) + + @JvmStatic + fun isLandscapeOrientation(): Boolean = + MainApp.getAppContext().resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE +} \ No newline at end of file diff --git a/app/src/main/java/com/nmc/android/utils/KeyboardUtils.java b/app/src/main/java/com/nmc/android/utils/KeyboardUtils.java new file mode 100644 index 000000000000..ec43ac2dd3a8 --- /dev/null +++ b/app/src/main/java/com/nmc/android/utils/KeyboardUtils.java @@ -0,0 +1,21 @@ +package com.nmc.android.utils; + +import android.app.Activity; +import android.content.Context; +import android.view.View; +import android.view.inputmethod.InputMethodManager; + +public class KeyboardUtils { + + public static void showSoftKeyboard(Context context, View view) { + view.requestFocus(); + InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); + imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT); + } + + public static void hideKeyboardFrom(Context context, View view) { + view.clearFocus(); + InputMethodManager imm = (InputMethodManager) context.getSystemService(Activity.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(view.getWindowToken(), 0); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/nmc/android/utils/SearchViewThemeUtils.kt b/app/src/main/java/com/nmc/android/utils/SearchViewThemeUtils.kt new file mode 100644 index 000000000000..dc2267f10164 --- /dev/null +++ b/app/src/main/java/com/nmc/android/utils/SearchViewThemeUtils.kt @@ -0,0 +1,22 @@ +package com.nmc.android.utils + +import android.content.Context +import android.widget.ImageView +import androidx.appcompat.widget.SearchView +import com.owncloud.android.R + +object SearchViewThemeUtils { + fun themeSearchView(context: Context, searchView: SearchView) { + val fontColor = context.resources.getColor(R.color.fontAppbar, null) + val editText: SearchView.SearchAutoComplete = searchView.findViewById(R.id.search_src_text) + editText.textSize = 16F + editText.setTextColor(fontColor) + editText.highlightColor = context.resources.getColor(R.color.et_highlight_color, null) + editText.setHintTextColor(context.resources.getColor(R.color.fontSecondaryAppbar, null)) + val closeButton: ImageView = searchView.findViewById(R.id.search_close_btn) + closeButton.setColorFilter(fontColor) + val searchButton: ImageView = searchView.findViewById(R.id.search_button) + searchButton.setImageResource(R.drawable.ic_search) + searchButton.setColorFilter(fontColor) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/owncloud/android/operations/CreateShareWithShareeOperation.java b/app/src/main/java/com/owncloud/android/operations/CreateShareWithShareeOperation.java index 71a348d64b69..f4bf527a1745 100644 --- a/app/src/main/java/com/owncloud/android/operations/CreateShareWithShareeOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/CreateShareWithShareeOperation.java @@ -237,7 +237,9 @@ protected RemoteOperationResult run(OwnCloudClient client) { // once creating share link update other information UpdateShareInfoOperation updateShareInfoOperation = new UpdateShareInfoOperation(share, getStorageManager()); - updateShareInfoOperation.setExpirationDateInMillis(expirationDateInMillis); + if (expirationDateInMillis > 0) { + updateShareInfoOperation.setExpirationDateInMillis(expirationDateInMillis); + } updateShareInfoOperation.setHideFileDownload(hideFileDownload); updateShareInfoOperation.setNote(noteMessage); updateShareInfoOperation.setLabel(label); diff --git a/app/src/main/java/com/owncloud/android/providers/UsersAndGroupsSearchProvider.java b/app/src/main/java/com/owncloud/android/providers/UsersAndGroupsSearchProvider.java index 0cee694903a3..9af6a8dcad41 100644 --- a/app/src/main/java/com/owncloud/android/providers/UsersAndGroupsSearchProvider.java +++ b/app/src/main/java/com/owncloud/android/providers/UsersAndGroupsSearchProvider.java @@ -312,7 +312,11 @@ private Cursor searchForUsersOrGroups(Uri uri) { displayName = userName; subline = (status.getMessage() == null || status.getMessage().isEmpty()) ? null : status.getMessage(); - Uri.Builder builder = Uri.parse("content://" + AUTHORITY + "/icon").buildUpon(); + icon = R.drawable.ic_internal_share; + + // Commented for NMC customization + // uncomment the below code to show icon with initials + /*Uri.Builder builder = Uri.parse("content://" + AUTHORITY + "/icon").buildUpon(); builder.appendQueryParameter("shareWith", shareWith); builder.appendQueryParameter("displayName", displayName); @@ -322,7 +326,7 @@ private Cursor searchForUsersOrGroups(Uri uri) { builder.appendQueryParameter("icon", status.getIcon()); } - icon = builder.build(); + icon = builder.build();*/ dataUri = Uri.withAppendedPath(userBaseUri, shareWith); break; @@ -351,12 +355,22 @@ private Cursor searchForUsersOrGroups(Uri uri) { } if (displayName != null && dataUri != null) { - response.newRow() - .add(count++) // BaseColumns._ID - .add(displayName) // SearchManager.SUGGEST_COLUMN_TEXT_1 - .add(subline) // SearchManager.SUGGEST_COLUMN_TEXT_2 - .add(icon) // SearchManager.SUGGEST_COLUMN_ICON_1 - .add(dataUri); + //if display name is empty set sublime as primary text + if (displayName.equals("")) { + response.newRow() + .add(count++) // BaseColumns._ID + .add(subline) // SearchManager.SUGGEST_COLUMN_TEXT_1 + .add(displayName) // SearchManager.SUGGEST_COLUMN_TEXT_2 + .add(icon) // SearchManager.SUGGEST_COLUMN_ICON_1 + .add(dataUri); + } else { + response.newRow() + .add(count++) // BaseColumns._ID + .add(displayName) // SearchManager.SUGGEST_COLUMN_TEXT_1 + .add(subline) // SearchManager.SUGGEST_COLUMN_TEXT_2 + .add(icon) // SearchManager.SUGGEST_COLUMN_ICON_1 + .add(dataUri); + } } } @@ -438,7 +452,8 @@ public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode) thr } catch (FileNotFoundException e) { Log_OC.e(TAG, "File not found: " + e.getMessage()); } - + } catch (OutOfMemoryError oome) { + Log_OC.e(TAG, "Out of memory"); } catch (Exception e) { Log_OC.e(TAG, "Error opening file: " + e.getMessage()); } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.java index c8ef734ba84b..e01f408000de 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.java @@ -386,7 +386,8 @@ public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationRe operation instanceof UnshareOperation || operation instanceof SynchronizeFolderOperation || operation instanceof UpdateShareViaLinkOperation || - operation instanceof UpdateSharePermissionsOperation + operation instanceof UpdateSharePermissionsOperation || + operation instanceof UpdateShareInfoOperation ) { if (result.isSuccess()) { updateFileFromDB(); @@ -800,7 +801,6 @@ public void refreshList() { private void onCreateShareViaLinkOperationFinish(CreateShareViaLinkOperation operation, RemoteOperationResult result) { FileDetailSharingFragment sharingFragment = getShareFileFragment(); - final Fragment fileListFragment = getSupportFragmentManager().findFragmentByTag(FileDisplayActivity.TAG_LIST_OF_FILES); if (result.isSuccess()) { updateFileFromDB(); @@ -824,6 +824,8 @@ private void onCreateShareViaLinkOperationFinish(CreateShareViaLinkOperation ope sharingFragment.onUpdateShareInformation(result, file); } + //this has to be here to avoid the crash when creating link from inside of FileDetailSharingFragment + Fragment fileListFragment = getSupportFragmentManager().findFragmentByTag(FileDisplayActivity.TAG_LIST_OF_FILES); if (fileListFragment instanceof OCFileListFragment && file != null) { ((OCFileListFragment) fileListFragment).updateOCFile(file); } @@ -913,11 +915,17 @@ protected void onNewIntent(Intent intent) { * @param shareType */ protected void doShareWith(String shareeName, ShareType shareType) { - FileDetailFragment fragment = getFileDetailFragment(); + Fragment fragment = getFileDetailFragment(); if (fragment != null) { - fragment.initiateSharingProcess(shareeName, - shareType, - usersAndGroupsSearchConfig.getSearchOnlyUsers()); + ((FileDetailFragment) fragment).initiateSharingProcess(shareeName, + shareType, + usersAndGroupsSearchConfig.getSearchOnlyUsers()); + } else { + //if user sharing from Preview Image Fragment + fragment = getSupportFragmentManager().findFragmentByTag(ShareActivity.TAG_SHARE_FRAGMENT); + if (fragment != null) { + ((FileDetailSharingFragment) fragment).initiateSharingProcess(shareeName, shareType); + } } } @@ -931,9 +939,15 @@ protected void doShareWith(String shareeName, ShareType shareType) { @Override public void editExistingShare(OCShare share, int screenTypePermission, boolean isReshareShown, boolean isExpiryDateShown) { - FileDetailFragment fragment = getFileDetailFragment(); + Fragment fragment = getFileDetailFragment(); if (fragment != null) { - fragment.editExistingShare(share, screenTypePermission, isReshareShown, isExpiryDateShown); + ((FileDetailFragment) fragment).editExistingShare(share, screenTypePermission, isReshareShown, isExpiryDateShown); + } else { + //if user editing from Preview Image Fragment + fragment = getSupportFragmentManager().findFragmentByTag(ShareActivity.TAG_SHARE_FRAGMENT); + if (fragment != null) { + ((FileDetailSharingFragment) fragment).editExistingShare(share, screenTypePermission, isReshareShown, isExpiryDateShown); + } } } @@ -944,7 +958,7 @@ public void editExistingShare(OCShare share, int screenTypePermission, boolean i public void onShareProcessClosed() { FileDetailFragment fragment = getFileDetailFragment(); if (fragment != null) { - fragment.showHideFragmentView(false); + //do something } } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ShareActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/ShareActivity.java index b5da83327702..9d03ebec2fc3 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/ShareActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/ShareActivity.java @@ -42,6 +42,7 @@ import com.owncloud.android.operations.GetSharesForFileOperation; import com.owncloud.android.ui.fragment.FileDetailSharingFragment; import com.owncloud.android.ui.fragment.FileDetailsSharingProcessFragment; +import com.owncloud.android.ui.fragment.util.SharingMenuHelper; import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.MimeTypeUtil; @@ -140,11 +141,12 @@ protected void onStart() { @Override protected void doShareWith(String shareeName, ShareType shareType) { - getSupportFragmentManager().beginTransaction().replace(R.id.share_fragment_container, + getSupportFragmentManager().beginTransaction().add(R.id.share_fragment_container, FileDetailsSharingProcessFragment.newInstance(getFile(), shareeName, shareType, - false), + false, + SharingMenuHelper.canEditFile(this, getUser().get(), getStorageManager().getCapability(getUser().get()), getFile(), editorUtils)), FileDetailsSharingProcessFragment.TAG) .commit(); } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java index b9823f0fa1f5..88952b88f85f 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java @@ -74,6 +74,7 @@ public abstract class ToolbarActivity extends BaseActivity implements Injectable private TextView mInfoBoxMessage; protected AppCompatSpinner mToolbarSpinner; private boolean isHomeSearchToolbarShow = false; + private ImageView mToolbarBackIcon; @Inject public ThemeColorUtils themeColorUtils; @Inject public ThemeUtils themeUtils; @@ -92,6 +93,7 @@ private void setupToolbar(boolean isHomeSearchToolbarShow, boolean showSortListB mHomeSearchToolbar = findViewById(R.id.home_toolbar); mMenuButton = findViewById(R.id.menu_button); mSearchText = findViewById(R.id.search_text); + mToolbarBackIcon = findViewById(R.id.toolbar_back_icon); mSwitchAccountButton = findViewById(R.id.switch_account_button); if (showSortListButtonGroup) { @@ -113,6 +115,8 @@ private void setupToolbar(boolean isHomeSearchToolbarShow, boolean showSortListB viewThemeUtils.material.colorToolbarOverflowIcon(mToolbar); viewThemeUtils.platform.themeStatusBar(this); viewThemeUtils.material.colorMaterialTextButton(mSwitchAccountButton); + + mToolbarBackIcon.setOnClickListener(v -> onBackPressed()); } public void setupToolbar() { @@ -252,10 +256,20 @@ public boolean sortListGroupVisibility(){ public void setPreviewImageBitmap(Bitmap bitmap) { if (mPreviewImage != null) { mPreviewImage.setImageBitmap(bitmap); + resetPreviewImageConfiguration(); setPreviewImageVisibility(true); } } + /** + * reset preview image configuration if required the scale type and padding are changing in sharing screen so to + * reset it call this method + */ + private void resetPreviewImageConfiguration() { + mPreviewImage.setScaleType(ImageView.ScaleType.CENTER_CROP); + mPreviewImage.setPadding(0, 0, 0, 0); + } + /** * Change the drawable for the toolbar's preview image. * @@ -264,10 +278,35 @@ public void setPreviewImageBitmap(Bitmap bitmap) { public void setPreviewImageDrawable(Drawable drawable) { if (mPreviewImage != null) { mPreviewImage.setImageDrawable(drawable); + resetPreviewImageConfiguration(); setPreviewImageVisibility(true); } } + /** + * method to show/hide the toolbar custom back icon currently this is only showing for sharing details screen for + * thumbnail images + * + * @param show + */ + public void showToolbarBackImage(boolean show) { + if (mToolbarBackIcon == null) { + return; + } + ActionBar actionBar = getSupportActionBar(); + if (show) { + mToolbarBackIcon.setVisibility(View.VISIBLE); + if (actionBar != null) { + actionBar.setDisplayHomeAsUpEnabled(false); + } + } else { + mToolbarBackIcon.setVisibility(View.GONE); + if (actionBar != null) { + actionBar.setDisplayHomeAsUpEnabled(true); + } + } + } + /** * get the toolbar's preview image view. */ @@ -279,6 +318,15 @@ public FrameLayout getPreviewImageContainer() { return mPreviewImageContainer; } + /** + * method will expand the toolbar using app bar if its hidden due to scrolling + */ + public void expandToolbar() { + if (mAppBar != null) { + mAppBar.setExpanded(true, true); + } + } + public void updateToolbarSubtitle(@NonNull String subtitle) { ActionBar actionBar = getSupportActionBar(); if (actionBar != null) { diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.java b/app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.java index 25d13ccd2ea6..405a69fe8383 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.java @@ -26,7 +26,7 @@ package com.owncloud.android.ui.adapter; import android.content.Context; -import android.graphics.PorterDuff; +import android.content.res.ColorStateList; import android.text.TextUtils; import android.view.View; @@ -39,12 +39,14 @@ import androidx.annotation.NonNull; import androidx.core.content.res.ResourcesCompat; +import androidx.core.widget.TextViewCompat; import androidx.recyclerview.widget.RecyclerView; class LinkShareViewHolder extends RecyclerView.ViewHolder { private FileDetailsShareLinkShareItemBinding binding; private Context context; private ViewThemeUtils viewThemeUtils; + private boolean isTextFile; public LinkShareViewHolder(@NonNull View itemView) { super(itemView); @@ -52,25 +54,22 @@ public LinkShareViewHolder(@NonNull View itemView) { public LinkShareViewHolder(FileDetailsShareLinkShareItemBinding binding, Context context, - final ViewThemeUtils viewThemeUtils) { + final ViewThemeUtils viewThemeUtils, + boolean isTextFile) { this(binding.getRoot()); this.binding = binding; this.context = context; this.viewThemeUtils = viewThemeUtils; + this.isTextFile = isTextFile; } 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, + R.drawable.ic_external_share, null)); binding.copyLink.setVisibility(View.GONE); - - binding.icon.getBackground().setColorFilter(context.getResources().getColor(R.color.nc_grey), - PorterDuff.Mode.SRC_IN); - binding.icon.getDrawable().mutate().setColorFilter(context.getResources().getColor(R.color.icon_on_nc_grey), - PorterDuff.Mode.SRC_IN); } else { if (!TextUtils.isEmpty(publicShare.getLabel())) { String text = String.format(context.getString(R.string.share_link_with_label), publicShare.getLabel()); @@ -83,24 +82,40 @@ public void bind(OCShare publicShare, ShareeListAdapterListener listener) { } } - viewThemeUtils.platform.colorImageViewBackgroundAndIcon(binding.icon); } String permissionName = SharingMenuHelper.getPermissionName(context, publicShare); - setPermissionName(publicShare, permissionName); + setPermissionName(publicShare, permissionName, listener); binding.copyLink.setOnClickListener(v -> listener.copyLink(publicShare)); binding.overflowMenu.setOnClickListener(v -> listener.showSharingMenuActionSheet(publicShare)); - if (!SharingMenuHelper.isSecureFileDrop(publicShare)) { - binding.shareByLinkContainer.setOnClickListener(v -> listener.showPermissionsDialog(publicShare)); - } } - private void setPermissionName(OCShare publicShare, String permissionName) { + private void setPermissionName(OCShare publicShare, String permissionName, ShareeListAdapterListener listener) { + ColorStateList colorStateList = new ColorStateList( + new int[][]{ + new int[]{-android.R.attr.state_enabled}, + new int[]{android.R.attr.state_enabled}, + }, + new int[]{ + ResourcesCompat.getColor(context.getResources(), R.color.share_disabled_txt_color, + null), + ResourcesCompat.getColor(context.getResources(), R.color.primary, + null) + } + ); + TextViewCompat.setCompoundDrawableTintList(binding.permissionName, colorStateList); + binding.permissionName.setTextColor(colorStateList); + if (!TextUtils.isEmpty(permissionName) && !SharingMenuHelper.isSecureFileDrop(publicShare)) { + if (permissionName.equalsIgnoreCase(context.getResources().getString(R.string.share_permission_read_only)) && !isTextFile) { + binding.permissionName.setEnabled(false); + } else { + binding.permissionName.setEnabled(true); + binding.shareByLinkContainer.setOnClickListener(v -> listener.showPermissionsDialog(publicShare)); + } binding.permissionName.setText(permissionName); binding.permissionName.setVisibility(View.VISIBLE); - viewThemeUtils.androidx.colorPrimaryTextViewElement(binding.permissionName); } else { binding.permissionName.setVisibility(View.GONE); } diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/ListGridImageViewHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/ListGridImageViewHolder.kt index 298657a8b659..6bdd5f927fb3 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/ListGridImageViewHolder.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/ListGridImageViewHolder.kt @@ -36,6 +36,7 @@ interface ListGridImageViewHolder { val localFileIndicator: ImageView val imageFileName: TextView? val shared: ImageView + val sharedMessage: TextView? val checkbox: ImageView val itemLayout: View val unreadComments: ImageView diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java index 6e31483e7091..09cded8a56e3 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java @@ -548,7 +548,8 @@ private void bindListGridItemViewHolder(ListGridItemViewHolder holder, OCFile fi private void bindListItemViewHolder(ListItemViewHolder holder, OCFile file) { if ((file.isSharedWithMe() || file.isSharedWithSharee()) && !isMultiSelect() && !gridView && !hideItemOptions) { - holder.getSharedAvatars().setVisibility(View.VISIBLE); + //visibility gone as view not required for NMC + holder.getSharedAvatars().setVisibility(View.GONE); holder.getSharedAvatars().removeAllViews(); String fileOwner = file.getOwnerId(); diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListDelegate.kt b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListDelegate.kt index c055c44a1034..f6c96dfd85c9 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListDelegate.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListDelegate.kt @@ -28,6 +28,8 @@ import android.os.AsyncTask import android.view.View import android.widget.ImageView import androidx.core.content.ContextCompat +import android.widget.TextView +import androidx.appcompat.content.res.AppCompatResources import androidx.core.content.res.ResourcesCompat import com.elyeproj.loaderviewlibrary.LoaderImageView import com.nextcloud.android.common.ui.theme.utils.ColorRole @@ -376,22 +378,64 @@ class OCFileListDelegate( private fun showShareIcon(gridViewHolder: ListGridImageViewHolder, file: OCFile) { val sharedIconView = gridViewHolder.shared + //Initialising Textview for Message and setting its visibility + //only applicable for list item + val sharedMessageView: TextView? = gridViewHolder.sharedMessage + sharedMessageView?.visibility = if (com.nmc.android.utils.DisplayUtils.isShowDividerForList()) View.VISIBLE else View.GONE + if (gridViewHolder is OCFileListItemViewHolder || file.unreadCommentsCount == 0) { sharedIconView.visibility = View.VISIBLE - if (file.isSharedWithSharee || file.isSharedWithMe) { - if (showShareAvatar) { - sharedIconView.visibility = View.GONE - } else { - sharedIconView.visibility = View.VISIBLE - sharedIconView.setImageResource(R.drawable.shared_via_users) + when { + file.isSharedWithMe -> { + val sharedWithMeColor = ResourcesCompat.getColor( + context.resources, + R.color.shared_with_me_color, null + ) + val shareWithMeIcon = AppCompatResources.getDrawable(context, R.drawable.ic_shared_with_me) + val shareWithMeTintedIcon = + viewThemeUtils.platform.colorDrawable(shareWithMeIcon!!, sharedWithMeColor) + sharedIconView.setImageDrawable(shareWithMeTintedIcon) + sharedIconView.contentDescription = context.getString(R.string.shared_icon_shared) + //Added Code For Message Text + sharedMessageView?.text = context.resources.getString(R.string.placeholder_receivedMessage) + sharedMessageView?.setTextColor(sharedWithMeColor) + } + file.isSharedWithSharee -> { + val shareIcon = viewThemeUtils.platform.colorDrawable( + AppCompatResources.getDrawable(context, R.drawable.ic_shared)!!, + context.resources.getColor(R.color.primary, null) + ) + sharedIconView.setImageDrawable(shareIcon) sharedIconView.contentDescription = context.getString(R.string.shared_icon_shared) + //Added Code For Message Text + sharedMessageView?.text = context.resources.getString(R.string.placeholder_sharedMessage) + sharedMessageView?.setTextColor(context.resources.getColor(R.color.primary, null)) + } + file.isSharedViaLink -> { + val shareIcon = viewThemeUtils.platform.colorDrawable( + AppCompatResources.getDrawable(context, R.drawable.ic_shared)!!, + context.resources.getColor(R.color.primary, null) + ) + sharedIconView.setImageDrawable(shareIcon) + sharedIconView.contentDescription = context.getString(R.string.shared_icon_shared_via_link) + //Added Code For Message Text + sharedMessageView?.text = context.resources.getString(R.string.placeholder_sharedMessage) + sharedMessageView?.setTextColor(context.resources.getColor(R.color.primary, null)) + } + file.isEncrypted -> { + sharedIconView.visibility = View.GONE + } + else -> { + val unShareIconColor = ResourcesCompat.getColor( + context.resources, + R.color.list_icon_color, null + ) + val unShareIcon = AppCompatResources.getDrawable(context, R.drawable.ic_unshared) + val unShareTintedIcon = viewThemeUtils.platform.colorDrawable(unShareIcon!!, unShareIconColor) + sharedIconView.setImageDrawable(unShareTintedIcon) + sharedIconView.contentDescription = context.getString(R.string.shared_icon_share) + sharedMessageView?.visibility = View.GONE } - } else if (file.isSharedViaLink) { - sharedIconView.setImageResource(R.drawable.shared_via_link) - sharedIconView.contentDescription = context.getString(R.string.shared_icon_shared_via_link) - } else { - sharedIconView.setImageResource(R.drawable.ic_unshared) - sharedIconView.contentDescription = context.getString(R.string.shared_icon_share) } sharedIconView.setOnClickListener { ocFileListFragmentInterface.onShareIconClick(file) } } else { diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListGridImageViewHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListGridImageViewHolder.kt index 95d0417b2a74..3103ef8b6f4f 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListGridImageViewHolder.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListGridImageViewHolder.kt @@ -54,6 +54,8 @@ internal class OCFileListGridImageViewHolder(var binding: GridImageBinding) : get() = binding.localFileIndicator override val shared: ImageView get() = binding.sharedIcon + override val sharedMessage: TextView? + get() = null override val checkbox: ImageView get() = binding.customCheckbox override val itemLayout: View diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListGridItemViewHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListGridItemViewHolder.kt index 31967fe8ce17..a88b0b7b2888 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListGridItemViewHolder.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListGridItemViewHolder.kt @@ -54,6 +54,8 @@ internal class OCFileListGridItemViewHolder(var binding: GridItemBinding) : get() = null override val shared: ImageView get() = binding.sharedIcon + override val sharedMessage: TextView? + get() = null override val checkbox: ImageView get() = binding.customCheckbox override val itemLayout: View diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListItemViewHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListItemViewHolder.kt index 2c62a795e2e9..11ecdc0b1112 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListItemViewHolder.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListItemViewHolder.kt @@ -88,6 +88,8 @@ internal class OCFileListItemViewHolder(private var binding: ListItemBinding) : get() = null override val shared: ImageView get() = binding.sharedIcon + override val sharedMessage: TextView + get() = binding.sharedMessage override val checkbox: ImageView get() = binding.customCheckbox override val itemLayout: View diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/QuickSharingPermissionsAdapter.kt b/app/src/main/java/com/owncloud/android/ui/adapter/QuickSharingPermissionsAdapter.kt index ec37ad1899a7..f65abac36aac 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/QuickSharingPermissionsAdapter.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/QuickSharingPermissionsAdapter.kt @@ -62,7 +62,6 @@ class QuickSharingPermissionsAdapter( fun bindData(quickPermissionModel: QuickPermissionModel) { binding.tvQuickShareName.text = quickPermissionModel.permissionName if (quickPermissionModel.isSelected) { - viewThemeUtils.platform.colorImageView(binding.tvQuickShareCheckIcon) binding.tvQuickShareCheckIcon.visibility = View.VISIBLE } else { binding.tvQuickShareCheckIcon.visibility = View.INVISIBLE diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/ShareViewHolder.java b/app/src/main/java/com/owncloud/android/ui/adapter/ShareViewHolder.java index 3d5ae9762753..995cca6600a7 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/ShareViewHolder.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/ShareViewHolder.java @@ -76,22 +76,22 @@ public void bind(OCShare share, switch (share.getShareType()) { case GROUP: name = context.getString(R.string.share_group_clarification, name); - viewThemeUtils.files.createAvatar(share.getShareType(), binding.icon, context); + //viewThemeUtils.files.createAvatar(share.getShareType(), binding.icon, context); break; case ROOM: name = context.getString(R.string.share_room_clarification, name); - viewThemeUtils.files.createAvatar(share.getShareType(), binding.icon, context); + //viewThemeUtils.files.createAvatar(share.getShareType(), binding.icon, context); break; case CIRCLE: - viewThemeUtils.files.createAvatar(share.getShareType(), binding.icon, context); + //viewThemeUtils.files.createAvatar(share.getShareType(), binding.icon, context); break; case FEDERATED: name = context.getString(R.string.share_remote_clarification, name); - setImage(binding.icon, share.getSharedWithDisplayName(), R.drawable.ic_user); + //setImage(binding.icon, share.getSharedWithDisplayName(), R.drawable.ic_user); break; case USER: binding.icon.setTag(share.getShareWith()); - float avatarRadius = context.getResources().getDimension(R.dimen.list_item_avatar_icon_radius); + /* float avatarRadius = context.getResources().getDimension(R.dimen.list_item_avatar_icon_radius); DisplayUtils.setAvatar(user, share.getShareWith(), share.getSharedWithDisplayName(), @@ -99,15 +99,16 @@ public void bind(OCShare share, avatarRadius, context.getResources(), binding.icon, - context); + context);*/ binding.icon.setOnClickListener(v -> listener.showProfileBottomSheet(user, share.getShareWith())); default: - setImage(binding.icon, name, R.drawable.ic_user); + //setImage(binding.icon, name, R.drawable.ic_user); break; } binding.name.setText(name); + binding.icon.setImageResource(R.drawable.ic_internal_share); if (share.getShareWith().equalsIgnoreCase(userId) || share.getUserId().equalsIgnoreCase(userId)) { binding.overflowMenu.setVisibility(View.VISIBLE); diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/ShareeListAdapter.java b/app/src/main/java/com/owncloud/android/ui/adapter/ShareeListAdapter.java index 627446825582..b461e9ba9901 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/ShareeListAdapter.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/ShareeListAdapter.java @@ -64,6 +64,7 @@ public class ShareeListAdapter extends RecyclerView.Adapter shares, @@ -100,7 +101,8 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int parent, false), fileActivity, - viewThemeUtils); + viewThemeUtils, + isTextFile); } case NEW_PUBLIC_LINK -> { if (encrypted) { @@ -188,6 +190,10 @@ public boolean shouldCallGeneratedCallback(String tag, Object callContext) { return false; } + public void setTextFile(boolean textFile) { + isTextFile = textFile; + } + @SuppressLint("NotifyDataSetChanged") public void remove(OCShare share) { shares.remove(share); @@ -214,13 +220,6 @@ protected final void sortShares() { shares = links; shares.addAll(users); - - // add internal share link at end - if (!encrypted) { - final OCShare ocShare = new OCShare(); - ocShare.setShareType(ShareType.INTERNAL); - shares.add(ocShare); - } } public List getShares() { diff --git a/app/src/main/java/com/owncloud/android/ui/events/ShareSearchViewFocusEvent.kt b/app/src/main/java/com/owncloud/android/ui/events/ShareSearchViewFocusEvent.kt new file mode 100644 index 000000000000..0c695ef103b1 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/events/ShareSearchViewFocusEvent.kt @@ -0,0 +1,28 @@ +/* + * Nextcloud Android client application + * + * @author TSI-mc + * Copyright (C) 2022 TSI-mc + * Copyright (C) 2022 Nextcloud GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.owncloud.android.ui.events + +/** + * Event for search view focus while sharing a file/folder + * this event will be used to hide the view only for landscape mode so that user will have more space + */ +class ShareSearchViewFocusEvent(val hasFocus: Boolean) \ No newline at end of file diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java index 928478a94bbf..82c775300cae 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java @@ -30,17 +30,18 @@ import android.content.Context; import android.content.res.ColorStateList; import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; import android.os.Bundle; import android.view.LayoutInflater; import android.view.Menu; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; +import android.widget.ImageView; import android.widget.ProgressBar; import com.google.android.material.chip.Chip; import com.google.android.material.floatingactionbutton.FloatingActionButton; -import com.google.android.material.tabs.TabLayout; import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManager; import com.nextcloud.client.di.Injectable; @@ -49,6 +50,7 @@ import com.nextcloud.client.network.ClientFactory; import com.nextcloud.client.network.ConnectivityService; import com.nextcloud.client.preferences.AppPreferences; +import com.nextcloud.utils.EditorUtils; import com.nextcloud.ui.fileactions.FileActionsBottomSheet; import com.nextcloud.utils.MenuUtils; import com.nextcloud.utils.extensions.BundleExtensionsKt; @@ -57,6 +59,7 @@ import com.owncloud.android.databinding.FileDetailsFragmentBinding; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.datamodel.SyncedFolderProvider; import com.owncloud.android.datamodel.ThumbnailsCacheManager; import com.owncloud.android.files.services.FileUploader.FileUploaderBinder; import com.owncloud.android.lib.common.OwnCloudClient; @@ -68,12 +71,13 @@ import com.owncloud.android.lib.resources.shares.ShareType; import com.owncloud.android.ui.activity.FileDisplayActivity; import com.owncloud.android.ui.activity.ToolbarActivity; -import com.owncloud.android.ui.adapter.FileDetailTabAdapter; import com.owncloud.android.ui.dialog.RemoveFilesDialogFragment; import com.owncloud.android.ui.dialog.RenameFileDialogFragment; +import com.owncloud.android.ui.dialog.SendShareDialog; import com.owncloud.android.ui.events.FavoriteEvent; +import com.owncloud.android.ui.events.ShareSearchViewFocusEvent; +import com.owncloud.android.ui.fragment.util.SharingMenuHelper; import com.owncloud.android.utils.DisplayUtils; -import com.owncloud.android.utils.EncryptionUtils; import com.owncloud.android.utils.MimeTypeUtil; import com.owncloud.android.utils.theme.ViewThemeUtils; @@ -101,6 +105,7 @@ public class FileDetailFragment extends FileFragment implements OnClickListener, private static final String TAG = FileDetailFragment.class.getSimpleName(); private static final String FTAG_CONFIRMATION = "REMOVE_CONFIRMATION_FRAGMENT"; static final String FTAG_RENAME_FILE = "RENAME_FILE_FRAGMENT"; + private static final String FTAG_SHARING = "SHARING_DETAILS_FRAGMENT"; private static final String ARG_FILE = "FILE"; private static final String ARG_PARENT_FOLDER = "PARENT_FOLDER"; @@ -110,6 +115,10 @@ public class FileDetailFragment extends FileFragment implements OnClickListener, private User user; private OCFile parentFolder; private boolean previewLoaded; + /** + * variable to check if custom back icon on toolbar has to be shown + */ + private boolean isCustomBackIcon; private FileDetailsFragmentBinding binding; private ProgressListener progressListener; @@ -123,6 +132,8 @@ public class FileDetailFragment extends FileFragment implements OnClickListener, @Inject FileDataStorageManager storageManager; @Inject ViewThemeUtils viewThemeUtils; @Inject BackgroundJobManager backgroundJobManager; + @Inject EditorUtils editorUtils; + @Inject SyncedFolderProvider syncedFolderProvider; /** * Public factory method to create new FileDetailFragment instances. @@ -184,7 +195,7 @@ public FileDetailSharingFragment getFileDetailSharingFragment() { if (binding == null) { return null; } - return ((FileDetailTabAdapter) binding.pager.getAdapter()).getFileDetailSharingFragment(); + return (FileDetailSharingFragment)requireActivity().getSupportFragmentManager().findFragmentByTag(FTAG_SHARING); } /** @@ -193,7 +204,8 @@ public FileDetailSharingFragment getFileDetailSharingFragment() { * @return reference to the {@link FileDetailActivitiesFragment} */ public FileDetailActivitiesFragment getFileDetailActivitiesFragment() { - return ((FileDetailTabAdapter) binding.pager.getAdapter()).getFileDetailActivitiesFragment(); + //return ((FileDetailTabAdapter) binding.pager.getAdapter()).getFileDetailActivitiesFragment(); + return null; } public void goBackToOCFileListFragment() { @@ -233,12 +245,17 @@ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, } else { binding.emptyList.emptyListView.setVisibility(View.GONE); } - + Context context = getContext(); if (context == null) { return null; } + FloatingActionButton fabMain = requireActivity().findViewById(R.id.fab_main); + if (fabMain != null) { + fabMain.hide(); + } + if (getFile().getTags().isEmpty()) { binding.tagsGroup.setVisibility(View.GONE); } else { @@ -273,6 +290,13 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat } } + private void replaceSharingFragment() { + requireActivity().getSupportFragmentManager().beginTransaction() + .replace(R.id.sharing_frame_container, + FileDetailSharingFragment.newInstance(getFile(), user), + FTAG_SHARING).commit(); + } + private void onOverflowIconClicked() { final OCFile file = getFile(); final List additionalFilter = new ArrayList<>( @@ -298,66 +322,6 @@ private void onOverflowIconClicked() { .show(fragmentManager, "actions"); } - private void setupViewPager() { - binding.tabLayout.removeAllTabs(); - - binding.tabLayout.addTab(binding.tabLayout.newTab().setText(R.string.drawer_item_activities).setIcon(R.drawable.ic_activity)); - - - if (showSharingTab()) { - binding.tabLayout.addTab(binding.tabLayout.newTab().setText(R.string.share_dialog_title).setIcon(R.drawable.shared_via_users)); - } - - if (MimeTypeUtil.isImage(getFile())) { - binding.tabLayout.addTab(binding.tabLayout.newTab().setText(R.string.filedetails_details).setIcon(R.drawable.image_32dp)); - } - - viewThemeUtils.material.themeTabLayout(binding.tabLayout); - - final FileDetailTabAdapter adapter = new FileDetailTabAdapter(getFragmentManager(), - getFile(), - user, - showSharingTab()); - binding.pager.setAdapter(adapter); - binding.pager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(binding.tabLayout) { - @Override - public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { - final FileDetailActivitiesFragment fragment = getFileDetailActivitiesFragment(); - if (activeTab == 0 && fragment != null) { - fragment.markCommentsAsRead(); - } - super.onPageScrolled(position, positionOffset, positionOffsetPixels); - } - }); - binding.tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { - @Override - public void onTabSelected(TabLayout.Tab tab) { - binding.pager.setCurrentItem(tab.getPosition()); - if (tab.getPosition() == 0) { - final FileDetailActivitiesFragment fragment = getFileDetailActivitiesFragment(); - if (fragment != null) { - fragment.markCommentsAsRead(); - } - } - } - - @Override - public void onTabUnselected(TabLayout.Tab tab) { - // unused at the moment - } - - @Override - public void onTabReselected(TabLayout.Tab tab) { - // unused at the moment - } - }); - - TabLayout.Tab tab = binding.tabLayout.getTabAt(activeTab); - if (tab != null) { - tab.select(); - } - } - @Override public void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); @@ -380,10 +344,18 @@ public void onResume() { if (previewLoaded) { toolbarActivity.setPreviewImageVisibility(true); } + showHideCustomBackButton(); } } + //show custom back button for image previews + private void showHideCustomBackButton() { + if (toolbarActivity != null) { + toolbarActivity.showToolbarBackImage(isCustomBackIcon); + } + } + @Override public void onPause() { super.onPause(); @@ -395,6 +367,7 @@ public void onStop() { if (toolbarActivity != null) { toolbarActivity.hidePreviewImage(); + toolbarActivity.showToolbarBackImage(false); } EventBus.getDefault().unregister(this); @@ -523,11 +496,7 @@ public void updateFileDetails(boolean transferring, boolean refresh) { OCFile file = getFile(); // set file details - if (MimeTypeUtil.isImage(file)) { - binding.filename.setText(file.getFileName()); - } else { - binding.filename.setVisibility(View.GONE); - } + binding.filename.setText(file.getFileName()); binding.size.setText(DisplayUtils.bytesToHumanReadable(file.getFileLength())); boolean showDetailedTimestamp = preferences.isShowDetailedTimestampEnabled(); @@ -559,7 +528,10 @@ public void updateFileDetails(boolean transferring, boolean refresh) { } } - setupViewPager(); + // TODO: 06/21/23 remove this condition after Comments section included + if (SendShareDialog.isPeopleShareClicked) { + replaceSharingFragment(); + } getView().invalidate(); } @@ -600,56 +572,124 @@ private boolean readyToShow() { private void setFilePreview(OCFile file) { Bitmap resizedImage; - if (toolbarActivity != null && MimeTypeUtil.isImage(file)) { - String tagId = ThumbnailsCacheManager.PREFIX_RESIZED_IMAGE + getFile().getRemoteId(); - resizedImage = ThumbnailsCacheManager.getBitmapFromDiskCache(tagId); + if (toolbarActivity != null) { + if (file.isFolder()) { + toolbarActivity.setPreviewImageDrawable(MimeTypeUtil + .getFolderTypeIcon(file.isSharedWithMe() || file.isSharedWithSharee(), + file.isSharedViaLink(), file.isEncrypted(), + file.isGroupFolder(), + syncedFolderProvider.findByRemotePathAndAccount(file.getRemotePath(), user), + file.getMountType(), requireContext(), + viewThemeUtils)); + int leftRightPadding = requireContext().getResources().getDimensionPixelSize(R.dimen.standard_padding); + updatePreviewImageUI(leftRightPadding); - if (resizedImage != null && !file.isUpdateThumbnailNeeded()) { - toolbarActivity.setPreviewImageBitmap(resizedImage); previewLoaded = true; + isCustomBackIcon = false; } else { - // show thumbnail while loading resized image - Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache( - ThumbnailsCacheManager.PREFIX_THUMBNAIL + getFile().getRemoteId()); - - if (thumbnail != null) { - toolbarActivity.setPreviewImageBitmap(thumbnail); - } else { - thumbnail = ThumbnailsCacheManager.mDefaultImg; - } - - // generate new resized image - if (ThumbnailsCacheManager.cancelPotentialThumbnailWork(getFile(), toolbarActivity.getPreviewImageView()) && - containerActivity.getStorageManager() != null) { - final ThumbnailsCacheManager.ResizedImageGenerationTask task = - new ThumbnailsCacheManager.ResizedImageGenerationTask(this, - toolbarActivity.getPreviewImageView(), - toolbarActivity.getPreviewImageContainer(), - containerActivity.getStorageManager(), - connectivityService, - containerActivity.getStorageManager().getUser(), - getResources().getColor(R.color.background_color_inverse, - requireContext().getTheme()) - ); - - if (resizedImage == null) { - resizedImage = thumbnail; + if (file.getRemoteId() != null && file.isPreviewAvailable()) { + String tagId = ThumbnailsCacheManager.PREFIX_RESIZED_IMAGE + getFile().getRemoteId(); + resizedImage = ThumbnailsCacheManager.getBitmapFromDiskCache(tagId); + + if (resizedImage != null && !file.isUpdateThumbnailNeeded()) { + toolbarActivity.setPreviewImageBitmap(resizedImage); + toolbarActivity.showToolbarBackImage(true); + previewLoaded = true; + isCustomBackIcon = true; + } else { + // show thumbnail while loading resized image + Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache( + ThumbnailsCacheManager.PREFIX_THUMBNAIL + getFile().getRemoteId()); + + if (thumbnail != null) { + toolbarActivity.setPreviewImageBitmap(thumbnail); + toolbarActivity.showToolbarBackImage(true); + previewLoaded = true; + isCustomBackIcon = true; + } else { + Drawable drawable = MimeTypeUtil.getFileTypeIcon(file.getMimeType(), + file.getFileName(), + requireContext(), + viewThemeUtils); + if (drawable == null) { + thumbnail = ThumbnailsCacheManager.mDefaultImg; + toolbarActivity.setPreviewImageBitmap(thumbnail); + } else { + toolbarActivity.setPreviewImageDrawable(drawable); + previewLoaded = true; + isCustomBackIcon = false; + } + updatePreviewImageUIForFiles(); + } + + if (MimeTypeUtil.isImage(file)) { + // generate new resized image + if (ThumbnailsCacheManager.cancelPotentialThumbnailWork(getFile(), toolbarActivity.getPreviewImageView()) && + containerActivity.getStorageManager() != null) { + final ThumbnailsCacheManager.ResizedImageGenerationTask task = + new ThumbnailsCacheManager.ResizedImageGenerationTask(this, + toolbarActivity.getPreviewImageView(), + toolbarActivity.getPreviewImageContainer(), + containerActivity.getStorageManager(), + connectivityService, + containerActivity.getStorageManager().getUser(), + getResources().getColor(R.color.background_color_inverse, + requireContext().getTheme()) + ); + + if (resizedImage == null) { + resizedImage = thumbnail; + } + + final ThumbnailsCacheManager.AsyncResizedImageDrawable asyncDrawable = + new ThumbnailsCacheManager.AsyncResizedImageDrawable( + MainApp.getAppContext().getResources(), + resizedImage, + task + ); + + toolbarActivity.setPreviewImageDrawable(asyncDrawable); + toolbarActivity.showToolbarBackImage(true); + previewLoaded = true; + isCustomBackIcon = true; + task.execute(getFile()); + } + } } - - final ThumbnailsCacheManager.AsyncResizedImageDrawable asyncDrawable = - new ThumbnailsCacheManager.AsyncResizedImageDrawable( - MainApp.getAppContext().getResources(), - resizedImage, - task - ); - - toolbarActivity.setPreviewImageDrawable(asyncDrawable); + } else { + toolbarActivity.setPreviewImageDrawable(MimeTypeUtil.getFileTypeIcon(file.getMimeType(), + file.getFileName(), + requireContext(), + viewThemeUtils)); + updatePreviewImageUIForFiles(); previewLoaded = true; - task.execute(getFile()); + isCustomBackIcon = false; } } } else { previewLoaded = false; + isCustomBackIcon = false; + } + showHideCustomBackButton(); + } + + /** + * update preview image for files we are taking different paddings for files and folders + */ + private void updatePreviewImageUIForFiles() { + int leftRightPadding = requireContext().getResources().getDimensionPixelSize(R.dimen.standard_half_padding); + updatePreviewImageUI(leftRightPadding); + } + + /** + * change scale type and padding for folders and files without thumbnails + */ + private void updatePreviewImageUI(int leftRightPadding) { + if (toolbarActivity != null && toolbarActivity.getPreviewImageView() != null) { + toolbarActivity.getPreviewImageView().setScaleType(ImageView.ScaleType.FIT_START); + int topPadding = requireContext().getResources().getDimensionPixelSize(R.dimen.activity_row_layout_height); + int bottomPadding = requireContext().getResources().getDimensionPixelSize(R.dimen.standard_padding); + toolbarActivity.getPreviewImageView().setPadding(leftRightPadding, topPadding, leftRightPadding, bottomPadding); } } @@ -739,32 +779,15 @@ private void showEmptyContent() { public void initiateSharingProcess(String shareeName, ShareType shareType, boolean secureShare) { - requireActivity().getSupportFragmentManager().beginTransaction().add(R.id.sharing_frame_container, + requireActivity().getSupportFragmentManager().beginTransaction().replace(R.id.sharing_frame_container, FileDetailsSharingProcessFragment.newInstance(getFile(), shareeName, shareType, - secureShare), + secureShare, + SharingMenuHelper.canEditFile(requireActivity(), user, storageManager.getCapability(user), getFile(), editorUtils)), FileDetailsSharingProcessFragment.TAG) + .addToBackStack(null) .commit(); - - showHideFragmentView(true); - } - - /** - * method will handle the views need to be hidden when sharing process fragment shows - * - * @param isFragmentReplaced - */ - public void showHideFragmentView(boolean isFragmentReplaced) { - binding.tabLayout.setVisibility(isFragmentReplaced ? View.GONE : View.VISIBLE); - binding.pager.setVisibility(isFragmentReplaced ? View.GONE : View.VISIBLE); - binding.sharingFrameContainer.setVisibility(isFragmentReplaced ? View.VISIBLE : View.GONE); - FloatingActionButton mFabMain = requireActivity().findViewById(R.id.fab_main); - if (isFragmentReplaced) { - mFabMain.hide(); - } else { - mFabMain.show(); - } } /** @@ -777,12 +800,13 @@ public void showHideFragmentView(boolean isFragmentReplaced) { */ public void editExistingShare(OCShare share, int screenTypePermission, boolean isReshareShown, boolean isExpiryDateShown) { - requireActivity().getSupportFragmentManager().beginTransaction().add(R.id.sharing_frame_container, + requireActivity().getSupportFragmentManager().beginTransaction().replace(R.id.sharing_frame_container, FileDetailsSharingProcessFragment.newInstance(share, screenTypePermission, isReshareShown, - isExpiryDateShown), + isExpiryDateShown, + SharingMenuHelper.canEditFile(requireActivity(), user, storageManager.getCapability(user), getFile(), editorUtils)), FileDetailsSharingProcessFragment.TAG) + .addToBackStack(null) .commit(); - showHideFragmentView(true); } @Subscribe(threadMode = ThreadMode.BACKGROUND) @@ -807,22 +831,14 @@ public void onMessageEvent(FavoriteEvent event) { } } - private boolean showSharingTab() { - if (getFile().isEncrypted()) { - if (parentFolder == null) { - parentFolder = storageManager.getFileById(getFile().getParentId()); - } - if (EncryptionUtils.supportsSecureFiledrop(getFile(), user) && !parentFolder.isEncrypted()) { - return true; - } else { - // sharing not allowed for encrypted files, thus only show first tab (activities) - // sharing not allowed for encrypted subfolders - return false; - } - } else { - // unencrypted files/folders - return true; - } + /** + * hide the view for landscape mode to have more space for the user to type in search view + * {@link FileDetailSharingFragment#scrollToSearchViewPosition(boolean)} + * @param event + */ + @Subscribe(threadMode = ThreadMode.MAIN) + public void onMessageEvent(ShareSearchViewFocusEvent event) { + binding.shareDetailFileContainer.setVisibility(event.getHasFocus() ? View.GONE : View.VISIBLE); } /** diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java index 600338bbf7d2..cd190d4f0038 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java @@ -27,26 +27,26 @@ import android.Manifest; import android.accounts.AccountManager; -import android.app.Activity; +import android.annotation.SuppressLint; import android.app.SearchManager; import android.content.Context; -import android.content.Intent; -import android.database.Cursor; +import android.content.res.Configuration; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; -import android.provider.ContactsContract; -import android.text.InputType; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import com.google.android.material.appbar.AppBarLayout; import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManager; import com.nextcloud.client.di.Injectable; import com.nextcloud.client.network.ClientFactory; import com.nextcloud.utils.extensions.BundleExtensionsKt; +import com.nextcloud.utils.EditorUtils; +import com.nmc.android.utils.SearchViewThemeUtils; import com.owncloud.android.R; import com.owncloud.android.databinding.FileDetailsSharingFragmentBinding; import com.owncloud.android.datamodel.FileDataStorageManager; @@ -66,13 +66,17 @@ import com.owncloud.android.ui.adapter.ShareeListAdapterListener; import com.owncloud.android.ui.asynctasks.RetrieveHoverCardAsyncTask; import com.owncloud.android.ui.dialog.SharePasswordDialogFragment; +import com.owncloud.android.ui.events.ShareSearchViewFocusEvent; import com.owncloud.android.ui.fragment.util.FileDetailSharingFragmentHelper; +import com.owncloud.android.ui.fragment.util.SharingMenuHelper; import com.owncloud.android.ui.helpers.FileOperationsHelper; import com.owncloud.android.utils.ClipboardUtil; import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.PermissionUtil; import com.owncloud.android.utils.theme.ViewThemeUtils; +import org.greenrobot.eventbus.EventBus; + import java.util.ArrayList; import java.util.List; @@ -106,8 +110,11 @@ public class FileDetailSharingFragment extends Fragment implements ShareeListAda private OnEditShareListener onEditShareListener; + private boolean isSearchViewFocused; + @Inject UserAccountManager accountManager; @Inject ClientFactory clientFactory; + @Inject EditorUtils editorUtils; @Inject ViewThemeUtils viewThemeUtils; @Inject UsersAndGroupsSearchConfig searchConfig; @@ -159,6 +166,7 @@ public void onActivityCreated(Bundle savedInstanceState) { refreshSharesFromDB(); } + @SuppressLint("ClickableViewAccessibility") @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { binding = FileDetailsSharingFragmentBinding.inflate(inflater, container, false); @@ -180,7 +188,16 @@ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, binding.sharesList.setLayoutManager(new LinearLayoutManager(requireContext())); - binding.pickContactEmailBtn.setOnClickListener(v -> checkContactPermission()); + binding.shareCreateNewLink.setOnClickListener(v -> createPublicShareLink()); + + //remove focus from search view on click of root view + binding.shareContainer.setOnClickListener(v -> binding.searchView.clearFocus()); + + //enable-disable scrollview scrolling + binding.fileDetailsNestedScrollView.setOnTouchListener((view1, motionEvent) -> { + //true means disable the scrolling and false means enable the scrolling + return com.nmc.android.utils.DisplayUtils.isLandscapeOrientation() && isSearchViewFocused; + }); setupView(); @@ -220,58 +237,123 @@ public void onStop() { private void setupView() { setShareWithYou(); + } - OCFile parentFile = fileDataStorageManager.getFileById(file.getParentId()); - + private void setUpSearchView() { FileDetailSharingFragmentHelper.setupSearchView( (SearchManager) fileActivity.getSystemService(Context.SEARCH_SERVICE), binding.searchView, fileActivity.getComponentName()); - viewThemeUtils.androidx.themeToolbarSearchView(binding.searchView); + SearchViewThemeUtils.INSTANCE.themeSearchView(requireContext(), binding.searchView); - if (file.canReshare()) { - if (file.isEncrypted() || (parentFile != null && parentFile.isEncrypted())) { - if (file.getE2eCounter() == -1) { - // V1 cannot share - binding.searchContainer.setVisibility(View.GONE); - } else { - binding.searchView.setQueryHint(getResources().getString(R.string.secure_share_search)); + binding.searchView.setQueryHint(getResources().getString(R.string.share_search)); + binding.searchView.setVisibility(View.VISIBLE); + binding.labelPersonalShare.setVisibility(View.VISIBLE); + + binding.searchView.setOnQueryTextFocusChangeListener((view, hasFocus) -> { + isSearchViewFocused = hasFocus; + scrollToSearchViewPosition(false); + }); + + } + + /** + * @param isDeviceRotated true when user rotated the device and false when user is already in landscape mode + */ + private void scrollToSearchViewPosition(boolean isDeviceRotated) { + if (com.nmc.android.utils.DisplayUtils.isLandscapeOrientation()) { + if (isSearchViewFocused) { + binding.fileDetailsNestedScrollView.post(() -> { + //ignore the warning because there can be case that the scrollview can be null + if (binding.fileDetailsNestedScrollView == null) { + return; + } - if (file.isSharedViaLink()) { - binding.searchView.setQueryHint(getResources().getString(R.string.share_not_allowed_when_file_drop)); - binding.searchView.setInputType(InputType.TYPE_NULL); - disableSearchView(binding.searchView); + //need to hide app bar to have more space in landscape mode while search view is focused + hideAppBar(); + + //send the event to hide the share top view to have more space + //need to use this here else white view will be visible for sometime + EventBus.getDefault().post(new ShareSearchViewFocusEvent(isSearchViewFocused)); + + if (isDeviceRotated) { + //during the rotation we need to use getTop() method for proper alignment of search view + //-25 just to avoid blank space at top + binding.fileDetailsNestedScrollView.smoothScrollTo(0, binding.searchView.getTop() - 20); + } else { + //when user is already in landscape mode and search view gets focus + //we need to user getBottom() method for proper alignment of search view + //-100 just to avoid blank space at top + binding.fileDetailsNestedScrollView.smoothScrollTo(0, binding.searchView.getBottom() - 100); } - } + }); } else { - binding.searchView.setQueryHint(getResources().getString(R.string.share_search)); + //send the event to show the share top view again + EventBus.getDefault().post(new ShareSearchViewFocusEvent(isSearchViewFocused)); } } else { - binding.searchView.setQueryHint(getResources().getString(R.string.reshare_not_allowed)); - binding.searchView.setInputType(InputType.TYPE_NULL); - binding.pickContactEmailBtn.setVisibility(View.GONE); - disableSearchView(binding.searchView); + //in portrait mode we need to see the layout everytime + //send the event to show the share top view + EventBus.getDefault().post(new ShareSearchViewFocusEvent(false)); } } - private void disableSearchView(View view) { - view.setEnabled(false); + private void hideAppBar() { + if (requireActivity() instanceof FileDisplayActivity) { + AppBarLayout appBarLayout = requireActivity().findViewById(R.id.appbar); - if (view instanceof ViewGroup viewGroup) { - for (int i = 0; i < viewGroup.getChildCount(); i++) { - disableSearchView(viewGroup.getChildAt(i)); + if (appBarLayout != null) { + appBarLayout.setExpanded(false, true); } } } + /** + * will be called from FileActivity when user is sharing from PreviewImageFragment + * + * @param shareeName + * @param shareType + */ + public void initiateSharingProcess(String shareeName, ShareType shareType) { + requireActivity().getSupportFragmentManager().beginTransaction().replace(R.id.share_fragment_container, + FileDetailsSharingProcessFragment.newInstance(file, + shareeName, + shareType, + SharingMenuHelper.canEditFile(requireActivity(), user, capabilities, file, editorUtils)), + FileDetailsSharingProcessFragment.TAG) + .addToBackStack(null) + .commit(); + } + + /** + * open the new sharing screen process to modify the created share this will be called from PreviewImageFragment + * + * @param share + * @param screenTypePermission + * @param isReshareShown + * @param isExpiryDateShown + */ + public void editExistingShare(OCShare share, int screenTypePermission, boolean isReshareShown, + boolean isExpiryDateShown) { + requireActivity().getSupportFragmentManager().beginTransaction().replace(R.id.share_fragment_container, + FileDetailsSharingProcessFragment.newInstance(share, screenTypePermission, isReshareShown, + isExpiryDateShown, SharingMenuHelper.canEditFile(requireActivity(), user, capabilities, file, editorUtils)), + FileDetailsSharingProcessFragment.TAG) + .addToBackStack(null) + .commit(); + } + private void setShareWithYou() { if (accountManager.userOwnsFile(file, user)) { binding.sharedWithYouContainer.setVisibility(View.GONE); + binding.shareCreateNewLink.setVisibility(View.VISIBLE); + binding.tvSharingDetailsMessage.setText(getResources().getString(R.string.sharing_description)); + setUpSearchView(); } else { binding.sharedWithYouUsername.setText( String.format(getString(R.string.shared_with_you_by), file.getOwnerDisplayName())); - DisplayUtils.setAvatar(user, + /* DisplayUtils.setAvatar(user, file.getOwnerId(), this, getResources().getDimension( @@ -279,7 +361,7 @@ private void setShareWithYou() { getResources(), binding.sharedWithYouAvatar, getContext()); - binding.sharedWithYouAvatar.setVisibility(View.VISIBLE); + binding.sharedWithYouAvatar.setVisibility(View.VISIBLE);*/ String note = file.getNote(); @@ -289,6 +371,16 @@ private void setShareWithYou() { } else { binding.sharedWithYouNoteContainer.setVisibility(View.GONE); } + + if (file.canReshare()) { + binding.tvSharingDetailsMessage.setText(getResources().getString(R.string.reshare_allowed) + " " + getResources().getString(R.string.sharing_description)); + setUpSearchView(); + } else { + binding.searchView.setVisibility(View.GONE); + binding.labelPersonalShare.setVisibility(View.GONE); + binding.shareCreateNewLink.setVisibility(View.GONE); + binding.tvSharingDetailsMessage.setText(getResources().getString(R.string.reshare_not_allowed)); + } } } @@ -465,6 +557,10 @@ public void refreshSharesFromDB() { } adapter.getShares().clear(); + //update flag in adapter + adapter.setTextFile(SharingMenuHelper.canEditFile(requireActivity(), user, + capabilities, file, editorUtils)); + // to show share with users/groups info List shares = fileDataStorageManager.getSharesWithForAFile(file.getRemotePath(), user.getAccountName()); @@ -480,16 +576,32 @@ public void refreshSharesFromDB() { ShareType.PUBLIC_LINK, ""); - if (publicShares.isEmpty() && containsNoNewPublicShare(adapter.getShares()) && + /*if (publicShares.isEmpty() && containsNoNewPublicShare(adapter.getShares()) && (!file.isEncrypted() || capabilities.getEndToEndEncryption().isTrue())) { final OCShare ocShare = new OCShare(); ocShare.setShareType(ShareType.NEW_PUBLIC_LINK); publicShares.add(ocShare); } else { adapter.removeNewPublicShare(); + }*/ + + if (publicShares.isEmpty() && containsNoNewPublicShare(adapter.getShares())) { + final OCShare ocShare = new OCShare(); + ocShare.setShareType(ShareType.NEW_PUBLIC_LINK); + publicShares.add(ocShare); + } else { + adapter.removeNewPublicShare(); } adapter.addShares(publicShares); + + showHideView((shares == null || shares.isEmpty()) && (publicShares == null || publicShares.isEmpty())); + } + + private void showHideView(boolean isEmptyList) { + binding.sharesList.setVisibility(isEmptyList ? View.GONE : View.VISIBLE); + binding.tvYourShares.setVisibility(isEmptyList ? View.GONE : View.VISIBLE); + binding.tvEmptyShares.setVisibility(isEmptyList ? View.VISIBLE : View.GONE); } private void checkContactPermission() { @@ -578,6 +690,11 @@ public void search(String query) { searchView.setQuery(query, true); } + @Override + public void openIn(OCShare share) { + fileOperationsHelper.sendShareFile(file, true); + } + @Override public void advancedPermissions(OCShare share) { modifyExistingShare(share, FileDetailsSharingProcessFragment.SCREEN_TYPE_PERMISSION); @@ -609,11 +726,6 @@ public void sendLink(OCShare share) { } } - @Override - public void addAnotherLink(OCShare share) { - createPublicShareLink(); - } - private void modifyExistingShare(OCShare share, int screenTypePermission) { onEditShareListener.editExistingShare(share, screenTypePermission, !isReshareForbidden(share), capabilities.getVersion().isNewerOrEqual(OwnCloudVersion.nextcloud_18)); @@ -662,4 +774,13 @@ void editExistingShare(OCShare share, int screenTypePermission, boolean isReshar void onShareProcessClosed(); } + + @Override + public void onConfigurationChanged(@NonNull Configuration newConfig) { + super.onConfigurationChanged(newConfig); + + //when user is in portrait mode and search view is focused and keyboard is open + //so when user rotate the device we have to fix the search view properly in landscape mode + scrollToSearchViewPosition(true); + } } diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingMenuBottomSheetDialog.java b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingMenuBottomSheetDialog.java index f71c25855380..b1e6acc0805d 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingMenuBottomSheetDialog.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingMenuBottomSheetDialog.java @@ -62,14 +62,6 @@ protected void onCreate(Bundle savedInstanceState) { getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); } - viewThemeUtils.platform.themeDialog(binding.getRoot()); - - viewThemeUtils.platform.colorImageView(binding.menuIconAddAnotherLink); - viewThemeUtils.platform.colorImageView(binding.menuIconAdvancedPermissions); - viewThemeUtils.platform.colorImageView(binding.menuIconSendLink); - viewThemeUtils.platform.colorImageView(binding.menuIconUnshare); - viewThemeUtils.platform.colorImageView(binding.menuIconSendNewEmail); - updateUI(); setupClickListener(); @@ -81,12 +73,16 @@ protected void onCreate(Bundle savedInstanceState) { } private void updateUI() { + if (ocShare.isFolder()) { + binding.menuShareOpenIn.setVisibility(View.GONE); + } else { + binding.menuShareOpenIn.setVisibility(View.VISIBLE); + } + if (ocShare.getShareType() == ShareType.PUBLIC_LINK) { - binding.menuShareAddAnotherLink.setVisibility(View.VISIBLE); - binding.menuShareSendLink.setVisibility(View.VISIBLE); + binding.menuShareSendNewEmail.setVisibility(View.GONE); } else { - binding.menuShareAddAnotherLink.setVisibility(View.GONE); - binding.menuShareSendLink.setVisibility(View.GONE); + binding.menuShareSendNewEmail.setVisibility(View.VISIBLE); } if (SharingMenuHelper.isSecureFileDrop(ocShare)) { @@ -96,6 +92,11 @@ private void updateUI() { } private void setupClickListener() { + binding.menuShareOpenIn.setOnClickListener(v -> { + actions.openIn(ocShare); + dismiss(); + }); + binding.menuShareAdvancedPermissions.setOnClickListener(v -> { actions.advancedPermissions(ocShare); dismiss(); @@ -116,10 +117,6 @@ private void setupClickListener() { dismiss(); }); - binding.menuShareAddAnotherLink.setOnClickListener(v -> { - actions.addAnotherLink(ocShare); - dismiss(); - }); } @Override diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingMenuBottomSheetActions.java b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingMenuBottomSheetActions.java index 8e6ab6a1acd9..23ff93318850 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingMenuBottomSheetActions.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingMenuBottomSheetActions.java @@ -29,6 +29,10 @@ * Actions interface to be implemented by any class that makes use of {@link FileDetailSharingMenuBottomSheetDialog}. */ public interface FileDetailsSharingMenuBottomSheetActions { + /** + * open sharing options only applicable for files + */ + void openIn(OCShare share); /** * open advanced permission for selected share @@ -50,8 +54,4 @@ public interface FileDetailsSharingMenuBottomSheetActions { */ void sendLink(OCShare share); - /** - * create another link only valid for {@link ShareType#PUBLIC_LINK} - */ - void addAnotherLink(OCShare share); } diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java index 3af6eb035c0a..770546d96504 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java @@ -101,6 +101,7 @@ import com.owncloud.android.ui.dialog.CreateFolderDialogFragment; import com.owncloud.android.ui.dialog.RemoveFilesDialogFragment; import com.owncloud.android.ui.dialog.RenameFileDialogFragment; +import com.owncloud.android.ui.dialog.SendShareDialog; import com.owncloud.android.ui.dialog.SetupEncryptionDialogFragment; import com.owncloud.android.ui.dialog.SyncFileNotEnoughSpaceDialogFragment; import com.owncloud.android.ui.events.ChangeMenuEvent; @@ -589,6 +590,8 @@ public void createRichWorkspace() { @Override public void onShareIconClick(OCFile file) { + //NMC Customization + SendShareDialog.isPeopleShareClicked = true; if (file.isFolder()) { mContainerActivity.showDetails(file, 1); } else { @@ -1166,6 +1169,8 @@ public boolean onFileActionChosen(@IdRes final int itemId, Set checkedFi OCFile singleFile = checkedFiles.iterator().next(); if (itemId == R.id.action_send_share_file) { + //NMC Customization + SendShareDialog.isPeopleShareClicked = true; mContainerActivity.getFileOperationsHelper().sendShareFile(singleFile); return true; } else if (itemId == R.id.action_open_file_with) { @@ -1194,7 +1199,8 @@ public boolean onFileActionChosen(@IdRes final int itemId, Set checkedFi if (mActiveActionMode != null) { mActiveActionMode.finish(); } - + //NMC Customization + SendShareDialog.isPeopleShareClicked = true; mContainerActivity.showDetails(singleFile); mContainerActivity.showSortListGroup(false); return true; diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/QuickSharingPermissionsBottomSheetDialog.java b/app/src/main/java/com/owncloud/android/ui/fragment/QuickSharingPermissionsBottomSheetDialog.java index 0e337dbdd3fd..e8495eb08775 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/QuickSharingPermissionsBottomSheetDialog.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/QuickSharingPermissionsBottomSheetDialog.java @@ -31,6 +31,7 @@ import com.owncloud.android.databinding.QuickSharingPermissionsBottomSheetFragmentBinding; import com.owncloud.android.datamodel.QuickPermissionModel; import com.owncloud.android.lib.resources.shares.OCShare; +import com.owncloud.android.lib.resources.shares.ShareType; import com.owncloud.android.ui.activity.FileActivity; import com.owncloud.android.ui.adapter.QuickSharingPermissionsAdapter; import com.owncloud.android.ui.fragment.util.SharingMenuHelper; @@ -42,8 +43,6 @@ import androidx.recyclerview.widget.LinearLayoutManager; import static com.owncloud.android.lib.resources.shares.OCShare.CREATE_PERMISSION_FLAG; -import static com.owncloud.android.lib.resources.shares.OCShare.MAXIMUM_PERMISSIONS_FOR_FILE; -import static com.owncloud.android.lib.resources.shares.OCShare.MAXIMUM_PERMISSIONS_FOR_FOLDER; import static com.owncloud.android.lib.resources.shares.OCShare.READ_PERMISSION_FLAG; /** @@ -77,8 +76,6 @@ protected void onCreate(Bundle savedInstanceState) { getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); } - viewThemeUtils.platform.themeDialog(binding.getRoot()); - setUpRecyclerView(); setOnShowListener(d -> BottomSheetBehavior.from((View) binding.getRoot().getParent()) @@ -114,22 +111,21 @@ public void onDismissSheet() { * @param position */ private void handlePermissionChanged(List quickPermissionModelList, int position) { - if (quickPermissionModelList.get(position).getPermissionName().equalsIgnoreCase(fileActivity.getResources().getString(R.string.link_share_allow_upload_and_editing)) - || quickPermissionModelList.get(position).getPermissionName().equalsIgnoreCase(fileActivity.getResources().getString(R.string.link_share_editing))) { + if (quickPermissionModelList.get(position).getPermissionName().equalsIgnoreCase(fileActivity.getResources().getString(R.string.share_permission_can_edit))) { if (ocShare.isFolder()) { actions.onQuickPermissionChanged(ocShare, - MAXIMUM_PERMISSIONS_FOR_FOLDER); + SharingMenuHelper.CAN_EDIT_PERMISSIONS_FOR_FOLDER); } else { actions.onQuickPermissionChanged(ocShare, - MAXIMUM_PERMISSIONS_FOR_FILE); + SharingMenuHelper.CAN_EDIT_PERMISSIONS_FOR_FILE); } } else if (quickPermissionModelList.get(position).getPermissionName().equalsIgnoreCase(fileActivity.getResources().getString(R.string - .link_share_view_only))) { + .share_permission_read_only))) { actions.onQuickPermissionChanged(ocShare, READ_PERMISSION_FLAG); } else if (quickPermissionModelList.get(position).getPermissionName().equalsIgnoreCase(fileActivity.getResources().getString(R.string - .link_share_file_drop))) { + .share_permission_file_drop))) { actions.onQuickPermissionChanged(ocShare, CREATE_PERMISSION_FLAG); } @@ -144,8 +140,13 @@ private List getQuickPermissionList() { String[] permissionArray; if (ocShare.isFolder()) { - permissionArray = - fileActivity.getResources().getStringArray(R.array.folder_share_permission_dialog_values); + if (ocShare.getShareType() == ShareType.EMAIL || ocShare.getShareType() == ShareType.PUBLIC_LINK) { + permissionArray = + fileActivity.getResources().getStringArray(R.array.folder_share_permission_dialog_values); + } else { + permissionArray = + fileActivity.getResources().getStringArray(R.array.folder_internal_share_permission_dialog_values); + } } else { permissionArray = fileActivity.getResources().getStringArray(R.array.file_share_permission_dialog_values); @@ -153,7 +154,6 @@ private List getQuickPermissionList() { //get the checked item position int checkedItem = SharingMenuHelper.getPermissionCheckedItem(fileActivity, ocShare, permissionArray); - final List quickPermissionModelList = new ArrayList<>(permissionArray.length); for (int i = 0; i < permissionArray.length; i++) { QuickPermissionModel quickPermissionModel = new QuickPermissionModel(permissionArray[i], checkedItem == i); diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/util/SharingMenuHelper.java b/app/src/main/java/com/owncloud/android/ui/fragment/util/SharingMenuHelper.java index 6e1ca9bb8f99..9fb65390eabe 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/util/SharingMenuHelper.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/util/SharingMenuHelper.java @@ -26,24 +26,40 @@ import android.content.res.Resources; import android.view.MenuItem; +import com.nextcloud.client.account.User; +import com.nextcloud.utils.EditorUtils; 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.status.OCCapability; import java.text.SimpleDateFormat; import java.util.Date; +import androidx.annotation.NonNull; + import static com.owncloud.android.lib.resources.shares.OCShare.CREATE_PERMISSION_FLAG; -import static com.owncloud.android.lib.resources.shares.OCShare.MAXIMUM_PERMISSIONS_FOR_FILE; -import static com.owncloud.android.lib.resources.shares.OCShare.MAXIMUM_PERMISSIONS_FOR_FOLDER; +import static com.owncloud.android.lib.resources.shares.OCShare.DELETE_PERMISSION_FLAG; 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; /** * Helper calls for visibility logic of the sharing menu. */ public final class SharingMenuHelper { + //updated Edit permissions for folder and files + //because the MAXIMUM_PERMISSIONS_FOR_FILE and MAXIMUM_PERMISSIONS_FOR_FOLDER permission in OCShare + //are not valid anymore due to functionality changes + public static final int CAN_EDIT_PERMISSIONS_FOR_FILE = + READ_PERMISSION_FLAG + UPDATE_PERMISSION_FLAG; + + public static final int CAN_EDIT_PERMISSIONS_FOR_FOLDER = + READ_PERMISSION_FLAG + UPDATE_PERMISSION_FLAG + CREATE_PERMISSION_FLAG + DELETE_PERMISSION_FLAG; + + private SharingMenuHelper() { // utility class -> private constructor } @@ -101,9 +117,9 @@ public static boolean isUploadAndEditingAllowed(OCShare share) { return false; } - return (share.getPermissions() & (share.isFolder() ? MAXIMUM_PERMISSIONS_FOR_FOLDER : - MAXIMUM_PERMISSIONS_FOR_FILE)) == (share.isFolder() ? MAXIMUM_PERMISSIONS_FOR_FOLDER : - MAXIMUM_PERMISSIONS_FOR_FILE); + return (share.getPermissions() & (share.isFolder() ? CAN_EDIT_PERMISSIONS_FOR_FOLDER: + CAN_EDIT_PERMISSIONS_FOR_FILE)) == (share.isFolder() ? CAN_EDIT_PERMISSIONS_FOR_FOLDER : + CAN_EDIT_PERMISSIONS_FOR_FILE); } public static boolean isReadOnly(OCShare share) { @@ -131,13 +147,13 @@ public static boolean isSecureFileDrop(OCShare share) { } public static String getPermissionName(Context context, OCShare share) { - if (SharingMenuHelper.isUploadAndEditingAllowed(share)) { + if (isUploadAndEditingAllowed(share)) { return context.getResources().getString(R.string.share_permission_can_edit); - } else if (SharingMenuHelper.isReadOnly(share)) { - return context.getResources().getString(R.string.share_permission_view_only); + } else if (isReadOnly(share)) { + return context.getResources().getString(R.string.share_permission_read_only); } else if (SharingMenuHelper.isSecureFileDrop(share)) { return context.getResources().getString(R.string.share_permission_secure_file_drop); - } else if (SharingMenuHelper.isFileDrop(share)) { + } else if (isFileDrop(share)) { return context.getResources().getString(R.string.share_permission_file_drop); } return null; @@ -146,18 +162,18 @@ public static String getPermissionName(Context context, OCShare share) { /** * method to get the current checked index from the list of permissions * + * @param context + * @param share + * @param permissionArray + * @return */ public static int getPermissionCheckedItem(Context context, OCShare share, String[] permissionArray) { if (SharingMenuHelper.isUploadAndEditingAllowed(share)) { - if (share.isFolder()) { - return getPermissionIndexFromArray(context, permissionArray, R.string.link_share_allow_upload_and_editing); - } else { - return getPermissionIndexFromArray(context, permissionArray, R.string.link_share_editing); - } + return getPermissionIndexFromArray(context, permissionArray, R.string.share_permission_can_edit); } else if (SharingMenuHelper.isReadOnly(share)) { - return getPermissionIndexFromArray(context, permissionArray, R.string.link_share_view_only); + return getPermissionIndexFromArray(context, permissionArray, R.string.share_permission_read_only); } else if (SharingMenuHelper.isFileDrop(share)) { - return getPermissionIndexFromArray(context, permissionArray, R.string.link_share_file_drop); + return getPermissionIndexFromArray(context, permissionArray, R.string.share_permission_file_drop); } return 0;//default first item selected } @@ -174,4 +190,28 @@ private static int getPermissionIndexFromArray(Context context, String[] permiss public static boolean canReshare(OCShare share) { return (share.getPermissions() & SHARE_PERMISSION_FLAG) > 0; } + + /** + * method to check if the file should not be a text file or any of the office files + * this method will be used during sharing process to disable/enable edit option + */ + public static boolean canEditFile(@NonNull Context context, @NonNull User user, + @NonNull OCCapability capability, @NonNull OCFile file, + @NonNull EditorUtils editorUtils) { + + //if OCFile is folder then no need to check further direct return true + //as edit permission should be available for folder restriction is only applicable for files + if (file.isFolder()) { + return true; + } + + //check for text files like .md, .txt, etc + boolean isTextFile = editorUtils.isEditorAvailable(user, file.getMimeType()) && !file.isEncrypted(); + + //check for office files like .docx, .pptx, .xls, etc + boolean isOfficeFile = capability.getRichDocumentsMimeTypeList() != null && capability.getRichDocumentsMimeTypeList().contains(file.getMimeType()) && + capability.getRichDocumentsDirectEditing().isTrue() && !file.isEncrypted(); + + return isTextFile || isOfficeFile; + } } diff --git a/app/src/main/res/drawable-night/ic_internal_share.xml b/app/src/main/res/drawable-night/ic_internal_share.xml new file mode 100644 index 000000000000..1a3f4dbf20f0 --- /dev/null +++ b/app/src/main/res/drawable-night/ic_internal_share.xml @@ -0,0 +1,12 @@ + + + diff --git a/app/src/main/res/drawable/ic_calendar.xml b/app/src/main/res/drawable/ic_calendar.xml new file mode 100644 index 000000000000..708aae2ccb04 --- /dev/null +++ b/app/src/main/res/drawable/ic_calendar.xml @@ -0,0 +1,12 @@ + + + diff --git a/app/src/main/res/drawable/ic_clipboard.xml b/app/src/main/res/drawable/ic_clipboard.xml new file mode 100644 index 000000000000..9827d48041c1 --- /dev/null +++ b/app/src/main/res/drawable/ic_clipboard.xml @@ -0,0 +1,12 @@ + + + diff --git a/app/src/main/res/drawable/ic_external_share.xml b/app/src/main/res/drawable/ic_external_share.xml new file mode 100644 index 000000000000..8c4f995ba247 --- /dev/null +++ b/app/src/main/res/drawable/ic_external_share.xml @@ -0,0 +1,12 @@ + + + diff --git a/app/src/main/res/drawable/ic_internal_share.xml b/app/src/main/res/drawable/ic_internal_share.xml new file mode 100644 index 000000000000..e6b2da36f6d0 --- /dev/null +++ b/app/src/main/res/drawable/ic_internal_share.xml @@ -0,0 +1,12 @@ + + + diff --git a/app/src/main/res/drawable/ic_open_in.xml b/app/src/main/res/drawable/ic_open_in.xml new file mode 100644 index 000000000000..de3b7b7e2cea --- /dev/null +++ b/app/src/main/res/drawable/ic_open_in.xml @@ -0,0 +1,12 @@ + + + diff --git a/app/src/main/res/drawable/ic_pencil_edit.xml b/app/src/main/res/drawable/ic_pencil_edit.xml new file mode 100644 index 000000000000..a1089345a7b3 --- /dev/null +++ b/app/src/main/res/drawable/ic_pencil_edit.xml @@ -0,0 +1,12 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_shared.xml b/app/src/main/res/drawable/ic_shared.xml new file mode 100644 index 000000000000..a7aa8ad46e52 --- /dev/null +++ b/app/src/main/res/drawable/ic_shared.xml @@ -0,0 +1,12 @@ + + + diff --git a/app/src/main/res/drawable/ic_shared_with_me.xml b/app/src/main/res/drawable/ic_shared_with_me.xml new file mode 100644 index 000000000000..24e5a42e0c32 --- /dev/null +++ b/app/src/main/res/drawable/ic_shared_with_me.xml @@ -0,0 +1,13 @@ + + + + diff --git a/app/src/main/res/drawable/share_et_bg.xml b/app/src/main/res/drawable/share_et_bg.xml new file mode 100644 index 000000000000..10e93e811aae --- /dev/null +++ b/app/src/main/res/drawable/share_et_bg.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/share_search_background.xml b/app/src/main/res/drawable/share_search_background.xml new file mode 100644 index 000000000000..2d8cc41165bb --- /dev/null +++ b/app/src/main/res/drawable/share_search_background.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/sharing_email_warning_bg.xml b/app/src/main/res/drawable/sharing_email_warning_bg.xml new file mode 100644 index 000000000000..b4c4f72141cd --- /dev/null +++ b/app/src/main/res/drawable/sharing_email_warning_bg.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/app/src/main/res/drawable/toolbar_back_arrow_bg.xml b/app/src/main/res/drawable/toolbar_back_arrow_bg.xml new file mode 100644 index 000000000000..42c6b7aabba2 --- /dev/null +++ b/app/src/main/res/drawable/toolbar_back_arrow_bg.xml @@ -0,0 +1,5 @@ + + + + diff --git a/app/src/main/res/layout/file_details_fragment.xml b/app/src/main/res/layout/file_details_fragment.xml index 41795e3aceb1..ae874a690503 100644 --- a/app/src/main/res/layout/file_details_fragment.xml +++ b/app/src/main/res/layout/file_details_fragment.xml @@ -29,6 +29,7 @@ android:orientation="vertical"> @@ -46,8 +47,8 @@ android:layout_height="wrap_content" android:ellipsize="middle" android:text="@string/placeholder_filename" - android:textColor="@color/text_color" - android:textSize="20sp" + android:textColor="@color/share_title_txt_color" + android:textSize="@dimen/txt_size_14sp" android:textStyle="bold" tools:text="@string/placeholder_filename" /> @@ -71,8 +72,8 @@ android:id="@+id/size" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textSize="@dimen/two_line_primary_text_size" - android:textColor="@color/list_item_lastmod_and_filesize_text" + android:textSize="@dimen/txt_size_14sp" + android:textColor="@color/share_subtitle_txt_color" tools:text="@string/placeholder_fileSize" /> + android:textColor="@color/share_subtitle_txt_color" + android:textSize="@dimen/two_line_secondary_text_size" /> + app:iconTint="@color/grey_60" /> @@ -185,24 +187,8 @@ android:layout_height="1dp" android:background="@color/list_divider_background" /> - - - - diff --git a/app/src/main/res/layout/file_details_share_link_share_item.xml b/app/src/main/res/layout/file_details_share_link_share_item.xml index 901c2b0a9d2e..eefa76088cce 100644 --- a/app/src/main/res/layout/file_details_share_link_share_item.xml +++ b/app/src/main/res/layout/file_details_share_link_share_item.xml @@ -30,18 +30,18 @@ android:id="@+id/share_by_link_container" android:layout_width="match_parent" android:layout_height="@dimen/sharee_list_item_size" + android:layout_marginTop="@dimen/standard_margin" android:orientation="horizontal"> + android:textSize="@dimen/txt_size_14sp" /> + android:scaleType="fitStart" + app:tint="@color/primary" + android:paddingStart="@dimen/standard_padding" + android:paddingEnd="@dimen/standard_half_padding" + android:src="@drawable/ic_clipboard" /> diff --git a/app/src/main/res/layout/file_details_share_public_link_add_new_item.xml b/app/src/main/res/layout/file_details_share_public_link_add_new_item.xml index 31ff3b78620a..c71446c6171b 100644 --- a/app/src/main/res/layout/file_details_share_public_link_add_new_item.xml +++ b/app/src/main/res/layout/file_details_share_public_link_add_new_item.xml @@ -29,8 +29,8 @@ android:orientation="horizontal"> + app:tint="@color/text_color" + android:src="@drawable/ic_internal_share" /> diff --git a/app/src/main/res/layout/file_details_sharing_fragment.xml b/app/src/main/res/layout/file_details_sharing_fragment.xml index f9122e926f92..68a0fbb61956 100644 --- a/app/src/main/res/layout/file_details_sharing_fragment.xml +++ b/app/src/main/res/layout/file_details_sharing_fragment.xml @@ -19,119 +19,180 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . --> - + android:id="@+id/file_details_nested_scroll_view" + android:layout_height="match_parent"> - - + android:layout_height="match_parent" + android:layout_marginTop="@dimen/standard_margin" + android:orientation="vertical"> - - - - - - - - - + android:paddingLeft="@dimen/standard_padding" + android:paddingRight="@dimen/standard_padding" + android:text="@string/share_dialog_title" + android:textColor="@color/share_title_txt_color" + android:textSize="@dimen/txt_size_16sp" + android:textStyle="bold" /> - + - - + android:layout_height="wrap_content" + android:orientation="vertical" + android:paddingLeft="@dimen/standard_half_padding" + android:paddingTop="@dimen/standard_quarter_padding" + android:paddingRight="@dimen/standard_padding"> - + android:text="@string/shared_with_you_by" + android:textSize="@dimen/two_line_primary_text_size" /> + + + + + + + + - - + - + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/file_details_sharing_menu_bottom_sheet_fragment.xml b/app/src/main/res/layout/file_details_sharing_menu_bottom_sheet_fragment.xml index b0313fba3692..5983cccd50ef 100644 --- a/app/src/main/res/layout/file_details_sharing_menu_bottom_sheet_fragment.xml +++ b/app/src/main/res/layout/file_details_sharing_menu_bottom_sheet_fragment.xml @@ -24,17 +24,50 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" + android:background="@color/bottom_sheet_bg_color" android:orientation="vertical" - android:paddingTop="@dimen/dialog_padding" - android:background="@color/bg_default"> + android:paddingTop="@dimen/standard_padding"> + + + + + + + + @@ -64,7 +97,8 @@ @@ -96,7 +130,8 @@ @@ -130,7 +165,8 @@ @@ -162,7 +198,8 @@ diff --git a/app/src/main/res/layout/item_quick_share_permissions.xml b/app/src/main/res/layout/item_quick_share_permissions.xml index fde3966601bf..73b084658d8d 100644 --- a/app/src/main/res/layout/item_quick_share_permissions.xml +++ b/app/src/main/res/layout/item_quick_share_permissions.xml @@ -21,17 +21,15 @@ + android:layout_height="wrap_content" + android:padding="@dimen/standard_padding"> + + + + Bearbeiten Alle Benachrichtigungen löschen Papierkorb leeren - Senden/Teilen + Teilen Kachelansicht Listenansicht Kontakte und Kalender wiederherstellen @@ -34,6 +34,9 @@ Neue sichere Dateiablage hinzufügen Hinzufügen zu %1$s Erweiterte Einstellungen + Erstellen erlauben + Löschen erlauben + Bearbeitung erlauben Weiterteilen erlauben Zeigt ein Widget aus dem Dashboard an Suche in %s @@ -109,6 +112,7 @@ Schauen Sie später noch einmal vorbei oder laden Sie neu. Checkbox Wähle einen lokalen Ordner… + Ort wählen Wähle einen entfernten Ordner … Bitte eine Vorlage auswählen und einen Dateinamen eingeben. Wählen Sie, welche Datei behalten werden soll! @@ -124,20 +128,21 @@ Abbrechen Synchronisierung abbrechen Konto auswählen - Bestätigen + Übernehmen Kopieren Löschen Fehler Nicht genügend Speicher Unbekannter Fehler Lade… - Nächste + Weiter Nein OK Ausstehend Löschen Umbenennen Speichern + Auswählen Senden Teilen Überspringen @@ -207,6 +212,7 @@ Zu sichernde Daten Zugangsdaten falsch Benutzerkonto löschen + Konto %s und alle lokalen Dateien löschen?\n\nLöschung kann nicht rückgängig gemacht werden. Einträgen entfernen Link löschen Auswahl aufheben @@ -466,10 +472,10 @@ Letzte Sicherung:%1$s Link Link-Name - Hochladen und Bearbeiten erlauben - Bearbeitung - Dateien ablegen (nur Hochladen) - Nur anzeigen + Hochladen & Bearbeiten + Sammelbox + Nur Lesen + Bearbeiten Layout der Liste Weitere Ergebnisse laden Es befinden sich keine Dateien in diesem Ordner. @@ -539,7 +545,7 @@ Nur ein Konto zulässig Keine App verfügbar um PDFs anzuzeigen Keine App zum Senden der ausgewählten Dateien verfügbar - Bitte mindestens eine Berechtigung zum Teilen auswählen. + Senden Notiz konnte nicht versandt werden Notiz-Symbol Ausführen der Aktion fehlgeschlagen. @@ -682,7 +688,8 @@ Kontolöschung anfordern Löschung anfordern Beim Diensteanbieter die dauerhafte Löschung des Kontos anfordern - Das Weiterverteilen ist nicht erlaubt + Weiterteilen ist nicht erlaubt. + Weiterteilen ist erlaubt. Resharing/Wiederteilen ist nicht erlaubt. Kein verkleinertes Bild verfügbar. Vollbild herunterladen? Datei wiederherstellen @@ -713,7 +720,7 @@ Bitte eine Vorlage auswählen Vorlage auswählen Senden - Freigabe senden + Notiz an Empfänger senden Icon für den Senden-Button Setze als Notiz setzen @@ -733,29 +740,29 @@ Der interne Freigabelink funktioniert nur für Benutzer mit Zugriff auf diesen Ordner auf %1$s Link teilen + Link zum Ordner + Link zur Datei Sie müssen ein Passwort eingeben Es ist ein Fehler beim Freigeben der Datei oder des Ordners aufgetreten. Teilen nicht möglich. Bitte prüfen Sie, ob die Datei existiert. diese Datei zu teilen Passwort eingeben (optional) Passwort eingeben - Teile Link (%1$s) + Link \"%1$s\" Ablaufdatum setzen Passwort setzen Erneutes teilen ist für die sichere Dateiablage nicht zugelassen Passwortgeschützt - Kann bearbeiten - Dateiablage - Sichere Dateiablage Nur anzeigen Berechtigungen zum Teilen + Freigabe aufheben %1$s (remote) %1$s (Unterhaltung) - Name, Federated-Cloud-ID oder E-Mail-Adresse … - Neue E-Mail senden + Kontaktname oder E-Mail Notiz an Empfänger Einstellungen Download verbergen + Passwortschutz (%1$s) Link teilen Link teilen Entfernen @@ -872,6 +879,7 @@ Es gibt ungelesene Kommentare Verschlüsselung aufheben Aus den Favoriten entfernen + Freigabe aufheben Es ist ein Fehler beim Entfernen der Freigabe für diese Datei oder den Ordner aufgetreten. Entfernen der Freigabe nicht möglich. Bitte prüfen Sie, ob die Datei existiert. diese Datei nicht mehr zu teilen @@ -1003,6 +1011,43 @@ Inhalte von %1$d Datei konnten nicht synchronisiert werden (Konflikte: %2$d) Inhalte von %1$d Dateien konnten nicht synchronisiert werden (Konflikte: %2$d) + + Nur lesen + Kann Bearbeiten + Sammelbox + Berechtigungen + Erweiterte Berechtigungen + Freigabe senden + Bitte wählen Sie mindestens eine Berechtigung zum Teilen aus. + Sie müssen das Ablaufdatum auswählen. + Bitte Anmerkung eingeben. + Email senden + Öffnen mit... + Erweiterte Berechtigungen + Neue Email versenden + Download Limit + Das Feld für das Download-Limit darf nicht leer sein. + Downlimit eingeben + Downloads:\u0020 + Der Wert für das Download limit sollte größer als 0 sein. + Sie teilen mit einer/einem MagentaCLOUD Nutzer(in). Sie können ihr oder ihm erlauben, den Ordner oder die Dateien weiterzuteilen. + Der Passwortschutz ist aktiviert. Sie müssen dem Empfänger das Passwort + selbst mitteilen.\n\nWenn Sie die Freigabe über die MagentaCLOUD verschicken und das Passwort in den + Nachrichtentext eintragen, wird es unverschlüsselt im Klartext übertragen. + Empfangen + Geteilt + Sie können Links erstellen oder Freigaben per Mail versenden. Wenn Sie + MagentaCLOUD Nutzer einladen, bieten sich Ihnen mehr Möglichkeiten der Zusammenarbeit. + persönliche Freigabe per E-Mail + Link erstellen + Ihre Freigaben + Linkbezeichnung + Ihre Linkbezeichnung + Bei der Sammelbox ist nur das Hochladen erlaubt. Nur Sie sehen Dateien und Ordner die hochgeladen worden sind. + Password Protection + Expiration Date + Noch keine Freigaben erstellt. + %1$d Datei im %2$s-Verzeichnis konnten nicht kopiert werden nach %1$d Dateien im %2$s-Verzeichnis konnten nicht kopiert werden nach diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml index 08bf64d552ad..88a29668c4cd 100644 --- a/app/src/main/res/values-night/colors.xml +++ b/app/src/main/res/values-night/colors.xml @@ -48,4 +48,70 @@ #1E1E1E @android:color/white + + + #FFFFFF + @color/grey_30 + @color/grey_30 + #CCCCCC + @color/grey_70 + @color/grey_80 + #2D2D2D + @color/grey_70 + @color/grey_70 + + + @color/grey_80 + @color/grey_0 + + + @color/grey_80 + @color/grey_0 + + + @color/grey_60 + @color/grey_0 + @color/grey_0 + @color/grey_30 + #FFFFFF + @color/grey_30 + @color/grey_80 + #FFFFFF + + + @color/grey_80 + @color/grey_30 + @color/grey_0 + + + @color/grey_80 + @color/grey_0 + @color/grey_80 + + + @color/grey_70 + @color/grey_60 + @color/grey_70 + @color/grey_60 + + + @color/grey_70 + @color/grey_70 + + + #FFFFFF + @color/grey_30 + @color/grey_0 + @color/grey_0 + @color/grey_0 + @color/grey_0 + @color/grey_60 + @color/grey_0 + #FFFFFF + + + #121212 + @color/grey_0 + @color/grey_80 + @color/grey_80 diff --git a/app/src/main/res/values-sw480dp/bool.xml b/app/src/main/res/values-sw480dp/bool.xml new file mode 100644 index 000000000000..8e66f10e898c --- /dev/null +++ b/app/src/main/res/values-sw480dp/bool.xml @@ -0,0 +1,4 @@ + + + true + diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index ecb0d4a1595f..a83c663e6cf3 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -20,14 +20,19 @@ - @string/link_share_view_only - @string/link_share_allow_upload_and_editing - @string/link_share_file_drop + @string/share_permission_read_only + @string/share_permission_can_edit + @string/share_permission_file_drop + + + + @string/share_permission_read_only + @string/share_permission_can_edit - @string/link_share_view_only - @string/link_share_editing + @string/share_permission_read_only + @string/share_permission_can_edit @string/sub_folder_rule_month diff --git a/app/src/main/res/values/bool.xml b/app/src/main/res/values/bool.xml new file mode 100644 index 000000000000..c2dcd8baf0ea --- /dev/null +++ b/app/src/main/res/values/bool.xml @@ -0,0 +1,4 @@ + + + false + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 58fcdecf1fc2..951b6765b8a4 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -82,4 +82,95 @@ @android:color/white #666666 #A5A5A5 + + + #191919 + @color/primary + #191919 + #191919 + @color/grey_30 + @android:color/white + #FFFFFF + @color/grey_0 + #CCCCCC + #77c4ff + #B3FFFFFF + @color/grey_10 + + + #101010 + #F2F2F2 + #E5E5E5 + #B2B2B2 + #666666 + #4C4C4C + #333333 + + + @color/design_snackbar_background_color + @color/white + + + #FFFFFF + #191919 + + + @color/grey_0 + #191919 + @color/primary + #191919 + @color/primary + @color/grey_30 + @color/white + #191919 + + + #FFFFFF + #191919 + #191919 + + + #FFFFFF + #191919 + #FFFFFF + + + @color/primary + #F399C7 + @color/grey_0 + @color/grey_0 + #FFFFFF + @color/grey_30 + @color/grey_0 + @color/grey_0 + + + @color/primary + @color/grey_30 + @color/grey_30 + #CCCCCC + + + #191919 + @color/grey_30 + #191919 + #191919 + #191919 + #191919 + @color/grey_30 + #191919 + #000000 + #191919 + #F6E5EB + #C16F81 + #0D39DF + #0099ff + + + @color/grey_0 + #191919 + @color/grey_0 + @color/grey_30 + #77b6bb + #5077b6bb diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml new file mode 100644 index 000000000000..cc9e25255a10 --- /dev/null +++ b/app/src/main/res/values/dimens.xml @@ -0,0 +1,31 @@ + + + 4dp + 16dp + 24dp + 6dp + 18sp + 15sp + 15dp + 56dp + 86dp + 80dp + 11sp + 30dp + 55dp + 258dp + 17sp + 20dp + 160dp + 50dp + 150dp + 55dp + 48dp + 48dp + 24dp + 26dp + 20sp + 145dp + 1dp + 13sp + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 38f62342eec6..ccf3de7f9aa0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -369,6 +369,7 @@ Subfolder options + Unshare Unable to share. Please check whether the file exists. An error occurred while trying to share this file or folder. Unable to unshare. Please check whether the file exists. @@ -506,11 +507,12 @@ Share link Send link Password-protected + Unshare Set password Share with… Unset - Name, Federated Cloud ID or email address … + Contact name or email Secure share … %1$s (group) @@ -748,6 +750,9 @@ Name Password Add to %1$s + Allow creating + Allow deleting + Allow editing Upload files Upload from camera Scan document from camera @@ -814,6 +819,7 @@ No App available to handle mail address No App available to handle maps Hide download + Password protect (%1$s) Unread comments exist This icon indicates availability of live photo Failed to load document! @@ -862,7 +868,8 @@ Downloads Avatar from shared user Shared with you by %1$s - Resharing is not allowed + Resharing is not allowed. + Resharing is allowed. Retrieving file… Associated account not found! Logged in as %1$s @@ -950,10 +957,11 @@ Add another link Add new public share link New name - Share link (%1$s) + Link \'%1$s\' Share link + Link to folder + Link to file Allow resharing - View only Editing Allow upload and editing File drop (upload only) @@ -989,6 +997,36 @@ This might be due to a backup restore on another device. Falling back to default. Please check settings to adjust data storage folder. Close Login with %1$s to %2$s + + Read only + You must select expiration date. + Please enter note. + Send email + Open in… + Advanced permissions + You can create links or send shares by mail. If you invite MagentaCLOUD users, you have more opportunities for collaboration. + Your Message + Personal share by mail + Add link + Your Shares + Link Label + Your custom link label + With File drop, only uploading is allowed. Only you can see files and folders that have been uploaded. + Password Protection + Expiration Date + No shares created yet. + Download Limit + Download limit cannot be empty. + Enter download limit + Downloads:\u0020 + Download limit should be greater than 0. + You are sharing with a MagentaCLOUD user and you can allow her or him to reshare. + Password protection has been enabled. You have to provide the password to + the recipient.\n\nIf you send a share via MagentaCLOUD and paste the password in this message, it will be + transmitted unencrypted in plaintext. + Received + Shared + Login via direct link failed! Update Android System WebView Please update the Android System WebView app for a login @@ -999,12 +1037,12 @@ Create Please select one template Please choose a template and enter a file name. - View only + Read only Can edit - File drop + Filedrop only Secure file drop - Share Permissions - Advanced Settings + Permissions + Advanced Permissions Next Send Share Please select at least one permission to share. diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index c1ef3a3c3f36..e77b4cb9380a 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -204,6 +204,16 @@ bold + + +