diff --git a/library/ui-strings/src/main/res/values/strings_sc.xml b/library/ui-strings/src/main/res/values/strings_sc.xml index 3d16118bab..066f4911c2 100644 --- a/library/ui-strings/src/main/res/values/strings_sc.xml +++ b/library/ui-strings/src/main/res/values/strings_sc.xml @@ -200,4 +200,7 @@ When changing the system language, also change the app\'s language Element\'s new simplified layout with optional tabs + + Unread + diff --git a/matrix-sdk-android/src/main/java/de/spiritcroc/android/sdk/internal/database/migration/MigrateScSessionTo007.kt b/matrix-sdk-android/src/main/java/de/spiritcroc/android/sdk/internal/database/migration/MigrateScSessionTo007.kt new file mode 100644 index 0000000000..6e68dd0b84 --- /dev/null +++ b/matrix-sdk-android/src/main/java/de/spiritcroc/android/sdk/internal/database/migration/MigrateScSessionTo007.kt @@ -0,0 +1,26 @@ +package de.spiritcroc.android.sdk.internal.database.migration + +import de.spiritcroc.android.sdk.internal.util.database.ScRealmMigrator +import io.realm.DynamicRealm +import org.matrix.android.sdk.api.extensions.tryOrNull +import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity +import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields + +internal class MigrateScSessionTo007(realm: DynamicRealm) : ScRealmMigrator(realm, 7) { + + override fun doMigrate(realm: DynamicRealm) { + realm.schema.get("RoomSummaryEntity") + ?.addField(RoomSummaryEntityFields.TREAT_AS_UNREAD_LEVEL, Int::class.java) + ?.transform { obj -> + val unreadCount = tryOrNull { obj.getInt(RoomSummaryEntityFields.UNREAD_COUNT) } + val treatAsUnreadLevel = RoomSummaryEntity.calculateUnreadLevel( + obj.getInt(RoomSummaryEntityFields.HIGHLIGHT_COUNT), + obj.getInt(RoomSummaryEntityFields.NOTIFICATION_COUNT), + obj.getBoolean(RoomSummaryEntityFields.MARKED_UNREAD), + unreadCount + ) + obj.setInt(RoomSummaryEntityFields.TREAT_AS_UNREAD_LEVEL, treatAsUnreadLevel) + } + ?.addIndex(RoomSummaryEntityFields.TREAT_AS_UNREAD_LEVEL) + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomSortOrder.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomSortOrder.kt index 9368ad6bf4..25f1bf20de 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomSortOrder.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomSortOrder.kt @@ -30,6 +30,11 @@ enum class RoomSortOrder { */ ACTIVITY, + /** + * Show unread above read + */ + UNREAD_AND_ACTIVITY, + /** * Sort room list by room priority and last activity: favorite room first, low priority room last, * then descending last activity. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt index 4ebdbadafd..f406909627 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt @@ -22,6 +22,7 @@ import de.spiritcroc.android.sdk.internal.database.migration.MigrateScSessionTo0 import de.spiritcroc.android.sdk.internal.database.migration.MigrateScSessionTo004 import de.spiritcroc.android.sdk.internal.database.migration.MigrateScSessionTo005 import de.spiritcroc.android.sdk.internal.database.migration.MigrateScSessionTo006 +import de.spiritcroc.android.sdk.internal.database.migration.MigrateScSessionTo007 import io.realm.DynamicRealm import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo001 import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo002 @@ -80,7 +81,7 @@ internal class RealmSessionStoreMigration @Inject constructor( companion object { // SC-specific DB changes on top of Element - private val scSchemaVersion = 6L + private val scSchemaVersion = 7L private val scSchemaVersionOffset = (1L shl 12) val schemaVersion = 37L + @@ -144,5 +145,6 @@ internal class RealmSessionStoreMigration @Inject constructor( if (oldScVersion <= 3) MigrateScSessionTo004(realm).perform() if (oldScVersion <= 4) MigrateScSessionTo005(realm).perform() if (oldScVersion <= 5) MigrateScSessionTo006(realm).perform() + if (oldScVersion <= 6) MigrateScSessionTo007(realm).perform() } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomSummaryEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomSummaryEntity.kt index 6c1fd61a5f..c7e2d4303c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomSummaryEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomSummaryEntity.kt @@ -21,6 +21,7 @@ import io.realm.RealmList import io.realm.RealmObject import io.realm.annotations.Index import io.realm.annotations.PrimaryKey +import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel import org.matrix.android.sdk.api.session.room.model.Membership @@ -122,16 +123,19 @@ internal open class RoomSummaryEntity( var notificationCount: Int = 0 set(value) { if (value != field) field = value + updateTreatAsUnread() } var highlightCount: Int = 0 set(value) { if (value != field) field = value + updateTreatAsUnread() } var unreadCount: Int? = null set(value) { if (value != field) field = value + updateTreatAsUnread() } /* -> safeUnreadCount get() { @@ -193,9 +197,25 @@ internal open class RoomSummaryEntity( var markedUnread: Boolean = false set(value) { - if (value != field) field = value + if (value != field) { + field = value + updateTreatAsUnread() + } } + /** + * SchildiChat: Helper var so we can sort depending on how "unread" a chat is: mentions > {notifications, marked unread} > unreads > all read + * Make sure to call `updateTreatAsUnread()` when necessary. + */ + @Index + private var treatAsUnreadLevel: Int = calculateUnreadLevel() + private fun updateTreatAsUnread() { + treatAsUnreadLevel = calculateUnreadLevel() + } + private fun calculateUnreadLevel(): Int { + return calculateUnreadLevel(highlightCount, notificationCount, markedUnread, unreadCount) + } + private var tags: RealmList = RealmList() fun tags(): List = tags @@ -377,7 +397,23 @@ internal open class RoomSummaryEntity( } } - companion object + companion object { + private const val UNREAD_LEVEL_HIGHLIGHT = 4 + private const val UNREAD_LEVEL_NOTIFIED = 3 + private const val UNREAD_LEVEL_MARKED_UNREAD = UNREAD_LEVEL_NOTIFIED + private const val UNREAD_LEVEL_SILENT_UNREAD = 1 + private const val UNREAD_LEVEL_NONE = 0 + + fun calculateUnreadLevel(highlightCount: Int, notificationCount: Int, markedUnread: Boolean, unreadCount: Int?): Int { + return when { + highlightCount > 0 -> UNREAD_LEVEL_HIGHLIGHT + notificationCount > 0 -> UNREAD_LEVEL_NOTIFIED + markedUnread -> UNREAD_LEVEL_MARKED_UNREAD + unreadCount?.let { it > 0 }.orFalse() -> UNREAD_LEVEL_SILENT_UNREAD + else -> UNREAD_LEVEL_NONE + } + } + } // Keep sync with RoomSummary.scHasUnreadMessages! diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/query/QueryRoomOrderProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/query/QueryRoomOrderProcessor.kt index 8df0482993..04dba256d7 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/query/QueryRoomOrderProcessor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/query/QueryRoomOrderProcessor.kt @@ -30,6 +30,11 @@ internal fun RealmQuery.process(sortOrder: RoomSortOrder): Re RoomSortOrder.ACTIVITY -> { sort(RoomSummaryEntityFields.LAST_ACTIVITY_TIME, Sort.DESCENDING) } + RoomSortOrder.UNREAD_AND_ACTIVITY -> { + sort( + arrayOf(RoomSummaryEntityFields.TREAT_AS_UNREAD_LEVEL, RoomSummaryEntityFields.LAST_ACTIVITY_TIME), + arrayOf(Sort.DESCENDING, Sort.DESCENDING)) + } RoomSortOrder.PRIORITY_AND_ACTIVITY -> { sort( arrayOf( diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt index cde3f58f30..606ed240bf 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt @@ -23,8 +23,10 @@ import android.view.MenuItem import android.view.View import android.view.ViewGroup import android.view.ViewTreeObserver +import androidx.core.content.edit import androidx.core.view.isVisible import androidx.fragment.app.Fragment +import androidx.preference.PreferenceManager import androidx.recyclerview.widget.LinearLayoutManager import androidx.viewpager2.adapter.FragmentStateAdapter import androidx.viewpager2.widget.ViewPager2 @@ -74,6 +76,7 @@ import im.vector.app.features.workers.signout.ServerBackupStatusAction 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.room.RoomSortOrder import org.matrix.android.sdk.api.session.room.model.RoomSummary import timber.log.Timber import javax.inject.Inject @@ -124,6 +127,7 @@ class HomeDetailFragment : private var pagerTab: HomeTab? = null private var pagerPagingEnabled: Boolean = false private var previousSelectedSpacePair: Pair? = null + private var roomSortOrderSettings: RoomSortOrderSettings? = null override fun getMenuRes() = R.menu.room_list @@ -133,14 +137,64 @@ class HomeDetailFragment : viewModel.handle(HomeDetailAction.MarkAllRoomsRead) true } + R.id.menu_room_sort_order_activity -> { + storeRoomSortOrder(RoomSortOrder.ACTIVITY) + updateViewPager() + true + } + R.id.menu_room_sort_order_unread -> { + storeRoomSortOrder(RoomSortOrder.UNREAD_AND_ACTIVITY) + updateViewPager() + true + } else -> false } } + private fun currentEffectiveSpace(): String? { + return if (pagerPagingEnabled) { + getSpaceIdForPageIndex(views.roomListContainerPager.currentItem) + } else { + SPACE_ID_FOLLOW_APP + } + } + + private fun storeRoomSortOrder(roomSortOrder: RoomSortOrder) { + val sharedPreferences = context?.let { PreferenceManager.getDefaultSharedPreferences(it) } ?: return + val space = currentEffectiveSpace() + val pref = if (space == null) VectorPreferences.SETTINGS_ROOM_SORT_ORDER_NULL else VectorPreferences.SETTINGS_ROOM_SORT_ORDER_NON_NULL + sharedPreferences.edit { + putString(pref, roomSortOrder.toString()) + } + } + + private fun updateViewPager() { + withState(viewModel) { + val selectedSpace = it.selectedSpaceIgnoreSwipe ?: return@withState + spaceStateHandler.persistSelectedSpace() + setupViewPager(selectedSpace, it.rootSpacesOrdered, it.currentTab) + } + } + override fun handlePrepareMenu(menu: Menu) { withState(viewModel) { state -> val isRoomList = state.currentTab is HomeTab.RoomList menu.findItem(R.id.menu_home_mark_all_as_read).isVisible = isRoomList && hasUnreadRooms + menu.findItem(R.id.menu_room_sort_order).isVisible = true + + // Room sort order + val space = currentEffectiveSpace() + val roomSortOrder = + if (space == null) { + roomSortOrderSettings?.nullSpace + } else { + roomSortOrderSettings?.space + } + when (roomSortOrder) { + RoomSortOrder.ACTIVITY -> menu.findItem(R.id.menu_room_sort_order_activity).isChecked = true + RoomSortOrder.UNREAD_AND_ACTIVITY -> menu.findItem(R.id.menu_room_sort_order_unread).isChecked = true + else -> Unit + } } } @@ -605,10 +659,11 @@ class HomeDetailFragment : views.spaceBarRecyclerView.isVisible = false } val safeSpaces = if (pagingEnabled) unsafeSpaces else listOf() + val newRoomSortOrderSettings = loadRoomSortOrderSettings() // Check if we need to recreate the adapter for a new tab if (oldAdapter != null) { - val changed = pagerTab != tab || pagerSpaces != safeSpaces || pagerPagingEnabled != pagingEnabled - viewPagerDimber.i{ "has changed: $changed (${pagerTab != tab} ${pagerSpaces != safeSpaces} ${pagerPagingEnabled != pagingEnabled} $selectedIndex ${selectedSpacePair.second} ${views.roomListContainerPager.currentItem}) | $safeSpaces" } + val changed = pagerTab != tab || pagerSpaces != safeSpaces || pagerPagingEnabled != pagingEnabled || roomSortOrderSettings != newRoomSortOrderSettings + viewPagerDimber.i{ "has changed: $changed (${pagerTab != tab} ${pagerSpaces != safeSpaces} ${pagerPagingEnabled != pagingEnabled} ${roomSortOrderSettings != newRoomSortOrderSettings} $selectedIndex ${selectedSpacePair.second} ${views.roomListContainerPager.currentItem}) | $safeSpaces" } if (!changed) { // No need to re-setup pager, just check for selected page if (pagingEnabled) { @@ -653,6 +708,7 @@ class HomeDetailFragment : spaceStateHandler.persistSelectedSpace() pagerSpaces = safeSpaces pagerTab = tab + roomSortOrderSettings = newRoomSortOrderSettings if (pagerPagingEnabled != pagingEnabled) { pagerPagingEnabled = pagingEnabled // Update counts which depend on pagerPagingEnabled @@ -691,10 +747,10 @@ class HomeDetailFragment : val params = if (pagingEnabled) { val spaceId = getSpaceIdForPageIndex(position) viewPagerDimber.i{"Home pager: position $position -> space $spaceId"} - RoomListParams(tab.displayMode, spaceId).toMvRxBundle() + RoomListParams(tab.displayMode, spaceId, getRoomSortOrder(spaceId)).toMvRxBundle() } else { viewPagerDimber.i{"Home pager: paging disabled; position $position -> follow"} - RoomListParams(tab.displayMode, SPACE_ID_FOLLOW_APP).toMvRxBundle() + RoomListParams(tab.displayMode, SPACE_ID_FOLLOW_APP, getRoomSortOrder(SPACE_ID_FOLLOW_APP)).toMvRxBundle() } childFragmentManager.fragmentFactory.instantiate(activity!!.classLoader, RoomListFragment::class.java.name).apply { arguments = params @@ -753,6 +809,26 @@ class HomeDetailFragment : return if (position == 0) null else spaces[position-1] } + data class RoomSortOrderSettings(val nullSpace: RoomSortOrder, val space: RoomSortOrder) + + private fun loadRoomSortOrderSettings(): RoomSortOrderSettings? { + val sharedPreferences = context?.let { PreferenceManager.getDefaultSharedPreferences(it) } ?: return null + val default = RoomSortOrder.ACTIVITY + return RoomSortOrderSettings( + tryOrNull { sharedPreferences.getString(VectorPreferences.SETTINGS_ROOM_SORT_ORDER_NULL, null)?.let { RoomSortOrder.valueOf(it) } } ?: default, + tryOrNull { sharedPreferences.getString(VectorPreferences.SETTINGS_ROOM_SORT_ORDER_NON_NULL, null)?.let { RoomSortOrder.valueOf(it) } } ?: default, + ) + } + + private fun getRoomSortOrder(space: String?): RoomSortOrder { + return ( + if (space == null) + roomSortOrderSettings?.nullSpace + else + roomSortOrderSettings?.space + ) ?: RoomSortOrder.ACTIVITY + } + private fun createDialPadFragment(): Fragment { val fragment = childFragmentManager.fragmentFactory.instantiate(vectorBaseActivity.classLoader, DialPadFragment::class.java.name) return (fragment as DialPadFragment).apply { diff --git a/vector/src/main/java/im/vector/app/features/home/NewHomeDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/NewHomeDetailFragment.kt index fe3e3c2af8..caee2102d8 100644 --- a/vector/src/main/java/im/vector/app/features/home/NewHomeDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/NewHomeDetailFragment.kt @@ -123,6 +123,7 @@ class NewHomeDetailFragment : val isRoomList = state.currentTab is HomeTab.RoomList menu.findItem(R.id.menu_home_mark_all_as_read).isVisible = isRoomList && hasUnreadRooms menu.findItem(R.id.menu_home_dialpad).isVisible = state.showDialPadTab + menu.findItem(R.id.menu_room_sort_order).isVisible = false } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt index 805e6c79de..6d82da7577 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt @@ -68,6 +68,8 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import kotlinx.parcelize.Parcelize import org.matrix.android.sdk.api.extensions.orTrue +import org.matrix.android.sdk.api.extensions.tryOrNull +import org.matrix.android.sdk.api.session.room.RoomSortOrder import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo import org.matrix.android.sdk.api.session.room.model.tag.RoomTag @@ -77,7 +79,8 @@ import javax.inject.Inject @Parcelize data class RoomListParams( val displayMode: RoomListDisplayMode, - val explicitSpaceId: String? = SPACE_ID_FOLLOW_APP + val explicitSpaceId: String? = SPACE_ID_FOLLOW_APP, + val roomSortOrder: RoomSortOrder = RoomSortOrder.ACTIVITY ) : Parcelable @AndroidEntryPoint diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListSectionBuilder.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListSectionBuilder.kt index 3f15eeb8c0..7ff7012361 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListSectionBuilder.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListSectionBuilder.kt @@ -51,6 +51,7 @@ import org.matrix.android.sdk.api.query.toActiveSpaceOrNoFilter import org.matrix.android.sdk.api.query.toActiveSpaceOrOrphanRooms import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.getRoomSummary +import org.matrix.android.sdk.api.session.room.RoomSortOrder import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams import org.matrix.android.sdk.api.session.room.UpdatableLivePageResult import org.matrix.android.sdk.api.session.room.model.Membership @@ -82,28 +83,28 @@ class RoomListSectionBuilder( private val dimber = Dimber("ViewPager", DbgUtil.DBG_VIEW_PAGER) - fun buildSections(mode: RoomListDisplayMode, explicitSpaceId: String?): List { + fun buildSections(mode: RoomListDisplayMode, explicitSpaceId: String?, sortOrder: RoomSortOrder = RoomSortOrder.ACTIVITY): List { dimber.i { "Build sections for $mode, $explicitSpaceId" } val sections = mutableListOf() val activeSpaceAwareQueries = mutableListOf() when (mode) { RoomListDisplayMode.PEOPLE -> { // 4 sections Invites / Fav / Dms / Low Priority - buildDmSections(sections, activeSpaceAwareQueries, explicitSpaceId) + buildDmSections(sections, activeSpaceAwareQueries, explicitSpaceId, sortOrder) } RoomListDisplayMode.ROOMS -> { // 6 sections invites / Fav / Rooms / Low Priority / Server notice / Suggested rooms - buildRoomsSections(sections, activeSpaceAwareQueries, explicitSpaceId) + buildRoomsSections(sections, activeSpaceAwareQueries, explicitSpaceId, sortOrder) } RoomListDisplayMode.ALL -> { - buildUnifiedSections(sections, activeSpaceAwareQueries, explicitSpaceId) + buildUnifiedSections(sections, activeSpaceAwareQueries, explicitSpaceId, sortOrder) } RoomListDisplayMode.FILTERED -> { // Used when searching for rooms buildFilteredSection(sections) } RoomListDisplayMode.NOTIFICATIONS -> { - buildNotificationsSection(sections, activeSpaceAwareQueries, explicitSpaceId) + buildNotificationsSection(sections, activeSpaceAwareQueries, explicitSpaceId, sortOrder) } } @@ -125,13 +126,14 @@ class RoomListSectionBuilder( return sections } - private fun buildUnifiedSections(sections: MutableList, activeSpaceAwareQueries: MutableList, explicitSpaceId: String?) { + private fun buildUnifiedSections(sections: MutableList, activeSpaceAwareQueries: MutableList, explicitSpaceId: String?, sortOrder: RoomSortOrder) { addSection( sections = sections, activeSpaceUpdaters = activeSpaceAwareQueries, nameRes = R.string.invitations_header, notifyOfLocalEcho = true, explicitSpaceId = explicitSpaceId, + sortOrder = sortOrder, spaceFilterStrategy = RoomListViewModel.SpaceFilterStrategy.ALL_IF_SPACE_NULL, countRoomAsNotif = true ) { @@ -149,6 +151,7 @@ class RoomListSectionBuilder( RoomListViewModel.SpaceFilterStrategy.ALL_IF_SPACE_NULL }, explicitSpaceId = explicitSpaceId, + sortOrder = sortOrder ) { it.memberships = listOf(Membership.JOIN) it.roomTagQueryFilter = RoomTagQueryFilter(true, null, null) @@ -160,6 +163,7 @@ class RoomListSectionBuilder( nameRes = R.string.normal_priority_header, notifyOfLocalEcho = false, explicitSpaceId = explicitSpaceId, + sortOrder = sortOrder, spaceFilterStrategy = if (onlyOrphansInHome) { RoomListViewModel.SpaceFilterStrategy.ORPHANS_IF_SPACE_NULL } else { @@ -176,6 +180,7 @@ class RoomListSectionBuilder( nameRes = R.string.low_priority_header, notifyOfLocalEcho = false, explicitSpaceId = explicitSpaceId, + sortOrder = sortOrder, spaceFilterStrategy = if (onlyOrphansInHome) { RoomListViewModel.SpaceFilterStrategy.ORPHANS_IF_SPACE_NULL } else { @@ -192,6 +197,7 @@ class RoomListSectionBuilder( nameRes = R.string.system_alerts_header, notifyOfLocalEcho = false, explicitSpaceId = explicitSpaceId, + sortOrder = sortOrder, spaceFilterStrategy = if (onlyOrphansInHome) { RoomListViewModel.SpaceFilterStrategy.ORPHANS_IF_SPACE_NULL } else { @@ -208,7 +214,8 @@ class RoomListSectionBuilder( private fun buildRoomsSections( sections: MutableList, activeSpaceAwareQueries: MutableList, - explicitSpaceId: String? + explicitSpaceId: String?, + sortOrder: RoomSortOrder ) { if (autoAcceptInvites.showInvites()) { addSection( @@ -217,6 +224,7 @@ class RoomListSectionBuilder( nameRes = R.string.invitations_header, notifyOfLocalEcho = true, explicitSpaceId = explicitSpaceId, + sortOrder = sortOrder, spaceFilterStrategy = RoomListViewModel.SpaceFilterStrategy.ALL_IF_SPACE_NULL, countRoomAsNotif = true ) { @@ -236,6 +244,7 @@ class RoomListSectionBuilder( RoomListViewModel.SpaceFilterStrategy.ALL_IF_SPACE_NULL }, explicitSpaceId = explicitSpaceId, + sortOrder = sortOrder ) { it.memberships = listOf(Membership.JOIN) it.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS @@ -248,6 +257,7 @@ class RoomListSectionBuilder( nameRes = R.string.bottom_action_rooms, notifyOfLocalEcho = false, explicitSpaceId = explicitSpaceId, + sortOrder = sortOrder, spaceFilterStrategy = if (onlyOrphansInHome) { RoomListViewModel.SpaceFilterStrategy.ORPHANS_IF_SPACE_NULL } else { @@ -265,6 +275,7 @@ class RoomListSectionBuilder( nameRes = R.string.low_priority_header, notifyOfLocalEcho = false, explicitSpaceId = explicitSpaceId, + sortOrder = sortOrder, spaceFilterStrategy = if (onlyOrphansInHome) { RoomListViewModel.SpaceFilterStrategy.ORPHANS_IF_SPACE_NULL } else { @@ -282,6 +293,7 @@ class RoomListSectionBuilder( nameRes = R.string.system_alerts_header, notifyOfLocalEcho = false, explicitSpaceId = explicitSpaceId, + sortOrder = sortOrder, spaceFilterStrategy = if (onlyOrphansInHome) { RoomListViewModel.SpaceFilterStrategy.ORPHANS_IF_SPACE_NULL } else { @@ -366,7 +378,8 @@ class RoomListSectionBuilder( private fun buildDmSections( sections: MutableList, activeSpaceAwareQueries: MutableList, - explicitSpaceId: String? + explicitSpaceId: String?, + sortOrder: RoomSortOrder ) { if (autoAcceptInvites.showInvites()) { addSection( @@ -375,6 +388,7 @@ class RoomListSectionBuilder( nameRes = R.string.invitations_header, notifyOfLocalEcho = true, explicitSpaceId = explicitSpaceId, + sortOrder = sortOrder, spaceFilterStrategy = if (onlyOrphansInHome) { RoomListViewModel.SpaceFilterStrategy.ORPHANS_IF_SPACE_NULL } else { @@ -397,7 +411,8 @@ class RoomListSectionBuilder( } else { RoomListViewModel.SpaceFilterStrategy.ALL_IF_SPACE_NULL }, - explicitSpaceId = explicitSpaceId + explicitSpaceId = explicitSpaceId, + sortOrder = sortOrder ) { it.memberships = listOf(Membership.JOIN) it.roomCategoryFilter = RoomCategoryFilter.ONLY_DM @@ -414,7 +429,8 @@ class RoomListSectionBuilder( } else { RoomListViewModel.SpaceFilterStrategy.ALL_IF_SPACE_NULL }, - explicitSpaceId = explicitSpaceId + explicitSpaceId = explicitSpaceId, + sortOrder = sortOrder ) { it.memberships = listOf(Membership.JOIN) it.roomCategoryFilter = RoomCategoryFilter.ONLY_DM @@ -431,7 +447,8 @@ class RoomListSectionBuilder( } else { RoomListViewModel.SpaceFilterStrategy.ALL_IF_SPACE_NULL }, - explicitSpaceId = explicitSpaceId + explicitSpaceId = explicitSpaceId, + sortOrder = sortOrder ) { it.memberships = listOf(Membership.JOIN) it.roomCategoryFilter = RoomCategoryFilter.ONLY_DM @@ -451,7 +468,8 @@ class RoomListSectionBuilder( private fun buildNotificationsSection( sections: MutableList, activeSpaceAwareQueries: MutableList, - explicitSpaceId: String? + explicitSpaceId: String?, + sortOrder: RoomSortOrder ) { if (autoAcceptInvites.showInvites()) { addSection( @@ -460,6 +478,7 @@ class RoomListSectionBuilder( nameRes = R.string.invitations_header, notifyOfLocalEcho = true, explicitSpaceId = explicitSpaceId, + sortOrder = sortOrder, spaceFilterStrategy = if (onlyOrphansInHome) { RoomListViewModel.SpaceFilterStrategy.ORPHANS_IF_SPACE_NULL } else { @@ -477,6 +496,7 @@ class RoomListSectionBuilder( nameRes = R.string.bottom_action_rooms, notifyOfLocalEcho = false, explicitSpaceId = explicitSpaceId, + sortOrder = sortOrder, spaceFilterStrategy = if (onlyOrphansInHome) { RoomListViewModel.SpaceFilterStrategy.ORPHANS_IF_SPACE_NULL } else { @@ -522,6 +542,7 @@ class RoomListSectionBuilder( spaceFilterStrategy: RoomListViewModel.SpaceFilterStrategy = RoomListViewModel.SpaceFilterStrategy.NONE, countRoomAsNotif: Boolean = false, explicitSpaceId: String?, + sortOrder: RoomSortOrder, query: (RoomSummaryQueryParams.Builder) -> Unit ) { withQueryParams(query) { roomQueryParams -> @@ -537,7 +558,8 @@ class RoomListSectionBuilder( val name = stringProvider.getString(nameRes) val filteredPagedRoomSummariesLive = session.roomService().getFilteredPagedRoomSummariesLive( roomQueryParams.process(spaceFilterStrategy, spaceStateHandler.explicitOrSafeActiveSpaceId(explicitSpaceId)), - pagedListConfig + pagedListConfig, + sortOrder ) when (spaceFilterStrategy) { RoomListViewModel.SpaceFilterStrategy.ORPHANS_IF_SPACE_NULL -> { diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt index 0d5837bfb4..97e4b57ca3 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt @@ -47,6 +47,7 @@ import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.getRoom import org.matrix.android.sdk.api.session.getRoomSummary +import org.matrix.android.sdk.api.session.room.RoomSortOrder import org.matrix.android.sdk.api.session.room.UpdatableLivePageResult import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState import org.matrix.android.sdk.api.session.room.model.localecho.RoomLocalEcho @@ -152,8 +153,8 @@ class RoomListViewModel @AssistedInject constructor( ) val sections: List by lazy { - viewPagerDimber.i { "Build sections for ${initialState.displayMode}, ${initialState.explicitSpaceId}" } - roomListSectionBuilder.buildSections(initialState.displayMode, initialState.explicitSpaceId) + viewPagerDimber.i { "Build sections for ${initialState.displayMode}, ${initialState.explicitSpaceId} ${initialState.roomSortOrder}" } + roomListSectionBuilder.buildSections(initialState.displayMode, initialState.explicitSpaceId, initialState.roomSortOrder) } override fun handle(action: RoomListAction) { diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewState.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewState.kt index f9e8887b2b..615d729fd9 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewState.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewState.kt @@ -20,6 +20,7 @@ import com.airbnb.mvrx.Async import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import im.vector.app.features.home.RoomListDisplayMode +import org.matrix.android.sdk.api.session.room.RoomSortOrder import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo @@ -32,8 +33,9 @@ data class RoomListViewState( val currentUserName: String? = null, val asyncSelectedSpace: Async = Uninitialized, // In comparison to currentRoomGrouping, the explicit space id fixes a filter method that should not change afterwards - val explicitSpaceId: String? = null + val explicitSpaceId: String? = null, + val roomSortOrder: RoomSortOrder = RoomSortOrder.ACTIVITY, ) : MavericksState { - constructor(args: RoomListParams) : this(displayMode = args.displayMode, explicitSpaceId = args.explicitSpaceId) + constructor(args: RoomListParams) : this(displayMode = args.displayMode, explicitSpaceId = args.explicitSpaceId, roomSortOrder = args.roomSortOrder) } diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt b/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt index d7586e19e0..196dbeda0d 100755 --- a/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt @@ -248,6 +248,8 @@ class VectorPreferences @Inject constructor( private const val SETTINGS_SPACE_BACK_NAVIGATION = "SETTINGS_SPACE_BACK_NAVIGATION" const val SETTINGS_FOLLOW_SYSTEM_LOCALE = "SETTINGS_FOLLOW_SYSTEM_LOCALE" const val SETTINGS_FORCE_ALLOW_BACKGROUND_SYNC = "SETTINGS_FORCE_ALLOW_BACKGROUND_SYNC" + const val SETTINGS_ROOM_SORT_ORDER_NULL = "SETTINGS_ROOM_SORT_ORDER_NULL" + const val SETTINGS_ROOM_SORT_ORDER_NON_NULL = "SETTINGS_ROOM_SORT_ORDER_NON_NULL" private const val DID_ASK_TO_ENABLE_SESSION_PUSH = "DID_ASK_TO_ENABLE_SESSION_PUSH" diff --git a/vector/src/main/res/menu/room_list.xml b/vector/src/main/res/menu/room_list.xml index ad375d241b..e3868e5dec 100644 --- a/vector/src/main/res/menu/room_list.xml +++ b/vector/src/main/res/menu/room_list.xml @@ -14,4 +14,21 @@ android:visible="false" app:showAsAction="never" tools:visible="true" /> + + + + + + + + +