Loading app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/RecipientLayoutCreator.kt 0 → 100644 +119 −0 Original line number Diff line number Diff line package com.fsck.k9.ui.messageview import android.text.SpannableStringBuilder private const val LIST_SEPARATOR = ", " /** * Calculates how many recipient names can be displayed given the available width. * * We display up to [maxNumberOfRecipientNames] recipient names, then the number of additional recipients. * * Example: * to me, Alice, Bob, Charly, Dora +11 * * If there's not enough room to display the first recipient name, we return it anyway and expect the component that is * actually rendering the text to ellipsize [RecipientLayoutData.recipientNames], but not * [RecipientLayoutData.additionalRecipients]. */ internal class RecipientLayoutCreator( private val textMeasure: TextMeasure, private val maxNumberOfRecipientNames: Int, private val recipientsPrefix: String, private val additionalRecipientSpacing: Int, private val additionalRecipientsPrefix: String ) { fun createRecipientLayout( recipientNames: List<CharSequence>, totalNumberOfRecipients: Int, availableWidth: Int ): RecipientLayoutData { require(recipientNames.isNotEmpty()) val displayRecipientsBuilder = SpannableStringBuilder() if (recipientNames.size == 1) { displayRecipientsBuilder.append(recipientsPrefix) displayRecipientsBuilder.append(recipientNames.first()) return RecipientLayoutData( recipientNames = displayRecipientsBuilder, additionalRecipients = null ) } val additionalRecipientsBuilder = StringBuilder(additionalRecipientsPrefix + 10) val maxRecipientNames = recipientNames.size.coerceAtMost(maxNumberOfRecipientNames) for (numberOfDisplayRecipients in maxRecipientNames downTo 2) { displayRecipientsBuilder.clear() displayRecipientsBuilder.append(recipientsPrefix) recipientNames.asSequence() .take(numberOfDisplayRecipients) .joinTo(displayRecipientsBuilder, separator = LIST_SEPARATOR) additionalRecipientsBuilder.setLength(0) val numberOfAdditionalRecipients = totalNumberOfRecipients - numberOfDisplayRecipients if (numberOfAdditionalRecipients > 0) { additionalRecipientsBuilder.append(additionalRecipientsPrefix) additionalRecipientsBuilder.append(numberOfAdditionalRecipients) } if (doesTextFitAvailableWidth(displayRecipientsBuilder, additionalRecipientsBuilder, availableWidth)) { return RecipientLayoutData( recipientNames = displayRecipientsBuilder, additionalRecipients = additionalRecipientsBuilder.toStringOrNull() ) } } displayRecipientsBuilder.clear() displayRecipientsBuilder.append(recipientsPrefix) displayRecipientsBuilder.append(recipientNames.first()) return RecipientLayoutData( recipientNames = displayRecipientsBuilder, additionalRecipients = "$additionalRecipientsPrefix${totalNumberOfRecipients - 1}" ) } private fun doesTextFitAvailableWidth( displayRecipients: CharSequence, additionalRecipients: CharSequence, availableWidth: Int ): Boolean { val recipientNamesWidth = textMeasure.measureRecipientNames(displayRecipients) if (recipientNamesWidth > availableWidth) { return false } else if (additionalRecipients.isEmpty()) { return true } val totalWidth = recipientNamesWidth + additionalRecipientSpacing + textMeasure.measureRecipientCount(additionalRecipients) return totalWidth <= availableWidth } } private fun StringBuilder.toStringOrNull(): String? { return if (isEmpty()) null else toString() } internal data class RecipientLayoutData( val recipientNames: CharSequence, val additionalRecipients: String? ) internal interface TextMeasure { /** * Measure the width of the supplied recipient names when rendered. */ fun measureRecipientNames(text: CharSequence): Int /** * Measure the width of the supplied recipient count when rendered. */ fun measureRecipientCount(text: CharSequence): Int } app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/RecipientNamesView.kt 0 → 100644 +184 −0 Original line number Diff line number Diff line package com.fsck.k9.ui.messageview import android.content.Context import android.util.AttributeSet import android.util.TypedValue import android.view.LayoutInflater import android.view.ViewGroup import android.widget.TextView import androidx.core.view.isGone import com.fsck.k9.ui.R private const val MAX_NUMBER_OF_RECIPIENT_NAMES = 5 /** * View that displays the names of recipients of a message. * * Up to [MAX_NUMBER_OF_RECIPIENT_NAMES] names of recipients are displayed, followed by the number of recipients that * weren't displayed. * * Examples: * - to me, Alice, Bob, Charly +3 * - to Camila Hyphenated-Nam… +5 * * This custom layout uses [RecipientLayoutCreator] to figure out how many recipient names can be displayed without * being truncated. If not even one recipient name can be displayed without being truncated, we first measure the space * needed for number of additional recipients, then use the rest to display the first recipient and ellipsize the end. */ class RecipientNamesView(context: Context, attrs: AttributeSet?) : ViewGroup(context, attrs) { val maxNumberOfRecipientNames: Int = MAX_NUMBER_OF_RECIPIENT_NAMES private val recipientLayoutCreator: RecipientLayoutCreator private val recipientNameTextView: TextView private val recipientCountTextView: TextView private val additionRecipientSpacing: Int init { LayoutInflater.from(context).inflate(R.layout.recipient_names, this, true) recipientNameTextView = findViewById(R.id.recipient_names) recipientCountTextView = findViewById(R.id.recipient_count) additionRecipientSpacing = (recipientCountTextView.layoutParams as MarginLayoutParams).marginStart } private var recipientNames: List<CharSequence> = emptyList() private var numberOfRecipients: Int = 0 private val textMeasure = object : TextMeasure { override fun measureRecipientNames(text: CharSequence): Int { return measureWidth(recipientNameTextView, text) } override fun measureRecipientCount(text: CharSequence): Int { return measureWidth(recipientCountTextView, text) } private fun measureWidth(textView: TextView, text: CharSequence): Int { textView.text = text val widthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED) val heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST) textView.measure(widthMeasureSpec, heightMeasureSpec) return textView.measuredWidth } } init { recipientLayoutCreator = RecipientLayoutCreator( textMeasure = textMeasure, maxNumberOfRecipientNames = MAX_NUMBER_OF_RECIPIENT_NAMES, recipientsPrefix = context.getString(R.string.message_view_recipient_prefix), additionalRecipientSpacing = additionRecipientSpacing, additionalRecipientsPrefix = context.getString(R.string.message_view_additional_recipient_prefix) ) if (isInEditMode) { recipientNames = listOf( "Grace Hopper", "Katherine Johnson", "Margaret Hamilton", "Adele Goldberg", "Steve Shirley" ) numberOfRecipients = 8 } } fun setTextSize(textSize: Int) { recipientNameTextView.setTextSize(TypedValue.COMPLEX_UNIT_SP, textSize.toFloat()) recipientCountTextView.setTextSize(TypedValue.COMPLEX_UNIT_SP, textSize.toFloat()) } fun setRecipients(recipientNames: List<CharSequence>, numberOfRecipients: Int) { if (recipientNames != this.recipientNames && numberOfRecipients != this.numberOfRecipients) { this.recipientNames = recipientNames this.numberOfRecipients = numberOfRecipients requestLayout() } } override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { require(MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED) { "Width of RecipientNamesView needs to be constrained" } recipientNameTextView.measure(widthMeasureSpec, heightMeasureSpec) recipientCountTextView.measure(widthMeasureSpec, heightMeasureSpec) val width = MeasureSpec.getSize(widthMeasureSpec) val height = maxOf(recipientNameTextView.measuredHeight, recipientCountTextView.measuredHeight) setMeasuredDimension(width, height) } override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { val availableWidth = width val recipientLayoutData = recipientLayoutCreator.createRecipientLayout( recipientNames, numberOfRecipients, availableWidth ) recipientNameTextView.text = recipientLayoutData.recipientNames val additionalRecipientsVisible = recipientLayoutData.additionalRecipients != null val remainingWidth: Int if (additionalRecipientsVisible) { recipientCountTextView.isGone = false recipientCountTextView.text = recipientLayoutData.additionalRecipients recipientCountTextView.measure( MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(measuredHeight, MeasureSpec.AT_MOST) ) remainingWidth = availableWidth - additionRecipientSpacing - recipientCountTextView.measuredWidth } else { recipientCountTextView.isGone = true remainingWidth = availableWidth } recipientNameTextView.measure( MeasureSpec.makeMeasureSpec(remainingWidth, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(measuredHeight, MeasureSpec.AT_MOST) ) if (layoutDirection == LAYOUT_DIRECTION_LTR) { val recipientNameRight = recipientNameTextView.measuredWidth recipientNameTextView.layout( 0, 0, recipientNameRight, recipientNameTextView.measuredHeight ) val recipientCountLeft = recipientNameRight + additionRecipientSpacing recipientCountTextView.layout( recipientCountLeft, 0, recipientCountLeft + recipientCountTextView.measuredWidth, recipientCountTextView.measuredHeight ) } else { val recipientNameLeft = width - recipientNameTextView.measuredWidth recipientNameTextView.layout( recipientNameLeft, 0, right, recipientNameTextView.measuredHeight ) val recipientCountRight = recipientNameLeft - additionRecipientSpacing recipientCountTextView.layout( recipientCountRight - recipientCountTextView.measuredWidth, 0, recipientCountRight, 0 + recipientCountTextView.measuredHeight ) } } override fun checkLayoutParams(p: LayoutParams?): Boolean { return p is MarginLayoutParams } override fun generateDefaultLayoutParams(): LayoutParams { return MarginLayoutParams(0, 0) } override fun generateLayoutParams(attrs: AttributeSet?): LayoutParams { return MarginLayoutParams(context, attrs) } } app/ui/legacy/src/main/java/com/fsck/k9/view/MessageHeader.java +33 −6 Original line number Diff line number Diff line Loading @@ -25,7 +25,10 @@ import com.fsck.k9.K9; import com.fsck.k9.activity.misc.ContactPicture; import com.fsck.k9.contacts.ContactPictureLoader; import com.fsck.k9.helper.ClipboardManager; import com.fsck.k9.helper.Contacts; import com.fsck.k9.helper.MessageHelper; import com.fsck.k9.helper.RealAddressFormatter; import com.fsck.k9.helper.RealContactNameProvider; import com.fsck.k9.mail.Address; import com.fsck.k9.mail.Flag; import com.fsck.k9.mail.Message; Loading @@ -34,7 +37,10 @@ import com.fsck.k9.message.ReplyActionStrategy; import com.fsck.k9.message.ReplyActions; import com.fsck.k9.ui.R; import com.fsck.k9.ui.helper.RelativeDateTimeFormatter; import com.fsck.k9.ui.messageview.DisplayRecipients; import com.fsck.k9.ui.messageview.DisplayRecipientsExtractor; import com.fsck.k9.ui.messageview.MessageHeaderOnMenuItemClickListener; import com.fsck.k9.ui.messageview.RecipientNamesView; import com.google.android.material.chip.Chip; import com.google.android.material.snackbar.Snackbar; Loading @@ -51,8 +57,7 @@ public class MessageHeader extends LinearLayout implements OnClickListener, OnLo private ImageView contactPictureView; private TextView fromView; private ImageView cryptoStatusIcon; private TextView toView; private TextView toCountView; private RecipientNamesView recipientNamesView; private TextView dateView; private ImageView menuPrimaryActionView; Loading Loading @@ -82,15 +87,17 @@ public class MessageHeader extends LinearLayout implements OnClickListener, OnLo contactPictureView = findViewById(R.id.contact_picture); fromView = findViewById(R.id.from); cryptoStatusIcon = findViewById(R.id.crypto_status_icon); toView = findViewById(R.id.to); toCountView = findViewById(R.id.to_count); recipientNamesView = findViewById(R.id.recipients); dateView = findViewById(R.id.date); fontSizes.setViewTextSize(subjectView, fontSizes.getMessageViewSubject()); fontSizes.setViewTextSize(dateView, fontSizes.getMessageViewDate()); fontSizes.setViewTextSize(fromView, fontSizes.getMessageViewSender()); fontSizes.setViewTextSize(toView, fontSizes.getMessageViewRecipients()); fontSizes.setViewTextSize(toCountView, fontSizes.getMessageViewRecipients()); int recipientTextSize = fontSizes.getMessageViewRecipients(); if (recipientTextSize != FontSizes.FONT_DEFAULT) { recipientNamesView.setTextSize(recipientTextSize); } subjectView.setOnClickListener(this); subjectView.setOnLongClickListener(this); Loading Loading @@ -230,11 +237,31 @@ public class MessageHeader extends LinearLayout implements OnClickListener, OnLo dateView.setText(""); } setRecipientNames(message, account); setReplyActions(message, account); setVisibility(View.VISIBLE); } private void setRecipientNames(Message message, Account account) { Integer contactNameColor = K9.isChangeContactNameColor() ? K9.getContactNameColor() : null; RealContactNameProvider contactNameProvider = new RealContactNameProvider(Contacts.getInstance(getContext())); RealAddressFormatter addressFormatter = new RealAddressFormatter(contactNameProvider, account, K9.isShowCorrespondentNames(), K9.isShowContactName(), contactNameColor, getContext().getString(R.string.message_view_me_text)); DisplayRecipientsExtractor displayRecipientsExtractor = new DisplayRecipientsExtractor(addressFormatter, recipientNamesView.getMaxNumberOfRecipientNames()); DisplayRecipients displayRecipients = displayRecipientsExtractor.extractDisplayRecipients(message, account); recipientNamesView.setRecipients(displayRecipients.getRecipientNames(), displayRecipients.getNumberOfRecipients()); } private void setReplyActions(Message message, Account account) { ReplyActions replyActions = replyActionStrategy.getReplyActions(account, message); this.replyActions = replyActions; Loading app/ui/legacy/src/main/res/layout/message_view_header.xml +7 −30 Original line number Diff line number Diff line Loading @@ -118,7 +118,7 @@ android:id="@+id/date" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="16dp" android:layout_marginEnd="4dp" android:ellipsize="none" android:singleLine="true" android:textAppearance="@style/TextAppearance.MaterialComponents.Caption" Loading @@ -134,49 +134,26 @@ android:layout_height="18sp" android:layout_marginStart="16dp" android:visibility="gone" app:layout_constraintBottom_toBottomOf="@+id/to" app:layout_constraintBottom_toBottomOf="@+id/recipients" app:layout_constraintStart_toEndOf="@id/contact_picture" app:srcCompat="@drawable/status_lock_disabled" app:tint="?attr/openpgp_grey" tools:visibility="visible" /> <TextView android:id="@+id/to" android:layout_width="wrap_content" <com.fsck.k9.ui.messageview.RecipientNamesView android:id="@+id/recipients" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="4dp" android:layout_marginEnd="4dp" android:layout_marginBottom="16dp" android:ellipsize="end" android:maxLines="1" android:text="to me" android:textAppearance="@style/TextAppearance.MaterialComponents.Body2" android:textColor="?android:attr/textColorSecondary" app:layout_constrainedWidth="true" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@id/to_count" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintHorizontal_chainStyle="packed" app:layout_constraintEnd_toStartOf="@+id/menu_primary_action" app:layout_constraintStart_toEndOf="@id/crypto_status_icon" app:layout_constraintTop_toBottomOf="@+id/from" app:layout_constraintVertical_bias="0.0" app:layout_goneMarginStart="16dp" /> <TextView android:id="@+id/to_count" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginHorizontal="4dp" android:layout_marginStart="4dp" android:layout_marginEnd="16dp" android:layout_weight="1" android:singleLine="true" android:text="+2" android:textAppearance="@style/TextAppearance.MaterialComponents.Body2" android:textColor="?attr/colorAccent" app:layout_constraintBottom_toBottomOf="@+id/to" app:layout_constraintEnd_toStartOf="@+id/menu_primary_action" app:layout_constraintStart_toEndOf="@id/to" /> <ImageView android:id="@+id/menu_primary_action" android:layout_width="48dp" Loading app/ui/legacy/src/main/res/layout/recipient_names.xml 0 → 100644 +27 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <merge xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" tools:parentTag="LinearLayout" tools:orientation="horizontal"> <TextView android:id="@+id/recipient_names" android:layout_width="wrap_content" android:layout_height="wrap_content" android:ellipsize="end" android:maxLines="1" android:textAppearance="@style/TextAppearance.MaterialComponents.Body2" android:textColor="?android:attr/textColorSecondary" tools:text="to me" /> <TextView android:id="@+id/recipient_count" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="4dp" android:singleLine="true" android:textAppearance="@style/TextAppearance.MaterialComponents.Body2" android:textColor="?attr/colorAccent" tools:text="+2" /> </merge> Loading
app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/RecipientLayoutCreator.kt 0 → 100644 +119 −0 Original line number Diff line number Diff line package com.fsck.k9.ui.messageview import android.text.SpannableStringBuilder private const val LIST_SEPARATOR = ", " /** * Calculates how many recipient names can be displayed given the available width. * * We display up to [maxNumberOfRecipientNames] recipient names, then the number of additional recipients. * * Example: * to me, Alice, Bob, Charly, Dora +11 * * If there's not enough room to display the first recipient name, we return it anyway and expect the component that is * actually rendering the text to ellipsize [RecipientLayoutData.recipientNames], but not * [RecipientLayoutData.additionalRecipients]. */ internal class RecipientLayoutCreator( private val textMeasure: TextMeasure, private val maxNumberOfRecipientNames: Int, private val recipientsPrefix: String, private val additionalRecipientSpacing: Int, private val additionalRecipientsPrefix: String ) { fun createRecipientLayout( recipientNames: List<CharSequence>, totalNumberOfRecipients: Int, availableWidth: Int ): RecipientLayoutData { require(recipientNames.isNotEmpty()) val displayRecipientsBuilder = SpannableStringBuilder() if (recipientNames.size == 1) { displayRecipientsBuilder.append(recipientsPrefix) displayRecipientsBuilder.append(recipientNames.first()) return RecipientLayoutData( recipientNames = displayRecipientsBuilder, additionalRecipients = null ) } val additionalRecipientsBuilder = StringBuilder(additionalRecipientsPrefix + 10) val maxRecipientNames = recipientNames.size.coerceAtMost(maxNumberOfRecipientNames) for (numberOfDisplayRecipients in maxRecipientNames downTo 2) { displayRecipientsBuilder.clear() displayRecipientsBuilder.append(recipientsPrefix) recipientNames.asSequence() .take(numberOfDisplayRecipients) .joinTo(displayRecipientsBuilder, separator = LIST_SEPARATOR) additionalRecipientsBuilder.setLength(0) val numberOfAdditionalRecipients = totalNumberOfRecipients - numberOfDisplayRecipients if (numberOfAdditionalRecipients > 0) { additionalRecipientsBuilder.append(additionalRecipientsPrefix) additionalRecipientsBuilder.append(numberOfAdditionalRecipients) } if (doesTextFitAvailableWidth(displayRecipientsBuilder, additionalRecipientsBuilder, availableWidth)) { return RecipientLayoutData( recipientNames = displayRecipientsBuilder, additionalRecipients = additionalRecipientsBuilder.toStringOrNull() ) } } displayRecipientsBuilder.clear() displayRecipientsBuilder.append(recipientsPrefix) displayRecipientsBuilder.append(recipientNames.first()) return RecipientLayoutData( recipientNames = displayRecipientsBuilder, additionalRecipients = "$additionalRecipientsPrefix${totalNumberOfRecipients - 1}" ) } private fun doesTextFitAvailableWidth( displayRecipients: CharSequence, additionalRecipients: CharSequence, availableWidth: Int ): Boolean { val recipientNamesWidth = textMeasure.measureRecipientNames(displayRecipients) if (recipientNamesWidth > availableWidth) { return false } else if (additionalRecipients.isEmpty()) { return true } val totalWidth = recipientNamesWidth + additionalRecipientSpacing + textMeasure.measureRecipientCount(additionalRecipients) return totalWidth <= availableWidth } } private fun StringBuilder.toStringOrNull(): String? { return if (isEmpty()) null else toString() } internal data class RecipientLayoutData( val recipientNames: CharSequence, val additionalRecipients: String? ) internal interface TextMeasure { /** * Measure the width of the supplied recipient names when rendered. */ fun measureRecipientNames(text: CharSequence): Int /** * Measure the width of the supplied recipient count when rendered. */ fun measureRecipientCount(text: CharSequence): Int }
app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/RecipientNamesView.kt 0 → 100644 +184 −0 Original line number Diff line number Diff line package com.fsck.k9.ui.messageview import android.content.Context import android.util.AttributeSet import android.util.TypedValue import android.view.LayoutInflater import android.view.ViewGroup import android.widget.TextView import androidx.core.view.isGone import com.fsck.k9.ui.R private const val MAX_NUMBER_OF_RECIPIENT_NAMES = 5 /** * View that displays the names of recipients of a message. * * Up to [MAX_NUMBER_OF_RECIPIENT_NAMES] names of recipients are displayed, followed by the number of recipients that * weren't displayed. * * Examples: * - to me, Alice, Bob, Charly +3 * - to Camila Hyphenated-Nam… +5 * * This custom layout uses [RecipientLayoutCreator] to figure out how many recipient names can be displayed without * being truncated. If not even one recipient name can be displayed without being truncated, we first measure the space * needed for number of additional recipients, then use the rest to display the first recipient and ellipsize the end. */ class RecipientNamesView(context: Context, attrs: AttributeSet?) : ViewGroup(context, attrs) { val maxNumberOfRecipientNames: Int = MAX_NUMBER_OF_RECIPIENT_NAMES private val recipientLayoutCreator: RecipientLayoutCreator private val recipientNameTextView: TextView private val recipientCountTextView: TextView private val additionRecipientSpacing: Int init { LayoutInflater.from(context).inflate(R.layout.recipient_names, this, true) recipientNameTextView = findViewById(R.id.recipient_names) recipientCountTextView = findViewById(R.id.recipient_count) additionRecipientSpacing = (recipientCountTextView.layoutParams as MarginLayoutParams).marginStart } private var recipientNames: List<CharSequence> = emptyList() private var numberOfRecipients: Int = 0 private val textMeasure = object : TextMeasure { override fun measureRecipientNames(text: CharSequence): Int { return measureWidth(recipientNameTextView, text) } override fun measureRecipientCount(text: CharSequence): Int { return measureWidth(recipientCountTextView, text) } private fun measureWidth(textView: TextView, text: CharSequence): Int { textView.text = text val widthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED) val heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST) textView.measure(widthMeasureSpec, heightMeasureSpec) return textView.measuredWidth } } init { recipientLayoutCreator = RecipientLayoutCreator( textMeasure = textMeasure, maxNumberOfRecipientNames = MAX_NUMBER_OF_RECIPIENT_NAMES, recipientsPrefix = context.getString(R.string.message_view_recipient_prefix), additionalRecipientSpacing = additionRecipientSpacing, additionalRecipientsPrefix = context.getString(R.string.message_view_additional_recipient_prefix) ) if (isInEditMode) { recipientNames = listOf( "Grace Hopper", "Katherine Johnson", "Margaret Hamilton", "Adele Goldberg", "Steve Shirley" ) numberOfRecipients = 8 } } fun setTextSize(textSize: Int) { recipientNameTextView.setTextSize(TypedValue.COMPLEX_UNIT_SP, textSize.toFloat()) recipientCountTextView.setTextSize(TypedValue.COMPLEX_UNIT_SP, textSize.toFloat()) } fun setRecipients(recipientNames: List<CharSequence>, numberOfRecipients: Int) { if (recipientNames != this.recipientNames && numberOfRecipients != this.numberOfRecipients) { this.recipientNames = recipientNames this.numberOfRecipients = numberOfRecipients requestLayout() } } override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { require(MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED) { "Width of RecipientNamesView needs to be constrained" } recipientNameTextView.measure(widthMeasureSpec, heightMeasureSpec) recipientCountTextView.measure(widthMeasureSpec, heightMeasureSpec) val width = MeasureSpec.getSize(widthMeasureSpec) val height = maxOf(recipientNameTextView.measuredHeight, recipientCountTextView.measuredHeight) setMeasuredDimension(width, height) } override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { val availableWidth = width val recipientLayoutData = recipientLayoutCreator.createRecipientLayout( recipientNames, numberOfRecipients, availableWidth ) recipientNameTextView.text = recipientLayoutData.recipientNames val additionalRecipientsVisible = recipientLayoutData.additionalRecipients != null val remainingWidth: Int if (additionalRecipientsVisible) { recipientCountTextView.isGone = false recipientCountTextView.text = recipientLayoutData.additionalRecipients recipientCountTextView.measure( MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(measuredHeight, MeasureSpec.AT_MOST) ) remainingWidth = availableWidth - additionRecipientSpacing - recipientCountTextView.measuredWidth } else { recipientCountTextView.isGone = true remainingWidth = availableWidth } recipientNameTextView.measure( MeasureSpec.makeMeasureSpec(remainingWidth, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(measuredHeight, MeasureSpec.AT_MOST) ) if (layoutDirection == LAYOUT_DIRECTION_LTR) { val recipientNameRight = recipientNameTextView.measuredWidth recipientNameTextView.layout( 0, 0, recipientNameRight, recipientNameTextView.measuredHeight ) val recipientCountLeft = recipientNameRight + additionRecipientSpacing recipientCountTextView.layout( recipientCountLeft, 0, recipientCountLeft + recipientCountTextView.measuredWidth, recipientCountTextView.measuredHeight ) } else { val recipientNameLeft = width - recipientNameTextView.measuredWidth recipientNameTextView.layout( recipientNameLeft, 0, right, recipientNameTextView.measuredHeight ) val recipientCountRight = recipientNameLeft - additionRecipientSpacing recipientCountTextView.layout( recipientCountRight - recipientCountTextView.measuredWidth, 0, recipientCountRight, 0 + recipientCountTextView.measuredHeight ) } } override fun checkLayoutParams(p: LayoutParams?): Boolean { return p is MarginLayoutParams } override fun generateDefaultLayoutParams(): LayoutParams { return MarginLayoutParams(0, 0) } override fun generateLayoutParams(attrs: AttributeSet?): LayoutParams { return MarginLayoutParams(context, attrs) } }
app/ui/legacy/src/main/java/com/fsck/k9/view/MessageHeader.java +33 −6 Original line number Diff line number Diff line Loading @@ -25,7 +25,10 @@ import com.fsck.k9.K9; import com.fsck.k9.activity.misc.ContactPicture; import com.fsck.k9.contacts.ContactPictureLoader; import com.fsck.k9.helper.ClipboardManager; import com.fsck.k9.helper.Contacts; import com.fsck.k9.helper.MessageHelper; import com.fsck.k9.helper.RealAddressFormatter; import com.fsck.k9.helper.RealContactNameProvider; import com.fsck.k9.mail.Address; import com.fsck.k9.mail.Flag; import com.fsck.k9.mail.Message; Loading @@ -34,7 +37,10 @@ import com.fsck.k9.message.ReplyActionStrategy; import com.fsck.k9.message.ReplyActions; import com.fsck.k9.ui.R; import com.fsck.k9.ui.helper.RelativeDateTimeFormatter; import com.fsck.k9.ui.messageview.DisplayRecipients; import com.fsck.k9.ui.messageview.DisplayRecipientsExtractor; import com.fsck.k9.ui.messageview.MessageHeaderOnMenuItemClickListener; import com.fsck.k9.ui.messageview.RecipientNamesView; import com.google.android.material.chip.Chip; import com.google.android.material.snackbar.Snackbar; Loading @@ -51,8 +57,7 @@ public class MessageHeader extends LinearLayout implements OnClickListener, OnLo private ImageView contactPictureView; private TextView fromView; private ImageView cryptoStatusIcon; private TextView toView; private TextView toCountView; private RecipientNamesView recipientNamesView; private TextView dateView; private ImageView menuPrimaryActionView; Loading Loading @@ -82,15 +87,17 @@ public class MessageHeader extends LinearLayout implements OnClickListener, OnLo contactPictureView = findViewById(R.id.contact_picture); fromView = findViewById(R.id.from); cryptoStatusIcon = findViewById(R.id.crypto_status_icon); toView = findViewById(R.id.to); toCountView = findViewById(R.id.to_count); recipientNamesView = findViewById(R.id.recipients); dateView = findViewById(R.id.date); fontSizes.setViewTextSize(subjectView, fontSizes.getMessageViewSubject()); fontSizes.setViewTextSize(dateView, fontSizes.getMessageViewDate()); fontSizes.setViewTextSize(fromView, fontSizes.getMessageViewSender()); fontSizes.setViewTextSize(toView, fontSizes.getMessageViewRecipients()); fontSizes.setViewTextSize(toCountView, fontSizes.getMessageViewRecipients()); int recipientTextSize = fontSizes.getMessageViewRecipients(); if (recipientTextSize != FontSizes.FONT_DEFAULT) { recipientNamesView.setTextSize(recipientTextSize); } subjectView.setOnClickListener(this); subjectView.setOnLongClickListener(this); Loading Loading @@ -230,11 +237,31 @@ public class MessageHeader extends LinearLayout implements OnClickListener, OnLo dateView.setText(""); } setRecipientNames(message, account); setReplyActions(message, account); setVisibility(View.VISIBLE); } private void setRecipientNames(Message message, Account account) { Integer contactNameColor = K9.isChangeContactNameColor() ? K9.getContactNameColor() : null; RealContactNameProvider contactNameProvider = new RealContactNameProvider(Contacts.getInstance(getContext())); RealAddressFormatter addressFormatter = new RealAddressFormatter(contactNameProvider, account, K9.isShowCorrespondentNames(), K9.isShowContactName(), contactNameColor, getContext().getString(R.string.message_view_me_text)); DisplayRecipientsExtractor displayRecipientsExtractor = new DisplayRecipientsExtractor(addressFormatter, recipientNamesView.getMaxNumberOfRecipientNames()); DisplayRecipients displayRecipients = displayRecipientsExtractor.extractDisplayRecipients(message, account); recipientNamesView.setRecipients(displayRecipients.getRecipientNames(), displayRecipients.getNumberOfRecipients()); } private void setReplyActions(Message message, Account account) { ReplyActions replyActions = replyActionStrategy.getReplyActions(account, message); this.replyActions = replyActions; Loading
app/ui/legacy/src/main/res/layout/message_view_header.xml +7 −30 Original line number Diff line number Diff line Loading @@ -118,7 +118,7 @@ android:id="@+id/date" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="16dp" android:layout_marginEnd="4dp" android:ellipsize="none" android:singleLine="true" android:textAppearance="@style/TextAppearance.MaterialComponents.Caption" Loading @@ -134,49 +134,26 @@ android:layout_height="18sp" android:layout_marginStart="16dp" android:visibility="gone" app:layout_constraintBottom_toBottomOf="@+id/to" app:layout_constraintBottom_toBottomOf="@+id/recipients" app:layout_constraintStart_toEndOf="@id/contact_picture" app:srcCompat="@drawable/status_lock_disabled" app:tint="?attr/openpgp_grey" tools:visibility="visible" /> <TextView android:id="@+id/to" android:layout_width="wrap_content" <com.fsck.k9.ui.messageview.RecipientNamesView android:id="@+id/recipients" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="4dp" android:layout_marginEnd="4dp" android:layout_marginBottom="16dp" android:ellipsize="end" android:maxLines="1" android:text="to me" android:textAppearance="@style/TextAppearance.MaterialComponents.Body2" android:textColor="?android:attr/textColorSecondary" app:layout_constrainedWidth="true" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@id/to_count" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintHorizontal_chainStyle="packed" app:layout_constraintEnd_toStartOf="@+id/menu_primary_action" app:layout_constraintStart_toEndOf="@id/crypto_status_icon" app:layout_constraintTop_toBottomOf="@+id/from" app:layout_constraintVertical_bias="0.0" app:layout_goneMarginStart="16dp" /> <TextView android:id="@+id/to_count" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginHorizontal="4dp" android:layout_marginStart="4dp" android:layout_marginEnd="16dp" android:layout_weight="1" android:singleLine="true" android:text="+2" android:textAppearance="@style/TextAppearance.MaterialComponents.Body2" android:textColor="?attr/colorAccent" app:layout_constraintBottom_toBottomOf="@+id/to" app:layout_constraintEnd_toStartOf="@+id/menu_primary_action" app:layout_constraintStart_toEndOf="@id/to" /> <ImageView android:id="@+id/menu_primary_action" android:layout_width="48dp" Loading
app/ui/legacy/src/main/res/layout/recipient_names.xml 0 → 100644 +27 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <merge xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" tools:parentTag="LinearLayout" tools:orientation="horizontal"> <TextView android:id="@+id/recipient_names" android:layout_width="wrap_content" android:layout_height="wrap_content" android:ellipsize="end" android:maxLines="1" android:textAppearance="@style/TextAppearance.MaterialComponents.Body2" android:textColor="?android:attr/textColorSecondary" tools:text="to me" /> <TextView android:id="@+id/recipient_count" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="4dp" android:singleLine="true" android:textAppearance="@style/TextAppearance.MaterialComponents.Body2" android:textColor="?attr/colorAccent" tools:text="+2" /> </merge>