Add conversation settings pager in toolbar

pull/1014/head
charles 2 years ago
parent 29efd41939
commit 290e646679

@ -0,0 +1,179 @@
package org.thoughtcrime.securesms.conversation
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.LinearLayout
import androidx.annotation.DimenRes
import androidx.core.view.isVisible
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.tabs.TabLayoutMediator
import dagger.hilt.android.AndroidEntryPoint
import network.loki.messenger.R
import network.loki.messenger.databinding.ViewConversationActionBarBinding
import network.loki.messenger.databinding.ViewConversationSettingBinding
import org.session.libsession.messaging.open_groups.OpenGroup
import org.session.libsession.utilities.ExpirationUtil
import org.session.libsession.utilities.recipients.Recipient
import org.thoughtcrime.securesms.conversation.v2.utilities.MentionManagerUtilities
import org.thoughtcrime.securesms.database.GroupDatabase
import org.thoughtcrime.securesms.database.LokiAPIDatabase
import org.thoughtcrime.securesms.mms.GlideRequests
import org.thoughtcrime.securesms.util.DateUtils
import java.util.Locale
import javax.inject.Inject
import kotlin.math.roundToInt
@AndroidEntryPoint
class ConversationActionBarView : LinearLayout {
private lateinit var binding: ViewConversationActionBarBinding
@Inject lateinit var lokiApiDb: LokiAPIDatabase
@Inject lateinit var groupDb: GroupDatabase
var delegate: ConversationActionBarDelegate? = null
private val settingsAdapter = ConversationSettingsAdapter { setting ->
if (setting.settingType == ConversationSettingType.EXPIRATION) {
delegate?.onExpirationSettingClicked()
}
}
constructor(context: Context) : super(context) { initialize() }
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { initialize() }
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { initialize() }
private fun initialize() {
binding = ViewConversationActionBarBinding.inflate(LayoutInflater.from(context), this, true)
binding.settingsPager.adapter = settingsAdapter
val mediator = TabLayoutMediator(binding.settingsTabLayout, binding.settingsPager) { _, _ -> }
mediator.attach()
}
fun bind(threadId: Long, recipient: Recipient, glide: GlideRequests) {
binding.conversationTitleView.text = when {
recipient.isLocalNumber -> context.getString(R.string.note_to_self)
else -> recipient.toShortString()
}
@DimenRes val sizeID: Int = if (recipient.isClosedGroupRecipient) {
R.dimen.medium_profile_picture_size
} else {
R.dimen.small_profile_picture_size
}
val size = resources.getDimension(sizeID).roundToInt()
binding.profilePictureView.root.layoutParams = LayoutParams(size, size)
binding.profilePictureView.root.glide = glide
MentionManagerUtilities.populateUserPublicKeyCacheIfNeeded(threadId, context)
binding.profilePictureView.root.update(recipient)
}
fun update(recipient: Recipient) {
binding.profilePictureView.root.update(recipient)
binding.conversationTitleView.text = when {
recipient.isLocalNumber -> context.getString(R.string.note_to_self)
else -> recipient.toShortString()
}
updateSubtitle(recipient)
}
fun updateSubtitle(recipient: Recipient, openGroup: OpenGroup? = null) {
val settings = mutableListOf<ConversationSetting>()
if (recipient.expireMessages > 0) {
settings.add(
ConversationSetting(
"${context.getString(R.string.expiration_type_disappear_after_read)} - ${ExpirationUtil.getExpirationAbbreviatedDisplayValue(context, recipient.expireMessages)}" ,
ConversationSettingType.EXPIRATION,
R.drawable.ic_timer
)
)
}
if (recipient.isMuted) {
settings.add(
ConversationSetting(
if (recipient.mutedUntil != Long.MAX_VALUE) {
context.getString(R.string.ConversationActivity_muted_until_date, DateUtils.getFormattedDateTime(recipient.mutedUntil, "EEE, MMM d, yyyy HH:mm", Locale.getDefault()))
} else {
context.getString(R.string.ConversationActivity_muted_forever)
},
ConversationSettingType.NOTIFICATION,
R.drawable.ic_outline_notifications_off_24
)
)
}
if (recipient.isGroupRecipient) {
settings.add(
ConversationSetting(
openGroup?.let {
val userCount = lokiApiDb.getUserCount(it.room, it.server) ?: 0
context.getString(R.string.ConversationActivity_active_member_count, userCount)
} ?: run {
val userCount = groupDb.getGroupMemberAddresses(recipient.address.toGroupString(), true).size
context.getString(R.string.ConversationActivity_member_count, userCount)
},
ConversationSettingType.MEMBER_COUNT,
)
)
}
settingsAdapter.submitList(settings)
binding.settingsTabLayout.isVisible = settings.size > 1
}
class ConversationSettingsAdapter(
private val settingsListener: (ConversationSetting) -> Unit
) : ListAdapter<ConversationSetting, ConversationSettingsAdapter.SettingViewHolder>(SettingsDiffer()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SettingViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
return SettingViewHolder(ViewConversationSettingBinding.inflate(layoutInflater, parent, false))
}
override fun onBindViewHolder(holder: SettingViewHolder, position: Int) {
holder.bind(getItem(position), itemCount) {
settingsListener.invoke(it)
}
}
class SettingViewHolder(
private val binding: ViewConversationSettingBinding
): RecyclerView.ViewHolder(binding.root) {
fun bind(setting: ConversationSetting, itemCount: Int, listener: (ConversationSetting) -> Unit) {
binding.root.setOnClickListener { listener.invoke(setting) }
binding.iconImageView.setImageResource(setting.iconResId)
binding.iconImageView.isVisible = setting.iconResId > 0
binding.titleView.text = setting.title
binding.leftArrowImageView.isVisible = itemCount > 1
binding.rightArrowImageView.isVisible = itemCount > 1
}
}
class SettingsDiffer: DiffUtil.ItemCallback<ConversationSetting>() {
override fun areItemsTheSame(oldItem: ConversationSetting, newItem: ConversationSetting) = oldItem === newItem
override fun areContentsTheSame(oldItem: ConversationSetting, newItem: ConversationSetting) = oldItem == newItem
}
}
}
fun interface ConversationActionBarDelegate {
fun onExpirationSettingClicked()
}
data class ConversationSetting(
val title: String,
val settingType: ConversationSettingType,
val iconResId: Int = 0
)
enum class ConversationSettingType {
EXPIRATION,
MEMBER_COUNT,
NOTIFICATION
}

