Uses second layout to center room summary item title

This commit is contained in:
ericdecanini 2022-05-17 18:03:34 +02:00
parent 83bd9bca86
commit 03acf4505a
5 changed files with 355 additions and 47 deletions

View File

@ -19,7 +19,6 @@ package im.vector.app.features.home.room.list
import android.view.HapticFeedbackConstants import android.view.HapticFeedbackConstants
import android.view.View import android.view.View
import android.widget.ImageView import android.widget.ImageView
import android.widget.Space
import android.widget.TextView import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.isInvisible import androidx.core.view.isInvisible
@ -141,11 +140,6 @@ abstract class RoomSummaryItem : VectorEpoxyModel<RoomSummaryItem.Holder>() {
private fun renderForFilteredDisplayMode(holder: Holder) { private fun renderForFilteredDisplayMode(holder: Holder) {
holder.subtitleView.text = subtitle holder.subtitleView.text = subtitle
holder.centerTitle(shouldCenter = subtitle.isEmpty())
}
private fun Holder.centerTitle(shouldCenter: Boolean) {
centerTitleSpace.isVisible = shouldCenter
} }
override fun unbind(holder: Holder) { override fun unbind(holder: Holder) {
@ -182,6 +176,5 @@ abstract class RoomSummaryItem : VectorEpoxyModel<RoomSummaryItem.Holder>() {
val roomAvatarFailSendingImageView by bind<ImageView>(R.id.roomAvatarFailSendingImageView) val roomAvatarFailSendingImageView by bind<ImageView>(R.id.roomAvatarFailSendingImageView)
val roomAvatarPresenceImageView by bind<PresenceStateImageView>(R.id.roomAvatarPresenceImageView) val roomAvatarPresenceImageView by bind<PresenceStateImageView>(R.id.roomAvatarPresenceImageView)
val rootView by bind<ConstraintLayout>(R.id.itemRoomLayout) val rootView by bind<ConstraintLayout>(R.id.itemRoomLayout)
val centerTitleSpace by bind<Space>(R.id.centerTitleSpace)
} }
} }

View File

