1
0
mirror of https://github.com/vector-im/riotX-android synced 2025-10-06 00:02:48 +02:00

Compare commits

...

4 Commits

Author SHA1 Message Date
Benoit Marty
071db7a4d8 With icon from design team 2020-09-10 17:06:32 +02:00
Benoit Marty
2c747cc847 Show switch if collapsed or for invites 2020-09-10 14:41:54 +02:00
Benoit Marty
87322ce8b0 Rename ids 2020-09-10 14:11:58 +02:00
Benoit Marty
5286e6983c Experiment Grid view on Home 2020-09-09 19:44:45 +02:00
17 changed files with 427 additions and 61 deletions

View File

@@ -112,7 +112,7 @@ android {
ndkVersion "21.3.6528147"
defaultConfig {
applicationId "im.vector.app"
applicationId "im.vector.app.grid"
// Set to API 21: see #405
minSdkVersion 21
targetSdkVersion 29
@@ -186,7 +186,7 @@ android {
buildTypes {
debug {
applicationIdSuffix ".debug"
resValue "string", "app_name", "Element dbg"
resValue "string", "app_name", "Element Grid"
resValue "bool", "debug_mode", "true"
buildConfigField "boolean", "LOW_PRIVACY_LOG_ENABLE", "false"
@@ -195,7 +195,7 @@ android {
}
release {
resValue "string", "app_name", "Element (Riot.im)"
resValue "string", "app_name", "Element Grid"
resValue "bool", "debug_mode", "false"
buildConfigField "boolean", "LOW_PRIVACY_LOG_ENABLE", "false"

View File

@@ -10,7 +10,7 @@
"client_info": {
"mobilesdk_app_id": "1:912726360885:android:4ef8f3a0021e774d",
"android_client_info": {
"package_name": "im.vector.app.debug"
"package_name": "im.vector.app.grid.debug"
}
},
"oauth_client": [

View File

@@ -10,7 +10,7 @@
"client_info": {
"mobilesdk_app_id": "1:912726360885:android:4ef8f3a0021e774d",
"android_client_info": {
"package_name": "im.vector.app"
"package_name": "im.vector.app.grid"
}
},
"oauth_client": [

View File

@@ -17,9 +17,11 @@
package im.vector.app.features.home.room.list
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.DrawableCompat
import androidx.core.view.isVisible
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.app.R
@@ -32,8 +34,11 @@ abstract class RoomCategoryItem : VectorEpoxyModel<RoomCategoryItem.Holder>() {
@EpoxyAttribute lateinit var title: CharSequence
@EpoxyAttribute var expanded: Boolean = false
@EpoxyAttribute var mode: RoomListViewState.CategoryMode = RoomListViewState.CategoryMode.List
@EpoxyAttribute var unreadNotificationCount: Int = 0
@EpoxyAttribute var showHighlighted: Boolean = false
@EpoxyAttribute var showSwitchMode: Boolean = true
@EpoxyAttribute var changeModeListener: ((RoomListViewState.CategoryMode) -> Unit)? = null
@EpoxyAttribute var listener: (() -> Unit)? = null
override fun bind(holder: Holder) {
@@ -47,11 +52,32 @@ abstract class RoomCategoryItem : VectorEpoxyModel<RoomCategoryItem.Holder>() {
holder.titleView.setCompoundDrawablesWithIntrinsicBounds(null, null, expandedArrowDrawable, null)
holder.titleView.text = title
holder.rootView.setOnClickListener { listener?.invoke() }
if (showSwitchMode && expanded) {
holder.modeSwitch.isVisible = true
holder.modeSwitch.setImageResource(
when (mode) {
RoomListViewState.CategoryMode.Grid -> R.drawable.ic_mode_grid_24dp
RoomListViewState.CategoryMode.List -> R.drawable.ic_mode_list_24dp
}
)
holder.modeSwitch.setOnClickListener {
changeModeListener?.invoke(
when (mode) {
RoomListViewState.CategoryMode.Grid -> RoomListViewState.CategoryMode.List
RoomListViewState.CategoryMode.List -> RoomListViewState.CategoryMode.Grid
}
)
}
} else {
holder.modeSwitch.isVisible = false
}
}
class Holder : VectorEpoxyHolder() {
val unreadCounterBadgeView by bind<UnreadCounterBadgeView>(R.id.roomCategoryUnreadCounterBadgeView)
val titleView by bind<TextView>(R.id.roomCategoryTitleView)
val modeSwitch by bind<ImageView>(R.id.roomCategoryMode)
val rootView by bind<ViewGroup>(R.id.roomCategoryRootView)
}
}

View File

@@ -23,6 +23,7 @@ import org.matrix.android.sdk.api.session.room.notification.RoomNotificationStat
sealed class RoomListAction : VectorViewModelAction {
data class SelectRoom(val roomSummary: RoomSummary) : RoomListAction()
data class ToggleCategory(val category: RoomCategory) : RoomListAction()
data class ChangeCategoryMode(val category: RoomCategory, val newCategoryMode: RoomListViewState.CategoryMode) : RoomListAction()
data class AcceptInvitation(val roomSummary: RoomSummary) : RoomListAction()
data class RejectInvitation(val roomSummary: RoomSummary) : RoomListAction()
data class FilterWith(val filter: String) : RoomListAction()

View File

@@ -18,13 +18,14 @@ package im.vector.app.features.home.room.list
import android.os.Bundle
import android.os.Parcelable
import android.util.DisplayMetrics
import android.view.Menu
import android.view.MenuItem
import android.view.View
import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.airbnb.epoxy.OnModelBuildFinishedListener
import com.airbnb.mvrx.Fail
@@ -40,6 +41,7 @@ import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.OnBackPressed
import im.vector.app.core.platform.StateView
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.utils.DimensionConverter
import im.vector.app.features.home.RoomListDisplayMode
import im.vector.app.features.home.room.list.actions.RoomListActionsArgs
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsBottomSheet
@@ -47,12 +49,13 @@ import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedA
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedActionViewModel
import im.vector.app.features.home.room.list.widget.FabMenuView
import im.vector.app.features.notifications.NotificationDrawerManager
import im.vector.app.features.roomprofile.uploads.media.IMAGE_SIZE_DP
import kotlinx.android.parcel.Parcelize
import kotlinx.android.synthetic.main.fragment_room_list.*
import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState
import kotlinx.android.parcel.Parcelize
import kotlinx.android.synthetic.main.fragment_room_list.*
import javax.inject.Inject
@Parcelize
@@ -64,8 +67,8 @@ class RoomListFragment @Inject constructor(
private val roomController: RoomSummaryController,
val roomListViewModelFactory: RoomListViewModel.Factory,
private val notificationDrawerManager: NotificationDrawerManager,
private val sharedViewPool: RecyclerView.RecycledViewPool
private val sharedViewPool: RecyclerView.RecycledViewPool,
private val dimensionConverter: DimensionConverter
) : VectorBaseFragment(), RoomSummaryController.Listener, OnBackPressed, FabMenuView.Listener {
private var modelBuildListener: OnModelBuildFinishedListener? = null
@@ -103,10 +106,10 @@ class RoomListFragment @Inject constructor(
sharedActionViewModel = activityViewModelProvider.get(RoomListQuickActionsSharedActionViewModel::class.java)
roomListViewModel.observeViewEvents {
when (it) {
is RoomListViewEvents.Loading -> showLoading(it.message)
is RoomListViewEvents.Failure -> showFailure(it.throwable)
is RoomListViewEvents.Loading -> showLoading(it.message)
is RoomListViewEvents.Failure -> showFailure(it.throwable)
is RoomListViewEvents.SelectRoom -> handleSelectRoom(it)
is RoomListViewEvents.Done -> Unit
is RoomListViewEvents.Done -> Unit
}.exhaustive
}
@@ -139,9 +142,9 @@ class RoomListFragment @Inject constructor(
private fun setupCreateRoomButton() {
when (roomListParams.displayMode) {
RoomListDisplayMode.NOTIFICATIONS -> createChatFabMenu.isVisible = true
RoomListDisplayMode.PEOPLE -> createChatRoomButton.isVisible = true
RoomListDisplayMode.ROOMS -> createGroupRoomButton.isVisible = true
else -> Unit // No button in this mode
RoomListDisplayMode.PEOPLE -> createChatRoomButton.isVisible = true
RoomListDisplayMode.ROOMS -> createGroupRoomButton.isVisible = true
else -> Unit // No button in this mode
}
createChatRoomButton.debouncedClicks {
@@ -166,8 +169,8 @@ class RoomListFragment @Inject constructor(
when (roomListParams.displayMode) {
RoomListDisplayMode.NOTIFICATIONS -> createChatFabMenu.hide()
RoomListDisplayMode.PEOPLE -> createChatRoomButton.hide()
RoomListDisplayMode.ROOMS -> createGroupRoomButton.hide()
else -> Unit
RoomListDisplayMode.ROOMS -> createGroupRoomButton.hide()
else -> Unit
}
}
}
@@ -191,7 +194,8 @@ class RoomListFragment @Inject constructor(
}
private fun setupRecyclerView() {
val layoutManager = LinearLayoutManager(context)
val numOfColumns = getNumberOfColumns()
val layoutManager = GridLayoutManager(context, numOfColumns)
stateRestorer = LayoutManagerStateRestorer(layoutManager).register()
roomListView.layoutManager = layoutManager
roomListView.itemAnimator = RoomListAnimator()
@@ -202,40 +206,49 @@ class RoomListFragment @Inject constructor(
roomController.addModelBuildListener(modelBuildListener)
roomListView.adapter = roomController.adapter
stateView.contentView = roomListView
roomController.spanCount = numOfColumns
layoutManager.spanSizeLookup = roomController.spanSizeLookup
}
private fun getNumberOfColumns(): Int {
val displayMetrics = DisplayMetrics()
requireActivity().windowManager.defaultDisplay.getMetrics(displayMetrics)
return dimensionConverter.pxToDp(displayMetrics.widthPixels) / ITEM_SIZE_DP
}
private val showFabRunnable = Runnable {
if (isAdded) {
when (roomListParams.displayMode) {
RoomListDisplayMode.NOTIFICATIONS -> createChatFabMenu.show()
RoomListDisplayMode.PEOPLE -> createChatRoomButton.show()
RoomListDisplayMode.ROOMS -> createGroupRoomButton.show()
else -> Unit
RoomListDisplayMode.PEOPLE -> createChatRoomButton.show()
RoomListDisplayMode.ROOMS -> createGroupRoomButton.show()
else -> Unit
}
}
}
private fun handleQuickActions(quickAction: RoomListQuickActionsSharedAction) {
when (quickAction) {
is RoomListQuickActionsSharedAction.NotificationsAllNoisy -> {
is RoomListQuickActionsSharedAction.NotificationsAllNoisy -> {
roomListViewModel.handle(RoomListAction.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.ALL_MESSAGES_NOISY))
}
is RoomListQuickActionsSharedAction.NotificationsAll -> {
is RoomListQuickActionsSharedAction.NotificationsAll -> {
roomListViewModel.handle(RoomListAction.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.ALL_MESSAGES))
}
is RoomListQuickActionsSharedAction.NotificationsMentionsOnly -> {
roomListViewModel.handle(RoomListAction.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.MENTIONS_ONLY))
}
is RoomListQuickActionsSharedAction.NotificationsMute -> {
is RoomListQuickActionsSharedAction.NotificationsMute -> {
roomListViewModel.handle(RoomListAction.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.MUTE))
}
is RoomListQuickActionsSharedAction.Settings -> {
is RoomListQuickActionsSharedAction.Settings -> {
navigator.openRoomProfile(requireActivity(), quickAction.roomId)
}
is RoomListQuickActionsSharedAction.Favorite -> {
is RoomListQuickActionsSharedAction.Favorite -> {
roomListViewModel.handle(RoomListAction.ToggleFavorite(quickAction.roomId))
}
is RoomListQuickActionsSharedAction.Leave -> {
is RoomListQuickActionsSharedAction.Leave -> {
AlertDialog.Builder(requireContext())
.setTitle(R.string.room_participants_leave_prompt_title)
.setMessage(R.string.room_participants_leave_prompt_msg)
@@ -252,8 +265,8 @@ class RoomListFragment @Inject constructor(
override fun invalidate() = withState(roomListViewModel) { state ->
when (state.asyncFilteredRooms) {
is Incomplete -> renderLoading()
is Success -> renderSuccess(state)
is Fail -> renderFailure(state.asyncFilteredRooms.error)
is Success -> renderSuccess(state)
is Fail -> renderFailure(state.asyncFilteredRooms.error)
}
roomController.update(state)
// Mark all as read menu
@@ -308,13 +321,13 @@ class RoomListFragment @Inject constructor(
ContextCompat.getDrawable(requireContext(), R.drawable.ic_home_bottom_chat),
getString(R.string.room_list_people_empty_body)
)
RoomListDisplayMode.ROOMS ->
RoomListDisplayMode.ROOMS ->
StateView.State.Empty(
getString(R.string.room_list_rooms_empty_title),
ContextCompat.getDrawable(requireContext(), R.drawable.ic_home_bottom_group),
getString(R.string.room_list_rooms_empty_body)
)
else ->
else ->
// Always display the content in this mode, because if the footer
StateView.State.Content
}
@@ -368,7 +381,15 @@ class RoomListFragment @Inject constructor(
roomListViewModel.handle(RoomListAction.ToggleCategory(roomCategory))
}
override fun onChangeModeRoomCategory(roomCategory: RoomCategory, newMode: RoomListViewState.CategoryMode) {
roomListViewModel.handle(RoomListAction.ChangeCategoryMode(roomCategory, newMode))
}
override fun createRoom(initialName: String) {
navigator.openCreateRoom(requireActivity(), initialName)
}
companion object {
const val ITEM_SIZE_DP = 80
}
}

View File

@@ -64,6 +64,7 @@ class RoomListViewModel @Inject constructor(initialState: RoomListViewState,
when (action) {
is RoomListAction.SelectRoom -> handleSelectRoom(action)
is RoomListAction.ToggleCategory -> handleToggleCategory(action)
is RoomListAction.ChangeCategoryMode -> handleChangeCategoryMode(action)
is RoomListAction.AcceptInvitation -> handleAcceptInvitation(action)
is RoomListAction.RejectInvitation -> handleRejectInvitation(action)
is RoomListAction.FilterWith -> handleFilter(action)
@@ -84,6 +85,10 @@ class RoomListViewModel @Inject constructor(initialState: RoomListViewState,
this.toggle(action.category)
}
private fun handleChangeCategoryMode(action: RoomListAction.ChangeCategoryMode) = setState {
this.setMode(action.category, action.newCategoryMode)
}
private fun handleFilter(action: RoomListAction.FilterWith) {
setState {
copy(

View File

@@ -37,7 +37,12 @@ data class RoomListViewState(
val isDirectRoomsExpanded: Boolean = true,
val isGroupRoomsExpanded: Boolean = true,
val isLowPriorityRoomsExpanded: Boolean = true,
val isServerNoticeRoomsExpanded: Boolean = true
val isServerNoticeRoomsExpanded: Boolean = true,
val favouriteRoomsMode: CategoryMode = CategoryMode.List,
val directRoomsMode: CategoryMode = CategoryMode.List,
val groupRoomsMode: CategoryMode = CategoryMode.List,
val lowPriorityRoomsMode: CategoryMode = CategoryMode.List,
val serverNoticeRoomsMode: CategoryMode = CategoryMode.List
) : MvRxState {
constructor(args: RoomListParams) : this(displayMode = args.displayMode)
@@ -53,6 +58,22 @@ data class RoomListViewState(
}
}
enum class CategoryMode {
List,
Grid
}
fun getCategoryMode(roomCategory: RoomCategory): CategoryMode {
return when (roomCategory) {
RoomCategory.INVITE -> CategoryMode.List
RoomCategory.FAVOURITE -> favouriteRoomsMode
RoomCategory.DIRECT -> directRoomsMode
RoomCategory.GROUP -> groupRoomsMode
RoomCategory.LOW_PRIORITY -> lowPriorityRoomsMode
RoomCategory.SERVER_NOTICE -> serverNoticeRoomsMode
}
}
fun toggle(roomCategory: RoomCategory): RoomListViewState {
return when (roomCategory) {
RoomCategory.INVITE -> copy(isInviteExpanded = !isInviteExpanded)
@@ -64,6 +85,17 @@ data class RoomListViewState(
}
}
fun setMode(roomCategory: RoomCategory, newCategoryMode: CategoryMode): RoomListViewState {
return when (roomCategory) {
RoomCategory.INVITE -> this
RoomCategory.FAVOURITE -> copy(favouriteRoomsMode = newCategoryMode)
RoomCategory.DIRECT -> copy(directRoomsMode = newCategoryMode)
RoomCategory.GROUP -> copy(groupRoomsMode = newCategoryMode)
RoomCategory.LOW_PRIORITY -> copy(lowPriorityRoomsMode = newCategoryMode)
RoomCategory.SERVER_NOTICE -> copy(serverNoticeRoomsMode = newCategoryMode)
}
}
val hasUnread: Boolean
get() = asyncFilteredRooms.invoke()
?.flatMap { it.value }

View File

@@ -16,24 +16,30 @@
package im.vector.app.features.home.room.list
import android.view.View
import androidx.annotation.StringRes
import com.airbnb.epoxy.EpoxyController
import im.vector.app.R
import im.vector.app.core.epoxy.helpFooterItem
import im.vector.app.core.resources.StringProvider
import im.vector.app.core.resources.UserPreferencesProvider
import im.vector.app.core.utils.DebouncedClickListener
import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.RoomListDisplayMode
import im.vector.app.features.home.room.filtered.FilteredRoomFooterItem
import im.vector.app.features.home.room.filtered.filteredRoomFooterItem
import im.vector.app.features.home.room.list.grid.roomGridItem
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.util.toMatrixItem
import javax.inject.Inject
class RoomSummaryController @Inject constructor(private val stringProvider: StringProvider,
private val roomSummaryItemFactory: RoomSummaryItemFactory,
private val roomListNameFilter: RoomListNameFilter,
private val userPreferencesProvider: UserPreferencesProvider
private val userPreferencesProvider: UserPreferencesProvider,
private val avatarRenderer: AvatarRenderer
) : EpoxyController() {
var listener: Listener? = null
@@ -73,6 +79,7 @@ class RoomSummaryController @Inject constructor(private val stringProvider: Stri
.filter { it.membership == Membership.JOIN && roomListNameFilter.test(it) }
buildRoomModels(filteredSummaries,
RoomListViewState.CategoryMode.List,
viewState.roomMembershipChanges,
emptySet())
@@ -87,11 +94,23 @@ class RoomSummaryController @Inject constructor(private val stringProvider: Stri
return@forEach
} else {
val isExpanded = viewState.isCategoryExpanded(category)
buildRoomCategory(viewState, summaries, category.titleRes, viewState.isCategoryExpanded(category)) {
listener?.onToggleRoomCategory(category)
}
val mode = viewState.getCategoryMode(category)
buildRoomCategory(
viewState,
category,
summaries,
isExpanded,
mode,
{ newMode ->
listener?.onChangeModeRoomCategory(category, newMode)
},
{
listener?.onToggleRoomCategory(category)
}
)
if (isExpanded) {
buildRoomModels(summaries,
mode,
viewState.roomMembershipChanges,
emptySet())
// Never set showHelp to true for invitation
@@ -111,6 +130,7 @@ class RoomSummaryController @Inject constructor(private val stringProvider: Stri
helpFooterItem {
id("long_click_help")
text(stringProvider.getString(R.string.help_long_click_on_room_for_more_options))
spanSizeOverride { _, _, _ -> spanCount }
}
}
@@ -119,13 +139,16 @@ class RoomSummaryController @Inject constructor(private val stringProvider: Stri
id("filter_footer")
listener(listener)
currentFilter(viewState.roomFilter)
spanSizeOverride { _, _, _ -> spanCount }
}
}
private fun buildRoomCategory(viewState: RoomListViewState,
category: RoomCategory,
summaries: List<RoomSummary>,
@StringRes titleRes: Int,
isExpanded: Boolean,
mode: RoomListViewState.CategoryMode,
changeModeListener: (RoomListViewState.CategoryMode) -> Unit,
mutateExpandedState: () -> Unit) {
// TODO should add some business logic later
val unreadCount = if (summaries.isEmpty()) {
@@ -135,33 +158,67 @@ class RoomSummaryController @Inject constructor(private val stringProvider: Stri
}
val showHighlighted = summaries.any { it.highlightCount > 0 }
roomCategoryItem {
id(titleRes)
title(stringProvider.getString(titleRes))
id(category.titleRes)
title(stringProvider.getString(category.titleRes))
expanded(isExpanded)
unreadNotificationCount(unreadCount)
showHighlighted(showHighlighted)
showSwitchMode(category != RoomCategory.INVITE)
mode(mode)
listener {
mutateExpandedState()
update(viewState)
}
changeModeListener(changeModeListener)
spanSizeOverride { _, _, _ -> spanCount }
}
}
private fun buildRoomModels(summaries: List<RoomSummary>,
mode: RoomListViewState.CategoryMode,
roomChangedMembershipStates: Map<String, ChangeMembershipState>,
selectedRoomIds: Set<String>) {
summaries.forEach { roomSummary ->
roomSummaryItemFactory
.create(roomSummary,
roomChangedMembershipStates,
selectedRoomIds,
listener)
.addTo(this)
when (mode) {
RoomListViewState.CategoryMode.List ->
summaries.forEach { roomSummary ->
roomSummaryItemFactory
.create(roomSummary,
roomChangedMembershipStates,
selectedRoomIds,
spanCount,
listener)
.addTo(this)
}
RoomListViewState.CategoryMode.Grid -> {
// Use breadcrumbs for the moment
summaries.forEach { roomSummary ->
roomGridItem {
id(roomSummary.roomId)
hasTypingUsers(roomSummary.typingUsers.isNotEmpty())
avatarRenderer(avatarRenderer)
matrixItem(roomSummary.toMatrixItem())
unreadNotificationCount(roomSummary.notificationCount)
showHighlighted(roomSummary.highlightCount > 0)
hasUnreadMessage(roomSummary.hasUnreadMessages)
hasDraft(roomSummary.userDrafts.isNotEmpty())
itemLongClickListener { _ ->
listener?.onRoomLongClicked(roomSummary) ?: false
}
itemClickListener(
DebouncedClickListener(View.OnClickListener { _ ->
listener?.onRoomClicked(roomSummary)
})
)
spanCount
}
}
}
}
}
interface Listener : FilteredRoomFooterItem.FilteredRoomFooterItemListener {
fun onToggleRoomCategory(roomCategory: RoomCategory)
fun onChangeModeRoomCategory(roomCategory: RoomCategory, newMode: RoomListViewState.CategoryMode)
fun onRoomClicked(room: RoomSummary)
fun onRoomLongClicked(room: RoomSummary): Boolean
fun onRejectRoomInvitation(room: RoomSummary)

View File

@@ -41,18 +41,20 @@ class RoomSummaryItemFactory @Inject constructor(private val displayableEventFor
fun create(roomSummary: RoomSummary,
roomChangeMembershipStates: Map<String, ChangeMembershipState>,
selectedRoomIds: Set<String>,
spanCount: Int,
listener: RoomSummaryController.Listener?): VectorEpoxyModel<*> {
return when (roomSummary.membership) {
Membership.INVITE -> {
val changeMembershipState = roomChangeMembershipStates[roomSummary.roomId] ?: ChangeMembershipState.Unknown
createInvitationItem(roomSummary, changeMembershipState, listener)
createInvitationItem(roomSummary, changeMembershipState, spanCount, listener)
}
else -> createRoomItem(roomSummary, selectedRoomIds, listener?.let { it::onRoomClicked }, listener?.let { it::onRoomLongClicked })
else -> createRoomItem(roomSummary, selectedRoomIds, spanCount, listener?.let { it::onRoomClicked }, listener?.let { it::onRoomLongClicked })
}
}
private fun createInvitationItem(roomSummary: RoomSummary,
changeMembershipState: ChangeMembershipState,
spanCount: Int,
listener: RoomSummaryController.Listener?): VectorEpoxyModel<*> {
val secondLine = if (roomSummary.isDirect) {
roomSummary.inviterId
@@ -71,11 +73,13 @@ class RoomSummaryItemFactory @Inject constructor(private val displayableEventFor
.acceptListener { listener?.onAcceptRoomInvitation(roomSummary) }
.rejectListener { listener?.onRejectRoomInvitation(roomSummary) }
.listener { listener?.onRoomClicked(roomSummary) }
.spanSizeOverride { _, _, _ -> spanCount }
}
fun createRoomItem(
roomSummary: RoomSummary,
selectedRoomIds: Set<String>,
spanCount: Int,
onClick: ((RoomSummary) -> Unit)?,
onLongClick: ((RoomSummary) -> Boolean)?
): VectorEpoxyModel<*> {
@@ -113,5 +117,6 @@ class RoomSummaryItemFactory @Inject constructor(private val displayableEventFor
onClick?.invoke(roomSummary)
})
)
.spanSizeOverride { _, _, _ -> spanCount }
}
}

View File

@@ -0,0 +1,76 @@
/*
* 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.grid
import android.view.HapticFeedbackConstants
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.core.view.isVisible
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.app.R
import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel
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(layout = R.layout.item_room_grid)
abstract class RoomGridItem : VectorEpoxyModel<RoomGridItem.Holder>() {
@EpoxyAttribute var hasTypingUsers: Boolean = false
@EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer
@EpoxyAttribute lateinit var matrixItem: MatrixItem
@EpoxyAttribute var unreadNotificationCount: Int = 0
@EpoxyAttribute var showHighlighted: Boolean = false
@EpoxyAttribute var hasUnreadMessage: Boolean = false
@EpoxyAttribute var hasDraft: Boolean = false
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var itemClickListener: View.OnClickListener? = null
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var itemLongClickListener: View.OnLongClickListener? = null
override fun bind(holder: Holder) {
super.bind(holder)
holder.rootView.setOnClickListener(itemClickListener)
holder.rootView.setOnLongClickListener {
it.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
itemLongClickListener?.onLongClick(it) ?: false
}
holder.unreadIndentIndicator.isVisible = hasUnreadMessage
avatarRenderer.render(matrixItem, holder.avatarImageView)
holder.unreadCounterBadgeView.render(UnreadCounterBadgeView.State(unreadNotificationCount, showHighlighted))
holder.draftIndentIndicator.isVisible = hasDraft
holder.typingIndicator.isVisible = hasTypingUsers
holder.roomName.text = matrixItem.getBestName()
}
override fun unbind(holder: Holder) {
holder.rootView.setOnClickListener(null)
super.unbind(holder)
}
class Holder : VectorEpoxyHolder() {
val unreadCounterBadgeView by bind<UnreadCounterBadgeView>(R.id.itemRoomGridUnreadCounterBadgeView)
val unreadIndentIndicator by bind<View>(R.id.itemRoomGridUnreadIndicator)
val draftIndentIndicator by bind<View>(R.id.itemRoomGridDraftBadge)
val typingIndicator by bind<View>(R.id.itemRoomGridTypingView)
val avatarImageView by bind<ImageView>(R.id.itemRoomGridImageView)
val roomName by bind<TextView>(R.id.itemRoomGridRoomName)
val rootView by bind<ViewGroup>(R.id.itemRoomGridRoot)
}
}

View File

@@ -52,7 +52,13 @@ class IncomingShareController @Inject constructor(private val roomSummaryItemFac
} else {
roomSummaries.forEach { roomSummary ->
roomSummaryItemFactory
.createRoomItem(roomSummary, data.selectedRoomIds, callback?.let { it::onRoomClicked }, callback?.let { it::onRoomLongClicked })
.createRoomItem(
roomSummary,
data.selectedRoomIds,
1,
callback?.let { it::onRoomClicked },
callback?.let { it::onRoomLongClicked }
)
.addTo(this)
}
}

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M18.4,4H5.6C4.72,4 4,4.72 4,5.6V18.4C4,19.28 4.72,20 5.6,20H18.4C19.28,20 20,19.28 20,18.4V5.6C20,4.72 19.28,4 18.4,4ZM8.8,18.4H5.6V15.2H8.8V18.4ZM8.8,13.6H5.6V10.4H8.8V13.6ZM8.8,8.8H5.6V5.6H8.8V8.8ZM13.6,18.4H10.4V15.2H13.6V18.4ZM13.6,13.6H10.4V10.4H13.6V13.6ZM13.6,8.8H10.4V5.6H13.6V8.8ZM18.4,18.4H15.2V15.2H18.4V18.4ZM18.4,13.6H15.2V10.4H18.4V13.6ZM18.4,8.8H15.2V5.6H18.4V8.8Z"
android:fillColor="#000000"/>
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M4,5C4,5.5523 4.4477,6 5,6C5.5523,6 6,5.5523 6,5C6,4.4477 5.5523,4 5,4C4.4477,4 4,4.4477 4,5ZM8,5C8,5.5523 8.4477,6 9,6H19C19.5523,6 20,5.5523 20,5C20,4.4477 19.5523,4 19,4H9C8.4477,4 8,4.4477 8,5ZM4,9.5C4,10.0523 4.4477,10.5 5,10.5C5.5523,10.5 6,10.0523 6,9.5C6,8.9477 5.5523,8.5 5,8.5C4.4477,8.5 4,8.9477 4,9.5ZM8,9.5C8,10.0523 8.4477,10.5 9,10.5H19C19.5523,10.5 20,10.0523 20,9.5C20,8.9477 19.5523,8.5 19,8.5H9C8.4477,8.5 8,8.9477 8,9.5ZM4,19C4,19.5523 4.4477,20 5,20C5.5523,20 6,19.5523 6,19C6,18.4477 5.5523,18 5,18C4.4477,18 4,18.4477 4,19ZM8,19C8,19.5523 8.4477,20 9,20H19C19.5523,20 20,19.5523 20,19C20,18.4477 19.5523,18 19,18H9C8.4477,18 8,18.4477 8,19ZM6,14.5C6,15.0523 5.5523,15.5 5,15.5C4.4477,15.5 4,15.0523 4,14.5C4,13.9477 4.4477,13.5 5,13.5C5.5523,13.5 6,13.9477 6,14.5ZM20,14.5C20,15.0523 19.5523,15.5 19,15.5H9C8.4477,15.5 8,15.0523 8,14.5C8,13.9477 8.4477,13.5 9,13.5H19C19.5523,13.5 20,13.9477 20,14.5Z"
android:fillColor="#000000"
android:fillType="evenOdd"/>
</vector>

View File

@@ -5,7 +5,7 @@
android:id="@+id/stateView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?riotx_header_panel_background">
android:background="?riotx_background">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/roomListView"

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/roomCategoryRootView"
android:layout_width="match_parent"
@@ -8,19 +9,14 @@
android:clickable="true"
android:focusable="true"
android:foreground="?attr/selectableItemBackground"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingStart="8dp"
android:paddingTop="12dp"
android:paddingEnd="@dimen/layout_horizontal_margin"
android:paddingBottom="4dp">
android:paddingStart="8dp">
<TextView
android:id="@+id/roomCategoryTitleView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
tools:drawableEnd="@drawable/ic_expand_more_white"
android:layout_marginTop="12dp"
android:layout_marginBottom="4dp"
android:drawableTint="?riotx_text_secondary"
android:ellipsize="end"
android:gravity="center_vertical"
@@ -28,12 +24,17 @@
android:textColor="?riotx_text_secondary"
android:textSize="14sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:drawableEnd="@drawable/ic_expand_more_white"
tools:text="@string/room_participants_header_direct_chats" />
<im.vector.app.features.home.room.list.UnreadCounterBadgeView
android:id="@+id/roomCategoryUnreadCounterBadgeView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:gravity="center"
android:minWidth="16dp"
android:minHeight="16dp"
@@ -41,7 +42,21 @@
android:paddingEnd="4dp"
android:textColor="@android:color/white"
android:textSize="10sp"
app:layout_constraintBottom_toBottomOf="@+id/roomCategoryTitleView"
app:layout_constraintStart_toEndOf="@+id/roomCategoryTitleView"
app:layout_constraintTop_toTopOf="@+id/roomCategoryTitleView"
tools:background="@drawable/bg_unread_highlight"
tools:text="24" />
</LinearLayout>
<ImageView
android:id="@+id/roomCategoryMode"
android:layout_width="48dp"
android:layout_height="0dp"
android:scaleType="center"
android:tint="?riotx_text_secondary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/ic_mode_list_24dp" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,103 @@
<?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/itemRoomGridRoot"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?riotx_background"
android:clickable="true"
android:focusable="true"
android:foreground="?attr/selectableItemBackground">
<ImageView
android:id="@+id/itemRoomGridUnreadIndicator"
android:layout_width="60dp"
android:layout_height="60dp"
android:src="@drawable/circle"
android:tint="?attr/colorAccent"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@+id/itemRoomGridImageView"
app:layout_constraintEnd_toEndOf="@+id/itemRoomGridImageView"
app:layout_constraintStart_toStartOf="@+id/itemRoomGridImageView"
app:layout_constraintTop_toTopOf="@+id/itemRoomGridImageView"
tools:visibility="visible" />
<ImageView
android:id="@+id/itemRoomGridImageView"
android:layout_width="56dp"
android:layout_height="56dp"
android:layout_marginStart="12dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="12dp"
android:layout_marginBottom="4dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@tools:sample/avatars" />
<im.vector.app.features.home.room.list.UnreadCounterBadgeView
android:id="@+id/itemRoomGridUnreadCounterBadgeView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:minWidth="18dp"
android:minHeight="18dp"
android:textColor="@android:color/white"
android:textSize="11sp"
android:visibility="gone"
app:layout_constraintCircle="@+id/itemRoomGridImageView"
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/itemRoomGridTypingView"
android:layout_width="20dp"
android:layout_height="20dp"
android:background="@drawable/bg_breadcrumbs_typing"
android:gravity="center"
android:text="@string/ellipsis"
android:textColor="@android:color/white"
android:textSize="11sp"
android:textStyle="bold"
android:visibility="gone"
app:layout_constraintCircle="@+id/itemRoomGridImageView"
app:layout_constraintCircleAngle="135"
app:layout_constraintCircleRadius="28dp"
tools:ignore="MissingConstraints"
tools:visibility="visible" />
<ImageView
android:id="@+id/itemRoomGridDraftBadge"
android:layout_width="20dp"
android:layout_height="20dp"
android:background="@drawable/circle"
android:padding="3dp"
android:src="@drawable/ic_edit"
android:visibility="gone"
app:layout_constraintCircle="@+id/itemRoomGridImageView"
app:layout_constraintCircleAngle="225"
app:layout_constraintCircleRadius="28dp"
tools:ignore="MissingConstraints"
tools:visibility="visible" />
<TextView
android:id="@+id/itemRoomGridRoomName"
android:layout_width="76dp"
android:layout_height="wrap_content"
android:layout_margin="2dp"
android:ellipsize="end"
android:gravity="center"
android:lines="2"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/itemRoomGridImageView"
tools:text="@sample/matrix.json/data/roomName" />
</androidx.constraintlayout.widget.ConstraintLayout>