@ -28,11 +28,9 @@ import android.view.MenuItem
import android.view.MotionEvent
import android.view.View
import android.view.WindowManager
import android.widget.LinearLayout
import android.widget.RelativeLayout
import android.widget.Toast
import androidx.activity.viewModels
import androidx.annotation.DimenRes
import androidx.appcompat.app.AlertDialog
import androidx.core.view.drawToBitmap
import androidx.core.view.isVisible
@ -88,6 +86,8 @@ import org.thoughtcrime.securesms.attachments.ScreenshotObserver
import org.thoughtcrime.securesms.audio.AudioRecorder
import org.thoughtcrime.securesms.contacts.SelectContactsActivity.Companion.selectedContactsKey
import org.thoughtcrime.securesms.contactshare.SimpleTextWatcher
import org.thoughtcrime.securesms.conversation.ConversationActionBarDelegate
import org.thoughtcrime.securesms.conversation.ConversationSetting
import org.thoughtcrime.securesms.conversation.expiration.ExpirationSettingsActivity
import org.thoughtcrime.securesms.conversation.v2.ConversationReactionOverlay.OnActionSelectedListener
import org.thoughtcrime.securesms.conversation.v2.ConversationReactionOverlay.OnReactionSelectedListener
@ -107,19 +107,16 @@ import org.thoughtcrime.securesms.conversation.v2.search.SearchBottomBar
import org.thoughtcrime.securesms.conversation.v2.search.SearchViewModel
import org.thoughtcrime.securesms.conversation.v2.utilities.AttachmentManager
import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog
import org.thoughtcrime.securesms.conversation.v2.utilities.MentionManagerUtilities
import org.thoughtcrime.securesms.conversation.v2.utilities.MentionUtilities
import org.thoughtcrime.securesms.conversation.v2.utilities.ResendMessageUtilities
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
import org.thoughtcrime.securesms.crypto.MnemonicUtilities
import org.thoughtcrime.securesms.database.GroupDatabase
import org.thoughtcrime.securesms.database.LokiAPIDatabase
import org.thoughtcrime.securesms.database.LokiMessageDatabase
import org.thoughtcrime.securesms.database.LokiThreadDatabase
import org.thoughtcrime.securesms.database.MmsDatabase
import org.thoughtcrime.securesms.database.MmsSmsDatabase
import org.thoughtcrime.securesms.database.ReactionDatabase
import org.thoughtcrime.securesms.database.RecipientDatabase
import org.thoughtcrime.securesms.database.SessionContactDatabase
import org.thoughtcrime.securesms.database.SmsDatabase
import org.thoughtcrime.securesms.database.Storage
@ -172,7 +169,7 @@ import kotlin.math.sqrt
class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDelegate,
InputBarRecordingViewDelegate, AttachmentManager.AttachmentListener, ActivityDispatcher,
ConversationActionModeCallbackDelegate, VisibleMessageViewDelegate, RecipientModifiedListener,
SearchBottomBar.EventListener, LoaderManager.LoaderCallbacks<Cursor>,
SearchBottomBar.EventListener, LoaderManager.LoaderCallbacks<Cursor>, ConversationActionBarDelegate,
OnReactionSelectedListener, ReactWithAnyEmojiDialogFragment.Callback, ReactionsDialogFragment.Callback,
ConversationMenuHelper.ConversationMenuListener {
@ -184,8 +181,6 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
@Inject lateinit var lokiThreadDb: LokiThreadDatabase
@Inject lateinit var sessionContactDb: SessionContactDatabase
@Inject lateinit var groupDb: GroupDatabase
@Inject lateinit var recipientDb: RecipientDatabase
@Inject lateinit var lokiApiDb: LokiAPIDatabase
@Inject lateinit var smsDb: SmsDatabase
@Inject lateinit var mmsDb: MmsDatabase
@Inject lateinit var lokiMessageDb: LokiMessageDatabase
@ -363,7 +358,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
updateUnreadCountIndicator()
setUpTypingObserver()
setUpRecipientObserver()
updateSubtitle()
binding?.toolbarContent?.updateSubtitle(viewModel.recipient!!)
getLatestOpenGroupInfoIfNeeded()
setUpBlockedBanner()
binding!!.searchBottomBar.setEventListener(this)
@ -460,23 +455,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
actionBar.title = ""
actionBar.setDisplayHomeAsUpEnabled(true)
actionBar.setHomeButtonEnabled(true)
binding!!.toolbarContent.conversationTitleView.text = when {
recipient.isLocalNumber -> getString(R.string.note_to_self)
else -> recipient.toShortString()
}
@DimenRes val sizeID: Int = if (viewModel.recipient?.isClosedGroupRecipient == true) {
R.dimen.medium_profile_picture_size
} else {
R.dimen.small_profile_picture_size
}
val size = resources.getDimension(sizeID).roundToInt()
binding!!.toolbarContent.profilePictureView.root.layoutParams = LinearLayout.LayoutParams(size, size)
binding!!.toolbarContent.profilePictureView.root.glide = glide
MentionManagerUtilities.populateUserPublicKeyCacheIfNeeded(viewModel.threadId, this)
val profilePictureView = binding!!.toolbarContent.profilePictureView.root
viewModel.recipient?.let { recipient ->
profilePictureView.update(recipient)
}
binding!!.toolbarContent.bind(viewModel.threadId, recipient, glide)
}
// called from onCreate
@ -566,8 +545,10 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
}
private fun getLatestOpenGroupInfoIfNeeded() {
viewModel.openGroup?.let {
OpenGroupApi.getMemberCount(it.room, it.server).successUi { updateSubtitle() }
viewModel.openGroup?.let { openGroup ->
OpenGroupApi.getMemberCount(openGroup.room, openGroup.server).successUi {
binding?.toolbarContent?.updateSubtitle(viewModel.recipient!!, openGroup)
}
}
}
@ -631,9 +612,8 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
menu,
menuInflater,
recipient,
viewModel.threadId,
this
) { onOptionsItemSelected(it) }
)
}
super.onPrepareOptionsMenu(menu)
return true
@ -657,13 +637,8 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
}
setUpMessageRequestsBar()
invalidateOptionsMenu()
updateSubtitle()
showOrHideInputIfNeeded()
binding?.toolbarContent?.profilePictureView?.root?.update(threadRecipient)
binding!!.toolbarContent.conversationTitleView.text = when {
threadRecipient.isLocalNumber -> getString(R.string.note_to_self)
else -> threadRecipient.toShortString()
}
binding?.toolbarContent?.update(threadRecipient)
}
}
@ -924,33 +899,13 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
binding.unreadCountIndicator.isVisible = (unreadCount != 0)
}
private fun updateSubtitle() {
val actionBarBinding = binding?.toolbarContent ?: return
val recipient = viewModel.recipient ?: return
actionBarBinding.muteIconImageView.isVisible = recipient.isMuted
actionBarBinding.conversationSubtitleView.isVisible = true
if (recipient.isMuted) {
if (recipient.mutedUntil != Long.MAX_VALUE) {
actionBarBinding.conversationSubtitleView.text = getString(R.string.ConversationActivity_muted_until_date, DateUtils.getFormattedDateTime(recipient.mutedUntil, "EEE, MMM d, yyyy HH:mm", Locale.getDefault()))
} else {
actionBarBinding.conversationSubtitleView.text = getString(R.string.ConversationActivity_muted_forever)
}
} else if (recipient.isGroupRecipient) {
viewModel.openGroup?.let { openGroup ->
val userCount = lokiApiDb.getUserCount(openGroup.room, openGroup.server) ?: 0
actionBarBinding.conversationSubtitleView.text = getString(R.string.ConversationActivity_active_member_count, userCount)
} ?: run {
val userCount = groupDb.getGroupMemberAddresses(recipient.address.toGroupString(), true).size
actionBarBinding.conversationSubtitleView.text = getString(R.string.ConversationActivity_member_count, userCount)
}
viewModel
} else {
actionBarBinding.conversationSubtitleView.isVisible = false
}
}
// endregion
// region Interaction
override fun onExpirationSettingClicked() {
viewModel.recipient?.let { showExpirationSettings(it) }
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == android.R.id.home) {
return false
@ -983,7 +938,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
Toast.makeText(this, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show()
}
override fun showExpiringMessagesDialog(thread: Recipient) {
override fun showExpirationSettings(thread: Recipient) {
if (thread.isClosedGroupRecipient) {
val group = groupDb.getGroup(thread.address.toGroupString()).orNull()
if (group?.isActive == false) { return }

@ -4,16 +4,11 @@ import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.graphics.BitmapFactory
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
import android.os.AsyncTask
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import androidx.annotation.ColorInt
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ContextThemeWrapper
@ -25,10 +20,8 @@ import androidx.core.graphics.drawable.IconCompat
import network.loki.messenger.R
import org.session.libsession.messaging.sending_receiving.MessageSender
import org.session.libsession.messaging.sending_receiving.leave
import org.session.libsession.utilities.ExpirationUtil
import org.session.libsession.utilities.GroupUtil.doubleDecodeGroupID
import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsession.utilities.getColorFromAttr
import org.session.libsession.utilities.recipients.Recipient
import org.session.libsignal.utilities.guava.Optional
import org.session.libsignal.utilities.toHexString
@ -53,31 +46,13 @@ object ConversationMenuHelper {
menu: Menu,
inflater: MenuInflater,
thread: Recipient,
threadId: Long,
context: Context,
onOptionsItemSelected: (MenuItem) -> Unit
context: Context
) {
// Prepare
menu.clear()
val isOpenGroup = thread.isOpenGroupRecipient
// Base menu (options that should always be present)
inflater.inflate(R.menu.menu_conversation, menu)
// Expiring messages
if (!isOpenGroup && (thread.hasApprovedMe() || thread.isClosedGroupRecipient || thread.isLocalNumber)) {
if (thread.expireMessages > 0) {
inflater.inflate(R.menu.menu_conversation_expiration_on, menu)
val item = menu.findItem(R.id.menu_expiring_messages)
val actionView = item.actionView
val iconView = actionView.findViewById<ImageView>(R.id.menu_badge_icon)
val badgeView = actionView.findViewById<TextView>(R.id.expiration_badge)
@ColorInt val color = context.getColorFromAttr(android.R.attr.textColorPrimary)
iconView.colorFilter = PorterDuffColorFilter(color, PorterDuff.Mode.MULTIPLY)
badgeView.text = ExpirationUtil.getExpirationAbbreviatedDisplayValue(context, thread.expireMessages)
actionView.setOnClickListener { onOptionsItemSelected(item) }
} else {
inflater.inflate(R.menu.menu_conversation_expiration_off, menu)
}
}
// One-on-one chat menu (options that should only be present for one-on-one chats)
if (thread.isContactRecipient) {
if (thread.isBlocked) {
@ -148,8 +123,6 @@ object ConversationMenuHelper {
R.id.menu_view_all_media -> { showAllMedia(context, thread) }
R.id.menu_search -> { search(context) }
R.id.menu_add_shortcut -> { addShortcut(context, thread) }
R.id.menu_expiring_messages -> { showExpiringMessagesDialog(context, thread) }
R.id.menu_expiring_messages_off -> { showExpiringMessagesDialog(context, thread) }
R.id.menu_unblock -> { unblock(context, thread) }
R.id.menu_block -> { block(context, thread, deleteThread = false) }
R.id.menu_block_delete -> { blockAndDelete(context, thread) }
@ -241,9 +214,9 @@ object ConversationMenuHelper {
}.execute()
}
private fun showExpiringMessagesDialog(context: Context, thread: Recipient) {
private fun showExpirationSettings(context: Context, thread: Recipient) {
val listener = context as? ConversationMenuListener ?: return
listener.showExpiringMessagesDialog(thread)
listener.showExpirationSettings(thread)
}
private fun unblock(context: Context, thread: Recipient) {
@ -344,7 +317,7 @@ object ConversationMenuHelper {
fun block(deleteThread: Boolean = false)
fun unblock()
fun copySessionID(sessionId: String)
fun showExpiringMessagesDialog(thread: Recipient)
fun showExpirationSettings(thread: Recipient)
}
}

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#000000"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M15.41,16.59L10.83,12l4.58,-4.59L14,6l-6,6 6,6 1.41,-1.41z"/>
</vector>

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#000000"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M8.59,16.59L13.17,12 8.59,7.41 10,6l6,6 -6,6 -1.41,-1.41z"/>
</vector>

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true">
<shape
android:shape="ring"
android:innerRadius="0dp"
android:thickness="2dp"
android:useLevel="false">
<solid android:color="?android:textColorPrimary"/>
</shape>
</item>
<item>
<shape
android:shape="ring"
android:innerRadius="0dp"
android:thickness="2dp"
android:useLevel="false">
<solid android:color="?android:textColorTertiary"/>
</shape>
</item>
</selector>

@ -16,8 +16,10 @@
android:background="?colorPrimary"
app:contentInsetStart="0dp">
<include android:id="@+id/toolbarContent"
layout="@layout/activity_conversation_v2_action_bar" />
<org.thoughtcrime.securesms.conversation.ConversationActionBarView
android:id="@+id/toolbarContent"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</androidx.appcompat.widget.Toolbar>

@ -1,67 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="horizontal"
android:gravity="center_vertical">
<include layout="@layout/view_profile_picture"
android:id="@+id/profilePictureView"
android:layout_width="@dimen/medium_profile_picture_size"
android:layout_height="@dimen/medium_profile_picture_size" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/conversationTitleView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
tools:text="@tools:sample/full_names"
android:textColor="?android:textColorPrimary"
android:textStyle="bold"
android:textSize="@dimen/very_large_font_size"
android:maxLines="1"
android:ellipsize="end" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:orientation="horizontal"
android:gravity="center_vertical">
<ImageView
android:id="@+id/muteIconImageView"
android:layout_width="14dp"
android:layout_height="14dp"
android:layout_marginEnd="4dp"
android:layout_gravity="center"
android:src="@drawable/ic_outline_notifications_off_24"
app:tint="?android:textColorPrimary"
android:alpha="0.6"
android:visibility="gone"
tools:visibility="visible"/>
<TextView
android:id="@+id/conversationSubtitleView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Muted"
android:textColor="?android:textColorPrimary"
android:alpha="0.6"
android:textSize="@dimen/very_small_font_size"
android:maxLines="1"
android:ellipsize="end" />
</LinearLayout>
</LinearLayout>
</LinearLayout>

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="?attr/actionBarSize"
android:gravity="center_vertical"
android:orientation="horizontal">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:id="@+id/conversationTitleView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="@tools:sample/full_names"
android:textColor="?android:textColorPrimary"
android:textStyle="bold"
android:textSize="@dimen/large_font_size"
android:maxLines="1"
android:ellipsize="end" />
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/settings_pager"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/very_small_spacing"/>
<com.google.android.material.tabs.TabLayout
android:id="@+id/settings_tab_layout"
android:layout_width="wrap_content"
android:layout_height="@dimen/very_small_spacing"
app:tabBackground="@drawable/tab_indicator_dot"
app:tabGravity="center"
app:tabIndicator="@null"
app:tabPaddingStart="@dimen/very_small_spacing"
app:tabPaddingEnd="@dimen/very_small_spacing"/>
</LinearLayout>
<include layout="@layout/view_profile_picture"
android:id="@+id/profilePictureView"
android:layout_width="@dimen/medium_profile_picture_size"
android:layout_height="@dimen/medium_profile_picture_size" />
</LinearLayout>

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="match_parent"
android:gravity="center_horizontal">
<ImageView
android:id="@+id/leftArrowImageView"
android:layout_width="14dp"
android:layout_height="14dp"
android:layout_gravity="center"
android:alpha="0.6"
android:visibility="gone"
android:src="@drawable/ic_baseline_keyboard_arrow_left_24dp"
app:tint="?android:textColorPrimary"
tools:visibility="visible" />
<ImageView
android:id="@+id/iconImageView"
android:layout_width="14dp"
android:layout_height="14dp"
android:layout_gravity="center"
android:layout_marginEnd="4dp"
android:alpha="0.6"
android:visibility="gone"
app:tint="?android:textColorPrimary"
tools:src="@drawable/ic_outline_notifications_off_24"
tools:visibility="visible" />
<TextView
android:id="@+id/titleView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0.6"
android:ellipsize="end"
android:maxLines="1"
android:layout_gravity="center"
android:textColor="?android:textColorPrimary"
android:textSize="@dimen/very_small_font_size"
tools:text="Muted" />
<ImageView
android:id="@+id/rightArrowImageView"
android:layout_width="14dp"
android:layout_height="14dp"
android:layout_gravity="center"
android:alpha="0.6"
android:visibility="gone"
android:src="@drawable/ic_baseline_keyboard_arrow_right_24dp"
app:tint="?android:textColorPrimary"
tools:visibility="visible" />
</LinearLayout>

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:title="@string/conversation_expiring_off__disappearing_messages"
android:id="@+id/menu_expiring_messages_off"
android:icon="@drawable/ic_baseline_timer_off_24" />
</menu>

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/menu_expiring_messages"
app:actionLayout="@layout/expiration_timer_menu"
app:showAsAction="always"
android:title="@string/menu_conversation_expiring_on__messages_expiring" />
</menu>
Loading…
Cancel
Save