@ -0,0 +1,134 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.features.home.room.list
import android.view.HapticFeedbackConstants
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.isVisible
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import com.amulyakhare.textdrawable.TextDrawable
import im.vector.app.R
import im.vector.app.core.epoxy.ClickListener
import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.epoxy.onClick
import im.vector.app.core.ui.views.PresenceStateImageView
import im.vector.app.core.ui.views.ShieldImageView
import im.vector.app.features.displayname.getBestName
import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.RoomListDisplayMode
import im.vector.app.features.themes.ThemeUtils
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
import org.matrix.android.sdk.api.session.presence.model.UserPresence
import org.matrix.android.sdk.api.util.MatrixItem
@EpoxyModelClass(layout = R.layout.item_room_centered)
abstract class RoomSummaryItemCentered : VectorEpoxyModel<RoomSummaryItemCentered.Holder>() {
@EpoxyAttribute
lateinit var avatarRenderer: AvatarRenderer
@EpoxyAttribute
lateinit var matrixItem: MatrixItem
@EpoxyAttribute
var displayMode: RoomListDisplayMode = RoomListDisplayMode.PEOPLE
@EpoxyAttribute
var encryptionTrustLevel: RoomEncryptionTrustLevel? = null
@EpoxyAttribute
var userPresence: UserPresence? = null
@EpoxyAttribute
var showPresence: Boolean = false
@EpoxyAttribute @JvmField
var isPublic: Boolean = false
@EpoxyAttribute
var unreadNotificationCount: Int = 0
@EpoxyAttribute
var hasUnreadMessage: Boolean = false
@EpoxyAttribute
var hasDraft: Boolean = false
@EpoxyAttribute
var hasFailedSending: Boolean = false
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
var itemLongClickListener: View.OnLongClickListener? = null
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
var itemClickListener: ClickListener? = null
@EpoxyAttribute
var showSelected: Boolean = false
override fun bind(holder: Holder) {
super.bind(holder)
holder.rootView.onClick(itemClickListener)
holder.rootView.setOnLongClickListener {
it.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
itemLongClickListener?.onLongClick(it) ?: false
}
holder.titleView.text = matrixItem.getBestName()
avatarRenderer.render(matrixItem, holder.avatarImageView)
holder.roomAvatarDecorationImageView.render(encryptionTrustLevel)
holder.roomAvatarPublicDecorationImageView.isVisible = isPublic
holder.roomAvatarFailSendingImageView.isVisible = hasFailedSending
renderSelection(holder, showSelected)
holder.roomAvatarPresenceImageView.render(showPresence, userPresence)
}
override fun unbind(holder: Holder) {
holder.rootView.setOnClickListener(null)
holder.rootView.setOnLongClickListener(null)
avatarRenderer.clear(holder.avatarImageView)
super.unbind(holder)
}
private fun renderSelection(holder: Holder, isSelected: Boolean) {
if (isSelected) {
holder.avatarCheckedImageView.visibility = View.VISIBLE
val backgroundColor = ThemeUtils.getColor(holder.view.context, R.attr.colorPrimary)
val backgroundDrawable = TextDrawable.builder().buildRound("", backgroundColor)
holder.avatarImageView.setImageDrawable(backgroundDrawable)
} else {
holder.avatarCheckedImageView.visibility = View.GONE
avatarRenderer.render(matrixItem, holder.avatarImageView)
}
}
class Holder : VectorEpoxyHolder() {
val titleView by bind<TextView>(R.id.roomNameView)
val avatarCheckedImageView by bind<ImageView>(R.id.roomAvatarCheckedImageView)
val avatarImageView by bind<ImageView>(R.id.roomAvatarImageView)
val roomAvatarDecorationImageView by bind<ShieldImageView>(R.id.roomAvatarDecorationImageView)
val roomAvatarPublicDecorationImageView by bind<ImageView>(R.id.roomAvatarPublicDecorationImageView)
val roomAvatarFailSendingImageView by bind<ImageView>(R.id.roomAvatarFailSendingImageView)
val roomAvatarPresenceImageView by bind<PresenceStateImageView>(R.id.roomAvatarPresenceImageView)
val rootView by bind<ConstraintLayout>(R.id.itemRoomLayout)
}
}

View File

