Experimental: Swipe to switch between root spaces

Change-Id: I8849a4a2fd2cea0a7904f8c4259adf8c09f6864a
This commit is contained in:
SpiritCroc 2021-10-03 16:07:26 +02:00
parent 3eef15ae3e
commit af0b3a8897
14 changed files with 355 additions and 34 deletions

View file

@ -0,0 +1,22 @@
package de.spiritcroc.viewpager
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
import timber.log.Timber
import java.lang.Exception
// Mainly taken from https://stackoverflow.com/a/63455547
fun ViewPager2.reduceDragSensitivity(factor: Int) {
try {
val recyclerViewField = ViewPager2::class.java.getDeclaredField("mRecyclerView")
recyclerViewField.isAccessible = true
val recyclerView = recyclerViewField.get(this) as RecyclerView
val touchSlopField = RecyclerView::class.java.getDeclaredField("mTouchSlop")
touchSlopField.isAccessible = true
val touchSlop = touchSlopField.get(recyclerView) as Int
touchSlopField.set(recyclerView, touchSlop * factor)
} catch (e: Exception) {
Timber.e("Cannot reduce viewpager drag sensitivity: $e")
}
}

View file

@ -24,14 +24,16 @@ import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import com.airbnb.mvrx.activityViewModel
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
import com.google.android.material.badge.BadgeDrawable
import de.spiritcroc.viewpager.reduceDragSensitivity
import im.vector.app.AppStateHandler
import im.vector.app.R
import im.vector.app.RoomGroupingMethod
import im.vector.app.core.extensions.commitTransaction
import im.vector.app.core.extensions.restart
import im.vector.app.core.extensions.toMvRxBundle
import im.vector.app.core.platform.ToolbarConfigurable
@ -48,6 +50,7 @@ import im.vector.app.features.call.dialpad.DialPadFragment
import im.vector.app.features.call.webrtc.WebRtcCallManager
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.RoomListSectionBuilderSpace.Companion.SPACE_ID_FOLLOW_APP
import im.vector.app.features.home.room.list.UnreadCounterBadgeView
import im.vector.app.features.popup.PopupAlertManager
import im.vector.app.features.popup.VerificationVectorAlert
@ -60,6 +63,7 @@ import im.vector.app.features.workers.signout.ServerBackupStatusViewModel
import org.matrix.android.sdk.api.session.group.model.GroupSummary
import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo
import timber.log.Timber
import javax.inject.Inject
class HomeDetailFragment @Inject constructor(
@ -73,6 +77,10 @@ class HomeDetailFragment @Inject constructor(
KeysBackupBanner.Delegate,
CurrentCallsView.Callback {
companion object {
const val DEBUG_VIEW_PAGER = true
}
private val viewModel: HomeDetailViewModel by fragmentViewModel()
private val unknownDeviceDetectorSharedViewModel: UnknownDeviceDetectorSharedViewModel by activityViewModel()
private val unreadMessagesSharedViewModel: UnreadMessagesSharedViewModel by activityViewModel()
@ -93,6 +101,12 @@ class HomeDetailFragment @Inject constructor(
}
}
private var initialPageSelected = false
private var pagerSpaces: List<String?>? = null
private var pagerTab: HomeTab? = null
private var pagerPagingEnabled: Boolean = false
private val pendingSpaceIds = mutableListOf<String?>()
override fun getMenuRes() = R.menu.room_list
override fun onOptionsItemSelected(item: MenuItem): Boolean {
@ -131,6 +145,29 @@ class HomeDetailFragment @Inject constructor(
checkNotificationTabStatus()
// Reduce sensitivity of viewpager to avoid scrolling horizontally by accident too easily
views.roomListContainerPager.reduceDragSensitivity(4)
// space pager: update appStateHandler's current page to update rest of the UI accordingly
views.roomListContainerPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
if (DEBUG_VIEW_PAGER) Timber.i("Home pager: selected page $position $initialPageSelected")
super.onPageSelected(position)
if (!initialPageSelected) {
// Do not apply before we have restored the previous value
if (position == 0) {
return
} else {
// User has swiped, store it anyways
initialPageSelected = true
}
}
val selectedId = getSpaceIdForPageIndex(position)
pendingSpaceIds.add(selectedId)
appStateHandler.setCurrentSpace(selectedId)
}
})
viewModel.onEach(HomeDetailViewState::roomGroupingMethod) { roomGroupingMethod ->
when (roomGroupingMethod) {
is RoomGroupingMethod.ByLegacyGroup -> {
@ -188,6 +225,10 @@ class HomeDetailFragment @Inject constructor(
)
}
viewModel.onEach(HomeDetailViewState::roomGroupingMethod, HomeDetailViewState::rootSpacesOrdered, HomeDetailViewState::currentTab) { roomGroupingMethod, rootSpacesOrdered, currentTab ->
setupViewPager(roomGroupingMethod, rootSpacesOrdered, currentTab)
}
sharedCallActionViewModel
.liveKnownCalls
.observe(viewLifecycleOwner) {
@ -438,12 +479,13 @@ class HomeDetailFragment @Inject constructor(
private fun updateUIForTab(tab: HomeTab) {
views.bottomNavigationView.menu.findItem(tab.toMenuId()).isChecked = true
views.groupToolbarTitleView.setText(tab.titleRes)
updateSelectedFragment(tab)
//updateSelectedFragment(tab)
invalidateOptionsMenu()
}
private fun HomeTab.toFragmentTag() = "FRAGMENT_TAG_$this"
/*
private fun updateSelectedFragment(tab: HomeTab) {
val fragmentTag = tab.toFragmentTag()
val fragmentToShow = childFragmentManager.findFragmentByTag(fragmentTag)
@ -471,6 +513,120 @@ class HomeDetailFragment @Inject constructor(
}
}
}
*/
private fun setupViewPager(roomGroupingMethod: RoomGroupingMethod, spaces: List<RoomSummary>?, tab: HomeTab) {
val oldAdapter = views.roomListContainerPager.adapter as? FragmentStateAdapter
val pagingAllowed = vectorPreferences.enableSpacePager()
if (DEBUG_VIEW_PAGER) Timber.i("Home pager: setup, old adapter: $oldAdapter")
val unsafeSpaces = spaces?.map { it.roomId } ?: listOf()
val selectedSpaceId = (roomGroupingMethod as? RoomGroupingMethod.BySpace)?.spaceSummary?.roomId
val selectedIndex = getPageIndexForSpaceId(selectedSpaceId, unsafeSpaces)
val pagingEnabled = pagingAllowed && roomGroupingMethod is RoomGroupingMethod.BySpace && unsafeSpaces.size > 0 && selectedIndex != null
val safeSpaces = if (pagingEnabled) unsafeSpaces else listOf()
// Check if we need to recreate the adapter for a new tab
if (oldAdapter != null) {
val changed = pagerTab != tab || pagerSpaces != safeSpaces || pagerPagingEnabled != pagingEnabled
if (DEBUG_VIEW_PAGER) Timber.i("Home pager: has changed: $changed (${pagerTab != tab} ${pagerSpaces != safeSpaces} ${pagerPagingEnabled != pagingEnabled} $selectedIndex ${views.roomListContainerPager.currentItem}) | $safeSpaces")
if (!changed) {
if (pagingEnabled) {
// No need to re-setup pager, just check for selected page
// Discard state changes that we created ourselves by swiping on the pager
while (pendingSpaceIds.size > 0) {
val pendingSpaceId = pendingSpaceIds.removeAt(0)
if (pendingSpaceId == selectedSpaceId) {
return
}
}
if (selectedIndex != null) {
if (selectedIndex != views.roomListContainerPager.currentItem) {
// post() mitigates a case where we could end up in an endless loop circling around the same few spaces
views.roomListContainerPager.post {
views.roomListContainerPager.currentItem = selectedIndex
}
}
return
}
} else {
// Nothing to change
return
}
} else {
// Clean up old fragments
val transaction = childFragmentManager.beginTransaction()
childFragmentManager.fragments
.forEach {
transaction.detach(it)
}
transaction.commit()
}
}
pagerSpaces = safeSpaces
pagerTab = tab
pagerPagingEnabled = pagingEnabled
initialPageSelected = false
pendingSpaceIds.clear()
views.roomListContainerPager.offscreenPageLimit = 2
val adapter = object: FragmentStateAdapter(this@HomeDetailFragment) {
override fun getItemCount(): Int {
if (!pagingEnabled) {
return 1
}
return when (tab) {
is HomeTab.DialPad -> 1
else -> safeSpaces.size + 1
}
}
override fun createFragment(position: Int): Fragment {
if (DEBUG_VIEW_PAGER) Timber.i("Home pager: create fragment for position $position")
return when (tab) {
is HomeTab.DialPad -> createDialPadFragment()
is HomeTab.RoomList -> {
val params = if (pagingEnabled) {
RoomListParams(tab.displayMode, getSpaceIdForPageIndex(position)).toMvRxBundle()
} else {
RoomListParams(tab.displayMode, SPACE_ID_FOLLOW_APP).toMvRxBundle()
}
childFragmentManager.fragmentFactory.instantiate(activity!!.classLoader, RoomListFragment::class.java.name).apply {
arguments = params
}
}
}
}
}
views.roomListContainerPager.adapter = adapter
if (pagingEnabled) {
views.roomListContainerPager.post {
try {
if (DEBUG_VIEW_PAGER) Timber.i("Home pager: set initial page $selectedIndex")
views.roomListContainerPager.setCurrentItem(selectedIndex ?: 0, false)
initialPageSelected = true
} catch (e: Exception) {
Timber.e("Home pager: Could not set initial page after creating adapter: $e")
}
}
}
}
private fun getPageIndexForSpaceId(spaceId: String?, spaces: List<String?>? = pagerSpaces): Int? {
if (spaceId == null) {
return 0
}
val indexInList = spaces?.indexOf(spaceId)
return when (indexInList) {
null, -1 -> null
else -> indexInList + 1
}
}
private fun getSpaceIdForPageIndex(position: Int, spaces: List<String?>? = pagerSpaces): String? {
val safeSpaces = spaces ?: return null
return if (position == 0) null else safeSpaces[position-1]
}
private fun createDialPadFragment(): Fragment {
val fragment = childFragmentManager.fragmentFactory.instantiate(vectorBaseActivity.classLoader, DialPadFragment::class.java.name)

View file

@ -38,19 +38,26 @@ import im.vector.app.features.invite.showInvites
import im.vector.app.features.settings.VectorDataStore
import im.vector.app.features.ui.UiStateRepository
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.query.ActiveSpaceFilter
import org.matrix.android.sdk.api.query.QueryStringValue
import org.matrix.android.sdk.api.query.RoomCategoryFilter
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.initsync.SyncStatusService
import org.matrix.android.sdk.api.session.room.RoomSortOrder
import org.matrix.android.sdk.api.session.room.accountdata.RoomAccountDataTypes
import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
import org.matrix.android.sdk.api.session.space.model.SpaceOrderContent
import org.matrix.android.sdk.api.session.space.model.TopLevelSpaceComparator
import org.matrix.android.sdk.api.util.toMatrixItem
import org.matrix.android.sdk.flow.flow
import timber.log.Timber
@ -92,6 +99,7 @@ class HomeDetailViewModel @AssistedInject constructor(
observeRoomSummaries()
updatePstnSupportFlag()
observeDataStore()
observeRootSpaces()
callManager.addProtocolsCheckerListener(this)
session.flow().liveUser(session.myUserId).execute {
copy(
@ -276,4 +284,46 @@ class HomeDetailViewModel @AssistedInject constructor(
}
.launchIn(viewModelScope)
}
// Taken from SpaceListViewModel.observeSpaceSummaries()
private fun observeRootSpaces() {
val spaceSummaryQueryParams = roomSummaryQueryParams {
memberships = listOf(Membership.JOIN, Membership.INVITE)
displayName = QueryStringValue.IsNotEmpty
excludeType = listOf(/**RoomType.MESSAGING,$*/
null)
}
val flowSession = session.flow()
combine(
flowSession
.liveUser(session.myUserId)
.map {
it.getOrNull()
},
flowSession
.liveSpaceSummaries(spaceSummaryQueryParams),
session
.accountDataService()
.getLiveRoomAccountDataEvents(setOf(RoomAccountDataTypes.EVENT_TYPE_SPACE_ORDER))
.asFlow()
) { _, communityGroups, _ ->
communityGroups
}
.execute { //async ->
val rootSpaces = session.spaceService().getRootSpaceSummaries()
val orders = rootSpaces.map {
it.roomId to session.getRoom(it.roomId)
?.getAccountDataEvent(RoomAccountDataTypes.EVENT_TYPE_SPACE_ORDER)
?.content.toModel<SpaceOrderContent>()
?.safeOrder()
}.toMap()
copy(
//asyncSpaces = async,
rootSpacesOrdered = rootSpaces.sortedWith(TopLevelSpaceComparator(orders)),
//spaceOrderInfo = orders
)
}
}
}

View file

@ -32,6 +32,7 @@ data class HomeDetailViewState(
val myMatrixItem: MatrixItem? = null,
val asyncRooms: Async<List<RoomSummary>> = Uninitialized,
val currentTab: HomeTab = HomeTab.RoomList(RoomListDisplayMode.ALL),
val rootSpacesOrdered: List<RoomSummary>? = null,
val notificationCountCatchup: Int = 0,
val notificationHighlightCatchup: Boolean = false,
val notificationCountPeople: Int = 0,

View file

@ -47,6 +47,7 @@ import im.vector.app.core.resources.UserPreferencesProvider
import im.vector.app.databinding.FragmentRoomListBinding
import im.vector.app.features.home.RoomListDisplayMode
import im.vector.app.features.home.room.filtered.FilteredRoomFooterItem
import im.vector.app.features.home.room.list.RoomListSectionBuilderSpace.Companion.SPACE_ID_FOLLOW_APP
import im.vector.app.features.home.room.list.actions.RoomListActionsArgs
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsBottomSheet
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedAction
@ -66,7 +67,8 @@ import javax.inject.Inject
@Parcelize
data class RoomListParams(
val displayMode: RoomListDisplayMode
val displayMode: RoomListDisplayMode,
val explicitSpaceId: String? = SPACE_ID_FOLLOW_APP
) : Parcelable
class RoomListFragment @Inject constructor(
@ -559,6 +561,9 @@ class RoomListFragment @Inject constructor(
}
override fun onSwitchSpace(spaceId: String?) {
if (roomListParams.explicitSpaceId == SPACE_ID_FOLLOW_APP) {
return
}
if (spaceId != expandStatusSpaceId) {
persistExpandStatus()
expandStatusSpaceId = spaceId

View file

@ -19,5 +19,5 @@ package im.vector.app.features.home.room.list
import im.vector.app.features.home.RoomListDisplayMode
interface RoomListSectionBuilder {
fun buildSections(mode: RoomListDisplayMode): List<RoomsSection>
fun buildSections(mode: RoomListDisplayMode, explicitSpaceId: String?): List<RoomsSection>
}

View file

@ -47,7 +47,7 @@ class RoomListSectionBuilderGroup(
private val onUpdatable: (UpdatableLivePageResult) -> Unit
) : RoomListSectionBuilder {
override fun buildSections(mode: RoomListDisplayMode): List<RoomsSection> {
override fun buildSections(mode: RoomListDisplayMode, explicitSpaceId: String?): List<RoomsSection> {
val activeGroupAwareQueries = mutableListOf<UpdatableLivePageResult>()
val sections = mutableListOf<RoomsSection>()
val actualGroupId = appStateHandler.safeActiveGroupId()

View file

@ -49,6 +49,7 @@ import org.matrix.android.sdk.api.session.room.UpdatableLivePageResult
import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount
import timber.log.Timber
import kotlin.math.exp
class RoomListSectionBuilderSpace(
private val session: Session,
@ -61,6 +62,10 @@ class RoomListSectionBuilderSpace(
private val onlyOrphansInHome: Boolean = false
) : RoomListSectionBuilder {
companion object {
const val SPACE_ID_FOLLOW_APP = "de.spiritcroc.riotx.SPACE_ID_FOLLOW_APP"
}
private val pagedListConfig = PagedList.Config.Builder()
.setPageSize(10)
.setInitialLoadSizeHint(20)
@ -68,20 +73,20 @@ class RoomListSectionBuilderSpace(
.setPrefetchDistance(10)
.build()
override fun buildSections(mode: RoomListDisplayMode): List<RoomsSection> {
override fun buildSections(mode: RoomListDisplayMode, explicitSpaceId: String?): List<RoomsSection> {
val sections = mutableListOf<RoomsSection>()
val activeSpaceAwareQueries = mutableListOf<RoomListViewModel.ActiveSpaceQueryUpdater>()
when (mode) {
RoomListDisplayMode.PEOPLE -> {
// 4 sections Invites / Fav / Dms / Low Priority
buildDmSections(sections, activeSpaceAwareQueries)
buildDmSections(sections, activeSpaceAwareQueries, explicitSpaceId)
}
RoomListDisplayMode.ROOMS -> {
// 6 sections invites / Fav / Rooms / Low Priority / Server notice / Suggested rooms
buildRoomsSections(sections, activeSpaceAwareQueries)
buildRoomsSections(sections, activeSpaceAwareQueries, explicitSpaceId)
}
RoomListDisplayMode.ALL -> {
buildUnifiedSections(sections, activeSpaceAwareQueries)
buildUnifiedSections(sections, activeSpaceAwareQueries, explicitSpaceId)
}
RoomListDisplayMode.FILTERED -> {
// Used when searching for rooms
@ -106,6 +111,7 @@ class RoomListSectionBuilderSpace(
activeSpaceUpdaters = activeSpaceAwareQueries,
nameRes = R.string.invitations_header,
notifyOfLocalEcho = true,
explicitSpaceId = explicitSpaceId,
spaceFilterStrategy = if (onlyOrphansInHome) {
RoomListViewModel.SpaceFilterStrategy.ORPHANS_IF_SPACE_NULL
} else {
@ -123,6 +129,7 @@ class RoomListSectionBuilderSpace(
activeSpaceUpdaters = activeSpaceAwareQueries,
nameRes = R.string.bottom_action_rooms,
notifyOfLocalEcho = false,
explicitSpaceId = explicitSpaceId,
spaceFilterStrategy = if (onlyOrphansInHome) {
RoomListViewModel.SpaceFilterStrategy.ORPHANS_IF_SPACE_NULL
} else {
@ -135,24 +142,31 @@ class RoomListSectionBuilderSpace(
}
}
appStateHandler.selectedRoomGroupingObservable
.distinctUntilChanged()
.onEach { groupingMethod ->
val selectedSpace = groupingMethod.orNull()?.space()
activeSpaceAwareQueries.onEach { updater ->
updater.updateForSpaceId(selectedSpace?.roomId)
}
}.launchIn(viewModelScope)
if (explicitSpaceId == SPACE_ID_FOLLOW_APP) {
appStateHandler.selectedRoomGroupingObservable
.distinctUntilChanged()
.onEach { groupingMethod ->
val selectedSpace = groupingMethod.orNull()?.space()
activeSpaceAwareQueries.onEach { updater ->
updater.updateForSpaceId(selectedSpace?.roomId)
}
}.launchIn(viewModelScope)
} else {
activeSpaceAwareQueries.onEach { updater ->
updater.updateForSpaceId(explicitSpaceId)
}
}
return sections
}
private fun buildUnifiedSections(sections: MutableList<RoomsSection>, activeSpaceAwareQueries: MutableList<RoomListViewModel.ActiveSpaceQueryUpdater>) {
private fun buildUnifiedSections(sections: MutableList<RoomsSection>, activeSpaceAwareQueries: MutableList<RoomListViewModel.ActiveSpaceQueryUpdater>, explicitSpaceId: String?) {
addSection(
sections = sections,
activeSpaceUpdaters = activeSpaceAwareQueries,
nameRes = R.string.invitations_header,
notifyOfLocalEcho = true,
explicitSpaceId = explicitSpaceId,
spaceFilterStrategy = RoomListViewModel.SpaceFilterStrategy.ALL_IF_SPACE_NULL,
countRoomAsNotif = true
) {
@ -164,7 +178,8 @@ class RoomListSectionBuilderSpace(
activeSpaceAwareQueries,
R.string.bottom_action_favourites,
false,
RoomListViewModel.SpaceFilterStrategy.ALL_IF_SPACE_NULL
RoomListViewModel.SpaceFilterStrategy.ALL_IF_SPACE_NULL,
explicitSpaceId = explicitSpaceId,
) {
it.memberships = listOf(Membership.JOIN)
it.roomTagQueryFilter = RoomTagQueryFilter(true, null, null)
@ -175,6 +190,7 @@ class RoomListSectionBuilderSpace(
activeSpaceUpdaters = activeSpaceAwareQueries,
nameRes = R.string.normal_priority_header,
notifyOfLocalEcho = false,
explicitSpaceId = explicitSpaceId,
spaceFilterStrategy = if (onlyOrphansInHome) {
RoomListViewModel.SpaceFilterStrategy.ORPHANS_IF_SPACE_NULL
} else {
@ -190,6 +206,7 @@ class RoomListSectionBuilderSpace(
activeSpaceUpdaters = activeSpaceAwareQueries,
nameRes = R.string.low_priority_header,
notifyOfLocalEcho = false,
explicitSpaceId = explicitSpaceId,
spaceFilterStrategy = if (onlyOrphansInHome) {
RoomListViewModel.SpaceFilterStrategy.ORPHANS_IF_SPACE_NULL
} else {
@ -205,6 +222,7 @@ class RoomListSectionBuilderSpace(
activeSpaceUpdaters = activeSpaceAwareQueries,
nameRes = R.string.system_alerts_header,
notifyOfLocalEcho = false,
explicitSpaceId = explicitSpaceId,
spaceFilterStrategy = if (onlyOrphansInHome) {
RoomListViewModel.SpaceFilterStrategy.ORPHANS_IF_SPACE_NULL
} else {
@ -215,17 +233,19 @@ class RoomListSectionBuilderSpace(
it.roomTagQueryFilter = RoomTagQueryFilter(null, null, true)
}
addSuggestedRoomsSection(sections)
addSuggestedRoomsSection(sections, explicitSpaceId)
}
private fun buildRoomsSections(sections: MutableList<RoomsSection>,
activeSpaceAwareQueries: MutableList<RoomListViewModel.ActiveSpaceQueryUpdater>) {
activeSpaceAwareQueries: MutableList<RoomListViewModel.ActiveSpaceQueryUpdater>,
explicitSpaceId: String?) {
if (autoAcceptInvites.showInvites()) {
addSection(
sections = sections,
activeSpaceUpdaters = activeSpaceAwareQueries,
nameRes = R.string.invitations_header,
notifyOfLocalEcho = true,
explicitSpaceId = explicitSpaceId,
spaceFilterStrategy = RoomListViewModel.SpaceFilterStrategy.ALL_IF_SPACE_NULL,
countRoomAsNotif = true
) {
@ -239,7 +259,8 @@ class RoomListSectionBuilderSpace(
activeSpaceAwareQueries,
R.string.bottom_action_favourites,
false,
RoomListViewModel.SpaceFilterStrategy.ALL_IF_SPACE_NULL
RoomListViewModel.SpaceFilterStrategy.ALL_IF_SPACE_NULL,
explicitSpaceId = explicitSpaceId,
) {
it.memberships = listOf(Membership.JOIN)
it.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS
@ -251,6 +272,7 @@ class RoomListSectionBuilderSpace(
activeSpaceUpdaters = activeSpaceAwareQueries,
nameRes = R.string.bottom_action_rooms,
notifyOfLocalEcho = false,
explicitSpaceId = explicitSpaceId,
spaceFilterStrategy = if (onlyOrphansInHome) {
RoomListViewModel.SpaceFilterStrategy.ORPHANS_IF_SPACE_NULL
} else {
@ -267,6 +289,7 @@ class RoomListSectionBuilderSpace(
activeSpaceUpdaters = activeSpaceAwareQueries,
nameRes = R.string.low_priority_header,
notifyOfLocalEcho = false,
explicitSpaceId = explicitSpaceId,
spaceFilterStrategy = if (onlyOrphansInHome) {
RoomListViewModel.SpaceFilterStrategy.ORPHANS_IF_SPACE_NULL
} else {
@ -283,6 +306,7 @@ class RoomListSectionBuilderSpace(
activeSpaceUpdaters = activeSpaceAwareQueries,
nameRes = R.string.system_alerts_header,
notifyOfLocalEcho = false,
explicitSpaceId = explicitSpaceId,
spaceFilterStrategy = if (onlyOrphansInHome) {
RoomListViewModel.SpaceFilterStrategy.ORPHANS_IF_SPACE_NULL
} else {
@ -294,12 +318,13 @@ class RoomListSectionBuilderSpace(
it.roomTagQueryFilter = RoomTagQueryFilter(null, null, true)
}
addSuggestedRoomsSection(sections)
addSuggestedRoomsSection(sections, explicitSpaceId)
}
private fun addSuggestedRoomsSection(sections: MutableList<RoomsSection>) {
private fun addSuggestedRoomsSection(sections: MutableList<RoomsSection>,
explicitSpaceId: String?) {
// add suggested rooms
val suggestedRoomsFlow = // MutableLiveData<List<SpaceChildInfo>>()
val suggestedRoomsFlow = if (explicitSpaceId == SPACE_ID_FOLLOW_APP) { // MutableLiveData<List<SpaceChildInfo>>()
appStateHandler.selectedRoomGroupingObservable
.distinctUntilChanged()
.flatMapLatest { groupingMethod ->
@ -321,6 +346,24 @@ class RoomListSectionBuilderSpace(
}.asFlow()
}
}
} else {
if (explicitSpaceId == null) {
flowOf(emptyList())
} else {
liveData(context = viewModelScope.coroutineContext + Dispatchers.IO) {
val spaceSum = tryOrNull {
session.spaceService()
.querySpaceChildren(explicitSpaceId, suggestedOnly = true, null, null)
}
val value = spaceSum?.children.orEmpty().distinctBy { it.childRoomId }
// i need to check if it's already joined.
val filtered = value.filter {
session.getRoomSummary(it.childRoomId)?.membership?.isActive() != true
}
emit(filtered)
}.asFlow()
}
}
val liveSuggestedRooms = MutableLiveData<SuggestedRoomInfo>()
combine(
@ -345,13 +388,15 @@ class RoomListSectionBuilderSpace(
}
private fun buildDmSections(sections: MutableList<RoomsSection>,
activeSpaceAwareQueries: MutableList<RoomListViewModel.ActiveSpaceQueryUpdater>) {
activeSpaceAwareQueries: MutableList<RoomListViewModel.ActiveSpaceQueryUpdater>,
explicitSpaceId: String?) {
if (autoAcceptInvites.showInvites()) {
addSection(
sections = sections,
activeSpaceUpdaters = activeSpaceAwareQueries,
nameRes = R.string.invitations_header,
notifyOfLocalEcho = true,
explicitSpaceId = explicitSpaceId,
spaceFilterStrategy = RoomListViewModel.SpaceFilterStrategy.ALL_IF_SPACE_NULL,
countRoomAsNotif = true
) {
@ -365,7 +410,8 @@ class RoomListSectionBuilderSpace(
activeSpaceAwareQueries,
R.string.bottom_action_favourites,
false,
RoomListViewModel.SpaceFilterStrategy.ALL_IF_SPACE_NULL
RoomListViewModel.SpaceFilterStrategy.ALL_IF_SPACE_NULL,
explicitSpaceId = explicitSpaceId
) {
it.memberships = listOf(Membership.JOIN)
it.roomCategoryFilter = RoomCategoryFilter.ONLY_DM
@ -377,7 +423,8 @@ class RoomListSectionBuilderSpace(
activeSpaceAwareQueries,
R.string.bottom_action_people_x,
false,
RoomListViewModel.SpaceFilterStrategy.ALL_IF_SPACE_NULL
RoomListViewModel.SpaceFilterStrategy.ALL_IF_SPACE_NULL,
explicitSpaceId = explicitSpaceId
) {
it.memberships = listOf(Membership.JOIN)
it.roomCategoryFilter = RoomCategoryFilter.ONLY_DM
@ -389,7 +436,8 @@ class RoomListSectionBuilderSpace(
activeSpaceAwareQueries,
R.string.low_priority_header,
false,
RoomListViewModel.SpaceFilterStrategy.ALL_IF_SPACE_NULL
RoomListViewModel.SpaceFilterStrategy.ALL_IF_SPACE_NULL,
explicitSpaceId = explicitSpaceId
) {
it.memberships = listOf(Membership.JOIN)
it.roomCategoryFilter = RoomCategoryFilter.ONLY_DM
@ -398,19 +446,28 @@ class RoomListSectionBuilderSpace(
}
private fun AppStateHandler.explicitOrSafeActiveSpaceId(explicitSpaceId: String?): String? {
return if (explicitSpaceId == SPACE_ID_FOLLOW_APP) {
safeActiveSpaceId()
} else {
explicitSpaceId
}
}
private fun addSection(sections: MutableList<RoomsSection>,
activeSpaceUpdaters: MutableList<RoomListViewModel.ActiveSpaceQueryUpdater>,
@StringRes nameRes: Int,
notifyOfLocalEcho: Boolean = false,
spaceFilterStrategy: RoomListViewModel.SpaceFilterStrategy = RoomListViewModel.SpaceFilterStrategy.NONE,
countRoomAsNotif: Boolean = false,
explicitSpaceId: String?,
query: (RoomSummaryQueryParams.Builder) -> Unit) {
withQueryParams(
{ query.invoke(it) },
{ roomQueryParams ->
val name = stringProvider.getString(nameRes)
session.getFilteredPagedRoomSummariesLive(
roomQueryParams.process(spaceFilterStrategy, appStateHandler.safeActiveSpaceId()),
roomQueryParams.process(spaceFilterStrategy, appStateHandler.explicitOrSafeActiveSpaceId(explicitSpaceId)),
pagedListConfig
).also {
when (spaceFilterStrategy) {
@ -461,7 +518,7 @@ class RoomListSectionBuilderSpace(
RoomAggregateNotificationCount(it.size, it.size, 0)
} else {
session.getNotificationCountForRooms(
roomQueryParams.process(spaceFilterStrategy, appStateHandler.safeActiveSpaceId())
roomQueryParams.process(spaceFilterStrategy, appStateHandler.explicitOrSafeActiveSpaceId(explicitSpaceId))
)
}
)

View file

@ -146,7 +146,7 @@ class RoomListViewModel @AssistedInject constructor(
}
val sections: List<RoomsSection> by lazy {
roomListSectionBuilder.buildSections(initialState.displayMode)
roomListSectionBuilder.buildSections(initialState.displayMode, initialState.explicitSpaceId)
}
override fun handle(action: RoomListAction) {

View file

@ -30,8 +30,10 @@ data class RoomListViewState(
val roomMembershipChanges: Map<String, ChangeMembershipState> = emptyMap(),
val asyncSuggestedRooms: Async<List<SpaceChildInfo>> = Uninitialized,
val currentUserName: String? = null,
val currentRoomGrouping: Async<RoomGroupingMethod> = Uninitialized
val currentRoomGrouping: Async<RoomGroupingMethod> = Uninitialized,
// In comparison to currentRoomGrouping, the explicit space id fixes a filter method that should not change afterwards
val explicitSpaceId: String? = null
) : MavericksState {
constructor(args: RoomListParams) : this(displayMode = args.displayMode)
constructor(args: RoomListParams) : this(displayMode = args.displayMode, explicitSpaceId = args.explicitSpaceId)
}

View file

@ -214,6 +214,7 @@ class VectorPreferences @Inject constructor(private val context: Context): Stati
const val SETTINGS_FORCE_ALLOW_BACKGROUND_SYNC = "SETTINGS_FORCE_ALLOW_BACKGROUND_SYNC"
private const val SETTINGS_JUMP_TO_BOTTOM_ON_SEND = "SETTINGS_JUMP_TO_BOTTOM_ON_SEND"
private const val SETTINGS_SPACE_MEMBERS_IN_SPACE_ROOMS = "SETTINGS_SPACE_MEMBERS_IN_SPACE_ROOMS"
private const val SETTINGS_ENABLE_SPACE_PAGER = "SETTINGS_ENABLE_SPACE_PAGER"
private const val DID_ASK_TO_ENABLE_SESSION_PUSH = "DID_ASK_TO_ENABLE_SESSION_PUSH"
private const val DID_PROMOTE_NEW_RESTRICTED_JOIN_RULE = "DID_PROMOTE_NEW_RESTRICTED_JOIN_RULE"
@ -1043,18 +1044,26 @@ class VectorPreferences @Inject constructor(private val context: Context): Stati
return Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP && defaultPrefs.getBoolean(SETTINGS_VOICE_MESSAGE, true)
}
// SC addition
fun jumpToBottomOnSend(): Boolean {
return defaultPrefs.getBoolean(SETTINGS_JUMP_TO_BOTTOM_ON_SEND, true)
}
// SC addition
fun forceUseCustomUpGateway(): Boolean {
return defaultPrefs.getBoolean(SETTINGS_UNIFIED_PUSH_FORCE_CUSTOM_GATEWAY, false)
}
// SC addition
fun forceAllowBackgroundSync(): Boolean {
return defaultPrefs.getBoolean(SETTINGS_FORCE_ALLOW_BACKGROUND_SYNC, false)
}
// SC addition
fun enableSpacePager(): Boolean {
return defaultPrefs.getBoolean(SETTINGS_ENABLE_SPACE_PAGER, false)
}
/**
* I likely do more fresh installs of the app than anyone else, so a shortcut to change some of the default settings to
* my preferred values can safe me some time
@ -1077,6 +1086,7 @@ class VectorPreferences @Inject constructor(private val context: Context): Stati
.putBoolean(SETTINGS_USE_RAGE_SHAKE_KEY, true)
.putBoolean(SETTINGS_UNIFIED_PUSH_FORCE_CUSTOM_GATEWAY, true)
.putBoolean(SETTINGS_AGGREGATE_UNREAD_COUNTS, false)
.putBoolean(SETTINGS_ENABLE_SPACE_PAGER, true)
.apply()
}

View file

@ -137,12 +137,21 @@
app:layout_constraintTop_toBottomOf="@id/syncStateView"
tools:visibility="visible" />
<!--
<androidx.fragment.app.FragmentContainerView
android:id="@+id/roomListContainer"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/bottomNavigationView"
app:layout_constraintTop_toBottomOf="@+id/homeKeysBackupBanner" />
-->
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/roomListContainerPager"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/bottomNavigationView"
app:layout_constraintTop_toBottomOf="@+id/homeKeysBackupBanner" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottomNavigationView"

View file

@ -151,4 +151,7 @@
<string name="settings_include_space_members_as_rooms">Show people in spaces</string>
<string name="settings_include_space_members_as_rooms_summary">If disabled, you can still add Direct Messages to Personal Spaces. If enabled, you\'ll automatically see everyone who is a member of the Space.</string>
<string name="settings_enable_space_pager">Swipe chat list to switch space</string>
<string name="settings_enable_space_pager_summary">Allow to switch between root spaces by swiping horizontally in the chat list</string>
</resources>

View file

@ -74,6 +74,12 @@
android:key="SETTINGS_LABS_UNREAD_NOTIFICATIONS_AS_TAB"
android:title="@string/labs_show_unread_notifications_as_tab" />
<im.vector.app.core.preference.VectorSwitchPreference
android:defaultValue="false"
android:key="SETTINGS_ENABLE_SPACE_PAGER"
android:title="@string/settings_enable_space_pager"
android:summary="@string/settings_enable_space_pager_summary" />
<im.vector.app.core.preference.VectorSwitchPreference
android:defaultValue="false"