Swipe spaces bottom bar
Change-Id: Ib15b3b28f5f429e73cf80130dc575ae2111a1cef
This commit is contained in:
parent
3b6821d627
commit
43d77f553e
|
@ -25,6 +25,7 @@ import android.view.ViewGroup
|
||||||
import android.view.ViewTreeObserver
|
import android.view.ViewTreeObserver
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||||
import androidx.viewpager2.widget.ViewPager2
|
import androidx.viewpager2.widget.ViewPager2
|
||||||
import com.airbnb.mvrx.UniqueOnly
|
import com.airbnb.mvrx.UniqueOnly
|
||||||
|
@ -60,6 +61,8 @@ import im.vector.app.features.home.room.list.RoomListFragment
|
||||||
import im.vector.app.features.home.room.list.RoomListParams
|
import im.vector.app.features.home.room.list.RoomListParams
|
||||||
import im.vector.app.features.home.room.list.RoomListSectionBuilder.Companion.SPACE_ID_FOLLOW_APP
|
import im.vector.app.features.home.room.list.RoomListSectionBuilder.Companion.SPACE_ID_FOLLOW_APP
|
||||||
import im.vector.app.features.home.room.list.UnreadCounterBadgeView
|
import im.vector.app.features.home.room.list.UnreadCounterBadgeView
|
||||||
|
import im.vector.app.features.home.room.list.home.spacebar.SpaceBarController
|
||||||
|
import im.vector.app.features.home.room.list.home.spacebar.SpaceBarData
|
||||||
import im.vector.app.features.popup.PopupAlertManager
|
import im.vector.app.features.popup.PopupAlertManager
|
||||||
import im.vector.app.features.popup.VerificationVectorAlert
|
import im.vector.app.features.popup.VerificationVectorAlert
|
||||||
import im.vector.app.features.settings.VectorLocaleProvider
|
import im.vector.app.features.settings.VectorLocaleProvider
|
||||||
|
@ -69,6 +72,7 @@ import im.vector.app.features.themes.ThemeUtils
|
||||||
import im.vector.app.features.workers.signout.BannerState
|
import im.vector.app.features.workers.signout.BannerState
|
||||||
import im.vector.app.features.workers.signout.ServerBackupStatusAction
|
import im.vector.app.features.workers.signout.ServerBackupStatusAction
|
||||||
import im.vector.app.features.workers.signout.ServerBackupStatusViewModel
|
import im.vector.app.features.workers.signout.ServerBackupStatusViewModel
|
||||||
|
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo
|
import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
@ -90,6 +94,7 @@ class HomeDetailFragment :
|
||||||
@Inject lateinit var vectorPreferences: VectorPreferences
|
@Inject lateinit var vectorPreferences: VectorPreferences
|
||||||
@Inject lateinit var spaceStateHandler: SpaceStateHandler
|
@Inject lateinit var spaceStateHandler: SpaceStateHandler
|
||||||
@Inject lateinit var vectorLocale: VectorLocaleProvider
|
@Inject lateinit var vectorLocale: VectorLocaleProvider
|
||||||
|
@Inject lateinit var spaceBarController: SpaceBarController
|
||||||
|
|
||||||
private val DEBUG_VIEW_PAGER = DbgUtil.isDbgEnabled(DbgUtil.DBG_VIEW_PAGER)
|
private val DEBUG_VIEW_PAGER = DbgUtil.isDbgEnabled(DbgUtil.DBG_VIEW_PAGER)
|
||||||
private val viewPagerDimber = Dimber("Home pager", DbgUtil.DBG_VIEW_PAGER)
|
private val viewPagerDimber = Dimber("Home pager", DbgUtil.DBG_VIEW_PAGER)
|
||||||
|
@ -145,6 +150,16 @@ class HomeDetailFragment :
|
||||||
|
|
||||||
private val currentCallsViewPresenter = CurrentCallsViewPresenter()
|
private val currentCallsViewPresenter = CurrentCallsViewPresenter()
|
||||||
|
|
||||||
|
private val spaceBarListener = object: SpaceBarController.SpaceBarListener {
|
||||||
|
override fun onSpaceBarSelectSpace(space: RoomSummary?) {
|
||||||
|
spaceStateHandler.setCurrentSpace(space?.roomId, from = SelectSpaceFrom.SELECT)
|
||||||
|
}
|
||||||
|
override fun onSpaceBarLongPressSpace(space: RoomSummary?): Boolean {
|
||||||
|
sharedActionViewModel.post(HomeActivitySharedAction.OpenDrawer)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
sharedActionViewModel = activityViewModelProvider.get(HomeSharedActionViewModel::class.java)
|
sharedActionViewModel = activityViewModelProvider.get(HomeSharedActionViewModel::class.java)
|
||||||
|
@ -156,6 +171,12 @@ class HomeDetailFragment :
|
||||||
|
|
||||||
checkNotificationTabStatus()
|
checkNotificationTabStatus()
|
||||||
|
|
||||||
|
val spaceBarAdapter = spaceBarController.also { controller ->
|
||||||
|
controller.spaceRoomListener = spaceBarListener
|
||||||
|
}.adapter
|
||||||
|
views.spaceBarRecyclerView.layoutManager = LinearLayoutManager(context)
|
||||||
|
views.spaceBarRecyclerView.adapter = spaceBarAdapter
|
||||||
|
|
||||||
// Reduce sensitivity of viewpager to avoid scrolling horizontally by accident too easily
|
// Reduce sensitivity of viewpager to avoid scrolling horizontally by accident too easily
|
||||||
views.roomListContainerPager.reduceDragSensitivity(4)
|
views.roomListContainerPager.reduceDragSensitivity(4)
|
||||||
|
|
||||||
|
@ -226,7 +247,9 @@ class HomeDetailFragment :
|
||||||
highlighted = state.otherSpacesUnread.isHighlight,
|
highlighted = state.otherSpacesUnread.isHighlight,
|
||||||
unread = state.otherSpacesUnread.unreadCount,
|
unread = state.otherSpacesUnread.unreadCount,
|
||||||
markedUnread = false
|
markedUnread = false
|
||||||
)
|
).also {
|
||||||
|
spaceBarController.submitHomeUnreadCounts(it)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -280,6 +303,9 @@ class HomeDetailFragment :
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
currentCallsViewPresenter.unBind()
|
currentCallsViewPresenter.unBind()
|
||||||
|
|
||||||
|
spaceBarController.spaceRoomListener = null
|
||||||
|
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -433,6 +459,9 @@ class HomeDetailFragment :
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onSpaceChange(spaceSummary: RoomSummary?) {
|
private fun onSpaceChange(spaceSummary: RoomSummary?) {
|
||||||
|
if (pagerPagingEnabled) {
|
||||||
|
spaceBarController.selectSpace(spaceSummary)
|
||||||
|
}
|
||||||
if (spaceSummary == null) {
|
if (spaceSummary == null) {
|
||||||
views.groupToolbarSpaceTitleView.isVisible = false
|
views.groupToolbarSpaceTitleView.isVisible = false
|
||||||
} else {
|
} else {
|
||||||
|
@ -441,6 +470,11 @@ class HomeDetailFragment :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun setCurrentPagerItem(index: Int, smoothScroll: Boolean) {
|
||||||
|
views.roomListContainerPager.setCurrentItem(index, smoothScroll)
|
||||||
|
spaceBarController.scrollToSpacePosition(index)
|
||||||
|
}
|
||||||
|
|
||||||
private fun setupKeysBackupBanner() {
|
private fun setupKeysBackupBanner() {
|
||||||
serverBackupStatusViewModel.handle(ServerBackupStatusAction.OnBannerDisplayed)
|
serverBackupStatusViewModel.handle(ServerBackupStatusAction.OnBannerDisplayed)
|
||||||
serverBackupStatusViewModel
|
serverBackupStatusViewModel
|
||||||
|
@ -556,6 +590,12 @@ class HomeDetailFragment :
|
||||||
getPageIndexForSpaceId(selectedSpaceId, unsafeSpaces)
|
getPageIndexForSpaceId(selectedSpaceId, unsafeSpaces)
|
||||||
}
|
}
|
||||||
val pagingEnabled = pagingAllowed && unsafeSpaces.isNotEmpty() && selectedIndex != null
|
val pagingEnabled = pagingAllowed && unsafeSpaces.isNotEmpty() && selectedIndex != null
|
||||||
|
if (pagingEnabled) {
|
||||||
|
views.spaceBarRecyclerView.isVisible = true
|
||||||
|
spaceBarController.submitData(SpaceBarData(spaces?.let { listOf<RoomSummary?>(null) + it }, selectedSpace))
|
||||||
|
} else {
|
||||||
|
views.spaceBarRecyclerView.isVisible = false
|
||||||
|
}
|
||||||
val safeSpaces = if (pagingEnabled) unsafeSpaces else listOf()
|
val safeSpaces = if (pagingEnabled) unsafeSpaces else listOf()
|
||||||
// Check if we need to recreate the adapter for a new tab
|
// Check if we need to recreate the adapter for a new tab
|
||||||
if (oldAdapter != null) {
|
if (oldAdapter != null) {
|
||||||
|
@ -582,7 +622,7 @@ class HomeDetailFragment :
|
||||||
// Do not smooth scroll large distances to avoid loading unnecessary many room lists
|
// Do not smooth scroll large distances to avoid loading unnecessary many room lists
|
||||||
val diff = selectedIndex - views.roomListContainerPager.currentItem
|
val diff = selectedIndex - views.roomListContainerPager.currentItem
|
||||||
val smoothScroll = abs(diff) <= 1
|
val smoothScroll = abs(diff) <= 1
|
||||||
views.roomListContainerPager.setCurrentItem(selectedIndex, smoothScroll)
|
setCurrentPagerItem(selectedIndex, smoothScroll)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
@ -656,7 +696,7 @@ class HomeDetailFragment :
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
viewPagerDimber.i{"Home pager: set initial page $selectedIndex"}
|
viewPagerDimber.i{"Home pager: set initial page $selectedIndex"}
|
||||||
views.roomListContainerPager.setCurrentItem(selectedIndex ?: 0, false)
|
setCurrentPagerItem(selectedIndex ?: 0, false)
|
||||||
initialPageSelected = true
|
initialPageSelected = true
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e("Home pager: Could not set initial page after creating adapter: $e")
|
Timber.e("Home pager: Could not set initial page after creating adapter: $e")
|
||||||
|
|
|
@ -0,0 +1,157 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 New Vector Ltd
|
||||||
|
* Copyright (c) 2022 SpiritCroc
|
||||||
|
*
|
||||||
|
* 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.spacebar
|
||||||
|
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
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.core.resources.StringProvider
|
||||||
|
import im.vector.app.features.home.AvatarRenderer
|
||||||
|
import im.vector.app.features.home.room.list.UnreadCounterBadgeView
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
|
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||||
|
import javax.inject.Inject
|
||||||
|
import kotlin.math.max
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
|
class SpaceBarController @Inject constructor(
|
||||||
|
val stringProvider: StringProvider,
|
||||||
|
private val avatarRenderer: AvatarRenderer,
|
||||||
|
) : EpoxyController() {
|
||||||
|
|
||||||
|
private var data: SpaceBarData = SpaceBarData()
|
||||||
|
private var topLevelUnreadCounts: UnreadCounterBadgeView.State.Count = UnreadCounterBadgeView.State.Count(0, false, 0, false)
|
||||||
|
|
||||||
|
interface SpaceBarListener {
|
||||||
|
fun onSpaceBarSelectSpace(space: RoomSummary?)
|
||||||
|
fun onSpaceBarLongPressSpace(space: RoomSummary?): Boolean
|
||||||
|
}
|
||||||
|
var spaceRoomListener: SpaceBarListener? = null
|
||||||
|
|
||||||
|
private var carousel: Carousel? = null
|
||||||
|
|
||||||
|
override fun buildModels() {
|
||||||
|
val host = this
|
||||||
|
data.spaces?.let {
|
||||||
|
addSpaces(host, it, data.selectedSpace)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addSpaces(host: SpaceBarController, spaces: List<RoomSummary?>, selectedSpace: RoomSummary?) {
|
||||||
|
carousel {
|
||||||
|
id("spaces_carousel")
|
||||||
|
padding(
|
||||||
|
Carousel.Padding(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
onBind { _, view, _ ->
|
||||||
|
host.carousel = view
|
||||||
|
host.scrollToSpace(selectedSpace?.roomId)
|
||||||
|
}
|
||||||
|
|
||||||
|
onUnbind { _, _ ->
|
||||||
|
host.carousel = null
|
||||||
|
}
|
||||||
|
|
||||||
|
withModelsFrom(spaces) { spaceSummary ->
|
||||||
|
val onClick = host.spaceRoomListener?.let { it::onSpaceBarSelectSpace }
|
||||||
|
val onLongClick = host.spaceRoomListener?.let { it::onSpaceBarLongPressSpace }
|
||||||
|
|
||||||
|
if (spaceSummary == null) {
|
||||||
|
SpaceBarItem_()
|
||||||
|
.id("de.spiritcroc.riotx.spacebarhome")
|
||||||
|
.avatarRenderer(host.avatarRenderer)
|
||||||
|
.matrixItem(null)
|
||||||
|
.unreadNotificationCount(host.topLevelUnreadCounts.count)
|
||||||
|
.showHighlighted(host.topLevelUnreadCounts.highlighted)
|
||||||
|
.unreadCount(host.topLevelUnreadCounts.unread)
|
||||||
|
.markedUnread(host.topLevelUnreadCounts.markedUnread)
|
||||||
|
.selected(selectedSpace == null)
|
||||||
|
.itemLongClickListener { _ -> onLongClick?.invoke(null) ?: false }
|
||||||
|
.itemClickListener { onClick?.invoke(null) }
|
||||||
|
} else {
|
||||||
|
SpaceBarItem_()
|
||||||
|
.id(spaceSummary.roomId)
|
||||||
|
.avatarRenderer(host.avatarRenderer)
|
||||||
|
.matrixItem(spaceSummary.toMatrixItem())
|
||||||
|
.unreadNotificationCount(spaceSummary.notificationCount)
|
||||||
|
.showHighlighted(spaceSummary.highlightCount > 0)
|
||||||
|
.unreadCount(spaceSummary.unreadCount ?: 0)
|
||||||
|
.markedUnread(spaceSummary.markedUnread)
|
||||||
|
.selected(spaceSummary.roomId == selectedSpace?.roomId)
|
||||||
|
.itemLongClickListener { _ -> onLongClick?.invoke(spaceSummary) ?: false }
|
||||||
|
.itemClickListener { onClick?.invoke(spaceSummary) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun submitData(data: SpaceBarData) {
|
||||||
|
this.data = data
|
||||||
|
requestModelBuild()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun submitHomeUnreadCounts(counts: UnreadCounterBadgeView.State.Count) {
|
||||||
|
this.topLevelUnreadCounts = counts
|
||||||
|
requestModelBuild()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun selectSpace(space: RoomSummary?) {
|
||||||
|
this.data = this.data.copy(selectedSpace = space)
|
||||||
|
requestModelBuild()
|
||||||
|
scrollToSpace(space?.roomId)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun scrollToSpace(spaceId: String?) {
|
||||||
|
val position = this.data.spaces?.indexOfFirst { spaceId == it?.roomId } ?: -1
|
||||||
|
if (position >= 0) {
|
||||||
|
scrollToSpacePosition(position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun scrollToSpacePosition(position: Int) {
|
||||||
|
val safeCarousel = carousel ?: return
|
||||||
|
var effectivePosition = position
|
||||||
|
val lm = safeCarousel.layoutManager as? LinearLayoutManager
|
||||||
|
if (lm != null) {
|
||||||
|
// Look-ahead of 1
|
||||||
|
if (lm.findFirstCompletelyVisibleItemPosition() >= position) {
|
||||||
|
effectivePosition--
|
||||||
|
} else if (lm.findLastVisibleItemPosition() <= position) {
|
||||||
|
effectivePosition++
|
||||||
|
}
|
||||||
|
effectivePosition = max(0, min(effectivePosition, lm.itemCount-1))
|
||||||
|
}
|
||||||
|
safeCarousel.smoothScrollToPosition(effectivePosition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private inline fun <T> CarouselModelBuilder.withModelsFrom(
|
||||||
|
items: List<T>,
|
||||||
|
modelBuilder: (T) -> EpoxyModel<*>
|
||||||
|
) {
|
||||||
|
models(items.map { modelBuilder(it) })
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 New Vector Ltd
|
||||||
|
* Copyright (c) 2022 SpiritCroc
|
||||||
|
*
|
||||||
|
* 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.spacebar
|
||||||
|
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
|
|
||||||
|
data class SpaceBarData(
|
||||||
|
val spaces: List<RoomSummary?>? = null,
|
||||||
|
val selectedSpace: RoomSummary? = null,
|
||||||
|
)
|
|
@ -0,0 +1,91 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 New Vector Ltd
|
||||||
|
* Copyright (c) 2022 SpiritCroc
|
||||||
|
*
|
||||||
|
* 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.spacebar
|
||||||
|
|
||||||
|
import android.content.res.ColorStateList
|
||||||
|
import android.view.HapticFeedbackConstants
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.ImageView
|
||||||
|
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.core.platform.CheckableConstraintLayout
|
||||||
|
import im.vector.app.features.displayname.getBestName
|
||||||
|
import im.vector.app.features.home.AvatarRenderer
|
||||||
|
import im.vector.app.features.home.room.list.UnreadCounterBadgeView
|
||||||
|
import im.vector.app.features.themes.ThemeUtils
|
||||||
|
import org.matrix.android.sdk.api.util.MatrixItem
|
||||||
|
|
||||||
|
@EpoxyModelClass
|
||||||
|
abstract class SpaceBarItem : VectorEpoxyModel<SpaceBarItem.Holder>(R.layout.space_bar_item) {
|
||||||
|
|
||||||
|
@EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer
|
||||||
|
@EpoxyAttribute var matrixItem: MatrixItem? = null
|
||||||
|
@EpoxyAttribute var unreadNotificationCount: Int = 0
|
||||||
|
@EpoxyAttribute var unreadCount: Int = 0
|
||||||
|
@EpoxyAttribute var markedUnread: Boolean = false
|
||||||
|
@EpoxyAttribute var showHighlighted: Boolean = false
|
||||||
|
@EpoxyAttribute var selected: 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
|
||||||
|
}
|
||||||
|
|
||||||
|
matrixItem.let {
|
||||||
|
if (it == null) {
|
||||||
|
holder.avatarImageView.setImageResource(R.drawable.ic_space_home)
|
||||||
|
holder.avatarImageView.imageTintList = ColorStateList.valueOf(ThemeUtils.getColor(holder.avatarImageView.context, R.attr.vctr_content_primary))
|
||||||
|
holder.avatarImageView.contentDescription = holder.rootView.context.getString(R.string.group_details_home)
|
||||||
|
} else {
|
||||||
|
holder.avatarImageView.imageTintList = null
|
||||||
|
avatarRenderer.render(it, holder.avatarImageView)
|
||||||
|
holder.avatarImageView.contentDescription = it.getBestName()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
holder.unreadCounterBadgeView.render(UnreadCounterBadgeView.State.Count(unreadNotificationCount, showHighlighted, unreadCount, markedUnread))
|
||||||
|
holder.rootView.isChecked = selected
|
||||||
|
}
|
||||||
|
|
||||||
|
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.spaceUnreadCounterBadgeView)
|
||||||
|
val avatarImageView by bind<ImageView>(R.id.spaceImageView)
|
||||||
|
val rootView by bind<CheckableConstraintLayout>(R.id.spaceRoot)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<item android:state_checked="true">
|
||||||
|
<layer-list>
|
||||||
|
<item>
|
||||||
|
<shape>
|
||||||
|
<solid android:color="@android:color/transparent" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
<item android:gravity="center_horizontal|bottom" >
|
||||||
|
<shape>
|
||||||
|
<size android:width="40dp" android:height="4dp" />
|
||||||
|
<solid android:color="?colorSecondary" />
|
||||||
|
<corners android:topLeftRadius="8dp" android:topRightRadius="8dp" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</layer-list>
|
||||||
|
</item>
|
||||||
|
|
||||||
|
<item>
|
||||||
|
<shape>
|
||||||
|
<solid android:color="@android:color/transparent" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
|
||||||
|
</selector>
|
|
@ -150,7 +150,7 @@
|
||||||
android:id="@+id/roomListContainerStateView"
|
android:id="@+id/roomListContainerStateView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/bottomNavigationView"
|
app:layout_constraintBottom_toTopOf="@+id/spaceBarRecyclerView"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/homeKeysBackupBanner">
|
app:layout_constraintTop_toBottomOf="@+id/homeKeysBackupBanner">
|
||||||
<androidx.viewpager2.widget.ViewPager2
|
<androidx.viewpager2.widget.ViewPager2
|
||||||
android:id="@+id/roomListContainerPager"
|
android:id="@+id/roomListContainerPager"
|
||||||
|
@ -158,6 +158,29 @@
|
||||||
android:layout_height="match_parent" />
|
android:layout_height="match_parent" />
|
||||||
</im.vector.app.core.platform.StateView>
|
</im.vector.app.core.platform.StateView>
|
||||||
|
|
||||||
|
<!-- Own view for spacebar bg in case of wrap_content of spaceBarRecyclerView is smaller than screen estate -->
|
||||||
|
<View
|
||||||
|
android:id="@+id/spaceBarBackground"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:background="@drawable/bg_bottom_navigation"
|
||||||
|
android:elevation="4dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/spaceBarRecyclerView"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/spaceBarRecyclerView"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
<!-- wrap_content -> center when fewer spaces than screen estate -->
|
||||||
|
<!-- needs higher elevation than spaceBarBackground to be rendered above: https://stackoverflow.com/a/27554343 -->
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/spaceBarRecyclerView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:elevation="80dp"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/bottomNavigationView" />
|
||||||
|
|
||||||
<com.google.android.material.bottomnavigation.BottomNavigationView
|
<com.google.android.material.bottomnavigation.BottomNavigationView
|
||||||
android:id="@+id/bottomNavigationView"
|
android:id="@+id/bottomNavigationView"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<im.vector.app.core.platform.CheckableConstraintLayout 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/spaceRoot"
|
||||||
|
android:layout_width="60dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:foreground="?attr/selectableItemBackground"
|
||||||
|
android:background="@drawable/bg_bottom_space_item"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:clipChildren="false"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
tools:viewBindingIgnore="true">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/spaceImageView"
|
||||||
|
android:layout_width="32dp"
|
||||||
|
android:layout_height="32dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
tools:ignore="ContentDescription"
|
||||||
|
tools:src="@sample/room_round_avatars" />
|
||||||
|
|
||||||
|
<im.vector.app.features.home.room.list.UnreadCounterBadgeView
|
||||||
|
android:id="@+id/spaceUnreadCounterBadgeView"
|
||||||
|
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/spaceImageView"
|
||||||
|
app:layout_constraintCircleAngle="45"
|
||||||
|
app:layout_constraintCircleRadius="21dp"
|
||||||
|
tools:background="@drawable/bg_unread_highlight"
|
||||||
|
tools:text="24"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/spaceTitle"
|
||||||
|
style="@style/Widget.Vector.TextView.Body"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:importantForAccessibility="no"
|
||||||
|
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/spaceImageView"
|
||||||
|
tools:text="Coffee" />
|
||||||
|
-->
|
||||||
|
|
||||||
|
</im.vector.app.core.platform.CheckableConstraintLayout>
|
Loading…
Reference in New Issue