@ -126,7 +126,28 @@ class RoomSummaryItemFactory @Inject constructor(private val displayableEventFor
} }
val typingMessage = typingHelper.getTypingMessage(roomSummary.typingUsers) val typingMessage = typingHelper.getTypingMessage(roomSummary.typingUsers)
return RoomSummaryItem_()
return if (subtitle.isBlank() && displayMode == RoomListDisplayMode.FILTERED) {
createCenteredRoomSummaryItem(roomSummary, displayMode, showSelected, unreadCount, onClick, onLongClick)
} else {
createRoomSummaryItem(roomSummary, displayMode, subtitle, latestEventTime, typingMessage,
latestFormattedEvent, showHighlighted, showSelected, unreadCount, onClick, onLongClick)
}
}
private fun createRoomSummaryItem(
roomSummary: RoomSummary,
displayMode: RoomListDisplayMode,
subtitle: String,
latestEventTime: String,
typingMessage: String,
latestFormattedEvent: CharSequence,
showHighlighted: Boolean,
showSelected: Boolean,
unreadCount: Int,
onClick: ((RoomSummary) -> Unit)?,
onLongClick: ((RoomSummary) -> Boolean)?
) = RoomSummaryItem_()
.id(roomSummary.roomId) .id(roomSummary.roomId)
.avatarRenderer(avatarRenderer) .avatarRenderer(avatarRenderer)
// We do not display shield in the room list anymore // We do not display shield in the room list anymore
@ -146,11 +167,33 @@ class RoomSummaryItemFactory @Inject constructor(private val displayableEventFor
.unreadNotificationCount(unreadCount) .unreadNotificationCount(unreadCount)
.hasUnreadMessage(roomSummary.hasUnreadMessages) .hasUnreadMessage(roomSummary.hasUnreadMessages)
.hasDraft(roomSummary.userDrafts.isNotEmpty()) .hasDraft(roomSummary.userDrafts.isNotEmpty())
.itemLongClickListener { _ -> .itemLongClickListener { _ -> onLongClick?.invoke(roomSummary) ?: false }
onLongClick?.invoke(roomSummary) ?: false .itemClickListener { onClick?.invoke(roomSummary) }
}
private fun createCenteredRoomSummaryItem(
roomSummary: RoomSummary,
displayMode: RoomListDisplayMode,
showSelected: Boolean,
unreadCount: Int,
onClick: ((RoomSummary) -> Unit)?,
onLongClick: ((RoomSummary) -> Boolean)?
) = RoomSummaryItemCentered_()
.id(roomSummary.roomId)
.avatarRenderer(avatarRenderer)
// We do not display shield in the room list anymore
// .encryptionTrustLevel(roomSummary.roomEncryptionTrustLevel)
.displayMode(displayMode)
.isPublic(roomSummary.isPublic)
.showPresence(roomSummary.isDirect)
.userPresence(roomSummary.directUserPresence)
.matrixItem(roomSummary.toMatrixItem())
.showSelected(showSelected)
.hasFailedSending(roomSummary.hasFailedSending)
.unreadNotificationCount(unreadCount)
.hasUnreadMessage(roomSummary.hasUnreadMessages)
.hasDraft(roomSummary.userDrafts.isNotEmpty())
.itemLongClickListener { _ -> onLongClick?.invoke(roomSummary) ?: false }
.itemClickListener { onClick?.invoke(roomSummary) } .itemClickListener { onClick?.invoke(roomSummary) }
}
private fun getSearchResultSubtitle(roomSummary: RoomSummary): String { private fun getSearchResultSubtitle(roomSummary: RoomSummary): String {
val userId = roomSummary.directUserId val userId = roomSummary.directUserId

View File

@ -112,27 +112,13 @@
app:layout_constraintTop_toBottomOf="@id/roomAvatarContainer" app:layout_constraintTop_toBottomOf="@id/roomAvatarContainer"
tools:layout_marginStart="20dp" /> tools:layout_marginStart="20dp" />
<Space
android:id="@+id/topMarginSpace"
android:layout_width="0dp"
android:layout_height="12dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Space
android:id="@+id/centerTitleSpace"
android:layout_width="match_parent"
android:layout_height="15dp"
android:visibility="gone"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/topMarginSpace" />
<TextView <TextView
android:id="@+id/roomNameView" android:id="@+id/roomNameView"
style="@style/Widget.Vector.TextView.Subtitle" style="@style/Widget.Vector.TextView.Subtitle"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="@dimen/layout_horizontal_margin" android:layout_marginStart="@dimen/layout_horizontal_margin"
android:layout_marginTop="12dp"
android:layout_marginEnd="8dp" android:layout_marginEnd="8dp"
android:duplicateParentState="true" android:duplicateParentState="true"
android:ellipsize="end" android:ellipsize="end"
@ -144,7 +130,7 @@
app:layout_constraintHorizontal_bias="0.0" app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintHorizontal_chainStyle="packed" app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toEndOf="@id/roomAvatarContainer" app:layout_constraintStart_toEndOf="@id/roomAvatarContainer"
app:layout_constraintTop_toBottomOf="@id/centerTitleSpace" app:layout_constraintTop_toTopOf="parent"
tools:text="@sample/users.json/data/displayName" /> tools:text="@sample/users.json/data/displayName" />
<ImageView <ImageView

View File

@ -0,0 +1,152 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:id="@+id/itemRoomLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:colorBackground"
android:clickable="true"
android:focusable="true"
android:foreground="?attr/selectableItemBackground"
tools:viewBindingIgnore="true">
<FrameLayout
android:id="@+id/roomAvatarContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="12dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/roomAvatarImageView"
android:layout_width="56dp"
android:layout_height="56dp"
android:importantForAccessibility="no"
tools:src="@sample/room_round_avatars" />
<ImageView
android:id="@+id/roomAvatarCheckedImageView"
android:layout_width="56dp"
android:layout_height="56dp"
android:contentDescription="@string/a11y_checked"
android:scaleType="centerInside"
android:src="@drawable/ic_material_done"
app:tint="@android:color/white"
tools:ignore="MissingPrefix" />
</FrameLayout>
<ImageView
android:id="@+id/roomAvatarFailSendingImageView"
android:layout_width="16dp"
android:layout_height="16dp"
android:contentDescription="@string/a11y_error_some_message_not_sent"
android:src="@drawable/ic_warning_badge"
app:layout_constraintCircle="@id/roomAvatarContainer"
app:layout_constraintCircleAngle="45"
app:layout_constraintCircleRadius="30dp"
tools:ignore="MissingConstraints" />
<!-- Note: this is always gone now -->
<im.vector.app.core.ui.views.ShieldImageView
android:id="@+id/roomAvatarDecorationImageView"
android:layout_width="24dp"
android:layout_height="24dp"
app:layout_constraintCircle="@id/roomAvatarContainer"
app:layout_constraintCircleAngle="135"
app:layout_constraintCircleRadius="28dp"
tools:ignore="MissingConstraints"
tools:visibility="gone" />
<ImageView
android:id="@+id/roomAvatarPublicDecorationImageView"
android:layout_width="20dp"
android:layout_height="20dp"
android:background="@drawable/background_circle"
android:contentDescription="@string/a11y_public_room"
android:padding="2dp"
android:src="@drawable/ic_public_room"
android:visibility="gone"
app:layout_constraintCircle="@id/roomAvatarContainer"
app:layout_constraintCircleAngle="135"
app:layout_constraintCircleRadius="28dp"
tools:ignore="MissingConstraints"
tools:visibility="visible" />
<im.vector.app.core.ui.views.PresenceStateImageView
android:id="@+id/roomAvatarPresenceImageView"
android:layout_width="16dp"
android:layout_height="16dp"
android:background="@drawable/background_circle"
android:importantForAccessibility="no"
android:padding="2dp"
android:visibility="gone"
app:layout_constraintCircle="@id/roomAvatarContainer"
app:layout_constraintCircleAngle="135"
app:layout_constraintCircleRadius="28dp"
tools:ignore="MissingConstraints"
tools:layout_constraintCircleRadius="8dp"
tools:src="@drawable/ic_presence_offline"
tools:visibility="visible" />
<!-- Margin bottom does not work, so I use space -->
<Space
android:id="@+id/roomAvatarBottomSpace"
android:layout_width="0dp"
android:layout_height="12dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/roomAvatarContainer"
tools:layout_marginStart="20dp" />
<TextView
android:id="@+id/roomNameView"
style="@style/Widget.Vector.TextView.Subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/layout_horizontal_margin"
android:layout_marginEnd="8dp"
android:duplicateParentState="true"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?vctr_content_primary"
android:textStyle="bold"
app:layout_constrainedWidth="true"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toEndOf="@id/roomAvatarContainer"
app:layout_constraintTop_toTopOf="@id/roomAvatarContainer"
app:layout_constraintBottom_toBottomOf="@id/roomAvatarContainer"
tools:text="@sample/users.json/data/displayName" />
<TextView
android:id="@+id/roomTypingView"
style="@style/Widget.Vector.TextView.Body"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:layout_marginEnd="8dp"
android:ellipsize="end"
android:maxLines="2"
android:textAlignment="viewStart"
android:textColor="?colorPrimary"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/roomNameView"
app:layout_constraintTop_toBottomOf="@id/roomNameView"
tools:text="Alice is typing…"
tools:visibility="gone" />
<!-- We use vctr_list_separator_system here for a better rendering -->
<View
android:id="@+id/roomDividerView"
android:layout_width="0dp"
android:layout_height="1dp"
android:background="?vctr_list_separator_system"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>