From aae5b29822d63bd7723e857bfcd8687692e455fe Mon Sep 17 00:00:00 2001 From: A117870935 Date: Mon, 15 May 2023 01:05:37 +0530 Subject: [PATCH] NMC-2051: Customized bottom sheet. NMC-1929: Collabora related customization with test cases. --- .../android/OCFileListBottomSheetDialogIT.kt | 117 +++++ .../ui/fileactions/ClientIntegration.kt | 4 +- .../ui/fileactions/FileActionsBottomSheet.kt | 18 +- .../TrashbinFileActionsBottomSheet.kt | 16 +- .../java/com/nextcloud/utils/BuildHelper.kt | 3 +- .../android/ui/dialog/SendFilesDialog.kt | 2 - .../GalleryFragmentBottomSheetDialog.kt | 17 - .../fragment/OCFileListBottomSheetDialog.kt | 376 +++++--------- .../main/res/drawable/ic_encrypted_folder.xml | 12 + .../main/res/drawable/ic_file_collection.xml | 12 + app/src/main/res/drawable/ic_new_txt_doc.xml | 34 ++ app/src/main/res/drawable/ic_upload_file.xml | 12 + .../res/layout/file_actions_bottom_sheet.xml | 2 + .../layout/file_actions_bottom_sheet_item.xml | 4 +- ...ile_list_actions_bottom_sheet_fragment.xml | 470 +++++++++--------- .../layout/fragment_gallery_bottom_sheet.xml | 14 +- ...ring_permissions_bottom_sheet_fragment.xml | 1 + .../main/res/layout/send_files_fragment.xml | 1 + .../main/res/layout/send_share_fragment.xml | 1 + app/src/main/res/values-de/strings.xml | 3 +- app/src/main/res/values-night/colors.xml | 65 +++ app/src/main/res/values/colors.xml | 90 ++++ app/src/main/res/values/dims.xml | 4 +- app/src/main/res/values/strings.xml | 6 +- app/src/main/res/values/styles.xml | 2 +- 25 files changed, 739 insertions(+), 547 deletions(-) create mode 100644 app/src/androidTest/java/com/nmc/android/OCFileListBottomSheetDialogIT.kt create mode 100644 app/src/main/res/drawable/ic_encrypted_folder.xml create mode 100644 app/src/main/res/drawable/ic_file_collection.xml create mode 100644 app/src/main/res/drawable/ic_new_txt_doc.xml create mode 100644 app/src/main/res/drawable/ic_upload_file.xml diff --git a/app/src/androidTest/java/com/nmc/android/OCFileListBottomSheetDialogIT.kt b/app/src/androidTest/java/com/nmc/android/OCFileListBottomSheetDialogIT.kt new file mode 100644 index 000000000000..f50401f4ecf4 --- /dev/null +++ b/app/src/androidTest/java/com/nmc/android/OCFileListBottomSheetDialogIT.kt @@ -0,0 +1,117 @@ +package com.nmc.android + +import android.os.Looper +import androidx.activity.result.contract.ActivityResultContract +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.intent.rule.IntentsTestRule +import androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed +import androidx.test.espresso.matcher.ViewMatchers.withText +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.google.gson.Gson +import com.nextcloud.client.device.DeviceInfo +import com.nextcloud.client.documentscan.AppScanOptionalFeature +import com.nextcloud.utils.EditorUtils +import com.owncloud.android.AbstractIT +import com.owncloud.android.datamodel.ArbitraryDataProvider +import com.owncloud.android.datamodel.ArbitraryDataProviderImpl +import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.lib.common.Creator +import com.owncloud.android.lib.common.DirectEditing +import com.owncloud.android.lib.resources.status.CapabilityBooleanType +import com.owncloud.android.ui.activity.FileDisplayActivity +import com.owncloud.android.ui.fragment.OCFileListBottomSheetActions +import com.owncloud.android.ui.fragment.OCFileListBottomSheetDialog +import com.owncloud.android.utils.MimeTypeUtil +import com.owncloud.android.utils.theme.CapabilityUtils +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.mockito.Mock +import org.mockito.MockitoAnnotations + +class OCFileListBottomSheetDialogIT : AbstractIT() { + + @Mock + private lateinit var actions: OCFileListBottomSheetActions + + @get:Rule + val activityRule = IntentsTestRule(FileDisplayActivity::class.java, true, true) + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + } + + @Test + fun validateCreateTextDocumentMenuOption() { + if (Looper.myLooper() == null) { + Looper.prepare() + } + + val info = DeviceInfo() + val ocFile = OCFile("/test.md") + + // add direct editing info + val creatorMap = mutableMapOf() + creatorMap["1"] = Creator( + "1", + "md", + "markdown file", + ".md", + MimeTypeUtil.MIMETYPE_TEXT_MARKDOWN, + false + ) + + val directEditing = DirectEditing( + creators = creatorMap + ) + + val json = Gson().toJson(directEditing) + ArbitraryDataProviderImpl(targetContext).storeOrUpdateKeyValue( + user.accountName, + ArbitraryDataProvider.DIRECT_EDITING, + json + ) + + val optionalCapability = activityRule.activity.capabilities + + if (optionalCapability.isPresent) { + val capability = optionalCapability.get() + capability.richDocuments = CapabilityBooleanType.TRUE + capability.richDocumentsDirectEditing = CapabilityBooleanType.TRUE + capability.richDocumentsTemplatesAvailable = CapabilityBooleanType.TRUE + capability.accountName = user.accountName + CapabilityUtils.updateCapability(capability) + } + + val appScanOptionalFeature: AppScanOptionalFeature = object : AppScanOptionalFeature() { + override fun getScanContract(): ActivityResultContract { + throw UnsupportedOperationException("Document scan is not available") + } + } + + val editorUtils = EditorUtils(ArbitraryDataProviderImpl(targetContext)) + val sut = OCFileListBottomSheetDialog( + activityRule.activity, + actions, + info, + user, + ocFile, + activityRule.activity.themeUtils, + activityRule.activity.viewThemeUtils, + editorUtils, + appScanOptionalFeature + ) + + activityRule.activity.runOnUiThread { sut.show() } + + waitForIdleSync() + + sut.behavior.state = BottomSheetBehavior.STATE_EXPANDED + + shortSleep() + + onView(withText("Create text document")).check(matches(isCompletelyDisplayed())) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/ui/fileactions/ClientIntegration.kt b/app/src/main/java/com/nextcloud/ui/fileactions/ClientIntegration.kt index d7dc8668ad34..24a061e64289 100644 --- a/app/src/main/java/com/nextcloud/ui/fileactions/ClientIntegration.kt +++ b/app/src/main/java/com/nextcloud/ui/fileactions/ClientIntegration.kt @@ -96,8 +96,8 @@ class ClientIntegration( withContext(Dispatchers.Main) { tintableDrawable?.let { - val tinted = viewThemeUtils.platform.tintDrawable(context, it) - icon.setImageDrawable(tinted) + // NMC-6005: no tinting required + icon.setImageDrawable(it) } } } diff --git a/app/src/main/java/com/nextcloud/ui/fileactions/FileActionsBottomSheet.kt b/app/src/main/java/com/nextcloud/ui/fileactions/FileActionsBottomSheet.kt index b689a3c404b8..d4843b9f8c3e 100644 --- a/app/src/main/java/com/nextcloud/ui/fileactions/FileActionsBottomSheet.kt +++ b/app/src/main/java/com/nextcloud/ui/fileactions/FileActionsBottomSheet.kt @@ -17,10 +17,10 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.annotation.IdRes -import androidx.appcompat.content.res.AppCompatResources import androidx.core.os.BundleCompat import androidx.core.os.bundleOf import androidx.core.view.isNotEmpty +import androidx.core.content.res.ResourcesCompat import androidx.core.view.isVisible import androidx.fragment.app.FragmentManager import androidx.fragment.app.setFragmentResult @@ -108,8 +108,6 @@ class FileActionsBottomSheet : bottomSheetDialog.behavior.state = BottomSheetBehavior.STATE_EXPANDED bottomSheetDialog.behavior.skipCollapsed = true - viewThemeUtils.platform.colorViewBackground(binding.bottomSheet, ColorRole.SURFACE) - clientIntegration = ClientIntegration(this, currentUserProvider.user, requireContext()) return binding.root @@ -156,7 +154,11 @@ class FileActionsBottomSheet : private fun setMultipleFilesThumbnail() { context?.let { - val drawable = viewThemeUtils.platform.tintDrawable(it, R.drawable.file_multiple, ColorRole.PRIMARY) + // NMC Customization + val drawable = viewThemeUtils.platform.colorDrawable( + ResourcesCompat.getDrawable(it.resources, R.drawable.file_multiple, null)!!, + it.resources.getColor(R.color.primary, null) + ) binding.thumbnailLayout.thumbnail.setImageDrawable(drawable) } } @@ -317,12 +319,8 @@ class FileActionsBottomSheet : } text.setText(action.title) if (action.icon != null) { - val drawable = - viewThemeUtils.platform.tintDrawable( - requireContext(), - AppCompatResources.getDrawable(requireContext(), action.icon)!! - ) - icon.setImageDrawable(drawable) + // NMC customization + icon.setImageResource(action.icon) } } return itemBinding.root diff --git a/app/src/main/java/com/nextcloud/ui/trashbinFileActions/TrashbinFileActionsBottomSheet.kt b/app/src/main/java/com/nextcloud/ui/trashbinFileActions/TrashbinFileActionsBottomSheet.kt index 6b51f44360b9..6d37f264f6c9 100644 --- a/app/src/main/java/com/nextcloud/ui/trashbinFileActions/TrashbinFileActionsBottomSheet.kt +++ b/app/src/main/java/com/nextcloud/ui/trashbinFileActions/TrashbinFileActionsBottomSheet.kt @@ -11,7 +11,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.annotation.IdRes -import androidx.appcompat.content.res.AppCompatResources +import androidx.core.content.res.ResourcesCompat import androidx.core.os.bundleOf import androidx.core.view.isEmpty import androidx.core.view.isVisible @@ -129,7 +129,11 @@ class TrashbinFileActionsBottomSheet : private fun setMultipleFilesThumbnail() { context?.let { - val drawable = viewThemeUtils.platform.tintDrawable(it, R.drawable.file_multiple, ColorRole.PRIMARY) + // NMC Customization + val drawable = viewThemeUtils.platform.colorDrawable( + ResourcesCompat.getDrawable(it.resources, R.drawable.file_multiple, null)!!, + it.resources.getColor(R.color.primary, null) + ) binding.thumbnailLayout.thumbnail.setImageDrawable(drawable) } } @@ -191,12 +195,8 @@ class TrashbinFileActionsBottomSheet : } text.setText(action.title) if (action.icon != null) { - val drawable = - viewThemeUtils.platform.tintDrawable( - requireContext(), - AppCompatResources.getDrawable(requireContext(), action.icon)!! - ) - icon.setImageDrawable(drawable) + // NMC customization + icon.setImageResource(action.icon) } } return itemBinding.root diff --git a/app/src/main/java/com/nextcloud/utils/BuildHelper.kt b/app/src/main/java/com/nextcloud/utils/BuildHelper.kt index 2d17528e83c3..1dd6f2473562 100644 --- a/app/src/main/java/com/nextcloud/utils/BuildHelper.kt +++ b/app/src/main/java/com/nextcloud/utils/BuildHelper.kt @@ -9,7 +9,8 @@ package com.nextcloud.utils import com.owncloud.android.BuildConfig object BuildHelper { - fun isFlavourGPlay(): Boolean = BuildConfig.FLAVOR == "gplay" + // NMC Customization to have it false always + fun isFlavourGPlay(): Boolean = false fun isHuaweiFlavor(): Boolean = BuildConfig.FLAVOR == "huawei" } diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/SendFilesDialog.kt b/app/src/main/java/com/owncloud/android/ui/dialog/SendFilesDialog.kt index 8dcfad3d5f9a..d4e56cc2b8db 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/SendFilesDialog.kt +++ b/app/src/main/java/com/owncloud/android/ui/dialog/SendFilesDialog.kt @@ -18,7 +18,6 @@ import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.GridLayoutManager import com.google.android.material.bottomsheet.BottomSheetDialogFragment -import com.nextcloud.android.common.ui.theme.utils.ColorRole import com.nextcloud.client.di.Injectable import com.nextcloud.client.utils.IntentUtil.createSendIntent import com.owncloud.android.R @@ -61,7 +60,6 @@ class SendFilesDialog : binding = SendFilesFragmentBinding.inflate(inflater, container, false) setupSendButtonRecyclerView() - viewThemeUtils?.platform?.colorViewBackground(binding.bottomSheet, ColorRole.SURFACE) return binding.root } diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragmentBottomSheetDialog.kt b/app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragmentBottomSheetDialog.kt index 5d5a4b2576c9..a9a101861f28 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragmentBottomSheetDialog.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragmentBottomSheetDialog.kt @@ -52,23 +52,6 @@ class GalleryFragmentBottomSheetDialog : } private fun setupLayout() { - viewThemeUtils.platform.colorViewBackground(binding.bottomSheet, ColorRole.SURFACE) - - listOf( - binding.tickMarkShowImages, - binding.tickMarkShowVideos - ).forEach { - viewThemeUtils.platform.colorImageView(it, ColorRole.PRIMARY) - } - - listOf( - binding.btnSelectMediaFolder, - binding.btnHideVideos, - binding.btnHideImages - ).forEach { - viewThemeUtils.material.colorMaterialButtonText(it) - } - when (currentMediaState) { MediaState.MEDIA_STATE_PHOTOS_ONLY -> { binding.tickMarkShowImages.visibility = View.VISIBLE diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListBottomSheetDialog.kt b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListBottomSheetDialog.kt index 1d049bd71c45..507af0bf6b82 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListBottomSheetDialog.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListBottomSheetDialog.kt @@ -8,22 +8,17 @@ */ package com.owncloud.android.ui.fragment -import android.graphics.drawable.Drawable +import android.content.res.ColorStateList import android.os.Build import android.os.Bundle -import android.util.TypedValue import android.view.ContextThemeWrapper import android.view.Gravity import android.view.View import android.widget.LinearLayout -import androidx.appcompat.content.res.AppCompatResources import androidx.core.content.ContextCompat -import androidx.core.view.isEmpty -import androidx.core.view.isNotEmpty import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.button.MaterialButton import com.google.gson.Gson -import com.nextcloud.android.common.ui.theme.utils.ColorRole import com.nextcloud.client.account.User import com.nextcloud.client.device.DeviceInfo import com.nextcloud.client.di.Injectable @@ -59,47 +54,6 @@ class OCFileListBottomSheetDialog( ) : BottomSheetDialog(fileActivity), Injectable { - companion object { - // Number of items to show in document creators overview - private const val CREATORS_OVERVIEW_ITEMS = 3 - private val DIRECT_EDITING_CREATORS_OFFICE_IDS = arrayOf("document", "spreadsheet", "presentation") - } - - private enum class CreatorType { - Office, - Other - } - - private val templateActions = listOf( - CreatorAction( - text = context.getString(R.string.create_document), - icon = AppCompatResources.getDrawable(context, R.drawable.file_doc), - action = { - actions.newDocument() - dismiss() - }, - type = CreatorType.Office - ), - CreatorAction( - text = context.getString(R.string.create_spreadsheet), - icon = AppCompatResources.getDrawable(context, R.drawable.file_xls), - action = { - actions.newSpreadsheet() - dismiss() - }, - type = CreatorType.Office - ), - CreatorAction( - text = context.getString(R.string.create_presentation), - icon = AppCompatResources.getDrawable(context, R.drawable.file_ppt), - action = { - actions.newPresentation() - dismiss() - }, - type = CreatorType.Office - ) - ) - private lateinit var binding: FileListActionsBottomSheetFragmentBinding override fun onCreate(savedInstanceState: Bundle?) { @@ -107,18 +61,18 @@ class OCFileListBottomSheetDialog( binding = FileListActionsBottomSheetFragmentBinding.inflate(layoutInflater) setContentView(binding.getRoot()) - applyBranding() + // NMC Customization + reorderUploadFromOtherAppsView() + checkTemplateVisibility() initCreatorContainer() if (!deviceInfo.hasCamera(context)) { binding.menuDirectCameraUpload.visibility = View.GONE } - createRichWorkspace() setupClickListener() filterActionsForOfflineOperations() - checkCreateEncryptedFolderVisibility() if (MainApp.isClientBranded() && isFlavourGPlay()) { // this way we can have branded clients with that permission @@ -135,46 +89,41 @@ class OCFileListBottomSheetDialog( } } - private fun checkCreateEncryptedFolderVisibility() { - fileActivity.capabilities.ifPresent { capabilities -> - binding.menuEncryptedMkdir.setVisibleIf(!file.isEncrypted && capabilities.endToEndEncryption.isTrue) + private fun checkTemplateVisibility() { + val optionalCapability = fileActivity.capabilities + if (optionalCapability.isEmpty) { + return } - } - private fun applyBranding() { - viewThemeUtils.material.run { - binding.run { - colorMaterialButtonContent(menuUploadFiles, ColorRole.PRIMARY) - colorMaterialButtonContent(menuUploadFromApp, ColorRole.PRIMARY) - colorMaterialButtonContent(menuDirectCameraUpload, ColorRole.PRIMARY) - colorMaterialButtonContent(menuScanDocUpload, ColorRole.PRIMARY) - colorMaterialButtonContent(menuMkdir, ColorRole.PRIMARY) - colorMaterialButtonContent(menuEncryptedMkdir, ColorRole.PRIMARY) - colorMaterialButtonContent(menuCreateRichWorkspace, ColorRole.PRIMARY) - colorMaterialButtonContent(menuMoreDocuments, ColorRole.PRIMARY) - colorMaterialButtonContent(menuBack, ColorRole.PRIMARY) - - viewThemeUtils.platform.tintDrawable( - context, - binding.menuMoreDocumentsExpand.drawable, - ColorRole.PRIMARY - ) + if (optionalCapability.get().endToEndEncryption.isTrue) { + // NMC-4348 fix + // show encrypted folder option for root and e2ee folder + binding.menuEncryptedMkdir.visibility = + if (file.isEncrypted || file.isRootDirectory) + View.VISIBLE + else + View.GONE + // for e2ee folder don't show normal folder option + if (file.isEncrypted) { + binding.menuMkdir.visibility = View.GONE } + } else { + binding.menuEncryptedMkdir.visibility = View.GONE } - viewThemeUtils.platform.colorViewBackground(binding.bottomSheet, ColorRole.SURFACE) + if (file.isEncrypted) { + return + } - val textColor = ContextCompat.getColor(context, R.color.text_color) + val capability = optionalCapability.get() + if (!capability.isTemplateAvailable()) { + return + } binding.run { - menuUploadFiles.setTextColor(textColor) - menuUploadFromApp.setTextColor(textColor) - menuDirectCameraUpload.setTextColor(textColor) - menuScanDocUpload.setTextColor(textColor) - menuMkdir.setTextColor(textColor) - menuEncryptedMkdir.setTextColor(textColor) - menuCreateRichWorkspace.setTextColor(textColor) - menuMoreDocuments.setTextColor(textColor) + menuNewDocument.visibility = View.VISIBLE + menuNewSpreadsheet.visibility = View.VISIBLE + menuNewPresentation.visibility = View.VISIBLE } } @@ -184,179 +133,84 @@ class OCFileListBottomSheetDialog( return } - // Create a list of supported creators, in the order to be shown (direct editing, then collabora) - val creatorsActions = ArrayList() - - // Check direct editing - val directEditing = creatorsActionsFromDirectEditing() - - // Check capabilities (e.g. collabora) - val capabilities = creatorsActionsFromCollabora() - - // First Direct Editing Office (if any) - creatorsActions.addAll(directEditing.filter { it.type == CreatorType.Office }) - // Then Capabilities Office (if any). Capabilities entry are all type office, no need to filter. - creatorsActions.addAll(capabilities) - // Then all the rest - creatorsActions.addAll(directEditing.filter { it.type != CreatorType.Office }) - - displayCreatorsActions(creatorsActions) - } + val json = ArbitraryDataProviderImpl(context) + .getValue(user, ArbitraryDataProvider.DIRECT_EDITING) - private fun displayCreatorsActions(creatorsActions: List) { - if (creatorsActions.isEmpty()) { - // If no creators at all, hide whole container (comprising separator) - binding.creatorsOverviewContainer.visibility = View.GONE + if (json.isEmpty() || file.isEncrypted) { return } - binding.creatorsOverviewContainer.visibility = View.VISIBLE - - with(binding) { - creatorsOverview.removeAllViews() - creators.removeAllViews() - - creatorsActions.forEachIndexed { index, action -> - if (creatorsActions.size == 1) { - // Single element is shown as row in the overview - creatorsOverview.addView(buildCreatorButton(action, false)) - } else if (index < CREATORS_OVERVIEW_ITEMS) { - // First x elements shown as boxes in the overview - creatorsOverview.addView(buildCreatorButton(action, true)) - } else { - // Other elements shown as rows in the overflow view - creators.addView(buildCreatorButton(action, false)) - } - } - - if (creatorsOverview.isNotEmpty()) { - creatorsOverview.visibility = View.VISIBLE - } - - menuMoreDocumentsContainer.visibility = if (creators.isEmpty()) View.GONE else View.VISIBLE + val directEditing = Gson().fromJson(json, DirectEditing::class.java) + if (directEditing.creators.isEmpty()) { + return } - } - private fun creatorsActionsFromDirectEditing(): List { - val creatorsActions = ArrayList() - ArbitraryDataProviderImpl(context) - .getValue(user, ArbitraryDataProvider.DIRECT_EDITING) - .takeIf(String::isNotEmpty) - ?.let { Gson().fromJson(it, DirectEditing::class.java) } - ?.creators - ?.values - ?.forEach { creator -> - val buttonText = creator.name - creatorsActions.add( - CreatorAction( - text = buttonText.replaceFirstChar(Char::titlecase), - icon = MimeTypeUtil.getFileTypeIcon( - creator.mimetype, - creator.extension, - context, - viewThemeUtils - ), - action = { - actions.showTemplate(creator, buttonText) - dismiss() - }, - type = if (creator.id in DIRECT_EDITING_CREATORS_OFFICE_IDS) { - CreatorType.Office - } else { - CreatorType.Other - } + binding.creatorsContainer.visibility = View.VISIBLE + binding.creators.removeAllViews() - ) - ) - } - return creatorsActions - } - - private fun creatorsActionsFromCollabora(): List { - val creatorsActions = ArrayList() - - fileActivity.capabilities - .filter { it.isTemplateAvailable() } - .ifPresent { creatorsActions.addAll(templateActions) } - - return creatorsActions - } - - /** - * @param creatorAction Action for which the button is created - * @param showAsBox If true, creates a squarish view with weight to be displayed horizontally, otherwise a row - */ - private fun buildCreatorButton(creatorAction: CreatorAction, showAsBox: Boolean): MaterialButton { - val itemHeight = context.resources.getDimensionPixelSize( - if (showAsBox) R.dimen.bottom_sheet_horizontal_item_height else R.dimen.bottom_sheet_item_height - ) + val itemHeight = context.resources.getDimensionPixelSize(R.dimen.bottom_sheet_item_height) val standardPadding = context.resources.getDimensionPixelSize(R.dimen.standard_padding) val iconSize = context.resources.getDimensionPixelSize(R.dimen.iconized_single_line_item_icon_size) - val creatorButton = MaterialButton( - ContextThemeWrapper( - context, - R.style.ThemeOverlay_App_Button_BottomSheetItem - ), - null, - com.google.android.material.R.attr.materialButtonStyle - ).apply { - id = View.generateViewId() - layoutParams = LinearLayout.LayoutParams( - if (showAsBox) 0 else LinearLayout.LayoutParams.MATCH_PARENT, - itemHeight, - if (showAsBox) 1f else 0f - ) - - gravity = if (showAsBox) Gravity.CENTER else Gravity.START or Gravity.CENTER_VERTICAL - setPaddingRelative(standardPadding, 0, standardPadding, 0) - - text = creatorAction.text - setTextColor(ContextCompat.getColor(context, R.color.text_color)) - setTextSize(TypedValue.COMPLEX_UNIT_PX, context.resources.getDimension(R.dimen.bottom_sheet_text_size)) - isAllCaps = false - - icon = creatorAction.icon - this.iconSize = iconSize - this.iconPadding = if (showAsBox) 0 else standardPadding - if (showAsBox) { - this.setPadding( - paddingLeft, - paddingTop + standardPadding, - paddingRight, - paddingBottom + standardPadding + for (creator in directEditing.creators.values) { + val creatorButton = MaterialButton( + ContextThemeWrapper( + context, + R.style.ThemeOverlay_App_Button_BottomSheetItem + ), + null, + com.google.android.material.R.attr.materialButtonStyle + ).apply { + id = View.generateViewId() + layoutParams = LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + itemHeight ) - } - iconGravity = if (showAsBox) MaterialButton.ICON_GRAVITY_TOP else MaterialButton.ICON_GRAVITY_START - iconTint = null - - cornerRadius = - if (showAsBox) { - context.resources.getDimensionPixelSize(R.dimen.button_corner_radius) - } else { - cornerRadius - } - setOnClickListener { - creatorAction.action() - } - } - return creatorButton - } + gravity = Gravity.START or Gravity.CENTER_VERTICAL + setPaddingRelative(standardPadding, 0, standardPadding, 0) - private fun createRichWorkspace() { - if (editorUtils.isEditorAvailable(user, MimeTypeUtil.MIMETYPE_TEXT_MARKDOWN) && !file.isEncrypted) { - // richWorkspace - // == "": no info set -> show button - // == null: disabled on server side -> hide button - // != "": info set -> hide button - if (file.richWorkspace == null || "" != file.richWorkspace) { - binding.menuCreateRichWorkspace.visibility = View.GONE - } else { - binding.menuCreateRichWorkspace.visibility = View.VISIBLE + // for NMC we have different text and icon for Markdown(.md) menu + val buttonText = if (creator.mimetype == MimeTypeUtil.MIMETYPE_TEXT_MARKDOWN) + fileActivity.getString(R.string.create_text_document) + else String.format( + fileActivity.getString(R.string.editor_placeholder), + fileActivity.getString(R.string.create_new), + creator.name + ) + text = buttonText + setTextColor(ContextCompat.getColor(context, R.color.bottom_sheet_txt_color)) + textSize = 16f + isAllCaps = false + + // for NMC we have different text and icon for Markdown(.md) menu + icon = if (creator.mimetype == MimeTypeUtil.MIMETYPE_TEXT_MARKDOWN) + ContextCompat.getDrawable( + context, + R.drawable.ic_new_txt_doc + ) + else MimeTypeUtil.getFileTypeIcon( + creator.mimetype, + creator.extension, + context, + viewThemeUtils + ) + this.iconSize = iconSize + this.iconPadding = standardPadding + iconGravity = MaterialButton.ICON_GRAVITY_START + // for NMC do not remove icon tinting for Markdown(.md) menu + iconTint = if (creator.mimetype == MimeTypeUtil.MIMETYPE_TEXT_MARKDOWN) + ColorStateList.valueOf(ContextCompat.getColor(context, R.color.bottom_sheet_icon_color)) + else + null + + setOnClickListener { + actions.showTemplate(creator, buttonText) + dismiss() + } } - } else { - binding.menuCreateRichWorkspace.visibility = View.GONE + + binding.creators.addView(creatorButton) } } @@ -399,7 +253,6 @@ class OCFileListBottomSheetDialog( } } else { menuScanDocUpload.visibility = View.GONE - menuScanDocUploadDivider.visibility = View.GONE } menuUploadFiles.setOnClickListener { @@ -407,17 +260,34 @@ class OCFileListBottomSheetDialog( dismiss() } - menuMoreDocuments.setOnClickListener { - bottomSheetViewSwitcher.showNext() + menuNewDocument.setOnClickListener { + actions.newDocument() + dismiss() + } + + menuNewSpreadsheet.setOnClickListener { + actions.newSpreadsheet() + dismiss() } - menuBack.setOnClickListener { - // Invert animation - bottomSheetViewSwitcher.showPrevious() + menuNewPresentation.setOnClickListener { + actions.newPresentation() + dismiss() } } } + private fun reorderUploadFromOtherAppsView() { + // move the upload from other app option + // below Create new folder or Create new e2ee folder + // NMC-3095 requirement + binding.actionLinear.removeView(binding.menuUploadFromApp) + binding.actionLinear.addView( + binding.menuUploadFromApp, + binding.actionLinear.indexOfChild(binding.menuEncryptedMkdir) + 1 + ) + } + private fun filterActionsForOfflineOperations() { fileActivity.connectivityService.isNetworkAndServerAvailable { result: Boolean? -> if (file.isRootDirectory) { @@ -430,16 +300,12 @@ class OCFileListBottomSheetDialog( menuUploadFromApp.visibility = View.GONE menuDirectCameraUpload.visibility = View.GONE menuScanDocUpload.visibility = View.GONE - creatorsOverviewContainer.visibility = View.GONE + menuNewDocument.visibility = View.GONE + menuNewSpreadsheet.visibility = View.GONE + menuNewPresentation.visibility = View.GONE + creatorsContainer.visibility = View.GONE } } } } - - private data class CreatorAction( - val text: String, - val icon: Drawable?, - val action: () -> Unit, - val type: CreatorType - ) } diff --git a/app/src/main/res/drawable/ic_encrypted_folder.xml b/app/src/main/res/drawable/ic_encrypted_folder.xml new file mode 100644 index 000000000000..351565589c6a --- /dev/null +++ b/app/src/main/res/drawable/ic_encrypted_folder.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/ic_file_collection.xml b/app/src/main/res/drawable/ic_file_collection.xml new file mode 100644 index 000000000000..8825d1f461cf --- /dev/null +++ b/app/src/main/res/drawable/ic_file_collection.xml @@ -0,0 +1,12 @@ + + + diff --git a/app/src/main/res/drawable/ic_new_txt_doc.xml b/app/src/main/res/drawable/ic_new_txt_doc.xml new file mode 100644 index 000000000000..2b0783479d48 --- /dev/null +++ b/app/src/main/res/drawable/ic_new_txt_doc.xml @@ -0,0 +1,34 @@ + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_upload_file.xml b/app/src/main/res/drawable/ic_upload_file.xml new file mode 100644 index 000000000000..3c16034fc8f9 --- /dev/null +++ b/app/src/main/res/drawable/ic_upload_file.xml @@ -0,0 +1,12 @@ + + + diff --git a/app/src/main/res/layout/file_actions_bottom_sheet.xml b/app/src/main/res/layout/file_actions_bottom_sheet.xml index cf2c493069b9..b4340d3e822f 100644 --- a/app/src/main/res/layout/file_actions_bottom_sheet.xml +++ b/app/src/main/res/layout/file_actions_bottom_sheet.xml @@ -13,6 +13,7 @@ style="@style/Widget.Material3.BottomSheet" android:layout_width="match_parent" android:layout_height="wrap_content" + android:background="@color/bottom_sheet_bg_color" android:layout_gravity="bottom" app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"> @@ -68,6 +69,7 @@ android:ellipsize="start" android:lines="1" android:textAppearance="@style/TextAppearance.Material3.HeadlineSmall" + android:textColor="@color/bottom_sheet_txt_color" tools:text="@string/placeholder_extension" /> diff --git a/app/src/main/res/layout/file_actions_bottom_sheet_item.xml b/app/src/main/res/layout/file_actions_bottom_sheet_item.xml index b2a7a30738ee..b3fbbebfe49f 100644 --- a/app/src/main/res/layout/file_actions_bottom_sheet_item.xml +++ b/app/src/main/res/layout/file_actions_bottom_sheet_item.xml @@ -7,6 +7,7 @@ ~ SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only --> diff --git a/app/src/main/res/layout/file_list_actions_bottom_sheet_fragment.xml b/app/src/main/res/layout/file_list_actions_bottom_sheet_fragment.xml index cac08a315360..cc435392ecdb 100644 --- a/app/src/main/res/layout/file_list_actions_bottom_sheet_fragment.xml +++ b/app/src/main/res/layout/file_list_actions_bottom_sheet_fragment.xml @@ -12,6 +12,7 @@ style="@style/Widget.Material3.BottomSheet" android:layout_width="match_parent" android:layout_height="match_parent" + android:background="@color/bottom_sheet_bg_color" android:orientation="vertical" android:paddingBottom="@dimen/standard_half_padding" app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"> @@ -20,263 +21,250 @@ android:layout_width="match_parent" android:layout_height="wrap_content" /> - + android:layout_height="wrap_content" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/title"> - - - + android:layout_height="match_parent" + android:orientation="vertical"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + android:layout_height="@dimen/bottom_sheet_item_height" + android:contentDescription="@string/upload_files" + android:gravity="start|center_vertical" + android:paddingStart="@dimen/standard_padding" + android:paddingEnd="@dimen/standard_padding" + android:text="@string/upload_files" + android:textColor="@color/bottom_sheet_txt_color" + android:textSize="@dimen/bottom_sheet_text_size" + app:icon="@drawable/ic_upload_file" + app:iconGravity="start" + app:iconPadding="@dimen/standard_padding" + app:iconSize="@dimen/iconized_single_line_item_icon_size" + app:iconTint="@color/bottom_sheet_icon_color" /> + + + + + + + + + + + + + + + + + + + android:orientation="vertical" + android:visibility="gone" + tools:visibility="visible"> - - - - - - - + + + + + + + diff --git a/app/src/main/res/layout/fragment_gallery_bottom_sheet.xml b/app/src/main/res/layout/fragment_gallery_bottom_sheet.xml index 9b20ba47c633..83c6c9832c3e 100644 --- a/app/src/main/res/layout/fragment_gallery_bottom_sheet.xml +++ b/app/src/main/res/layout/fragment_gallery_bottom_sheet.xml @@ -15,10 +15,10 @@ android:layout_height="match_parent"> + app:iconTint="@color/bottom_sheet_icon_color" + app:iconPadding="@dimen/standard_half_padding"/> + android:textColor="@color/bottom_sheet_txt_color" + app:icon="@drawable/ic_video_camera" + app:iconTint="@color/bottom_sheet_icon_color"/> + android:textColor="@color/bottom_sheet_txt_color" + app:icon="@drawable/nav_photos" + app:iconTint="@color/bottom_sheet_icon_color"/> diff --git a/app/src/main/res/layout/send_files_fragment.xml b/app/src/main/res/layout/send_files_fragment.xml index d256a9433067..085cb14f043b 100644 --- a/app/src/main/res/layout/send_files_fragment.xml +++ b/app/src/main/res/layout/send_files_fragment.xml @@ -15,6 +15,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="bottom" + android:background="@color/bottom_sheet_bg_color" app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"> Ende-zu-Ende-verschlüsselte Linkfreigaben Link erstellen Neu + Neues Textdokument erstellen Neues Dokument - Neuer verschlüsselter Ordner + Verschlüsselten Ordner erstellen Neuer Ordner Neue Präsentation Neue Tabelle diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml index 72908c65f15b..9408ab128c72 100644 --- a/app/src/main/res/values-night/colors.xml +++ b/app/src/main/res/values-night/colors.xml @@ -39,4 +39,69 @@ @android:color/white #2A2A2A + + + #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_70 + + + #FFFFFF + @color/grey_30 + @color/grey_0 + @color/grey_0 + @color/grey_0 + @color/grey_0 + @color/grey_60 + @color/grey_0 + #FFFFFF + #7d94f9 + + + #121212 + @color/grey_0 + @color/grey_80 + @color/grey_80 diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index d8edd0a0e602..490e2fa8ac37 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -88,4 +88,94 @@ #A5A5A5 #EFEFEF + + + #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 + #FFFFFF + @color/grey_30 + @color/grey_10 + @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 + #2238df + + + @color/grey_0 + #191919 + @color/grey_0 + @color/grey_30 + #77b6bb + #5077b6bb diff --git a/app/src/main/res/values/dims.xml b/app/src/main/res/values/dims.xml index ffc32ec8b7a8..641dbb21c9a2 100644 --- a/app/src/main/res/values/dims.xml +++ b/app/src/main/res/values/dims.xml @@ -14,8 +14,8 @@ 164dp 12sp 20dp - 40dp - 56dp + 16dp + 48dp 96dp 56dp 40dp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3b6cb7d99a11..98f19d4e4e74 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1013,8 +1013,8 @@ Failed to upload taken media Scan document from camera Upload content from other apps - Folder - Encrypted folder + New folder + Create new encrypted folder Virus detected. Upload cannot be completed! Tags Unable to fetch sharees. @@ -1209,6 +1209,8 @@ Resolve conflict Delete New + Create text document + %1$s %2$s Upload in progress The server is busy. Retrying… File request diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index c3f303d1839a..c6e35541420a 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -480,7 +480,7 @@