Merge branch 'feat/2348_tweaks-to-details-screen-step-1' into 'develop'

Tweaks to details screen (step 1)

See merge request android/mail/proton-mail-android!721
This commit is contained in:
Stefanija Boshkovska 2021-09-29 12:47:29 +00:00
commit 2910ffc5c5
21 changed files with 388 additions and 146 deletions

View File

@ -62,6 +62,7 @@ import ch.protonmail.android.utils.ui.TYPE_ITEM
import ch.protonmail.android.views.PMWebViewClient
import ch.protonmail.android.views.messageDetails.MessageDetailsAttachmentsView
import ch.protonmail.android.views.messageDetails.MessageDetailsHeaderView
import ch.protonmail.android.views.messageDetails.ReplyActionsView
import kotlinx.android.synthetic.main.layout_message_details.view.*
import kotlinx.android.synthetic.main.layout_message_details_web_view.view.*
import org.apache.http.protocol.HTTP
@ -88,7 +89,7 @@ internal class MessageDetailsAdapter(
private val onLoadMessageBody: (Message) -> Unit,
private val onAttachmentDownloadCallback: (Attachment) -> Unit,
private val onEditDraftClicked: (Message) -> Unit,
private val onReplyMessageClicked: (Message) -> Unit,
private val onReplyMessageClicked: (Constants.MessageActionType, Message) -> Unit,
private val onMoreMessageActionsClicked: (Message) -> Unit
) : ExpandableRecyclerAdapter<MessageDetailsListItem>(context) {
@ -144,6 +145,9 @@ internal class MessageDetailsAdapter(
itemView.messageWebViewContainer.addView(detailsMessageActions)
}
val replyActionsView = createReplyActionsView()
itemView.messageWebViewContainer.addView(replyActionsView)
ItemViewHolder(itemView)
}
}
@ -162,6 +166,13 @@ internal class MessageDetailsAdapter(
return detailsMessageActions
}
private fun createReplyActionsView(): ReplyActionsView {
val replyActionsView = ReplyActionsView(context)
replyActionsView.id = R.id.item_message_body_reply_actions_layout_id
replyActionsView.isVisible = false
return replyActionsView
}
private fun setupMessageBodyWebView(itemView: View): WebView? {
val context = context as MessageDetailsActivity
// Looks like some devices are not able to create a WebView in some conditions.
@ -296,6 +307,7 @@ internal class MessageDetailsAdapter(
setUpViewDividers()
setupMessageActionsView(message, listItem.messageFormattedHtmlWithQuotedHistory, webView)
setupReplyActionsView(message)
setupMessageContentActions(position, loadEmbeddedImagesButton, displayRemoteContentButton, editDraftButton)
itemView.messageWebViewContainer.postDelayed(EXPAND_MESSAGE_ANIMATION_DELAY_MS) {
@ -314,13 +326,7 @@ internal class MessageDetailsAdapter(
val messageActionsView: MessageDetailsActionsView =
itemView.messageWebViewContainer.findViewById(R.id.item_message_body_actions_layout_id) ?: return
val replyMode = if (message.toList.size + message.ccList.size > 1) {
MessageDetailsActionsView.ReplyMode.REPLY_ALL
} else {
MessageDetailsActionsView.ReplyMode.REPLY
}
val uiModel = MessageDetailsActionsView.UiModel(
replyMode,
messageHtmlWithQuotedHistory.isNullOrEmpty(),
message.isDraft()
)
@ -330,10 +336,26 @@ internal class MessageDetailsAdapter(
loadHtmlDataIntoWebView(webView, messageHtmlWithQuotedHistory.orEmpty())
showHistoryButton.isVisible = false
}
messageActionsView.onReplyClicked { onReplyMessageClicked(message) }
messageActionsView.onMoreActionsClicked { onMoreMessageActionsClicked(message) }
}
private fun setupReplyActionsView(message: Message) {
val replyActionsView: ReplyActionsView =
itemView.messageWebViewContainer.findViewById(R.id.item_message_body_reply_actions_layout_id) ?: return
replyActionsView.bind(message.toList.size + message.ccList.size > 1)
replyActionsView.onReplyActionClicked {
onReplyMessageClicked(Constants.MessageActionType.REPLY, message)
}
replyActionsView.onReplyAllActionClicked {
onReplyMessageClicked(Constants.MessageActionType.REPLY_ALL, message)
}
replyActionsView.onForwardActionClicked {
onReplyMessageClicked(Constants.MessageActionType.FORWARD, message)
}
}
private fun loadHtmlDataIntoWebView(webView: WebView, htmlContent: String) {
webView.loadDataWithBaseURL(
Constants.DUMMY_URL_PREFIX,
@ -592,6 +614,10 @@ internal class MessageDetailsAdapter(
this.blockRemoteResources(!isAutoShowRemoteImages)
val replyActionsView: ReplyActionsView =
itemView.messageWebViewContainer.findViewById(R.id.item_message_body_reply_actions_layout_id)
replyActionsView.isVisible = true
super.onPageFinished(view, url)
}
}

View File

@ -370,7 +370,8 @@ internal class MessageDetailsViewModel @Inject constructor(
fun isConversationEnabled() = conversationModeEnabled(location)
fun doesConversationHaveMoreThanOneMessage() = runBlocking {
conversationUiModel.first().messagesCount!! > 1
val messagesCount = conversationUiModel.first().messagesCount
if (messagesCount != null) messagesCount > 1 else false
}
private suspend fun loadConversationDetails(

View File

@ -584,40 +584,42 @@ internal class MessageDetailsActivity : BaseStoragePermissionActivity() {
openedFolderLabelId ?: openedFolderLocationId.toString(),
getCurrentSubject(),
getMessagesFrom(message.sender?.name),
message.isStarred ?: false
message.isStarred ?: false,
viewModel.doesConversationHaveMoreThanOneMessage()
)
.show(supportFragmentManager, MessageActionSheet::class.qualifiedName)
}
val actionsUiModel = BottomActionsView.UiModel(
if (message.toList.size + message.ccList.size > 1) R.drawable.ic_reply_all else R.drawable.ic_reply,
R.drawable.ic_envelope_dot,
if (viewModel.shouldShowDeleteActionInBottomActionBar()) R.drawable.ic_trash_empty else R.drawable.ic_trash,
R.drawable.ic_folder_move,
R.drawable.ic_label
)
messageDetailsActionsView.bind(actionsUiModel)
messageDetailsActionsView.setOnFourthActionClickListener {
showLabelsManager()
showLabelsActionSheet(LabelsActionSheet.Type.LABEL)
}
messageDetailsActionsView.setOnThirdActionClickListener {
showLabelsActionSheet(LabelsActionSheet.Type.FOLDER)
}
messageDetailsActionsView.setOnSecondActionClickListener {
if (viewModel.shouldShowDeleteActionInBottomActionBar()) {
viewModel.delete()
} else viewModel.moveLastMessageToTrash()
onBackPressed()
}
messageDetailsActionsView.setOnSecondActionClickListener {
messageDetailsActionsView.setOnFirstActionClickListener {
onBackPressed()
viewModel.markUnread()
}
messageDetailsActionsView.setOnFirstActionClickListener {
onReplyMessage(message)
}
}
private fun showLabelsManager() {
private fun showLabelsActionSheet(labelActionSheetType: LabelsActionSheet.Type = LabelsActionSheet.Type.LABEL) {
LabelsActionSheet.newInstance(
messageIds = listOf(messageOrConversationId),
currentFolderLocationId = openedFolderLocationId,
labelActionSheetType = labelActionSheetType,
actionSheetTarget =
if (viewModel.isConversationEnabled()) ActionSheetTarget.CONVERSATION_ITEM_IN_DETAIL_SCREEN
else ActionSheetTarget.MESSAGE_ITEM_IN_DETAIL_SCREEN
@ -625,15 +627,6 @@ internal class MessageDetailsActivity : BaseStoragePermissionActivity() {
.show(supportFragmentManager, LabelsActionSheet::class.qualifiedName)
}
private fun onReplyMessage(message: Message) {
val messageAction = if (message.toList.size + message.ccList.size > 1) {
Constants.MessageActionType.REPLY_ALL
} else {
Constants.MessageActionType.REPLY
}
executeMessageAction(messageAction, message.messageId)
}
private fun displayToolbarData(conversation: ConversationUiModel) {
starToggleButton.isChecked = conversation.isStarred
val isInvalidSubject = conversation.subject.isNullOrEmpty()
@ -870,8 +863,8 @@ internal class MessageDetailsActivity : BaseStoragePermissionActivity() {
storagePermissionHelper.checkPermission()
}
private fun onReplyMessageClicked(message: Message) {
onReplyMessage(message)
private fun onReplyMessageClicked(messageAction: Constants.MessageActionType, message: Message) {
executeMessageAction(messageAction, message.messageId)
}
private fun onShowMessageActionSheet(message: Message) {

View File

@ -25,10 +25,9 @@ import android.view.LayoutInflater
import android.view.View
import android.widget.Button
import android.widget.ImageButton
import androidx.annotation.DrawableRes
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.core.view.setPadding
import ch.protonmail.android.R
import ch.protonmail.android.databinding.MessageDetailsActionsBinding
@ -39,29 +38,24 @@ class MessageDetailsActionsView @JvmOverloads constructor(
) : ConstraintLayout(context, attrs, defStyleAttr) {
private val showHistoryButton: Button
private val replyButton: ImageButton
private val moreActionsButton: ImageButton
init {
setPadding(context.resources.getDimensionPixelSize(R.dimen.padding_m))
val binding = MessageDetailsActionsBinding.inflate(
LayoutInflater.from(context),
this
)
showHistoryButton = binding.detailsButtonShowHistory
replyButton = binding.detailsButtonReply
moreActionsButton = binding.detailsButtonMoreActions
}
fun bind(uiModel: UiModel) {
replyButton.setImageDrawable(ContextCompat.getDrawable(context, uiModel.replyMode.drawableId))
showHistoryButton.isVisible = !uiModel.hideShowHistory
this.isVisible = !uiModel.hideAllActions
}
fun onReplyClicked(callback: (View) -> Unit) {
replyButton.setOnClickListener { callback(it) }
}
fun onShowHistoryClicked(callback: (View) -> Unit) {
showHistoryButton.setOnClickListener { callback(it) }
}
@ -71,13 +65,7 @@ class MessageDetailsActionsView @JvmOverloads constructor(
}
data class UiModel(
val replyMode: ReplyMode,
val hideShowHistory: Boolean,
val hideAllActions: Boolean
)
enum class ReplyMode(@DrawableRes val drawableId: Int) {
REPLY(R.drawable.ic_reply),
REPLY_ALL(R.drawable.ic_reply_all),
}
}

View File

@ -74,6 +74,7 @@ class MessageActionSheet : BottomSheetDialogFragment() {
)
val mailboxLabelId = arguments?.getString(EXTRA_ARG_MAILBOX_LABEL_ID)
?: messageLocation.messageLocationTypeValue.toString()
val doesConversationHaveMoreThanOneMessage = arguments?.getBoolean(EXTRA_ARG_CONVERSATION_HAS_MORE_THAN_ONE_MESSAGE) ?: true
Timber.v("MessageActionSheet for location: $messageLocation")
val binding = FragmentMessageActionSheetBinding.inflate(inflater)
@ -85,7 +86,7 @@ class MessageActionSheet : BottomSheetDialogFragment() {
viewModel.setupViewState(messageIds, messageLocation, actionsTarget)
setupHeaderBindings(binding.actionSheetHeaderDetailsActions, arguments)
setupReplyActionsBindings(binding.includeLayoutActionSheetButtons, actionsTarget, messageIds.first())
setupReplyActionsBindings(binding.includeLayoutActionSheetButtons, actionsTarget, messageIds, doesConversationHaveMoreThanOneMessage)
setupManageSectionBindings(binding, viewModel, actionsTarget, messageIds, messageLocation, mailboxLabelId)
setupMoreSectionBindings(binding, actionsTarget, messageIds)
actionSheetHeader = binding.actionSheetHeaderDetailsActions
@ -174,30 +175,31 @@ class MessageActionSheet : BottomSheetDialogFragment() {
private fun setupReplyActionsBindings(
binding: LayoutMessageDetailsActionsSheetButtonsBinding,
actionsTarget: ActionSheetTarget,
messageId: String
messageIds: List<String>,
doesConversationHaveMoreThanOneMessage: Boolean
) {
with(binding) {
layoutDetailsActions.isVisible = actionsTarget in listOf(
ActionSheetTarget.MESSAGE_ITEM_IN_DETAIL_SCREEN,
ActionSheetTarget.MESSAGE_ITEM_WITHIN_CONVERSATION_DETAIL_SCREEN,
ActionSheetTarget.CONVERSATION_ITEM_IN_DETAIL_SCREEN
)
ActionSheetTarget.MESSAGE_ITEM_WITHIN_CONVERSATION_DETAIL_SCREEN
) || (actionsTarget == ActionSheetTarget.CONVERSATION_ITEM_IN_DETAIL_SCREEN &&
!doesConversationHaveMoreThanOneMessage)
textViewDetailsActionsReply.setOnClickListener {
(activity as? MessageDetailsActivity)?.executeMessageAction(
Constants.MessageActionType.REPLY, messageId
Constants.MessageActionType.REPLY, messageIds.first()
)
dismiss()
}
textViewDetailsActionsReplyAll.setOnClickListener {
(activity as? MessageDetailsActivity)?.executeMessageAction(
Constants.MessageActionType.REPLY_ALL, messageId
Constants.MessageActionType.REPLY_ALL, messageIds.first()
)
dismiss()
}
textViewDetailsActionsForward.setOnClickListener {
(activity as? MessageDetailsActivity)?.executeMessageAction(
Constants.MessageActionType.FORWARD, messageId
Constants.MessageActionType.FORWARD, messageIds.first()
)
dismiss()
}
@ -448,6 +450,7 @@ class MessageActionSheet : BottomSheetDialogFragment() {
private const val EXTRA_ARG_TITLE = "arg_message_details_actions_title"
private const val EXTRA_ARG_SUBTITLE = "arg_message_details_actions_sub_title"
private const val EXTRA_ARG_IS_STARED = "arg_extra_is_stared"
private const val EXTRA_ARG_CONVERSATION_HAS_MORE_THAN_ONE_MESSAGE = "arg_conversation_has_more_than_one_message"
private const val HEADER_SLIDE_THRESHOLD = 0.8f
internal const val EXTRA_ARG_ACTION_TARGET = "extra_arg_action_sheet_actions_target"
@ -469,7 +472,8 @@ class MessageActionSheet : BottomSheetDialogFragment() {
mailboxLabelId: String,
title: CharSequence,
subTitle: String? = null,
isStarred: Boolean = false
isStarred: Boolean = false,
doesConversationHaveMoreThanOneMessage: Boolean = true
): MessageActionSheet {
return MessageActionSheet().apply {
arguments = bundleOf(
@ -479,7 +483,8 @@ class MessageActionSheet : BottomSheetDialogFragment() {
EXTRA_ARG_IS_STARED to isStarred,
EXTRA_ARG_CURRENT_FOLDER_LOCATION_ID to currentFolderLocationId,
EXTRA_ARG_MAILBOX_LABEL_ID to mailboxLabelId,
EXTRA_ARG_ACTION_TARGET to actionSheetTarget
EXTRA_ARG_ACTION_TARGET to actionSheetTarget,
EXTRA_ARG_CONVERSATION_HAS_MORE_THAN_ONE_MESSAGE to doesConversationHaveMoreThanOneMessage
)
}
}

View File

@ -25,7 +25,6 @@ import android.graphics.Color
import android.graphics.Typeface
import android.os.Build
import android.text.format.Formatter
import android.text.method.LinkMovementMethod
import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.ImageView
@ -100,7 +99,6 @@ class MessageDetailsHeaderView @JvmOverloads constructor(
private val lockIconTextView: TextView
private val lockIconExtendedTextView: TextView
private val encryptionInfoTextView: TextView
private val learnMoreTextView: TextView
private val repliedImageView: ImageView
private val repliedAllImageView: ImageView
@ -149,7 +147,6 @@ class MessageDetailsHeaderView @JvmOverloads constructor(
lockIconTextView = binding.lockIconTextView
lockIconExtendedTextView = binding.lockIconExtendedTextView
encryptionInfoTextView = binding.encryptionInfoTextView
learnMoreTextView = binding.learnMoreTextView
repliedImageView = binding.repliedImageView
repliedAllImageView = binding.repliedAllImageView
@ -171,6 +168,7 @@ class MessageDetailsHeaderView @JvmOverloads constructor(
top = verticalPadding,
bottom = verticalPadding
)
background = context.getDrawable(R.color.background_norm)
val typefacePgp = Typeface.createFromAsset(context.assets, "pgp-icons-android.ttf")
lockIconTextView.typeface = typefacePgp
@ -215,7 +213,6 @@ class MessageDetailsHeaderView @JvmOverloads constructor(
lockIconExtendedTextView.text = context.getText(senderLockIcon.icon)
lockIconExtendedTextView.setTextColor(senderLockIcon.color)
encryptionInfoTextView.text = context.getText(senderLockIcon.tooltip)
learnMoreTextView.movementMethod = LinkMovementMethod.getInstance()
val messageLocation = message.location
getIconForMessageLocation(Constants.MessageLocationType.fromInt(messageLocation))?.let { icon ->

View File

@ -0,0 +1,75 @@
/*
* Copyright (c) 2020 Proton Technologies AG
*
* This file is part of ProtonMail.
*
* ProtonMail 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.
*
* ProtonMail 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 ProtonMail. If not, see https://www.gnu.org/licenses/.
*/
package ch.protonmail.android.views.messageDetails
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.widget.FrameLayout
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.isVisible
import androidx.core.view.setPadding
import ch.protonmail.android.R
import ch.protonmail.android.databinding.LayoutMessageDetailsReplyActionsBinding
/**
* A view containing reply, reply all and forward actions
*/
class ReplyActionsView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr) {
private val replyButton: FrameLayout
private val replyAllButton: FrameLayout
private val forwardButton: FrameLayout
init {
setPadding(context.resources.getDimensionPixelSize(R.dimen.padding_l))
clipToPadding = false
val binding = LayoutMessageDetailsReplyActionsBinding.inflate(
LayoutInflater.from(context),
this
)
replyButton = binding.replyButton
replyAllButton = binding.replyAllButton
forwardButton = binding.forwardButton
}
fun bind(shouldShowReplyAllAction: Boolean) {
replyAllButton.isVisible = shouldShowReplyAllAction
}
fun onReplyActionClicked(callback: (View) -> Unit) {
replyButton.setOnClickListener { callback(it) }
}
fun onReplyAllActionClicked(callback: (View) -> Unit) {
replyAllButton.setOnClickListener { callback(it) }
}
fun onForwardActionClicked(callback: (View) -> Unit) {
forwardButton.setOnClickListener { callback(it) }
}
}

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2020 Proton Technologies AG
~
~ This file is part of ProtonMail.
~
~ ProtonMail 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.
~
~ ProtonMail 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 ProtonMail. If not, see https://www.gnu.org/licenses/.
-->
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/background_secondary">
<item android:drawable="@drawable/shape_reply_actions_background" />
</ripple>

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2020 Proton Technologies AG
~
~ This file is part of ProtonMail.
~
~ ProtonMail 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.
~
~ ProtonMail 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 ProtonMail. If not, see https://www.gnu.org/licenses/.
-->
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/background_norm" />
<corners android:radius="24dp" />
<stroke android:color="@color/separator_norm" android:width="@dimen/size_divider" />
</shape>

View File

@ -114,6 +114,8 @@
android:layout_width="0dp"
android:layout_height="0dp"
android:visibility="invisible"
android:elevation="@dimen/elevation_m"
android:background="@color/background_norm"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />

View File

@ -46,6 +46,8 @@
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/padding_5xl"
android:background="@color/background_secondary"
android:fillViewport="true"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
@ -93,6 +95,7 @@
android:id="@+id/layout_no_connectivity_info"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/margin_s"
app:layout_constraintBottom_toTopOf="@id/messageDetailsActionsView"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
@ -101,6 +104,8 @@
android:id="@+id/messageDetailsActionsView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:elevation="@dimen/elevation_m"
android:background="@color/background_norm"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />

View File

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2020 Proton Technologies AG
~
~ This file is part of ProtonMail.
@ -18,71 +17,74 @@
~ along with ProtonMail. If not, see https://www.gnu.org/licenses/.
-->
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/interaction_strong">
android:background="@color/background_norm"
android:elevation="@dimen/elevation_m">
<View
style="@style/ViewSeparatorHorizontal"
android:layout_width="0dp"
android:layout_height="@dimen/size_divider"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageButton
android:id="@+id/firstActionImageButton"
android:layout_width="@dimen/message_details_actions_button_size"
android:layout_height="@dimen/message_details_actions_button_size"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/secondActionImageButton"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:background="?selectableItemBackgroundBorderless"
android:src="@drawable/ic_reply"
app:tint="@color/icon_inverted" />
android:src="@drawable/ic_envelope_dot"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/secondActionImageButton"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageButton
android:id="@+id/secondActionImageButton"
android:layout_width="@dimen/message_details_actions_button_size"
android:layout_height="@dimen/message_details_actions_button_size"
app:layout_constraintLeft_toRightOf="@id/firstActionImageButton"
app:layout_constraintRight_toLeftOf="@id/thirdActionImageButton"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:background="?selectableItemBackgroundBorderless"
android:src="@drawable/ic_envelope_dot"
app:tint="@color/icon_inverted" />
android:src="@drawable/ic_trash"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/thirdActionImageButton"
app:layout_constraintStart_toEndOf="@id/firstActionImageButton"
app:layout_constraintTop_toTopOf="parent" />
<ImageButton
android:id="@+id/thirdActionImageButton"
android:layout_width="@dimen/message_details_actions_button_size"
android:layout_height="@dimen/message_details_actions_button_size"
app:layout_constraintLeft_toRightOf="@id/secondActionImageButton"
app:layout_constraintRight_toLeftOf="@id/fourthActionImageButton"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:background="?selectableItemBackgroundBorderless"
android:src="@drawable/ic_trash"
app:tint="@color/icon_inverted" />
android:src="@drawable/ic_folder_move"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/fourthActionImageButton"
app:layout_constraintStart_toEndOf="@id/secondActionImageButton"
app:layout_constraintTop_toTopOf="parent" />
<ImageButton
android:id="@+id/fourthActionImageButton"
android:layout_width="@dimen/message_details_actions_button_size"
android:layout_height="@dimen/message_details_actions_button_size"
app:layout_constraintLeft_toRightOf="@id/thirdActionImageButton"
app:layout_constraintRight_toLeftOf="@id/moreActionImageButton"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:background="?selectableItemBackgroundBorderless"
android:src="@drawable/ic_label"
app:tint="@color/icon_inverted" />
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/moreActionImageButton"
app:layout_constraintStart_toEndOf="@id/thirdActionImageButton"
app:layout_constraintTop_toTopOf="parent" />
<ImageButton
android:id="@+id/moreActionImageButton"
android:layout_width="@dimen/message_details_actions_button_size"
android:layout_height="@dimen/message_details_actions_button_size"
app:layout_constraintLeft_toRightOf="@id/fourthActionImageButton"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:background="?selectableItemBackgroundBorderless"
android:src="@drawable/ic_three_dots_horizontal"
app:tint="@color/icon_inverted" />
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/fourthActionImageButton"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -22,13 +22,14 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
android:clipToPadding="false">
<View
android:id="@+id/messagesDividerView"
android:layout_width="0dp"
android:layout_height="4dp"
android:background="@drawable/list_divider"
android:layout_height="8dp"
android:background="@color/background_secondary"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
@ -38,6 +39,7 @@
android:id="@+id/headerView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:elevation="@dimen/elevation_s"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/messagesDividerView" />
@ -45,8 +47,8 @@
<View
android:id="@+id/lastConversationMessageCollapsedDivider"
android:layout_width="0dp"
android:layout_height="4dp"
android:background="@drawable/list_divider"
android:layout_height="8dp"
android:background="@color/background_secondary"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/headerView"

View File

@ -89,26 +89,13 @@
android:maxLines="1"
app:layout_constrainedWidth="true"
app:layout_constraintBottom_toBottomOf="@id/senderInitialView"
app:layout_constraintEnd_toStartOf="@id/lockIconTextView"
app:layout_constraintEnd_toStartOf="@id/collapsedMessageViews"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toEndOf="@id/forwardedImageView"
app:layout_constraintTop_toTopOf="@id/senderInitialView"
tools:text="@tools:sample/full_names" />
<TextView
android:id="@+id/lockIconTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/message_details_header_margin_normal"
android:layout_marginEnd="@dimen/message_details_header_margin_normal"
android:textSize="18sp"
app:layout_constraintBottom_toBottomOf="@id/senderNameTextView"
app:layout_constraintEnd_toStartOf="@id/messageDetailsIcons"
app:layout_constraintStart_toEndOf="@id/senderNameTextView"
app:layout_constraintTop_toTopOf="@id/senderNameTextView"
tools:text="@string/lock_default" />
<ch.protonmail.android.details.presentation.view.CollapsedMessageViews
android:id="@+id/collapsedMessageViews"
android:layout_width="wrap_content"
@ -128,9 +115,22 @@
android:layout_marginStart="@dimen/message_details_header_margin_small"
android:layout_marginEnd="@dimen/message_details_header_margin_small"
app:layout_constraintBottom_toBottomOf="@id/senderNameTextView"
app:layout_constraintEnd_toStartOf="@id/locationImageView"
app:layout_constraintEnd_toStartOf="@id/lockIconTextView"
app:layout_constraintTop_toTopOf="@id/senderNameTextView" />
<TextView
android:id="@+id/lockIconTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/message_details_header_margin_normal"
android:layout_marginEnd="@dimen/message_details_header_margin_normal"
android:layout_marginTop="@dimen/message_details_header_margin_small"
android:textSize="18sp"
app:layout_constraintBottom_toBottomOf="@id/senderNameTextView"
app:layout_constraintEnd_toStartOf="@id/locationImageView"
app:layout_constraintTop_toTopOf="@id/senderNameTextView"
tools:text="@string/lock_default" />
<ImageView
android:id="@+id/locationImageView"
android:layout_width="@dimen/message_details_header_small_icon_size"
@ -380,16 +380,6 @@
app:layout_constraintTop_toBottomOf="@id/storageTextView"
tools:text="End-to-end encrypted and signed message" />
<TextView
android:id="@+id/learnMoreTextView"
style="@style/Proton.Text.DefaultSmall.Interaction"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/sender_lock_learn_more"
android:textColorLink="@color/cornflower_blue"
app:layout_constraintLeft_toRightOf="@id/senderInitialBarrier"
app:layout_constraintTop_toBottomOf="@id/encryptionInfoTextView" />
<androidx.constraintlayout.widget.Group
android:id="@+id/collapsedHeaderGroup"
android:layout_width="wrap_content"
@ -406,7 +396,7 @@
app:constraint_referenced_ids="senderEmailTextView, lockIconExtendedTextView,
encryptionInfoTextView, locationExtendedImageView, locationTextView,
calendarImageView, timeDateExtendedTextView, storageImageView,
storageTextView, learnMoreTextView"
storageTextView"
tools:visibility="visible" />
</merge>

View File

@ -0,0 +1,96 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2020 Proton Technologies AG
~
~ This file is part of ProtonMail.
~
~ ProtonMail 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.
~
~ ProtonMail 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 ProtonMail. If not, see https://www.gnu.org/licenses/.
-->
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
<FrameLayout
android:id="@+id/replyButton"
style="@style/ReplyButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/margin_s"
android:padding="@dimen/padding_m"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/replyAllButton"
app:layout_constraintTop_toTopOf="parent">
<TextView
style="@style/ReplyButtonTextStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:text="@string/reply"
app:drawableStartCompat="@drawable/ic_reply" />
</FrameLayout>
<FrameLayout
android:id="@+id/replyAllButton"
style="@style/ReplyButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/margin_s"
android:padding="@dimen/padding_m"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@id/replyButton"
app:layout_constraintRight_toLeftOf="@id/forwardButton"
app:layout_constraintTop_toTopOf="parent">
<TextView
style="@style/ReplyButtonTextStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:text="@string/reply_all"
app:drawableStartCompat="@drawable/ic_reply_all" />
</FrameLayout>
<FrameLayout
android:id="@+id/forwardButton"
style="@style/ReplyButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/margin_s"
android:padding="@dimen/padding_m"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@id/replyAllButton"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
style="@style/ReplyButtonTextStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:text="@string/forward"
app:drawableStartCompat="@drawable/ic_forward" />
</FrameLayout>
</merge>

View File

@ -22,6 +22,8 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/background_norm"
android:elevation="@dimen/elevation_s"
android:focusableInTouchMode="true"
android:descendantFocusability="blocksDescendants"
android:animateLayoutChanges="true">

View File

@ -22,8 +22,7 @@
xmlns:tools="http://schemas.android.com/tools"
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/padding_m">
android:layout_height="wrap_content">
<Button
android:id="@+id/details_button_show_history"
@ -38,21 +37,6 @@
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
<ImageButton
android:id="@+id/details_button_reply"
style="@style/ProtonImage.ImageButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="@dimen/padding_m"
android:contentDescription="@string/reply"
android:background="?selectableItemBackgroundBorderless"
android:src="@drawable/ic_reply"
app:layout_constraintEnd_toStartOf="@id/details_button_more_actions"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:tint="@color/cornflower_blue" />
<ImageButton
android:id="@+id/details_button_more_actions"
style="@style/ProtonImage.ImageButton"

View File

@ -57,6 +57,11 @@
<dimen name="elevation_xl">8dp</dimen> // standard button high 8dp
<dimen name="elevation_xxl">16dp</dimen> // standard nav drawer 16dp
<!-- generic margin -->
<dimen name="margin_s">4dp</dimen>
<dimen name="margin_m">8dp</dimen>
<dimen name="margin_l">16dp</dimen>
<!-- Toolbars -->
<dimen name="toolbar_padding_start">0dp</dimen>
<dimen name="toolbar_padding_end">8dp</dimen>

View File

@ -27,6 +27,7 @@
<item name="item_message_body_web_view_id" type="id" />
<item name="item_message_body_progress_view_id" type="id" />
<item name="item_message_body_actions_layout_id" type="id" />
<item name="item_message_body_reply_actions_layout_id" type="id" />
<item name="monday" type="id" />
<item name="tuesday" type="id" />

View File

@ -680,7 +680,6 @@
<string name="sender_lock_internal">End-to-end encrypted message</string>
<string name="sender_lock_internal_verified">End-to-end encrypted message from verified address</string>
<string name="sender_lock_unknown_scheme" translatable="false">Unknown encryption scheme</string>
<string name="sender_lock_learn_more"><a href="https://protonmail.com/support/knowledge-base/what-is-encrypted/">Learn more</a></string>
<!-- Composer locks -->
<string name="composer_lock_internal">End-to-end encrypted</string>
<string name="composer_lock_internal_pinned">End-to-end encrypted to verified address</string>

View File

@ -141,20 +141,21 @@
</style>
<style name="ActionButtons">
<item name="android:layout_margin">@dimen/padding_m</item>
<item name="android:layout_margin">@dimen/margin_m</item>
<item name="android:clickable">true</item>
<item name="android:background">@drawable/shape_background_action_sheet_buttons</item>
<item name="android:drawablePadding">@dimen/padding_l</item>
<item name="android:drawablePadding">@dimen/padding_s</item>
<item name="android:elevation">@dimen/elevation_s</item>
<item name="android:focusable">true</item>
<item name="android:gravity">center</item>
<item name="android:paddingTop">@dimen/padding_l</item>
<item name="android:paddingBottom">@dimen/padding_l</item>
<item name="android:paddingStart">@dimen/padding_m</item>
<item name="android:paddingEnd">@dimen/padding_m</item>
<item name="android:paddingTop">@dimen/padding_m</item>
<item name="android:paddingBottom">@dimen/padding_m</item>
<item name="android:paddingStart">@dimen/padding_l</item>
<item name="android:paddingEnd">@dimen/padding_l</item>
<item name="android:textColor">@color/cornflower_blue</item>
<item name="android:textAppearance">@style/Proton.Text.Default</item>
<item name="android:lines">2</item>
<item name="android:maxLines">3</item>
<item name="android:ellipsize">end</item>
<item name="drawableTint">@color/cornflower_blue</item>
</style>
@ -200,6 +201,19 @@
<item name="android:textColor">@color/cornflower_blue</item>
<item name="android:background">@color/transparent</item>
</style>
<style name="ReplyButtonTextStyle">
<item name="android:textAppearance">@style/Proton.Text.DefaultSmall</item>
<item name="android:drawablePadding">4dp</item>
<item name="android:maxLines">3</item>
<item name="android:ellipsize">end</item>
<item name="android:breakStrategy">balanced</item>
</style>
<style name="ReplyButtonStyle">
<item name="android:background">@drawable/ripple_reply_actions</item>
<item name="android:elevation">@dimen/elevation_s</item>
</style>
<!-- endregion -->
<!-- region V3 -->