mirror of
https://github.com/vector-im/element-android.git
synced 2024-11-15 01:35:07 +08:00
recents carousel for new home screen layout (#6707)
This commit is contained in:
parent
6e1e31bac1
commit
6045eac87a
@ -74,6 +74,7 @@ ext.groups = [
|
|||||||
'com.github.javaparser',
|
'com.github.javaparser',
|
||||||
'com.github.piasy',
|
'com.github.piasy',
|
||||||
'com.github.shyiko.klob',
|
'com.github.shyiko.klob',
|
||||||
|
'com.github.rubensousa',
|
||||||
'com.google',
|
'com.google',
|
||||||
'com.google.android',
|
'com.google.android',
|
||||||
'com.google.api.grpc',
|
'com.google.api.grpc',
|
||||||
|
@ -427,6 +427,9 @@ dependencies {
|
|||||||
implementation libs.airbnb.epoxyPaging
|
implementation libs.airbnb.epoxyPaging
|
||||||
implementation libs.airbnb.mavericks
|
implementation libs.airbnb.mavericks
|
||||||
|
|
||||||
|
// Snap Helper https://github.com/rubensousa/GravitySnapHelper
|
||||||
|
implementation 'com.github.rubensousa:gravitysnaphelper:2.2.2'
|
||||||
|
|
||||||
// Nightly
|
// Nightly
|
||||||
// API-only library
|
// API-only library
|
||||||
gplayImplementation libs.google.appdistributionApi
|
gplayImplementation libs.google.appdistributionApi
|
||||||
|
@ -25,17 +25,21 @@ import android.content.res.Configuration
|
|||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.HandlerThread
|
import android.os.HandlerThread
|
||||||
import android.os.StrictMode
|
import android.os.StrictMode
|
||||||
|
import android.view.Gravity
|
||||||
import androidx.core.provider.FontRequest
|
import androidx.core.provider.FontRequest
|
||||||
import androidx.core.provider.FontsContractCompat
|
import androidx.core.provider.FontsContractCompat
|
||||||
import androidx.lifecycle.DefaultLifecycleObserver
|
import androidx.lifecycle.DefaultLifecycleObserver
|
||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.LifecycleOwner
|
||||||
import androidx.lifecycle.ProcessLifecycleOwner
|
import androidx.lifecycle.ProcessLifecycleOwner
|
||||||
import androidx.multidex.MultiDex
|
import androidx.multidex.MultiDex
|
||||||
|
import androidx.recyclerview.widget.SnapHelper
|
||||||
|
import com.airbnb.epoxy.Carousel
|
||||||
import com.airbnb.epoxy.EpoxyAsyncUtil
|
import com.airbnb.epoxy.EpoxyAsyncUtil
|
||||||
import com.airbnb.epoxy.EpoxyController
|
import com.airbnb.epoxy.EpoxyController
|
||||||
import com.airbnb.mvrx.Mavericks
|
import com.airbnb.mvrx.Mavericks
|
||||||
import com.facebook.stetho.Stetho
|
import com.facebook.stetho.Stetho
|
||||||
import com.gabrielittner.threetenbp.LazyThreeTen
|
import com.gabrielittner.threetenbp.LazyThreeTen
|
||||||
|
import com.github.rubensousa.gravitysnaphelper.GravitySnapHelper
|
||||||
import com.mapbox.mapboxsdk.Mapbox
|
import com.mapbox.mapboxsdk.Mapbox
|
||||||
import com.vanniktech.emoji.EmojiManager
|
import com.vanniktech.emoji.EmojiManager
|
||||||
import com.vanniktech.emoji.google.GoogleEmojiProvider
|
import com.vanniktech.emoji.google.GoogleEmojiProvider
|
||||||
@ -141,8 +145,9 @@ class VectorApplication :
|
|||||||
logInfo()
|
logInfo()
|
||||||
LazyThreeTen.init(this)
|
LazyThreeTen.init(this)
|
||||||
Mavericks.initialize(debugMode = false)
|
Mavericks.initialize(debugMode = false)
|
||||||
EpoxyController.defaultDiffingHandler = EpoxyAsyncUtil.getAsyncBackgroundHandler()
|
|
||||||
EpoxyController.defaultModelBuildingHandler = EpoxyAsyncUtil.getAsyncBackgroundHandler()
|
configureEpoxy()
|
||||||
|
|
||||||
registerActivityLifecycleCallbacks(VectorActivityLifecycleCallbacks(popupAlertManager))
|
registerActivityLifecycleCallbacks(VectorActivityLifecycleCallbacks(popupAlertManager))
|
||||||
val fontRequest = FontRequest(
|
val fontRequest = FontRequest(
|
||||||
"com.google.android.gms.fonts",
|
"com.google.android.gms.fonts",
|
||||||
@ -198,6 +203,16 @@ class VectorApplication :
|
|||||||
Mapbox.getInstance(this)
|
Mapbox.getInstance(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun configureEpoxy() {
|
||||||
|
EpoxyController.defaultDiffingHandler = EpoxyAsyncUtil.getAsyncBackgroundHandler()
|
||||||
|
EpoxyController.defaultModelBuildingHandler = EpoxyAsyncUtil.getAsyncBackgroundHandler()
|
||||||
|
Carousel.setDefaultGlobalSnapHelperFactory(object : Carousel.SnapHelperFactory() {
|
||||||
|
override fun buildSnapHelper(context: Context?): SnapHelper {
|
||||||
|
return GravitySnapHelper(Gravity.START)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
private fun enableStrictModeIfNeeded() {
|
private fun enableStrictModeIfNeeded() {
|
||||||
if (Config.ENABLE_STRICT_MODE_LOGS) {
|
if (Config.ENABLE_STRICT_MODE_LOGS) {
|
||||||
StrictMode.setThreadPolicy(
|
StrictMode.setThreadPolicy(
|
||||||
|
@ -30,6 +30,7 @@ import com.airbnb.mvrx.withState
|
|||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.epoxy.LayoutManagerStateRestorer
|
import im.vector.app.core.epoxy.LayoutManagerStateRestorer
|
||||||
|
import im.vector.app.core.extensions.cleanup
|
||||||
import im.vector.app.core.platform.StateView
|
import im.vector.app.core.platform.StateView
|
||||||
import im.vector.app.core.platform.VectorBaseFragment
|
import im.vector.app.core.platform.VectorBaseFragment
|
||||||
import im.vector.app.core.resources.UserPreferencesProvider
|
import im.vector.app.core.resources.UserPreferencesProvider
|
||||||
@ -43,6 +44,7 @@ import im.vector.app.features.home.room.list.RoomSummaryPagedController
|
|||||||
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsBottomSheet
|
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsBottomSheet
|
||||||
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedAction
|
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedAction
|
||||||
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedActionViewModel
|
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedActionViewModel
|
||||||
|
import im.vector.app.features.home.room.list.home.recent.RecentRoomCarouselController
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
@ -53,7 +55,8 @@ import javax.inject.Inject
|
|||||||
|
|
||||||
class HomeRoomListFragment @Inject constructor(
|
class HomeRoomListFragment @Inject constructor(
|
||||||
private val roomSummaryItemFactory: RoomSummaryItemFactory,
|
private val roomSummaryItemFactory: RoomSummaryItemFactory,
|
||||||
private val userPreferencesProvider: UserPreferencesProvider
|
private val userPreferencesProvider: UserPreferencesProvider,
|
||||||
|
private val recentRoomCarouselController: RecentRoomCarouselController
|
||||||
) : VectorBaseFragment<FragmentRoomListBinding>(),
|
) : VectorBaseFragment<FragmentRoomListBinding>(),
|
||||||
RoomListListener {
|
RoomListListener {
|
||||||
|
|
||||||
@ -180,6 +183,12 @@ class HomeRoomListFragment @Inject constructor(
|
|||||||
}
|
}
|
||||||
}.adapter
|
}.adapter
|
||||||
}
|
}
|
||||||
|
is HomeRoomSection.RecentRoomsData -> recentRoomCarouselController.also { controller ->
|
||||||
|
controller.listener = this
|
||||||
|
data.list.observe(viewLifecycleOwner) { list ->
|
||||||
|
controller.submitList(list)
|
||||||
|
}
|
||||||
|
}.adapter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,6 +201,12 @@ class HomeRoomListFragment @Inject constructor(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
views.roomListView.cleanup()
|
||||||
|
recentRoomCarouselController.listener = null
|
||||||
|
super.onDestroyView()
|
||||||
|
}
|
||||||
|
|
||||||
// region RoomListListener
|
// region RoomListListener
|
||||||
|
|
||||||
override fun onRoomClicked(room: RoomSummary) {
|
override fun onRoomClicked(room: RoomSummary) {
|
||||||
|
@ -37,6 +37,7 @@ import kotlinx.coroutines.flow.onEach
|
|||||||
import kotlinx.coroutines.flow.onStart
|
import kotlinx.coroutines.flow.onStart
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.matrix.android.sdk.api.extensions.orFalse
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
|
import org.matrix.android.sdk.api.query.QueryStringValue
|
||||||
import org.matrix.android.sdk.api.query.SpaceFilter
|
import org.matrix.android.sdk.api.query.SpaceFilter
|
||||||
import org.matrix.android.sdk.api.query.toActiveSpaceOrNoFilter
|
import org.matrix.android.sdk.api.query.toActiveSpaceOrNoFilter
|
||||||
import org.matrix.android.sdk.api.query.toActiveSpaceOrOrphanRooms
|
import org.matrix.android.sdk.api.query.toActiveSpaceOrOrphanRooms
|
||||||
@ -45,6 +46,7 @@ import org.matrix.android.sdk.api.session.getRoom
|
|||||||
import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams
|
import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams
|
||||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||||
import org.matrix.android.sdk.api.session.room.model.tag.RoomTag
|
import org.matrix.android.sdk.api.session.room.model.tag.RoomTag
|
||||||
|
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
||||||
import org.matrix.android.sdk.api.session.room.state.isPublic
|
import org.matrix.android.sdk.api.session.room.state.isPublic
|
||||||
|
|
||||||
class HomeRoomListViewModel @AssistedInject constructor(
|
class HomeRoomListViewModel @AssistedInject constructor(
|
||||||
@ -78,6 +80,7 @@ class HomeRoomListViewModel @AssistedInject constructor(
|
|||||||
private fun configureSections() {
|
private fun configureSections() {
|
||||||
val newSections = mutableSetOf<HomeRoomSection>()
|
val newSections = mutableSetOf<HomeRoomSection>()
|
||||||
|
|
||||||
|
newSections.add(getRecentRoomsSection())
|
||||||
newSections.add(getAllRoomsSection())
|
newSections.add(getAllRoomsSection())
|
||||||
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
@ -89,6 +92,18 @@ class HomeRoomListViewModel @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getRecentRoomsSection(): HomeRoomSection {
|
||||||
|
val liveList = session.roomService()
|
||||||
|
.getBreadcrumbsLive(roomSummaryQueryParams {
|
||||||
|
displayName = QueryStringValue.NoCondition
|
||||||
|
memberships = listOf(Membership.JOIN)
|
||||||
|
})
|
||||||
|
|
||||||
|
return HomeRoomSection.RecentRoomsData(
|
||||||
|
list = liveList
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private fun getAllRoomsSection(): HomeRoomSection.RoomSummaryData {
|
private fun getAllRoomsSection(): HomeRoomSection.RoomSummaryData {
|
||||||
val builder = RoomSummaryQueryParams.Builder().also {
|
val builder = RoomSummaryQueryParams.Builder().also {
|
||||||
it.memberships = listOf(Membership.JOIN)
|
it.memberships = listOf(Membership.JOIN)
|
||||||
|
@ -24,4 +24,8 @@ sealed class HomeRoomSection {
|
|||||||
data class RoomSummaryData(
|
data class RoomSummaryData(
|
||||||
val list: LiveData<PagedList<RoomSummary>>
|
val list: LiveData<PagedList<RoomSummary>>
|
||||||
) : HomeRoomSection()
|
) : HomeRoomSection()
|
||||||
|
|
||||||
|
data class RecentRoomsData(
|
||||||
|
val list: LiveData<List<RoomSummary>>
|
||||||
|
) : HomeRoomSection()
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022 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.home.recent
|
||||||
|
|
||||||
|
import android.content.res.Resources
|
||||||
|
import android.util.TypedValue
|
||||||
|
import com.airbnb.epoxy.Carousel
|
||||||
|
import com.airbnb.epoxy.CarouselModelBuilder
|
||||||
|
import com.airbnb.epoxy.EpoxyController
|
||||||
|
import com.airbnb.epoxy.EpoxyModel
|
||||||
|
import com.airbnb.epoxy.carousel
|
||||||
|
import im.vector.app.features.home.AvatarRenderer
|
||||||
|
import im.vector.app.features.home.room.list.RoomListListener
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
|
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class RecentRoomCarouselController @Inject constructor(
|
||||||
|
private val avatarRenderer: AvatarRenderer,
|
||||||
|
private val resources: Resources,
|
||||||
|
) : EpoxyController() {
|
||||||
|
|
||||||
|
private var data: List<RoomSummary>? = null
|
||||||
|
var listener: RoomListListener? = null
|
||||||
|
|
||||||
|
private val hPadding = TypedValue.applyDimension(
|
||||||
|
TypedValue.COMPLEX_UNIT_DIP,
|
||||||
|
16f,
|
||||||
|
resources.displayMetrics
|
||||||
|
).toInt()
|
||||||
|
|
||||||
|
private val itemSpacing = TypedValue.applyDimension(
|
||||||
|
TypedValue.COMPLEX_UNIT_DIP,
|
||||||
|
24f,
|
||||||
|
resources.displayMetrics
|
||||||
|
).toInt()
|
||||||
|
|
||||||
|
fun submitList(recentList: List<RoomSummary>) {
|
||||||
|
this.data = recentList
|
||||||
|
requestModelBuild()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun buildModels() {
|
||||||
|
val host = this
|
||||||
|
data?.let { data ->
|
||||||
|
carousel {
|
||||||
|
id("recents_carousel")
|
||||||
|
padding(Carousel.Padding(host.hPadding, host.itemSpacing))
|
||||||
|
withModelsFrom(data) { roomSummary ->
|
||||||
|
val onClick = host.listener?.let { it::onRoomClicked }
|
||||||
|
val onLongClick = host.listener?.let { it::onRoomLongClicked }
|
||||||
|
|
||||||
|
RecentRoomItem_()
|
||||||
|
.id(roomSummary.roomId)
|
||||||
|
.avatarRenderer(host.avatarRenderer)
|
||||||
|
.matrixItem(roomSummary.toMatrixItem())
|
||||||
|
.unreadNotificationCount(roomSummary.notificationCount)
|
||||||
|
.showHighlighted(roomSummary.highlightCount > 0)
|
||||||
|
.itemLongClickListener { _ -> onLongClick?.invoke(roomSummary) ?: false }
|
||||||
|
.itemClickListener { onClick?.invoke(roomSummary) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private inline fun <T> CarouselModelBuilder.withModelsFrom(
|
||||||
|
items: List<T>,
|
||||||
|
modelBuilder: (T) -> EpoxyModel<*>
|
||||||
|
) {
|
||||||
|
models(items.map { modelBuilder(it) })
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022 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.home.recent
|
||||||
|
|
||||||
|
import android.view.HapticFeedbackConstants
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.TextView
|
||||||
|
import com.airbnb.epoxy.EpoxyAttribute
|
||||||
|
import com.airbnb.epoxy.EpoxyModelClass
|
||||||
|
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.features.displayname.getBestName
|
||||||
|
import im.vector.app.features.home.AvatarRenderer
|
||||||
|
import im.vector.app.features.home.room.list.UnreadCounterBadgeView
|
||||||
|
import org.matrix.android.sdk.api.util.MatrixItem
|
||||||
|
|
||||||
|
@EpoxyModelClass
|
||||||
|
abstract class RecentRoomItem : VectorEpoxyModel<RecentRoomItem.Holder>(R.layout.item_recent_room) {
|
||||||
|
|
||||||
|
@EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer
|
||||||
|
@EpoxyAttribute lateinit var matrixItem: MatrixItem
|
||||||
|
@EpoxyAttribute var unreadNotificationCount: Int = 0
|
||||||
|
@EpoxyAttribute var showHighlighted: Boolean = false
|
||||||
|
|
||||||
|
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
|
||||||
|
var itemLongClickListener: View.OnLongClickListener? = null
|
||||||
|
|
||||||
|
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
|
||||||
|
var itemClickListener: ClickListener? = null
|
||||||
|
|
||||||
|
override fun bind(holder: Holder) {
|
||||||
|
super.bind(holder)
|
||||||
|
|
||||||
|
holder.rootView.onClick(itemClickListener)
|
||||||
|
holder.rootView.setOnLongClickListener {
|
||||||
|
it.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
|
||||||
|
itemLongClickListener?.onLongClick(it) ?: false
|
||||||
|
}
|
||||||
|
|
||||||
|
avatarRenderer.render(matrixItem, holder.avatarImageView)
|
||||||
|
holder.avatarImageView.contentDescription = matrixItem.getBestName()
|
||||||
|
holder.unreadCounterBadgeView.render(UnreadCounterBadgeView.State(unreadNotificationCount, showHighlighted))
|
||||||
|
holder.title.text = matrixItem.getBestName()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun unbind(holder: Holder) {
|
||||||
|
holder.rootView.setOnClickListener(null)
|
||||||
|
holder.rootView.setOnLongClickListener(null)
|
||||||
|
avatarRenderer.clear(holder.avatarImageView)
|
||||||
|
super.unbind(holder)
|
||||||
|
}
|
||||||
|
|
||||||
|
class Holder : VectorEpoxyHolder() {
|
||||||
|
val unreadCounterBadgeView by bind<UnreadCounterBadgeView>(R.id.recentUnreadCounterBadgeView)
|
||||||
|
val avatarImageView by bind<ImageView>(R.id.recentImageView)
|
||||||
|
val title by bind<TextView>(R.id.recentTitle)
|
||||||
|
val rootView by bind<ViewGroup>(R.id.recentRoot)
|
||||||
|
}
|
||||||
|
}
|
61
vector/src/main/res/layout/item_recent_room.xml
Normal file
61
vector/src/main/res/layout/item_recent_room.xml
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<?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/recentRoot"
|
||||||
|
android:layout_width="60dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?android:colorBackground"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:foreground="?attr/selectableItemBackground"
|
||||||
|
tools:viewBindingIgnore="true">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/recentImageView"
|
||||||
|
android:layout_width="60dp"
|
||||||
|
android:layout_height="60dp"
|
||||||
|
android:layout_marginHorizontal="12dp"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:ignore="ContentDescription"
|
||||||
|
tools:src="@sample/room_round_avatars" />
|
||||||
|
|
||||||
|
<im.vector.app.features.home.room.list.UnreadCounterBadgeView
|
||||||
|
android:id="@+id/recentUnreadCounterBadgeView"
|
||||||
|
style="@style/Widget.Vector.TextView.Micro"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:minWidth="18dp"
|
||||||
|
android:minHeight="18dp"
|
||||||
|
android:textColor="?colorOnError"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintCircle="@id/recentImageView"
|
||||||
|
app:layout_constraintCircleAngle="45"
|
||||||
|
app:layout_constraintCircleRadius="28dp"
|
||||||
|
tools:background="@drawable/bg_unread_highlight"
|
||||||
|
tools:ignore="MissingConstraints"
|
||||||
|
tools:text="24"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/recentTitle"
|
||||||
|
style="@style/Widget.Vector.TextView.Body"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:lines="1"
|
||||||
|
android:textColor="?vctr_content_primary"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/recentImageView"
|
||||||
|
tools:text="Coffee" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
Loading…
Reference in New Issue
Block a user