diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/Authenticator.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/Authenticator.kt index 77a3cde249..c1dfa465fb 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/Authenticator.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/Authenticator.kt @@ -67,5 +67,5 @@ interface Authenticator { /** * Create a session after a SSO successful login */ - fun createSessionFromSso(credentials: Credentials, homeServerConnectionConfig: HomeServerConnectionConfig): Session + fun createSessionFromSso(credentials: Credentials, homeServerConnectionConfig: HomeServerConnectionConfig, callback: MatrixCallback): Cancelable } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/DefaultAuthenticator.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/DefaultAuthenticator.kt index e379090677..ff49d4308b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/DefaultAuthenticator.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/DefaultAuthenticator.kt @@ -17,6 +17,7 @@ package im.vector.matrix.android.internal.auth import android.util.Patterns +import dagger.Lazy import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.auth.Authenticator import im.vector.matrix.android.api.auth.data.Credentials @@ -39,10 +40,9 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import okhttp3.OkHttpClient import javax.inject.Inject -import javax.inject.Provider internal class DefaultAuthenticator @Inject constructor(@Unauthenticated - private val okHttpClient: Provider, + private val okHttpClient: Lazy, private val retrofitFactory: RetrofitFactory, private val coroutineDispatchers: MatrixCoroutineDispatchers, private val sessionParamsStore: SessionParamsStore, @@ -112,14 +112,27 @@ internal class DefaultAuthenticator @Inject constructor(@Unauthenticated sessionManager.getOrCreateSession(sessionParams) } - override fun createSessionFromSso(credentials: Credentials, homeServerConnectionConfig: HomeServerConnectionConfig): Session { + override fun createSessionFromSso(credentials: Credentials, + homeServerConnectionConfig: HomeServerConnectionConfig, + callback: MatrixCallback): Cancelable { + val job = GlobalScope.launch(coroutineDispatchers.main) { + val sessionOrFailure = runCatching { + createSessionFromSso(credentials, homeServerConnectionConfig) + } + sessionOrFailure.foldToCallback(callback) + } + return CancelableCoroutine(job) + } + + private suspend fun createSessionFromSso(credentials: Credentials, + homeServerConnectionConfig: HomeServerConnectionConfig): Session = withContext(coroutineDispatchers.computation) { val sessionParams = SessionParams(credentials, homeServerConnectionConfig) sessionParamsStore.save(sessionParams) - return sessionManager.getOrCreateSession(sessionParams) + sessionManager.getOrCreateSession(sessionParams) } private fun buildAuthAPI(homeServerConnectionConfig: HomeServerConnectionConfig): AuthAPI { - val retrofit = retrofitFactory.create(okHttpClient.get(), homeServerConnectionConfig.homeServerUri.toString()) + val retrofit = retrofitFactory.create(okHttpClient, homeServerConnectionConfig.homeServerUri.toString()) return retrofit.create(AuthAPI::class.java) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/SessionParamsStore.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/SessionParamsStore.kt index e1fef7e2eb..17bcb9dc81 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/SessionParamsStore.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/SessionParamsStore.kt @@ -16,7 +16,6 @@ package im.vector.matrix.android.internal.auth -import arrow.core.Try import im.vector.matrix.android.api.auth.data.SessionParams internal interface SessionParamsStore { @@ -27,9 +26,9 @@ internal interface SessionParamsStore { fun getAll(): List - fun save(sessionParams: SessionParams): Try + suspend fun save(sessionParams: SessionParams) - fun delete(userId: String): Try + suspend fun delete(userId: String) - fun deleteAll(): Try + suspend fun deleteAll() } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/RealmSessionParamsStore.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/RealmSessionParamsStore.kt index 7ec5d24559..00fde2682e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/RealmSessionParamsStore.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/RealmSessionParamsStore.kt @@ -16,9 +16,9 @@ package im.vector.matrix.android.internal.auth.db -import arrow.core.Try import im.vector.matrix.android.api.auth.data.SessionParams import im.vector.matrix.android.internal.auth.SessionParamsStore +import im.vector.matrix.android.internal.database.awaitTransaction import im.vector.matrix.android.internal.di.AuthDatabase import io.realm.Realm import io.realm.RealmConfiguration @@ -62,41 +62,29 @@ internal class RealmSessionParamsStore @Inject constructor(private val mapper: S return sessionParams } - override fun save(sessionParams: SessionParams): Try { - return Try { + override suspend fun save(sessionParams: SessionParams) { + awaitTransaction(realmConfiguration) { val entity = mapper.map(sessionParams) if (entity != null) { - val realm = Realm.getInstance(realmConfiguration) - realm.executeTransaction { - it.insert(entity) - } - realm.close() + it.insert(entity) } } } - override fun delete(userId: String): Try { - return Try { - val realm = Realm.getInstance(realmConfiguration) - realm.executeTransaction { - it.where(SessionParamsEntity::class.java) - .equalTo(SessionParamsEntityFields.USER_ID, userId) - .findAll() - .deleteAllFromRealm() - } - realm.close() + override suspend fun delete(userId: String) { + awaitTransaction(realmConfiguration) { + it.where(SessionParamsEntity::class.java) + .equalTo(SessionParamsEntityFields.USER_ID, userId) + .findAll() + .deleteAllFromRealm() } } - override fun deleteAll(): Try { - return Try { - val realm = Realm.getInstance(realmConfiguration) - realm.executeTransaction { - it.where(SessionParamsEntity::class.java) - .findAll() - .deleteAllFromRealm() - } - realm.close() + override suspend fun deleteAll() { + awaitTransaction(realmConfiguration) { + it.where(SessionParamsEntity::class.java) + .findAll() + .deleteAllFromRealm() } } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/AsyncTransaction.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/AsyncTransaction.kt index 36e68e5cf3..0f8445d20f 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/AsyncTransaction.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/AsyncTransaction.kt @@ -20,14 +20,19 @@ import io.realm.RealmConfiguration import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.isActive import kotlinx.coroutines.withContext +import timber.log.Timber -suspend fun awaitTransaction(config: RealmConfiguration, transaction: suspend (realm: Realm) -> Unit) = withContext(Dispatchers.IO) { +suspend fun awaitTransaction(config: RealmConfiguration, transaction: suspend (realm: Realm) -> Unit) = withContext(Dispatchers.Default) { Realm.getInstance(config).use { bgRealm -> bgRealm.beginTransaction() try { + val start = System.currentTimeMillis() transaction(bgRealm) if (isActive) { bgRealm.commitTransaction() + val end = System.currentTimeMillis() + val time = end - start + Timber.v("Execute transaction in $time millis") } } finally { if (bgRealm.isInTransaction) { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/RoomSummaryMapper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/RoomSummaryMapper.kt index 0d0143d318..2577bec581 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/RoomSummaryMapper.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/RoomSummaryMapper.kt @@ -36,7 +36,7 @@ internal class RoomSummaryMapper @Inject constructor( } val latestEvent = roomSummaryEntity.latestPreviewableEvent?.let { - timelineEventMapper.map(it) + timelineEventMapper.map(it, buildReadReceipts = false) } if (latestEvent?.root?.isEncrypted() == true && latestEvent.root.mxDecryptionResult == null) { // TODO use a global event decryptor? attache to session and that listen to new sessionId? diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/FilterEntityQueries.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/FilterEntityQueries.kt index 8fbbb1311e..4f64f2896f 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/FilterEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/FilterEntityQueries.kt @@ -16,27 +16,26 @@ package im.vector.matrix.android.internal.database.query +import im.vector.matrix.android.internal.database.awaitTransaction import im.vector.matrix.android.internal.database.model.FilterEntity import im.vector.matrix.android.internal.session.filter.FilterFactory import io.realm.Realm -import io.realm.kotlin.createObject import io.realm.kotlin.where /** * Get the current filter, create one if it does not exist */ -internal fun FilterEntity.Companion.getFilter(realm: Realm): FilterEntity { +internal suspend fun FilterEntity.Companion.getFilter(realm: Realm): FilterEntity { var filter = realm.where().findFirst() if (filter == null) { - realm.executeTransaction { - realm.createObject().apply { - filterBodyJson = FilterFactory.createDefaultFilterBody().toJSONString() - roomEventFilterJson = FilterFactory.createDefaultRoomFilter().toJSONString() - filterId = "" - } + filter = FilterEntity().apply { + filterBodyJson = FilterFactory.createDefaultFilterBody().toJSONString() + roomEventFilterJson = FilterFactory.createDefaultRoomFilter().toJSONString() + filterId = "" + } + awaitTransaction(realm.configuration) { + it.insert(filter) } - filter = realm.where().findFirst()!! } - return filter } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/ReadReceiptEntityQueries.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/ReadReceiptEntityQueries.kt index e0a507f939..6b996d1285 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/ReadReceiptEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/ReadReceiptEntityQueries.kt @@ -24,8 +24,7 @@ import io.realm.kotlin.where internal fun ReadReceiptEntity.Companion.where(realm: Realm, roomId: String, userId: String): RealmQuery { return realm.where() - .equalTo(ReadReceiptEntityFields.ROOM_ID, roomId) - .equalTo(ReadReceiptEntityFields.USER_ID, userId) + .equalTo(ReadReceiptEntityFields.PRIMARY_KEY, buildPrimaryKey(roomId, userId)) } internal fun ReadReceiptEntity.Companion.whereUserId(realm: Realm, userId: String): RealmQuery { @@ -45,8 +44,10 @@ internal fun ReadReceiptEntity.Companion.createUnmanaged(roomId: String, eventId internal fun ReadReceiptEntity.Companion.getOrCreate(realm: Realm, roomId: String, userId: String): ReadReceiptEntity { return ReadReceiptEntity.where(realm, roomId, userId).findFirst() - ?: realm.createObject(ReadReceiptEntity::class.java, "${roomId}_$userId").apply { + ?: realm.createObject(ReadReceiptEntity::class.java, buildPrimaryKey(roomId, userId)).apply { this.roomId = roomId this.userId = userId } } + +private fun buildPrimaryKey(roomId: String, userId: String) = "${roomId}_$userId" diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/TimelineEventEntityQueries.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/TimelineEventEntityQueries.kt index 49474e8e6b..3bd035c0b1 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/TimelineEventEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/TimelineEventEntityQueries.kt @@ -111,7 +111,7 @@ internal fun RealmQuery.prev(since: Int? = null, strict: Bo internal fun RealmList.find(eventId: String): TimelineEventEntity? { return this.where() - .equalTo(TimelineEventEntityFields.ROOT.EVENT_ID, eventId) + .equalTo(TimelineEventEntityFields.EVENT_ID, eventId) .findFirst() } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixModule.kt index 811950ac15..c17864b82b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixModule.kt @@ -36,7 +36,7 @@ internal object MatrixModule { @MatrixScope fun providesMatrixCoroutineDispatchers(): MatrixCoroutineDispatchers { return MatrixCoroutineDispatchers(io = Dispatchers.IO, - computation = Dispatchers.IO, + computation = Dispatchers.Default, main = Dispatchers.Main, crypto = createBackgroundHandler("Crypto_Thread").asCoroutineDispatcher(), sync = Executors.newSingleThreadExecutor().asCoroutineDispatcher() diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/RetrofitFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/RetrofitFactory.kt index 15e6f76381..44ccd7c941 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/RetrofitFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/RetrofitFactory.kt @@ -17,17 +17,24 @@ package im.vector.matrix.android.internal.network import com.squareup.moshi.Moshi +import dagger.Lazy +import okhttp3.Call import okhttp3.OkHttpClient +import okhttp3.Request import retrofit2.Retrofit import retrofit2.converter.moshi.MoshiConverterFactory import javax.inject.Inject class RetrofitFactory @Inject constructor(private val moshi: Moshi) { - fun create(okHttpClient: OkHttpClient, baseUrl: String): Retrofit { + fun create(okHttpClient: Lazy, baseUrl: String): Retrofit { return Retrofit.Builder() .baseUrl(baseUrl) - .client(okHttpClient) + .callFactory(object : Call.Factory { + override fun newCall(request: Request): Call { + return okHttpClient.get().newCall(request) + } + }) .addConverterFactory(UnitConverterFactory) .addConverterFactory(MoshiConverterFactory.create(moshi)) .build() diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt index d038630a74..0e88894969 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt @@ -19,6 +19,7 @@ package im.vector.matrix.android.internal.session import android.content.Context import com.zhuinden.monarchy.Monarchy import dagger.Binds +import dagger.Lazy import dagger.Module import dagger.Provides import dagger.multibindings.IntoSet @@ -132,7 +133,7 @@ internal abstract class SessionModule { @JvmStatic @Provides @SessionScope - fun providesRetrofit(@Authenticated okHttpClient: OkHttpClient, + fun providesRetrofit(@Authenticated okHttpClient: Lazy, sessionParams: SessionParams, retrofitFactory: RetrofitFactory): Retrofit { return retrofitFactory diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/DefaultFilterRepository.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/DefaultFilterRepository.kt index 02e6db189a..53967784a1 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/DefaultFilterRepository.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/DefaultFilterRepository.kt @@ -16,54 +16,44 @@ package im.vector.matrix.android.internal.session.filter +import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.internal.database.model.FilterEntity import im.vector.matrix.android.internal.database.model.FilterEntityFields import im.vector.matrix.android.internal.database.query.getFilter -import im.vector.matrix.android.internal.di.SessionDatabase +import im.vector.matrix.android.internal.util.awaitTransaction import io.realm.Realm -import io.realm.RealmConfiguration import io.realm.kotlin.where import javax.inject.Inject -internal class DefaultFilterRepository @Inject constructor( - @SessionDatabase private val realmConfiguration: RealmConfiguration -) : FilterRepository { +internal class DefaultFilterRepository @Inject constructor(private val monarchy: Monarchy) : FilterRepository { - override fun storeFilter(filterBody: FilterBody, roomEventFilter: RoomEventFilter): Boolean { - val result: Boolean + override suspend fun storeFilter(filterBody: FilterBody, roomEventFilter: RoomEventFilter): Boolean { + return Realm.getInstance(monarchy.realmConfiguration).use { realm -> + val filter = FilterEntity.getFilter(realm) + val result = if (filter.filterBodyJson != filterBody.toJSONString()) { + // Filter has changed, store it and reset the filter Id + monarchy.awaitTransaction { + // We manage only one filter for now + val filterBodyJson = filterBody.toJSONString() + val roomEventFilterJson = roomEventFilter.toJSONString() - val realm = Realm.getInstance(realmConfiguration) + val filterEntity = FilterEntity.getFilter(it) - val filter = FilterEntity.getFilter(realm) - - if (filter.filterBodyJson != filterBody.toJSONString()) { - // Filter has changed, store it and reset the filter Id - realm.executeTransaction { - // We manage only one filter for now - val filterBodyJson = filterBody.toJSONString() - val roomEventFilterJson = roomEventFilter.toJSONString() - - val filterEntity = FilterEntity.getFilter(it) - - filterEntity.filterBodyJson = filterBodyJson - filterEntity.roomEventFilterJson = roomEventFilterJson - // Reset filterId - filterEntity.filterId = "" + filterEntity.filterBodyJson = filterBodyJson + filterEntity.roomEventFilterJson = roomEventFilterJson + // Reset filterId + filterEntity.filterId = "" + } + true + } else { + filter.filterId.isBlank() } - result = true - } else { - result = filter.filterId.isBlank() + result } - - realm.close() - - return result } - override fun storeFilterId(filterBody: FilterBody, filterId: String) { - val realm = Realm.getInstance(realmConfiguration) - - realm.executeTransaction { + override suspend fun storeFilterId(filterBody: FilterBody, filterId: String) { + monarchy.awaitTransaction { // We manage only one filter for now val filterBodyJson = filterBody.toJSONString() @@ -73,39 +63,24 @@ internal class DefaultFilterRepository @Inject constructor( ?.findFirst() ?.filterId = filterId } - - realm.close() } - override fun getFilter(): String { - val result: String - - val realm = Realm.getInstance(realmConfiguration) - - val filter = FilterEntity.getFilter(realm) - - result = if (filter.filterId.isBlank()) { - // Use the Json format - filter.filterBodyJson - } else { - // Use FilterId - filter.filterId + override suspend fun getFilter(): String { + return Realm.getInstance(monarchy.realmConfiguration).use { + val filter = FilterEntity.getFilter(it) + if (filter.filterId.isBlank()) { + // Use the Json format + filter.filterBodyJson + } else { + // Use FilterId + filter.filterId + } } - - realm.close() - - return result } - override fun getRoomFilter(): String { - val realm = Realm.getInstance(realmConfiguration) - - val filter = FilterEntity.getFilter(realm) - - val result = filter.roomEventFilterJson - - realm.close() - - return result + override suspend fun getRoomFilter(): String { + return Realm.getInstance(monarchy.realmConfiguration).use { + FilterEntity.getFilter(it).roomEventFilterJson + } } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/DefaultFilterService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/DefaultFilterService.kt index 84e820ebca..c85d949d0a 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/DefaultFilterService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/DefaultFilterService.kt @@ -21,36 +21,13 @@ import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith import javax.inject.Inject -internal class DefaultFilterService @Inject constructor(private val filterRepository: FilterRepository, - private val saveFilterTask: SaveFilterTask, +internal class DefaultFilterService @Inject constructor(private val saveFilterTask: SaveFilterTask, private val taskExecutor: TaskExecutor) : FilterService { // TODO Pass a list of support events instead override fun setFilter(filterPreset: FilterService.FilterPreset) { - val filterBody = when (filterPreset) { - FilterService.FilterPreset.RiotFilter -> { - FilterFactory.createRiotFilterBody() - } - FilterService.FilterPreset.NoFilter -> { - FilterFactory.createDefaultFilterBody() - } - } - - val roomFilter = when (filterPreset) { - FilterService.FilterPreset.RiotFilter -> { - FilterFactory.createRiotRoomFilter() - } - FilterService.FilterPreset.NoFilter -> { - FilterFactory.createDefaultRoomFilter() - } - } - - val updated = filterRepository.storeFilter(filterBody, roomFilter) - - if (updated) { - saveFilterTask - .configureWith(SaveFilterTask.Params(filterBody)) - .executeBy(taskExecutor) - } + saveFilterTask + .configureWith(SaveFilterTask.Params(filterPreset)) + .executeBy(taskExecutor) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/DefaultSaveFilterTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/DefaultSaveFilterTask.kt index b98049675e..08985bf17d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/DefaultSaveFilterTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/DefaultSaveFilterTask.kt @@ -16,18 +16,19 @@ package im.vector.matrix.android.internal.session.filter +import im.vector.matrix.android.api.session.sync.FilterService import im.vector.matrix.android.internal.di.UserId import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.task.Task import javax.inject.Inject /** - * Save a filter to the server + * Save a filter, in db and if any changes, upload to the server */ internal interface SaveFilterTask : Task { data class Params( - val filter: FilterBody + val filterPreset: FilterService.FilterPreset ) } @@ -37,10 +38,29 @@ internal class DefaultSaveFilterTask @Inject constructor(@UserId private val use ) : SaveFilterTask { override suspend fun execute(params: SaveFilterTask.Params) { - val filterResponse = executeRequest { - // TODO auto retry - apiCall = filterAPI.uploadFilter(userId, params.filter) + val filterBody = when (params.filterPreset) { + FilterService.FilterPreset.RiotFilter -> { + FilterFactory.createRiotFilterBody() + } + FilterService.FilterPreset.NoFilter -> { + FilterFactory.createDefaultFilterBody() + } + } + val roomFilter = when (params.filterPreset) { + FilterService.FilterPreset.RiotFilter -> { + FilterFactory.createRiotRoomFilter() + } + FilterService.FilterPreset.NoFilter -> { + FilterFactory.createDefaultRoomFilter() + } + } + val updated = filterRepository.storeFilter(filterBody, roomFilter) + if (updated) { + val filterResponse = executeRequest { + // TODO auto retry + apiCall = filterAPI.uploadFilter(userId, filterBody) + } + filterRepository.storeFilterId(filterBody, filterResponse.filterId) } - filterRepository.storeFilterId(params.filter, filterResponse.filterId) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/FilterRepository.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/FilterRepository.kt index 092d9ff766..d205ea8a87 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/FilterRepository.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/FilterRepository.kt @@ -21,20 +21,20 @@ internal interface FilterRepository { /** * Return true if the filterBody has changed, or need to be sent to the server */ - fun storeFilter(filterBody: FilterBody, roomEventFilter: RoomEventFilter): Boolean + suspend fun storeFilter(filterBody: FilterBody, roomEventFilter: RoomEventFilter): Boolean /** * Set the filterId of this filter */ - fun storeFilterId(filterBody: FilterBody, filterId: String) + suspend fun storeFilterId(filterBody: FilterBody, filterId: String) /** * Return filter json or filter id */ - fun getFilter(): String + suspend fun getFilter(): String /** * Return the room filter */ - fun getRoomFilter(): String + suspend fun getRoomFilter(): String } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAvatarResolver.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAvatarResolver.kt index 0b18279aa8..c9d5aeb6bb 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAvatarResolver.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAvatarResolver.kt @@ -21,7 +21,7 @@ import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.room.model.RoomAvatarContent import im.vector.matrix.android.api.session.room.model.RoomMember -import im.vector.matrix.android.internal.database.mapper.asDomain +import im.vector.matrix.android.internal.database.mapper.ContentMapper import im.vector.matrix.android.internal.database.model.EventEntity import im.vector.matrix.android.internal.database.model.EventEntityFields import im.vector.matrix.android.internal.database.query.prev @@ -41,8 +41,8 @@ internal class RoomAvatarResolver @Inject constructor(private val monarchy: Mona fun resolve(roomId: String): String? { var res: String? = null monarchy.doWithRealm { realm -> - val roomName = EventEntity.where(realm, roomId, EventType.STATE_ROOM_AVATAR).prev()?.asDomain() - res = roomName?.content.toModel()?.avatarUrl + val roomName = EventEntity.where(realm, roomId, EventType.STATE_ROOM_AVATAR).prev() + res = ContentMapper.map(roomName?.content).toModel()?.avatarUrl if (!res.isNullOrEmpty()) { return@doWithRealm } @@ -60,6 +60,6 @@ internal class RoomAvatarResolver @Inject constructor(private val monarchy: Mona } private fun EventEntity?.toRoomMember(): RoomMember? { - return this?.asDomain()?.content?.toModel() + return ContentMapper.map(this?.content).toModel() } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt index 0d28720ec6..1158c08984 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt @@ -21,7 +21,7 @@ import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.room.model.Membership import im.vector.matrix.android.api.session.room.model.RoomTopicContent -import im.vector.matrix.android.internal.database.mapper.asDomain +import im.vector.matrix.android.internal.database.mapper.ContentMapper import im.vector.matrix.android.internal.database.model.EventEntity import im.vector.matrix.android.internal.database.model.EventEntityFields import im.vector.matrix.android.internal.database.model.RoomSummaryEntity @@ -65,8 +65,10 @@ internal class RoomSummaryUpdater @Inject constructor(@UserId private val userId roomId: String, membership: Membership? = null, roomSummary: RoomSyncSummary? = null, - unreadNotifications: RoomSyncUnreadNotifications? = null) { - val roomSummaryEntity = RoomSummaryEntity.where(realm, roomId).findFirst() ?: realm.createObject(roomId) + unreadNotifications: RoomSyncUnreadNotifications? = null, + updateMembers: Boolean = false) { + val roomSummaryEntity = RoomSummaryEntity.where(realm, roomId).findFirst() + ?: realm.createObject(roomId) if (roomSummary != null) { if (roomSummary.heroes.isNotEmpty()) { @@ -88,24 +90,27 @@ internal class RoomSummaryUpdater @Inject constructor(@UserId private val userId } val latestPreviewableEvent = TimelineEventEntity.latestEvent(realm, roomId, includesSending = true, filterTypes = PREVIEWABLE_TYPES) - val lastTopicEvent = EventEntity.where(realm, roomId, EventType.STATE_ROOM_TOPIC).prev()?.asDomain() + val lastTopicEvent = EventEntity.where(realm, roomId, EventType.STATE_ROOM_TOPIC).prev() roomSummaryEntity.hasUnreadMessages = roomSummaryEntity.notificationCount > 0 - // avoid this call if we are sure there are unread events - || !isEventRead(monarchy, userId, roomId, latestPreviewableEvent?.eventId) - - val otherRoomMembers = RoomMembers(realm, roomId) - .queryRoomMembersEvent() - .notEqualTo(EventEntityFields.STATE_KEY, userId) - .findAll() - .asSequence() - .map { it.stateKey } + // avoid this call if we are sure there are unread events + || !isEventRead(monarchy, userId, roomId, latestPreviewableEvent?.eventId) roomSummaryEntity.displayName = roomDisplayNameResolver.resolve(roomId).toString() roomSummaryEntity.avatarUrl = roomAvatarResolver.resolve(roomId) - roomSummaryEntity.topic = lastTopicEvent?.content.toModel()?.topic + roomSummaryEntity.topic = ContentMapper.map(lastTopicEvent?.content).toModel()?.topic roomSummaryEntity.latestPreviewableEvent = latestPreviewableEvent - roomSummaryEntity.otherMemberIds.clear() - roomSummaryEntity.otherMemberIds.addAll(otherRoomMembers) + + if (updateMembers) { + val otherRoomMembers = RoomMembers(realm, roomId) + .queryRoomMembersEvent() + .notEqualTo(EventEntityFields.STATE_KEY, userId) + .findAll() + .asSequence() + .map { it.stateKey } + + roomSummaryEntity.otherMemberIds.clear() + roomSummaryEntity.otherMemberIds.addAll(otherRoomMembers) + } } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/LoadRoomMembersTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/LoadRoomMembersTask.kt index d952915d2c..7d9332ee84 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/LoadRoomMembersTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/LoadRoomMembersTask.kt @@ -74,7 +74,7 @@ internal class DefaultLoadRoomMembersTask @Inject constructor(private val roomAP it.updateSenderData() } roomEntity.areAllMembersLoaded = true - roomSummaryUpdater.update(realm, roomId) + roomSummaryUpdater.update(realm, roomId, updateMembers = true) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomDisplayNameResolver.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomDisplayNameResolver.kt index 965bd21cf4..2271631932 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomDisplayNameResolver.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomDisplayNameResolver.kt @@ -22,7 +22,7 @@ import im.vector.matrix.android.R import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.room.model.* -import im.vector.matrix.android.internal.database.mapper.asDomain +import im.vector.matrix.android.internal.database.mapper.ContentMapper import im.vector.matrix.android.internal.database.model.EventEntity import im.vector.matrix.android.internal.database.model.EventEntityFields import im.vector.matrix.android.internal.database.model.RoomEntity @@ -56,20 +56,20 @@ internal class RoomDisplayNameResolver @Inject constructor(private val context: var name: CharSequence? = null monarchy.doWithRealm { realm -> val roomEntity = RoomEntity.where(realm, roomId = roomId).findFirst() - val roomName = EventEntity.where(realm, roomId, EventType.STATE_ROOM_NAME).prev()?.asDomain() - name = roomName?.content.toModel()?.name + val roomName = EventEntity.where(realm, roomId, EventType.STATE_ROOM_NAME).prev() + name = ContentMapper.map(roomName?.content).toModel()?.name if (!name.isNullOrEmpty()) { return@doWithRealm } - val canonicalAlias = EventEntity.where(realm, roomId, EventType.STATE_CANONICAL_ALIAS).prev()?.asDomain() - name = canonicalAlias?.content.toModel()?.canonicalAlias + val canonicalAlias = EventEntity.where(realm, roomId, EventType.STATE_CANONICAL_ALIAS).prev() + name = ContentMapper.map(canonicalAlias?.content).toModel()?.canonicalAlias if (!name.isNullOrEmpty()) { return@doWithRealm } - val aliases = EventEntity.where(realm, roomId, EventType.STATE_ROOM_ALIASES).prev()?.asDomain() - name = aliases?.content.toModel()?.aliases?.firstOrNull() + val aliases = EventEntity.where(realm, roomId, EventType.STATE_ROOM_ALIASES).prev() + name = ContentMapper.map(aliases?.content).toModel()?.aliases?.firstOrNull() if (!name.isNullOrEmpty()) { return@doWithRealm } @@ -132,6 +132,6 @@ internal class RoomDisplayNameResolver @Inject constructor(private val context: } private fun EventEntity?.toRoomMember(): RoomMember? { - return this?.asDomain()?.content?.toModel() + return ContentMapper.map(this?.content).toModel() } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt index 5fb25834c0..4a003eb7d9 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt @@ -67,6 +67,7 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch } suspend fun handle(roomsSyncResponse: RoomsSyncResponse, isInitialSync: Boolean, reporter: DefaultInitialSyncProgressService? = null) { + Timber.v("Execute transaction from $this") monarchy.awaitTransaction { realm -> handleRoomSync(realm, HandlingStrategy.JOINED(roomsSyncResponse.join), isInitialSync, reporter) handleRoomSync(realm, HandlingStrategy.INVITED(roomsSyncResponse.invite), isInitialSync, reporter) @@ -133,6 +134,7 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch roomEntity.membership = Membership.JOIN // State event + if (roomSync.state != null && roomSync.state.events.isNotEmpty()) { val minStateIndex = roomEntity.untimelinedStateEvents.where().min(EventEntityFields.STATE_INDEX)?.toInt() ?: Int.MIN_VALUE @@ -146,7 +148,6 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch } } } - if (roomSync.timeline != null && roomSync.timeline.events.isNotEmpty()) { val chunkEntity = handleTimelineEvents( realm, @@ -157,14 +158,19 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch ) roomEntity.addOrUpdate(chunkEntity) } - roomSummaryUpdater.update(realm, roomId, Membership.JOIN, roomSync.summary, roomSync.unreadNotifications) + val hasRoomMember = roomSync.state?.events?.firstOrNull { + it.type == EventType.STATE_ROOM_MEMBER + } != null || roomSync.timeline?.events?.firstOrNull { + it.type == EventType.STATE_ROOM_MEMBER + } != null + + roomSummaryUpdater.update(realm, roomId, Membership.JOIN, roomSync.summary, roomSync.unreadNotifications, updateMembers = hasRoomMember) return roomEntity } private fun handleInvitedRoom(realm: Realm, roomId: String, - roomSync: - InvitedRoomSync): RoomEntity { + roomSync: InvitedRoomSync): RoomEntity { Timber.v("Handle invited sync for room $roomId") val roomEntity = RoomEntity.where(realm, roomId).findFirst() ?: realm.createObject(roomId) roomEntity.membership = Membership.INVITE @@ -172,7 +178,10 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch val chunkEntity = handleTimelineEvents(realm, roomEntity, roomSync.inviteState.events) roomEntity.addOrUpdate(chunkEntity) } - roomSummaryUpdater.update(realm, roomId, Membership.INVITE) + val hasRoomMember = roomSync.inviteState?.events?.firstOrNull { + it.type == EventType.STATE_ROOM_MEMBER + } != null + roomSummaryUpdater.update(realm, roomId, Membership.INVITE, updateMembers = hasRoomMember) return roomEntity } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncTokenStore.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncTokenStore.kt index f56ee3352f..350f2a1d83 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncTokenStore.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncTokenStore.kt @@ -16,27 +16,24 @@ package im.vector.matrix.android.internal.session.sync +import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.internal.database.model.SyncEntity -import im.vector.matrix.android.internal.di.SessionDatabase +import im.vector.matrix.android.internal.util.awaitTransaction import io.realm.Realm -import io.realm.RealmConfiguration import javax.inject.Inject -internal class SyncTokenStore @Inject constructor(@SessionDatabase private val realmConfiguration: RealmConfiguration) { +internal class SyncTokenStore @Inject constructor(private val monarchy: Monarchy) { fun getLastToken(): String? { - val realm = Realm.getInstance(realmConfiguration) - val token = realm.where(SyncEntity::class.java).findFirst()?.nextBatch - realm.close() - return token + return Realm.getInstance(monarchy.realmConfiguration).use { + it.where(SyncEntity::class.java).findFirst()?.nextBatch + } } - fun saveToken(token: String?) { - val realm = Realm.getInstance(realmConfiguration) - realm.executeTransaction { + suspend fun saveToken(token: String?) { + monarchy.awaitTransaction { val sync = SyncEntity(token) it.insertOrUpdate(sync) } - realm.close() } } diff --git a/vector/build.gradle b/vector/build.gradle index d0a757d4e0..e425d53a62 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -268,7 +268,7 @@ dependencies { implementation("com.airbnb.android:epoxy:$epoxy_version") kapt "com.airbnb.android:epoxy-processor:$epoxy_version" implementation "com.airbnb.android:epoxy-paging:$epoxy_version" - implementation 'com.airbnb.android:mvrx:1.1.0' + implementation 'com.airbnb.android:mvrx:1.3.0' // Work implementation "androidx.work:work-runtime-ktx:2.3.0-alpha01" diff --git a/vector/src/main/java/im/vector/riotx/AppStateHandler.kt b/vector/src/main/java/im/vector/riotx/AppStateHandler.kt index 7cb51b6373..cfbed0ee13 100644 --- a/vector/src/main/java/im/vector/riotx/AppStateHandler.kt +++ b/vector/src/main/java/im/vector/riotx/AppStateHandler.kt @@ -26,6 +26,7 @@ import im.vector.matrix.rx.rx import im.vector.riotx.features.home.HomeRoomListDataSource import im.vector.riotx.features.home.group.ALL_COMMUNITIES_GROUP_ID import im.vector.riotx.features.home.group.SelectedGroupDataSource +import im.vector.riotx.features.home.room.list.ChronologicalRoomComparator import io.reactivex.Observable import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable @@ -43,7 +44,8 @@ import javax.inject.Singleton class AppStateHandler @Inject constructor( private val sessionDataSource: ActiveSessionDataSource, private val homeRoomListDataSource: HomeRoomListDataSource, - private val selectedGroupDataSource: SelectedGroupDataSource) : LifecycleObserver { + private val selectedGroupDataSource: SelectedGroupDataSource, + private val chronologicalRoomComparator: ChronologicalRoomComparator) : LifecycleObserver { private val compositeDisposable = CompositeDisposable() @@ -64,31 +66,24 @@ class AppStateHandler @Inject constructor( .observeOn(AndroidSchedulers.mainThread()) .switchMap { it.orNull()?.rx()?.liveRoomSummaries() - ?: Observable.just(emptyList()) + ?: Observable.just(emptyList()) } .throttleLast(300, TimeUnit.MILLISECONDS), selectedGroupDataSource.observe(), BiFunction { rooms, selectedGroupOption -> val selectedGroup = selectedGroupOption.orNull() - val filteredDirectRooms = rooms - .filter { it.isDirect } - .filter { - if (selectedGroup == null || selectedGroup.groupId == ALL_COMMUNITIES_GROUP_ID) { - true - } else { - it.otherMemberIds - .intersect(selectedGroup.userIds) - .isNotEmpty() - } - } - - val filteredGroupRooms = rooms - .filter { !it.isDirect } - .filter { - selectedGroup?.groupId == ALL_COMMUNITIES_GROUP_ID - || selectedGroup?.roomIds?.contains(it.roomId) ?: true - } - filteredDirectRooms + filteredGroupRooms + val filteredRooms = rooms.filter { + if (selectedGroup == null || selectedGroup.groupId == ALL_COMMUNITIES_GROUP_ID) { + true + } else if (it.isDirect) { + it.otherMemberIds + .intersect(selectedGroup.userIds) + .isNotEmpty() + } else { + selectedGroup.roomIds.contains(it.roomId) + } + } + filteredRooms.sortedWith(chronologicalRoomComparator) } ) .subscribe { diff --git a/vector/src/main/java/im/vector/riotx/core/extensions/Activity.kt b/vector/src/main/java/im/vector/riotx/core/extensions/Activity.kt index 6e4ecf4775..6d7c3d39e6 100644 --- a/vector/src/main/java/im/vector/riotx/core/extensions/Activity.kt +++ b/vector/src/main/java/im/vector/riotx/core/extensions/Activity.kt @@ -21,31 +21,31 @@ import androidx.fragment.app.Fragment import im.vector.riotx.core.platform.VectorBaseActivity fun VectorBaseActivity.addFragment(frameId: Int, fragment: Fragment) { - supportFragmentManager.inTransaction { add(frameId, fragment) } + supportFragmentManager.commitTransactionNow { add(frameId, fragment) } } fun VectorBaseActivity.addFragment(frameId: Int, fragmentClass: Class, params: Parcelable? = null, tag: String? = null) { - supportFragmentManager.inTransaction { + supportFragmentManager.commitTransactionNow { add(frameId, fragmentClass, params.toMvRxBundle(), tag) } } fun VectorBaseActivity.replaceFragment(frameId: Int, fragment: Fragment, tag: String? = null) { - supportFragmentManager.inTransaction { replace(frameId, fragment, tag) } + supportFragmentManager.commitTransactionNow { replace(frameId, fragment, tag) } } fun VectorBaseActivity.replaceFragment(frameId: Int, fragmentClass: Class, params: Parcelable? = null, tag: String? = null) { - supportFragmentManager.inTransaction { + supportFragmentManager.commitTransactionNow { replace(frameId, fragmentClass, params.toMvRxBundle(), tag) } } fun VectorBaseActivity.addFragmentToBackstack(frameId: Int, fragment: Fragment, tag: String? = null) { - supportFragmentManager.inTransaction { replace(frameId, fragment).addToBackStack(tag) } + supportFragmentManager.commitTransaction { replace(frameId, fragment).addToBackStack(tag) } } fun VectorBaseActivity.addFragmentToBackstack(frameId: Int, fragmentClass: Class, params: Parcelable? = null, tag: String? = null) { - supportFragmentManager.inTransaction { + supportFragmentManager.commitTransaction { replace(frameId, fragmentClass, params.toMvRxBundle(), tag).addToBackStack(tag) } } diff --git a/vector/src/main/java/im/vector/riotx/core/extensions/Fragment.kt b/vector/src/main/java/im/vector/riotx/core/extensions/Fragment.kt index e6e83bfd67..7db27ececb 100644 --- a/vector/src/main/java/im/vector/riotx/core/extensions/Fragment.kt +++ b/vector/src/main/java/im/vector/riotx/core/extensions/Fragment.kt @@ -21,61 +21,61 @@ import androidx.fragment.app.Fragment import im.vector.riotx.core.platform.VectorBaseFragment fun VectorBaseFragment.addFragment(frameId: Int, fragment: Fragment) { - parentFragmentManager.inTransaction { add(frameId, fragment) } + parentFragmentManager.commitTransactionNow { add(frameId, fragment) } } fun VectorBaseFragment.addFragment(frameId: Int, fragmentClass: Class, params: Parcelable? = null, tag: String? = null) { - parentFragmentManager.inTransaction { + parentFragmentManager.commitTransactionNow { add(frameId, fragmentClass, params.toMvRxBundle(), tag) } } fun VectorBaseFragment.replaceFragment(frameId: Int, fragment: Fragment) { - parentFragmentManager.inTransaction { replace(frameId, fragment) } + parentFragmentManager.commitTransactionNow { replace(frameId, fragment) } } fun VectorBaseFragment.replaceFragment(frameId: Int, fragmentClass: Class, params: Parcelable? = null, tag: String? = null) { - parentFragmentManager.inTransaction { + parentFragmentManager.commitTransactionNow { replace(frameId, fragmentClass, params.toMvRxBundle(), tag) } } fun VectorBaseFragment.addFragmentToBackstack(frameId: Int, fragment: Fragment, tag: String? = null) { - parentFragmentManager.inTransaction { replace(frameId, fragment).addToBackStack(tag) } + parentFragmentManager.commitTransaction { replace(frameId, fragment, tag).addToBackStack(tag) } } fun VectorBaseFragment.addFragmentToBackstack(frameId: Int, fragmentClass: Class, params: Parcelable? = null, tag: String? = null) { - parentFragmentManager.inTransaction { + parentFragmentManager.commitTransaction { replace(frameId, fragmentClass, params.toMvRxBundle(), tag).addToBackStack(tag) } } -fun VectorBaseFragment.addChildFragment(frameId: Int, fragment: Fragment) { - childFragmentManager.inTransaction { add(frameId, fragment) } +fun VectorBaseFragment.addChildFragment(frameId: Int, fragment: Fragment, tag: String? = null) { + childFragmentManager.commitTransactionNow { add(frameId, fragment, tag) } } fun VectorBaseFragment.addChildFragment(frameId: Int, fragmentClass: Class, params: Parcelable? = null, tag: String? = null) { - childFragmentManager.inTransaction { + childFragmentManager.commitTransactionNow { add(frameId, fragmentClass, params.toMvRxBundle(), tag) } } -fun VectorBaseFragment.replaceChildFragment(frameId: Int, fragment: Fragment) { - childFragmentManager.inTransaction { replace(frameId, fragment) } +fun VectorBaseFragment.replaceChildFragment(frameId: Int, fragment: Fragment, tag: String? = null) { + childFragmentManager.commitTransactionNow { replace(frameId, fragment, tag) } } fun VectorBaseFragment.replaceChildFragment(frameId: Int, fragmentClass: Class, params: Parcelable? = null, tag: String? = null) { - childFragmentManager.inTransaction { + childFragmentManager.commitTransactionNow { replace(frameId, fragmentClass, params.toMvRxBundle(), tag) } } fun VectorBaseFragment.addChildFragmentToBackstack(frameId: Int, fragment: Fragment, tag: String? = null) { - childFragmentManager.inTransaction { replace(frameId, fragment).addToBackStack(tag) } + childFragmentManager.commitTransaction { replace(frameId, fragment).addToBackStack(tag) } } fun VectorBaseFragment.addChildFragmentToBackstack(frameId: Int, fragmentClass: Class, params: Parcelable? = null, tag: String? = null) { - childFragmentManager.inTransaction { + childFragmentManager.commitTransaction { replace(frameId, fragmentClass, params.toMvRxBundle(), tag).addToBackStack(tag) } } diff --git a/vector/src/main/java/im/vector/riotx/core/extensions/FragmentManager.kt b/vector/src/main/java/im/vector/riotx/core/extensions/FragmentManager.kt index 1d7815b2b7..caf1bf90f8 100644 --- a/vector/src/main/java/im/vector/riotx/core/extensions/FragmentManager.kt +++ b/vector/src/main/java/im/vector/riotx/core/extensions/FragmentManager.kt @@ -18,6 +18,10 @@ package im.vector.riotx.core.extensions import androidx.fragment.app.FragmentTransaction -inline fun androidx.fragment.app.FragmentManager.inTransaction(func: FragmentTransaction.() -> FragmentTransaction) { +inline fun androidx.fragment.app.FragmentManager.commitTransactionNow(func: FragmentTransaction.() -> FragmentTransaction) { + beginTransaction().func().commitNow() +} + +inline fun androidx.fragment.app.FragmentManager.commitTransaction(func: FragmentTransaction.() -> FragmentTransaction) { beginTransaction().func().commit() } diff --git a/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseActivity.kt b/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseActivity.kt index f0f6eea91d..4a3056657f 100644 --- a/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseActivity.kt +++ b/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseActivity.kt @@ -24,6 +24,7 @@ import android.view.Menu import android.view.MenuItem import android.view.View import androidx.annotation.* +import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.Toolbar import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.core.view.isVisible @@ -34,7 +35,6 @@ import androidx.lifecycle.ViewModelProviders import butterknife.BindView import butterknife.ButterKnife import butterknife.Unbinder -import com.airbnb.mvrx.BaseMvRxActivity import com.airbnb.mvrx.MvRx import com.bumptech.glide.util.Util import com.google.android.material.snackbar.Snackbar @@ -59,7 +59,7 @@ import io.reactivex.disposables.Disposable import timber.log.Timber import kotlin.system.measureTimeMillis -abstract class VectorBaseActivity : BaseMvRxActivity(), HasScreenInjector { +abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector { /* ========================================================================================== * UI * ========================================================================================== */ diff --git a/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseBottomSheetDialogFragment.kt b/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseBottomSheetDialogFragment.kt index 2c8f840a41..5727580653 100644 --- a/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseBottomSheetDialogFragment.kt +++ b/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseBottomSheetDialogFragment.kt @@ -25,24 +25,22 @@ import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProviders import com.airbnb.mvrx.MvRx import com.airbnb.mvrx.MvRxView -import com.airbnb.mvrx.MvRxViewModelStore +import com.airbnb.mvrx.MvRxViewId import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialogFragment import im.vector.riotx.core.di.DaggerScreenComponent import im.vector.riotx.core.di.ScreenComponent import im.vector.riotx.core.utils.DimensionConverter -import java.util.* /** * Add MvRx capabilities to bottomsheetdialog (like BaseMvRxFragment) */ abstract class VectorBaseBottomSheetDialogFragment : BottomSheetDialogFragment(), MvRxView { - override val mvrxViewModelStore by lazy { MvRxViewModelStore(viewModelStore) } - private lateinit var mvrxPersistedViewId: String + private val mvrxViewIdProperty = MvRxViewId() + final override val mvrxViewId: String by mvrxViewIdProperty private lateinit var screenComponent: ScreenComponent - final override val mvrxViewId: String by lazy { mvrxPersistedViewId } /* ========================================================================================== * View model @@ -78,10 +76,7 @@ abstract class VectorBaseBottomSheetDialogFragment : BottomSheetDialogFragment() protected open fun injectWith(screenComponent: ScreenComponent) = Unit override fun onCreate(savedInstanceState: Bundle?) { - mvrxViewModelStore.restoreViewModels(this, savedInstanceState) - mvrxPersistedViewId = savedInstanceState?.getString(PERSISTED_VIEW_ID_KEY) - ?: this::class.java.simpleName + "_" + UUID.randomUUID().toString() - + mvrxViewIdProperty.restoreFrom(savedInstanceState) super.onCreate(savedInstanceState) } @@ -98,8 +93,7 @@ abstract class VectorBaseBottomSheetDialogFragment : BottomSheetDialogFragment() override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) - mvrxViewModelStore.saveViewModels(outState) - outState.putString(PERSISTED_VIEW_ID_KEY, mvrxViewId) + mvrxViewIdProperty.saveTo(outState) } override fun onStart() { @@ -121,5 +115,3 @@ abstract class VectorBaseBottomSheetDialogFragment : BottomSheetDialogFragment() arguments = args?.let { Bundle().apply { putParcelable(MvRx.KEY_ARG, it) } } } } - -private const val PERSISTED_VIEW_ID_KEY = "mvrx:bottomsheet_persisted_view_id" diff --git a/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseFragment.kt b/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseFragment.kt index 255e18608a..9f94c15edd 100644 --- a/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseFragment.kt +++ b/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseFragment.kt @@ -114,11 +114,12 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), HasScreenInjector { super.onDestroyView() mUnBinder?.unbind() mUnBinder = null + uiDisposables.clear() } override fun onDestroy() { - super.onDestroy() uiDisposables.dispose() + super.onDestroy() } override fun injector(): ScreenComponent { @@ -181,7 +182,7 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), HasScreenInjector { private val uiDisposables = CompositeDisposable() - protected fun Disposable.disposeOnDestroy(): Disposable { + protected fun Disposable.disposeOnDestroyView(): Disposable { uiDisposables.add(this) return this } diff --git a/vector/src/main/java/im/vector/riotx/core/ui/views/ReadReceiptsView.kt b/vector/src/main/java/im/vector/riotx/core/ui/views/ReadReceiptsView.kt index c74e9a4111..6e4229908f 100644 --- a/vector/src/main/java/im/vector/riotx/core/ui/views/ReadReceiptsView.kt +++ b/vector/src/main/java/im/vector/riotx/core/ui/views/ReadReceiptsView.kt @@ -23,6 +23,7 @@ import android.widget.ImageView import android.widget.LinearLayout import androidx.core.view.isVisible import im.vector.riotx.R +import im.vector.riotx.core.glide.GlideApp import im.vector.riotx.features.home.AvatarRenderer import im.vector.riotx.features.home.room.detail.timeline.item.ReadReceiptData import kotlinx.android.synthetic.main.view_read_receipts.view.* @@ -105,4 +106,11 @@ class ReadReceiptsView @JvmOverloads constructor( isVisible = false } } + + fun unbind() { + receiptAvatars.forEach { + GlideApp.with(context.applicationContext).clear(it) + } + isVisible = false + } } diff --git a/vector/src/main/java/im/vector/riotx/core/utils/DataSource.kt b/vector/src/main/java/im/vector/riotx/core/utils/DataSource.kt index 6c317eb977..726d2ea697 100644 --- a/vector/src/main/java/im/vector/riotx/core/utils/DataSource.kt +++ b/vector/src/main/java/im/vector/riotx/core/utils/DataSource.kt @@ -34,14 +34,14 @@ interface MutableDataSource : DataSource { */ open class BehaviorDataSource(private val defaultValue: T? = null) : MutableDataSource { - private val storeRelay = createRelay() + private val behaviorRelay = createRelay() override fun observe(): Observable { - return storeRelay.hide().observeOn(Schedulers.computation()) + return behaviorRelay.hide().observeOn(Schedulers.computation()) } override fun post(value: T) { - storeRelay.accept(value) + behaviorRelay.accept(value) } private fun createRelay(): BehaviorRelay { @@ -58,13 +58,13 @@ open class BehaviorDataSource(private val defaultValue: T? = null) : MutableD */ open class PublishDataSource : MutableDataSource { - private val storeRelay = PublishRelay.create() + private val publishRelay = PublishRelay.create() override fun observe(): Observable { - return storeRelay.hide() + return publishRelay.hide() } override fun post(value: T) { - storeRelay.accept(value) + publishRelay.accept(value) } } diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/verification/SASVerificationActivity.kt b/vector/src/main/java/im/vector/riotx/features/crypto/verification/SASVerificationActivity.kt index cb5391be3c..cf80bf98fc 100644 --- a/vector/src/main/java/im/vector/riotx/features/crypto/verification/SASVerificationActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/crypto/verification/SASVerificationActivity.kt @@ -26,7 +26,7 @@ import im.vector.matrix.android.api.session.crypto.sas.IncomingSasVerificationTr import im.vector.matrix.android.api.session.crypto.sas.OutgoingSasVerificationRequest import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTxState import im.vector.riotx.R -import im.vector.riotx.core.extensions.inTransaction +import im.vector.riotx.core.extensions.commitTransaction import im.vector.riotx.core.extensions.observeEvent import im.vector.riotx.core.platform.SimpleFragmentActivity import im.vector.riotx.core.platform.WaitingViewData @@ -102,20 +102,20 @@ class SASVerificationActivity : SimpleFragmentActivity() { IncomingSasVerificationTransaction.UxState.SHOW_ACCEPT, IncomingSasVerificationTransaction.UxState.WAIT_FOR_KEY_AGREEMENT -> { supportActionBar?.setTitle(R.string.sas_incoming_request_title) - supportFragmentManager.inTransaction { + supportFragmentManager.commitTransaction { setCustomAnimations(R.anim.no_anim, R.anim.exit_fade_out) replace(R.id.container, SASVerificationIncomingFragment::class.java, null) } } IncomingSasVerificationTransaction.UxState.WAIT_FOR_VERIFICATION, IncomingSasVerificationTransaction.UxState.SHOW_SAS -> { - supportFragmentManager.inTransaction { + supportFragmentManager.commitTransaction { setCustomAnimations(R.anim.no_anim, R.anim.exit_fade_out) replace(R.id.container, SASVerificationShortCodeFragment::class.java, null) } } IncomingSasVerificationTransaction.UxState.VERIFIED -> { - supportFragmentManager.inTransaction { + supportFragmentManager.commitTransaction { setCustomAnimations(R.anim.no_anim, R.anim.exit_fade_out) replace(R.id.container, SASVerificationVerifiedFragment::class.java, null) } @@ -133,20 +133,20 @@ class SASVerificationActivity : SimpleFragmentActivity() { OutgoingSasVerificationRequest.UxState.UNKNOWN, OutgoingSasVerificationRequest.UxState.WAIT_FOR_START, OutgoingSasVerificationRequest.UxState.WAIT_FOR_KEY_AGREEMENT -> { - supportFragmentManager.inTransaction { + supportFragmentManager.commitTransaction { setCustomAnimations(R.anim.no_anim, R.anim.exit_fade_out) replace(R.id.container, SASVerificationStartFragment::class.java, null) } } OutgoingSasVerificationRequest.UxState.SHOW_SAS, OutgoingSasVerificationRequest.UxState.WAIT_FOR_VERIFICATION -> { - supportFragmentManager.inTransaction { + supportFragmentManager.commitTransaction { setCustomAnimations(R.anim.no_anim, R.anim.exit_fade_out) replace(R.id.container, SASVerificationShortCodeFragment::class.java, null) } } OutgoingSasVerificationRequest.UxState.VERIFIED -> { - supportFragmentManager.inTransaction { + supportFragmentManager.commitTransaction { setCustomAnimations(R.anim.no_anim, R.anim.exit_fade_out) replace(R.id.container, SASVerificationVerifiedFragment::class.java, null) } @@ -172,13 +172,13 @@ class SASVerificationActivity : SimpleFragmentActivity() { finish() } SasVerificationViewModel.NAVIGATE_SAS_DISPLAY -> { - supportFragmentManager.inTransaction { + supportFragmentManager.commitTransaction { setCustomAnimations(R.anim.enter_from_right, R.anim.exit_fade_out) replace(R.id.container, SASVerificationShortCodeFragment::class.java, null) } } SasVerificationViewModel.NAVIGATE_SUCCESS -> { - supportFragmentManager.inTransaction { + supportFragmentManager.commitTransaction { setCustomAnimations(R.anim.enter_from_right, R.anim.exit_fade_out) replace(R.id.container, SASVerificationVerifiedFragment::class.java, null) } diff --git a/vector/src/main/java/im/vector/riotx/features/home/HomeDetailAction.kt b/vector/src/main/java/im/vector/riotx/features/home/HomeDetailAction.kt index 189a1e8aeb..3309f987fd 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/HomeDetailAction.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/HomeDetailAction.kt @@ -17,8 +17,7 @@ package im.vector.riotx.features.home import im.vector.riotx.core.platform.VectorViewModelAction -import im.vector.riotx.features.home.room.list.RoomListFragment sealed class HomeDetailAction : VectorViewModelAction { - data class SwitchDisplayMode(val displayMode: RoomListFragment.DisplayMode) : HomeDetailAction() + data class SwitchDisplayMode(val displayMode: RoomListDisplayMode) : HomeDetailAction() } diff --git a/vector/src/main/java/im/vector/riotx/features/home/HomeDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/HomeDetailFragment.kt index 5b56a19648..02ac9a26d2 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/HomeDetailFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/HomeDetailFragment.kt @@ -18,6 +18,7 @@ package im.vector.riotx.features.home import android.os.Bundle import android.view.LayoutInflater +import android.view.View import androidx.core.view.forEachIndexed import androidx.lifecycle.Observer import com.airbnb.mvrx.fragmentViewModel @@ -28,7 +29,7 @@ import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupState import im.vector.matrix.android.api.session.group.model.GroupSummary import im.vector.riotx.R -import im.vector.riotx.core.extensions.addChildFragmentToBackstack +import im.vector.riotx.core.extensions.commitTransactionNow import im.vector.riotx.core.platform.ToolbarConfigurable import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.core.ui.views.KeysBackupBanner @@ -59,9 +60,8 @@ class HomeDetailFragment @Inject constructor( return R.layout.fragment_home_detail } - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) sharedActionViewModel = activityViewModelProvider.get(HomeSharedActionViewModel::class.java) setupBottomNavigationView() @@ -133,10 +133,10 @@ class HomeDetailFragment @Inject constructor( private fun setupBottomNavigationView() { bottomNavigationView.setOnNavigationItemSelectedListener { val displayMode = when (it.itemId) { - R.id.bottom_action_home -> RoomListFragment.DisplayMode.HOME - R.id.bottom_action_people -> RoomListFragment.DisplayMode.PEOPLE - R.id.bottom_action_rooms -> RoomListFragment.DisplayMode.ROOMS - else -> RoomListFragment.DisplayMode.HOME + R.id.bottom_action_home -> RoomListDisplayMode.HOME + R.id.bottom_action_people -> RoomListDisplayMode.PEOPLE + R.id.bottom_action_rooms -> RoomListDisplayMode.ROOMS + else -> RoomListDisplayMode.HOME } viewModel.handle(HomeDetailAction.SwitchDisplayMode(displayMode)) true @@ -152,25 +152,32 @@ class HomeDetailFragment @Inject constructor( } } - private fun switchDisplayMode(displayMode: RoomListFragment.DisplayMode) { + private fun switchDisplayMode(displayMode: RoomListDisplayMode) { groupToolbarTitleView.setText(displayMode.titleRes) updateSelectedFragment(displayMode) // Update the navigation view (for when we restore the tabs) bottomNavigationView.selectedItemId = when (displayMode) { - RoomListFragment.DisplayMode.PEOPLE -> R.id.bottom_action_people - RoomListFragment.DisplayMode.ROOMS -> R.id.bottom_action_rooms + RoomListDisplayMode.PEOPLE -> R.id.bottom_action_people + RoomListDisplayMode.ROOMS -> R.id.bottom_action_rooms else -> R.id.bottom_action_home } } - private fun updateSelectedFragment(displayMode: RoomListFragment.DisplayMode) { + private fun updateSelectedFragment(displayMode: RoomListDisplayMode) { val fragmentTag = "FRAGMENT_TAG_${displayMode.name}" - val fragment = childFragmentManager.findFragmentByTag(fragmentTag) - if (fragment == null) { - val params = RoomListParams(displayMode) - addChildFragmentToBackstack(R.id.roomListContainer, RoomListFragment::class.java, params, fragmentTag) - } else { - addChildFragmentToBackstack(R.id.roomListContainer, fragment, fragmentTag) + val fragmentToShow = childFragmentManager.findFragmentByTag(fragmentTag) + childFragmentManager.commitTransactionNow { + childFragmentManager.fragments + .filter { it != fragmentToShow } + .forEach { + hide(it) + } + if (fragmentToShow == null) { + val params = RoomListParams(displayMode) + add(R.id.roomListContainer, RoomListFragment::class.java, params.toMvRxBundle(), fragmentTag) + } else { + show(fragmentToShow) + } } } diff --git a/vector/src/main/java/im/vector/riotx/features/home/HomeDetailViewState.kt b/vector/src/main/java/im/vector/riotx/features/home/HomeDetailViewState.kt index b1c50b83cf..c7c5e4a233 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/HomeDetailViewState.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/HomeDetailViewState.kt @@ -17,14 +17,17 @@ package im.vector.riotx.features.home import arrow.core.Option +import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.Uninitialized import im.vector.matrix.android.api.session.group.model.GroupSummary +import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.session.sync.SyncState -import im.vector.riotx.features.home.room.list.RoomListFragment data class HomeDetailViewState( val groupSummary: Option = Option.empty(), - val displayMode: RoomListFragment.DisplayMode = RoomListFragment.DisplayMode.HOME, + val asyncRooms: Async> = Uninitialized, + val displayMode: RoomListDisplayMode = RoomListDisplayMode.HOME, val notificationCountCatchup: Int = 0, val notificationHighlightCatchup: Boolean = false, val notificationCountPeople: Int = 0, diff --git a/vector/src/main/java/im/vector/riotx/features/home/HomeDrawerFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/HomeDrawerFragment.kt index 5b052f5ff5..422b59671e 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/HomeDrawerFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/HomeDrawerFragment.kt @@ -17,6 +17,7 @@ package im.vector.riotx.features.home import android.os.Bundle +import android.view.View import im.vector.matrix.android.api.session.Session import im.vector.riotx.R import im.vector.riotx.core.extensions.observeK @@ -33,8 +34,8 @@ class HomeDrawerFragment @Inject constructor( override fun getLayoutResId() = R.layout.fragment_home_drawer - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) if (savedInstanceState == null) { replaceChildFragment(R.id.homeDrawerGroupListContainer, GroupListFragment::class.java) } diff --git a/vector/src/main/java/im/vector/riotx/features/home/RoomListDisplayMode.kt b/vector/src/main/java/im/vector/riotx/features/home/RoomListDisplayMode.kt new file mode 100644 index 0000000000..18b901a967 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/home/RoomListDisplayMode.kt @@ -0,0 +1,28 @@ +/* + * 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.riotx.features.home + +import androidx.annotation.StringRes +import im.vector.riotx.R + +enum class RoomListDisplayMode(@StringRes val titleRes: Int) { + HOME(R.string.bottom_action_home), + PEOPLE(R.string.bottom_action_people_x), + ROOMS(R.string.bottom_action_rooms), + FILTERED(/* Not used */ 0), + SHARE(/* Not used */ 0) + } diff --git a/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomDirectoryUsersFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomDirectoryUsersFragment.kt index fe43b89a52..cf6abf12e9 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomDirectoryUsersFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomDirectoryUsersFragment.kt @@ -18,6 +18,7 @@ package im.vector.riotx.features.home.createdirect import android.content.Context import android.os.Bundle +import android.view.View import android.view.inputmethod.InputMethodManager import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.withState @@ -40,8 +41,8 @@ class CreateDirectRoomDirectoryUsersFragment @Inject constructor( private lateinit var sharedActionViewModel: CreateDirectRoomSharedActionViewModel - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) sharedActionViewModel = activityViewModelProvider.get(CreateDirectRoomSharedActionViewModel::class.java) setupRecyclerView() setupSearchByMatrixIdView() @@ -61,7 +62,7 @@ class CreateDirectRoomDirectoryUsersFragment @Inject constructor( .subscribe { viewModel.handle(CreateDirectRoomAction.SearchDirectoryUsers(it.toString())) } - .disposeOnDestroy() + .disposeOnDestroyView() createDirectRoomSearchById.requestFocus() val imm = context?.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager imm?.showSoftInput(createDirectRoomSearchById, InputMethodManager.SHOW_IMPLICIT) diff --git a/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomKnownUsersFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomKnownUsersFragment.kt index 26265c8d56..12019fa39e 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomKnownUsersFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomKnownUsersFragment.kt @@ -21,6 +21,7 @@ package im.vector.riotx.features.home.createdirect import android.os.Bundle import android.view.Menu import android.view.MenuItem +import android.view.View import android.widget.ScrollView import androidx.core.view.size import com.airbnb.mvrx.activityViewModel @@ -50,8 +51,8 @@ class CreateDirectRoomKnownUsersFragment @Inject constructor( private val viewModel: CreateDirectRoomViewModel by activityViewModel() private lateinit var sharedActionViewModel: CreateDirectRoomSharedActionViewModel - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) sharedActionViewModel = activityViewModelProvider.get(CreateDirectRoomSharedActionViewModel::class.java) vectorBaseActivity.setSupportActionBar(createDirectRoomToolbar) setupRecyclerView() @@ -113,7 +114,7 @@ class CreateDirectRoomKnownUsersFragment @Inject constructor( } viewModel.handle(action) } - .disposeOnDestroy() + .disposeOnDestroyView() createDirectRoomFilter.setupAsSearch() createDirectRoomFilter.requestFocus() diff --git a/vector/src/main/java/im/vector/riotx/features/home/group/GroupListFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/group/GroupListFragment.kt index bc39491773..39f8c17f05 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/group/GroupListFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/group/GroupListFragment.kt @@ -17,6 +17,7 @@ package im.vector.riotx.features.home.group import android.os.Bundle +import android.view.View import com.airbnb.mvrx.Incomplete import com.airbnb.mvrx.Success import com.airbnb.mvrx.fragmentViewModel @@ -40,8 +41,8 @@ class GroupListFragment @Inject constructor( override fun getLayoutResId() = R.layout.fragment_group_list - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) sharedActionViewModel = activityViewModelProvider.get(HomeSharedActionViewModel::class.java) groupController.callback = this stateView.contentView = groupListEpoxyRecyclerView diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailAction.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailAction.kt index b37d03097c..2e59e70d08 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailAction.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailAction.kt @@ -42,10 +42,10 @@ sealed class RoomDetailAction : VectorViewModelAction { object AcceptInvite : RoomDetailAction() object RejectInvite : RoomDetailAction() - data class EnterEditMode(val eventId: String, val draft: String) : RoomDetailAction() - data class EnterQuoteMode(val eventId: String, val draft: String) : RoomDetailAction() - data class EnterReplyMode(val eventId: String, val draft: String) : RoomDetailAction() - data class ExitSpecialMode(val draft: String) : RoomDetailAction() + data class EnterEditMode(val eventId: String, val text: String) : RoomDetailAction() + data class EnterQuoteMode(val eventId: String, val text: String) : RoomDetailAction() + data class EnterReplyMode(val eventId: String, val text: String) : RoomDetailAction() + data class ExitSpecialMode(val text: String) : RoomDetailAction() data class ResendMessage(val eventId: String) : RoomDetailAction() data class RemoveFailedEcho(val eventId: String) : RoomDetailAction() diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt index a5bdd674f0..4643c30f4a 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt @@ -203,15 +203,14 @@ class RoomDetailFragment @Inject constructor( private var lockSendButton = false - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) sharedActionViewModel = activityViewModelProvider.get(MessageSharedActionViewModel::class.java) attachmentsHelper = AttachmentsHelper.create(this, this).register() keyboardStateUtils = KeyboardStateUtils(requireActivity()) setupToolbar(roomToolbar) setupRecyclerView() setupComposer() - setupAttachmentButton() setupInviteView() setupNotificationView() setupJumpToReadMarkerView() @@ -229,7 +228,7 @@ class RoomDetailFragment @Inject constructor( .subscribe { handleActions(it) } - .disposeOnDestroy() + .disposeOnDestroyView() roomDetailViewModel.navigateToEvent.observeEvent(this) { val scrollPosition = timelineEventController.searchPositionOfEvent(it) @@ -274,7 +273,10 @@ class RoomDetailFragment @Inject constructor( roomDetailViewModel.requestLiveData.observeEvent(this) { displayRoomDetailActionResult(it) } + } + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) if (savedInstanceState == null) { when (val sharedData = roomDetailArgs.sharedData) { is SharedData.Text -> roomDetailViewModel.handle(RoomDetailAction.SendMessage(sharedData.text, false)) @@ -284,6 +286,11 @@ class RoomDetailFragment @Inject constructor( } } + override fun onDestroyView() { + super.onDestroyView() + recyclerView.adapter = null + } + override fun onDestroy() { debouncer.cancelAll() super.onDestroy() @@ -470,6 +477,7 @@ class RoomDetailFragment @Inject constructor( } } recyclerView.adapter = timelineEventController.adapter + recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { if (recyclerView.scrollState == RecyclerView.SCROLL_STATE_IDLE) { @@ -490,6 +498,7 @@ class RoomDetailFragment @Inject constructor( } } }) + timelineEventController.callback = this if (vectorPreferences.swipeToReplyIsEnabled()) { @@ -592,21 +601,29 @@ class RoomDetailFragment @Inject constructor( }) .build() - composerLayout.sendButton.setOnClickListener { - if (lockSendButton) { - Timber.w("Send button is locked") - return@setOnClickListener - } - val textMessage = composerLayout.composerEditText.text.toString() - if (textMessage.isNotBlank()) { - lockSendButton = true - roomDetailViewModel.handle(RoomDetailAction.SendMessage(textMessage, vectorPreferences.isMarkdownEnabled())) - } - } - composerLayout.composerRelatedMessageCloseButton.setOnClickListener { - roomDetailViewModel.handle(RoomDetailAction.ExitSpecialMode(composerLayout.composerEditText.text.toString())) - } composerLayout.callback = object : TextComposerView.Callback { + override fun onAddAttachment() { + if (!::attachmentTypeSelector.isInitialized) { + attachmentTypeSelector = AttachmentTypeSelectorView(vectorBaseActivity, vectorBaseActivity.layoutInflater, this@RoomDetailFragment) + } + attachmentTypeSelector.show(composerLayout.attachmentButton, keyboardStateUtils.isKeyboardShowing) + } + + override fun onSendMessage(text: String) { + if (lockSendButton) { + Timber.w("Send button is locked") + return + } + if (text.isNotBlank()) { + lockSendButton = true + roomDetailViewModel.handle(RoomDetailAction.SendMessage(text, vectorPreferences.isMarkdownEnabled())) + } + } + + override fun onCloseRelatedMessage() { + roomDetailViewModel.handle(RoomDetailAction.ExitSpecialMode(composerLayout.text.toString())) + } + override fun onRichContentSelected(contentUri: Uri): Boolean { // We need WRITE_EXTERNAL permission return if (checkPermissions(PERMISSIONS_FOR_WRITING_FILES, this@RoomDetailFragment, PERMISSION_REQUEST_CODE_INCOMING_URI)) { @@ -629,15 +646,6 @@ class RoomDetailFragment @Inject constructor( return isHandled } - private fun setupAttachmentButton() { - composerLayout.attachmentButton.setOnClickListener { - if (!::attachmentTypeSelector.isInitialized) { - attachmentTypeSelector = AttachmentTypeSelectorView(vectorBaseActivity, vectorBaseActivity.layoutInflater, this) - } - attachmentTypeSelector.show(composerLayout.attachmentButton, keyboardStateUtils.isKeyboardShowing) - } - } - private fun setupInviteView() { inviteView.callback = this } @@ -1112,13 +1120,13 @@ class RoomDetailFragment @Inject constructor( roomDetailViewModel.handle(RoomDetailAction.UpdateQuickReactAction(action.eventId, action.clickedOn, action.add)) } is EventSharedAction.Edit -> { - roomDetailViewModel.handle(RoomDetailAction.EnterEditMode(action.eventId, composerLayout.composerEditText.text.toString())) + roomDetailViewModel.handle(RoomDetailAction.EnterEditMode(action.eventId, composerLayout.text.toString())) } is EventSharedAction.Quote -> { - roomDetailViewModel.handle(RoomDetailAction.EnterQuoteMode(action.eventId, composerLayout.composerEditText.text.toString())) + roomDetailViewModel.handle(RoomDetailAction.EnterQuoteMode(action.eventId, composerLayout.text.toString())) } is EventSharedAction.Reply -> { - roomDetailViewModel.handle(RoomDetailAction.EnterReplyMode(action.eventId, composerLayout.composerEditText.text.toString())) + roomDetailViewModel.handle(RoomDetailAction.EnterReplyMode(action.eventId, composerLayout.text.toString())) } is EventSharedAction.CopyPermalink -> { val permalink = PermalinkFactory.createPermalink(roomDetailArgs.roomId, action.eventId) diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt index dec77aa1c1..d2c2c7fdde 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt @@ -521,9 +521,10 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro } private fun handleEditAction(action: RoomDetailAction.EnterEditMode) { - saveCurrentDraft(action.draft) + saveCurrentDraft(action.text) room.getTimeLineEvent(action.eventId)?.let { timelineEvent -> + setState { copy(sendMode = SendMode.EDIT(timelineEvent, action.text)) } timelineEvent.root.eventId?.let { room.saveDraft(UserDraft.EDIT(it, timelineEvent.getTextEditableContent() ?: "")) } @@ -531,16 +532,17 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro } private fun handleQuoteAction(action: RoomDetailAction.EnterQuoteMode) { - saveCurrentDraft(action.draft) + saveCurrentDraft(action.text) room.getTimeLineEvent(action.eventId)?.let { timelineEvent -> + setState { copy(sendMode = SendMode.QUOTE(timelineEvent, action.text)) } withState { state -> // Save a new draft and keep the previously entered text, if it was not an edit timelineEvent.root.eventId?.let { if (state.sendMode is SendMode.EDIT) { room.saveDraft(UserDraft.QUOTE(it, "")) } else { - room.saveDraft(UserDraft.QUOTE(it, action.draft)) + room.saveDraft(UserDraft.QUOTE(it, action.text)) } } } @@ -548,16 +550,17 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro } private fun handleReplyAction(action: RoomDetailAction.EnterReplyMode) { - saveCurrentDraft(action.draft) + saveCurrentDraft(action.text) room.getTimeLineEvent(action.eventId)?.let { timelineEvent -> + setState { copy(sendMode = SendMode.REPLY(timelineEvent, action.text)) } withState { state -> // Save a new draft and keep the previously entered text, if it was not an edit timelineEvent.root.eventId?.let { if (state.sendMode is SendMode.EDIT) { room.saveDraft(UserDraft.REPLY(it, "")) } else { - room.saveDraft(UserDraft.REPLY(it, action.draft)) + room.saveDraft(UserDraft.REPLY(it, action.text)) } } } @@ -579,13 +582,14 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro } private fun handleExitSpecialMode(action: RoomDetailAction.ExitSpecialMode) { + setState { copy(sendMode = SendMode.REGULAR(action.text)) } withState { state -> // For edit, just delete the current draft if (state.sendMode is SendMode.EDIT) { room.deleteDraft() } else { // Save a new draft and keep the previously entered text - room.saveDraft(UserDraft.REGULAR(action.draft)) + room.saveDraft(UserDraft.REGULAR(action.text)) } } } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/composer/TextComposerView.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/composer/TextComposerView.kt index 0a6d3dde08..32307dc3d4 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/composer/TextComposerView.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/composer/TextComposerView.kt @@ -18,6 +18,7 @@ package im.vector.riotx.features.home.room.detail.composer import android.content.Context import android.net.Uri +import android.text.Editable import android.util.AttributeSet import android.view.ViewGroup import android.widget.ImageButton @@ -31,6 +32,7 @@ import androidx.transition.TransitionManager import butterknife.BindView import butterknife.ButterKnife import im.vector.riotx.R +import kotlinx.android.synthetic.main.merge_composer_layout.view.* /** * Encapsulate the timeline composer UX. @@ -39,7 +41,11 @@ import im.vector.riotx.R class TextComposerView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : ConstraintLayout(context, attrs, defStyleAttr) { - interface Callback : ComposerEditText.Callback + interface Callback : ComposerEditText.Callback { + fun onCloseRelatedMessage() + fun onSendMessage(text: String) + fun onAddAttachment() + } var callback: Callback? = null @@ -62,15 +68,31 @@ class TextComposerView @JvmOverloads constructor(context: Context, attrs: Attrib private val animationDuration = 100L + val text: Editable? + get() = composerEditText.text + init { inflate(context, R.layout.merge_composer_layout, this) ButterKnife.bind(this) collapse(false) - composerEditText.callback = object : Callback, ComposerEditText.Callback { + composerEditText.callback = object : ComposerEditText.Callback { override fun onRichContentSelected(contentUri: Uri): Boolean { return callback?.onRichContentSelected(contentUri) ?: false } } + composerRelatedMessageCloseButton.setOnClickListener { + collapse() + callback?.onCloseRelatedMessage() + } + + sendButton.setOnClickListener { + val textMessage = text?.toString() ?: "" + callback?.onSendMessage(textMessage) + } + + attachmentButton.setOnClickListener { + callback?.onAddAttachment() + } } fun collapse(animate: Boolean = true, transitionComplete: (() -> Unit)? = null) { diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/AbsMessageItem.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/AbsMessageItem.kt index bddee50861..2ca6bbfd37 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/AbsMessageItem.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/AbsMessageItem.kt @@ -139,6 +139,7 @@ abstract class AbsMessageItem : BaseEventItem() { override fun unbind(holder: H) { holder.readMarkerView.unbind() + holder.readReceiptsView.unbind() super.unbind(holder) } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/MessageImageVideoItem.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/MessageImageVideoItem.kt index 928a18fe4d..457f30cbf4 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/MessageImageVideoItem.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/MessageImageVideoItem.kt @@ -24,6 +24,7 @@ import androidx.core.view.isVisible import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyModelClass import im.vector.riotx.R +import im.vector.riotx.core.glide.GlideApp import im.vector.riotx.features.home.room.detail.timeline.helper.ContentUploadStateTrackerBinder import im.vector.riotx.features.media.ImageContentRenderer @@ -60,6 +61,7 @@ abstract class MessageImageVideoItem : AbsMessageItem { +class RoomListDisplayModeFilter(private val displayMode: RoomListDisplayMode) : Predicate { override fun test(roomSummary: RoomSummary): Boolean { if (roomSummary.membership.isLeft()) { return false } return when (displayMode) { - RoomListFragment.DisplayMode.HOME -> + RoomListDisplayMode.HOME -> roomSummary.notificationCount > 0 || roomSummary.membership == Membership.INVITE || roomSummary.userDrafts.isNotEmpty() - RoomListFragment.DisplayMode.PEOPLE -> roomSummary.isDirect && roomSummary.membership == Membership.JOIN - RoomListFragment.DisplayMode.ROOMS -> !roomSummary.isDirect && roomSummary.membership == Membership.JOIN - RoomListFragment.DisplayMode.FILTERED -> roomSummary.membership == Membership.JOIN - RoomListFragment.DisplayMode.SHARE -> roomSummary.membership == Membership.JOIN + RoomListDisplayMode.PEOPLE -> roomSummary.isDirect && roomSummary.membership == Membership.JOIN + RoomListDisplayMode.ROOMS -> !roomSummary.isDirect && roomSummary.membership == Membership.JOIN + RoomListDisplayMode.FILTERED -> roomSummary.membership == Membership.JOIN + RoomListDisplayMode.SHARE -> roomSummary.membership == Membership.JOIN } } } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt index df8bca7a2f..f2c6725a47 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt @@ -20,7 +20,7 @@ import android.os.Bundle import android.os.Parcelable import android.view.Menu import android.view.MenuItem -import androidx.annotation.StringRes +import android.view.View import androidx.appcompat.app.AlertDialog import androidx.core.content.ContextCompat import androidx.core.view.isVisible @@ -38,8 +38,10 @@ import im.vector.riotx.core.error.ErrorFormatter import im.vector.riotx.core.platform.OnBackPressed import im.vector.riotx.core.platform.StateView import im.vector.riotx.core.platform.VectorBaseFragment -import im.vector.riotx.features.home.room.list.actions.RoomListQuickActionsBottomSheet + +import im.vector.riotx.features.home.RoomListDisplayMode import im.vector.riotx.features.home.room.list.actions.RoomListQuickActionsSharedAction +import im.vector.riotx.features.home.room.list.actions.RoomListQuickActionsBottomSheet import im.vector.riotx.features.home.room.list.actions.RoomListQuickActionsSharedActionViewModel import im.vector.riotx.features.home.room.list.widget.FabMenuView import im.vector.riotx.features.notifications.NotificationDrawerManager @@ -51,7 +53,7 @@ import javax.inject.Inject @Parcelize data class RoomListParams( - val displayMode: RoomListFragment.DisplayMode, + val displayMode: RoomListDisplayMode, val sharedData: SharedData? = null ) : Parcelable @@ -63,14 +65,6 @@ class RoomListFragment @Inject constructor( ) : VectorBaseFragment(), RoomSummaryController.Listener, OnBackPressed, FabMenuView.Listener { - enum class DisplayMode(@StringRes val titleRes: Int) { - HOME(R.string.bottom_action_home), - PEOPLE(R.string.bottom_action_people_x), - ROOMS(R.string.bottom_action_rooms), - FILTERED(/* Not used */ 0), - SHARE(/* Not used */ 0) - } - private lateinit var sharedActionViewModel: RoomListQuickActionsSharedActionViewModel private val roomListParams: RoomListParams by args() private val roomListViewModel: RoomListViewModel by fragmentViewModel() @@ -97,13 +91,13 @@ class RoomListFragment @Inject constructor( super.onPrepareOptionsMenu(menu) } - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - sharedActionViewModel = activityViewModelProvider.get(RoomListQuickActionsSharedActionViewModel::class.java) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) setupCreateRoomButton() setupRecyclerView() - roomListViewModel.subscribe { renderState(it) } + sharedActionViewModel = activityViewModelProvider.get(RoomListQuickActionsSharedActionViewModel::class.java) + roomListViewModel.subscribe { renderState(it) } roomListViewModel.viewEvents .observe() .observeOn(AndroidSchedulers.mainThread()) @@ -113,18 +107,23 @@ class RoomListFragment @Inject constructor( is RoomListViewEvents.Failure -> showError(it) } } - .disposeOnDestroy() + .disposeOnDestroyView() createChatFabMenu.listener = this sharedActionViewModel .observe() .subscribe { handleQuickActions(it) } - .disposeOnDestroy() + .disposeOnDestroyView() + } + + override fun onDestroyView() { + super.onDestroyView() + roomListView.adapter = null } private fun openSelectedRoom(event: RoomListViewEvents.SelectRoom) { - if (roomListParams.displayMode == DisplayMode.SHARE) { + if (roomListParams.displayMode == RoomListDisplayMode.SHARE) { val sharedData = roomListParams.sharedData ?: return navigator.openRoomForSharing(requireActivity(), event.roomId, sharedData) } else { @@ -141,9 +140,9 @@ class RoomListFragment @Inject constructor( private fun setupCreateRoomButton() { when (roomListParams.displayMode) { - DisplayMode.HOME -> createChatFabMenu.isVisible = true - DisplayMode.PEOPLE -> createChatRoomButton.isVisible = true - DisplayMode.ROOMS -> createGroupRoomButton.isVisible = true + RoomListDisplayMode.HOME -> createChatFabMenu.isVisible = true + RoomListDisplayMode.PEOPLE -> createChatRoomButton.isVisible = true + RoomListDisplayMode.ROOMS -> createGroupRoomButton.isVisible = true else -> Unit // No button in this mode } @@ -155,7 +154,7 @@ class RoomListFragment @Inject constructor( } // Hide FAB when list is scrolling - roomListEpoxyRecyclerView.addOnScrollListener( + roomListView.addOnScrollListener( object : RecyclerView.OnScrollListener() { override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { createChatFabMenu.removeCallbacks(showFabRunnable) @@ -167,9 +166,9 @@ class RoomListFragment @Inject constructor( RecyclerView.SCROLL_STATE_DRAGGING, RecyclerView.SCROLL_STATE_SETTLING -> { when (roomListParams.displayMode) { - DisplayMode.HOME -> createChatFabMenu.hide() - DisplayMode.PEOPLE -> createChatRoomButton.hide() - DisplayMode.ROOMS -> createGroupRoomButton.hide() + RoomListDisplayMode.HOME -> createChatFabMenu.hide() + RoomListDisplayMode.PEOPLE -> createChatRoomButton.hide() + RoomListDisplayMode.ROOMS -> createGroupRoomButton.hide() else -> Unit } } @@ -180,7 +179,7 @@ class RoomListFragment @Inject constructor( fun filterRoomsWith(filter: String) { // Scroll the list to top - roomListEpoxyRecyclerView.scrollToPosition(0) + roomListView.scrollToPosition(0) roomListViewModel.handle(RoomListAction.FilterWith(filter)) } @@ -196,20 +195,20 @@ class RoomListFragment @Inject constructor( private fun setupRecyclerView() { val layoutManager = LinearLayoutManager(context) val stateRestorer = LayoutManagerStateRestorer(layoutManager).register() - roomListEpoxyRecyclerView.layoutManager = layoutManager - roomListEpoxyRecyclerView.itemAnimator = RoomListAnimator() + roomListView.layoutManager = layoutManager + roomListView.itemAnimator = RoomListAnimator() roomController.listener = this roomController.addModelBuildListener { it.dispatchTo(stateRestorer) } - stateView.contentView = roomListEpoxyRecyclerView - roomListEpoxyRecyclerView.setController(roomController) + roomListView.adapter = roomController.adapter + stateView.contentView = roomListView } private val showFabRunnable = Runnable { if (isAdded) { when (roomListParams.displayMode) { - DisplayMode.HOME -> createChatFabMenu.show() - DisplayMode.PEOPLE -> createChatRoomButton.show() - DisplayMode.ROOMS -> createGroupRoomButton.show() + RoomListDisplayMode.HOME -> createChatFabMenu.show() + RoomListDisplayMode.PEOPLE -> createChatRoomButton.show() + RoomListDisplayMode.ROOMS -> createGroupRoomButton.show() else -> Unit } } @@ -255,9 +254,9 @@ class RoomListFragment @Inject constructor( // Mark all as read menu when (roomListParams.displayMode) { - DisplayMode.HOME, - DisplayMode.PEOPLE, - DisplayMode.ROOMS -> { + RoomListDisplayMode.HOME, + RoomListDisplayMode.PEOPLE, + RoomListDisplayMode.ROOMS -> { val newValue = state.hasUnread if (hasUnreadRooms != newValue) { hasUnreadRooms = newValue @@ -285,7 +284,7 @@ class RoomListFragment @Inject constructor( } .isNullOrEmpty() val emptyState = when (roomListParams.displayMode) { - DisplayMode.HOME -> { + RoomListDisplayMode.HOME -> { if (hasNoRoom) { StateView.State.Empty( getString(R.string.room_list_catchup_welcome_title), @@ -299,13 +298,13 @@ class RoomListFragment @Inject constructor( getString(R.string.room_list_catchup_empty_body)) } } - DisplayMode.PEOPLE -> + RoomListDisplayMode.PEOPLE -> StateView.State.Empty( getString(R.string.room_list_people_empty_title), ContextCompat.getDrawable(requireContext(), R.drawable.ic_home_bottom_chat), getString(R.string.room_list_people_empty_body) ) - DisplayMode.ROOMS -> + RoomListDisplayMode.ROOMS -> StateView.State.Empty( getString(R.string.room_list_rooms_empty_title), ContextCompat.getDrawable(requireContext(), R.drawable.ic_home_bottom_group), diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModel.kt index 6fd5d9763c..e5924d9f2a 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModel.kt @@ -33,9 +33,7 @@ import javax.inject.Inject class RoomListViewModel @Inject constructor(initialState: RoomListViewState, private val session: Session, - private val roomSummariesSource: DataSource>, - private val alphabeticalRoomComparator: AlphabeticalRoomComparator, - private val chronologicalRoomComparator: ChronologicalRoomComparator) + private val roomSummariesSource: DataSource>) : VectorViewModel(initialState) { interface Factory { @@ -96,9 +94,6 @@ class RoomListViewModel @Inject constructor(initialState: RoomListViewState, roomSummariesSource .observe() .observeOn(Schedulers.computation()) - .map { - it.sortedWith(chronologicalRoomComparator) - } .execute { asyncRooms -> copy(asyncRooms = asyncRooms) } @@ -210,10 +205,11 @@ class RoomListViewModel @Inject constructor(initialState: RoomListViewState, } private fun buildRoomSummaries(rooms: List): RoomSummaries { + // Set up init size on directChats and groupRooms as they are the biggest ones val invites = ArrayList() val favourites = ArrayList() - val directChats = ArrayList() - val groupRooms = ArrayList() + val directChats = ArrayList(rooms.size) + val groupRooms = ArrayList(rooms.size) val lowPriorities = ArrayList() val serverNotices = ArrayList() @@ -231,21 +227,13 @@ class RoomListViewModel @Inject constructor(initialState: RoomListViewState, } } - val roomComparator = when (displayMode) { - RoomListFragment.DisplayMode.HOME -> chronologicalRoomComparator - RoomListFragment.DisplayMode.PEOPLE -> chronologicalRoomComparator - RoomListFragment.DisplayMode.ROOMS -> chronologicalRoomComparator - RoomListFragment.DisplayMode.FILTERED -> chronologicalRoomComparator - RoomListFragment.DisplayMode.SHARE -> chronologicalRoomComparator - } - return RoomSummaries().apply { - put(RoomCategory.INVITE, invites.sortedWith(roomComparator)) - put(RoomCategory.FAVOURITE, favourites.sortedWith(roomComparator)) - put(RoomCategory.DIRECT, directChats.sortedWith(roomComparator)) - put(RoomCategory.GROUP, groupRooms.sortedWith(roomComparator)) - put(RoomCategory.LOW_PRIORITY, lowPriorities.sortedWith(roomComparator)) - put(RoomCategory.SERVER_NOTICE, serverNotices.sortedWith(roomComparator)) + put(RoomCategory.INVITE, invites) + put(RoomCategory.FAVOURITE, favourites) + put(RoomCategory.DIRECT, directChats) + put(RoomCategory.GROUP, groupRooms) + put(RoomCategory.LOW_PRIORITY, lowPriorities) + put(RoomCategory.SERVER_NOTICE, serverNotices) } } } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModelFactory.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModelFactory.kt index 897e72d811..60ec92d8cf 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModelFactory.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModelFactory.kt @@ -18,22 +18,21 @@ package im.vector.riotx.features.home.room.list import im.vector.matrix.android.api.session.Session import im.vector.riotx.features.home.HomeRoomListDataSource +import im.vector.riotx.features.home.RoomListDisplayMode import im.vector.riotx.features.share.ShareRoomListDataSource import javax.inject.Inject import javax.inject.Provider class RoomListViewModelFactory @Inject constructor(private val session: Provider, private val homeRoomListDataSource: Provider, - private val shareRoomListDataSource: Provider, - private val alphabeticalRoomComparator: Provider, - private val chronologicalRoomComparator: Provider) : RoomListViewModel.Factory { + private val shareRoomListDataSource: Provider) + : RoomListViewModel.Factory { override fun create(initialState: RoomListViewState): RoomListViewModel { return RoomListViewModel( initialState, session.get(), - if (initialState.displayMode == RoomListFragment.DisplayMode.SHARE) shareRoomListDataSource.get() else homeRoomListDataSource.get(), - alphabeticalRoomComparator.get(), - chronologicalRoomComparator.get()) + if (initialState.displayMode == RoomListDisplayMode.SHARE) shareRoomListDataSource.get() else homeRoomListDataSource.get() + ) } } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewState.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewState.kt index 505554a8fb..b41b4b9eeb 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewState.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewState.kt @@ -23,9 +23,10 @@ import com.airbnb.mvrx.Uninitialized import im.vector.matrix.android.api.session.room.model.Membership import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.riotx.R +import im.vector.riotx.features.home.RoomListDisplayMode data class RoomListViewState( - val displayMode: RoomListFragment.DisplayMode, + val displayMode: RoomListDisplayMode, val asyncRooms: Async> = Uninitialized, val roomFilter: String = "", val asyncFilteredRooms: Async = Uninitialized, diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomSummaryController.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomSummaryController.kt index 5cd684b275..74dab6563f 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomSummaryController.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomSummaryController.kt @@ -24,6 +24,7 @@ import im.vector.riotx.R import im.vector.riotx.core.epoxy.helpFooterItem import im.vector.riotx.core.epoxy.noResultItem import im.vector.riotx.core.resources.StringProvider +import im.vector.riotx.features.home.RoomListDisplayMode import im.vector.riotx.core.resources.UserPreferencesProvider import im.vector.riotx.features.home.room.filtered.FilteredRoomFooterItem import im.vector.riotx.features.home.room.filtered.filteredRoomFooterItem @@ -58,8 +59,8 @@ class RoomSummaryController @Inject constructor(private val stringProvider: Stri override fun buildModels() { val nonNullViewState = viewState ?: return when (nonNullViewState.displayMode) { - RoomListFragment.DisplayMode.FILTERED, - RoomListFragment.DisplayMode.SHARE -> { + RoomListDisplayMode.FILTERED, + RoomListDisplayMode.SHARE -> { buildFilteredRooms(nonNullViewState) } else -> { @@ -106,7 +107,7 @@ class RoomSummaryController @Inject constructor(private val stringProvider: Stri viewState.rejectingErrorRoomsIds) when { - viewState.displayMode == RoomListFragment.DisplayMode.FILTERED -> addFilterFooter(viewState) + viewState.displayMode == RoomListDisplayMode.FILTERED -> addFilterFooter(viewState) filteredSummaries.isEmpty() -> addEmptyFooter() } } diff --git a/vector/src/main/java/im/vector/riotx/features/login/LoginFragment.kt b/vector/src/main/java/im/vector/riotx/features/login/LoginFragment.kt index f8295310f9..456e4b2bb3 100644 --- a/vector/src/main/java/im/vector/riotx/features/login/LoginFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/login/LoginFragment.kt @@ -62,7 +62,7 @@ class LoginFragment @Inject constructor() : VectorBaseFragment() { viewModel.handle(LoginAction.UpdateHomeServer(homeServerField.text.toString())) } } - .disposeOnDestroy() + .disposeOnDestroyView() homeServerField.setOnEditorActionListener { _, actionId, _ -> if (actionId == EditorInfo.IME_ACTION_DONE) { @@ -107,7 +107,7 @@ class LoginFragment @Inject constructor() : VectorBaseFragment() { } ) .subscribeBy { authenticateButton.isEnabled = it } - .disposeOnDestroy() + .disposeOnDestroyView() authenticateButton.setOnClickListener { authenticate() } authenticateButtonSso.setOnClickListener { openSso() } diff --git a/vector/src/main/java/im/vector/riotx/features/login/LoginViewModel.kt b/vector/src/main/java/im/vector/riotx/features/login/LoginViewModel.kt index 5a9c682f61..a0a7258e2a 100644 --- a/vector/src/main/java/im/vector/riotx/features/login/LoginViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/login/LoginViewModel.kt @@ -116,7 +116,6 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi private fun onSessionCreated(session: Session) { activeSessionHolder.setActiveSession(session) session.configureAndStart(pushRuleTriggerListener, sessionListener) - setState { copy( asyncLoginAction = Success(Unit) @@ -131,9 +130,15 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi // Should not happen Timber.w("homeServerConnectionConfig is null") } else { - val session = authenticator.createSessionFromSso(action.credentials, homeServerConnectionConfigFinal) + authenticator.createSessionFromSso(action.credentials, homeServerConnectionConfigFinal, object : MatrixCallback { + override fun onSuccess(data: Session) { + onSessionCreated(data) + } - onSessionCreated(session) + override fun onFailure(failure: Throwable) = setState { + copy(asyncLoginAction = Fail(failure)) + } + }) } } @@ -149,7 +154,7 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi // Do not retry if we already have flows for this config -> causes infinite focus loop if (newConfig?.homeServerUri?.toString() == homeServerConnectionConfig?.homeServerUri?.toString() - && state.asyncHomeServerLoginFlowRequest is Success) return@withState + && state.asyncHomeServerLoginFlowRequest is Success) return@withState currentTask?.cancel() homeServerConnectionConfig = newConfig diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiChooserFragment.kt b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiChooserFragment.kt index a32fa27bd5..5e705a70c2 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiChooserFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiChooserFragment.kt @@ -16,6 +16,7 @@ package im.vector.riotx.features.reactions import android.os.Bundle +import android.view.View import androidx.recyclerview.widget.RecyclerView import im.vector.riotx.R import im.vector.riotx.core.platform.VectorBaseFragment @@ -27,8 +28,8 @@ class EmojiChooserFragment @Inject constructor() : VectorBaseFragment() { private lateinit var viewModel: EmojiChooserViewModel - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) viewModel = activityViewModelProvider.get(EmojiChooserViewModel::class.java) viewModel.initWithContext(context!!) (view as? RecyclerView)?.let { diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultFragment.kt b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultFragment.kt index ca4f18fe83..029f468b70 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultFragment.kt @@ -16,6 +16,7 @@ package im.vector.riotx.features.reactions import android.os.Bundle +import android.view.View import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -37,9 +38,8 @@ class EmojiSearchResultFragment @Inject constructor( var sharedViewModel: EmojiChooserViewModel? = null - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) sharedViewModel = activityViewModelProvider.get(EmojiChooserViewModel::class.java) epoxyController.listener = object : ReactionClickListener { diff --git a/vector/src/main/java/im/vector/riotx/features/roomdirectory/PublicRoomsFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomdirectory/PublicRoomsFragment.kt index 21eedb4b24..b41c563256 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomdirectory/PublicRoomsFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomdirectory/PublicRoomsFragment.kt @@ -67,7 +67,7 @@ class PublicRoomsFragment @Inject constructor( .subscribeBy { viewModel.handle(RoomDirectoryAction.FilterWith(it.toString())) } - .disposeOnDestroy() + .disposeOnDestroyView() publicRoomsCreateNewRoom.setOnClickListener { sharedActionViewModel.post(RoomDirectorySharedAction.CreateRoom) diff --git a/vector/src/main/java/im/vector/riotx/features/roomdirectory/createroom/CreateRoomFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomdirectory/createroom/CreateRoomFragment.kt index 0399ea0b57..0dec14f50e 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomdirectory/createroom/CreateRoomFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomdirectory/createroom/CreateRoomFragment.kt @@ -18,6 +18,7 @@ package im.vector.riotx.features.roomdirectory.createroom import android.os.Bundle import android.view.MenuItem +import android.view.View import androidx.recyclerview.widget.LinearLayoutManager import com.airbnb.mvrx.Success import com.airbnb.mvrx.activityViewModel @@ -39,8 +40,8 @@ class CreateRoomFragment @Inject constructor(private val createRoomController: C override fun getMenuRes() = R.menu.vector_room_creation - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) vectorBaseActivity.setSupportActionBar(createRoomToolbar) sharedActionViewModel = activityViewModelProvider.get(RoomDirectorySharedActionViewModel::class.java) setupRecyclerView() diff --git a/vector/src/main/java/im/vector/riotx/features/roomdirectory/picker/RoomDirectoryPickerFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomdirectory/picker/RoomDirectoryPickerFragment.kt index bad8486405..ce7a57deba 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomdirectory/picker/RoomDirectoryPickerFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomdirectory/picker/RoomDirectoryPickerFragment.kt @@ -55,6 +55,9 @@ class RoomDirectoryPickerFragment @Inject constructor(val roomDirectoryPickerVie it.setDisplayShowHomeEnabled(true) it.setDisplayHomeAsUpEnabled(true) } + + sharedActionViewModel = activityViewModelProvider.get(RoomDirectorySharedActionViewModel::class.java) + setupRecyclerView() } override fun getMenuRes() = R.menu.menu_directory_server_picker @@ -69,12 +72,6 @@ class RoomDirectoryPickerFragment @Inject constructor(val roomDirectoryPickerVie return super.onOptionsItemSelected(item) } - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - sharedActionViewModel = activityViewModelProvider.get(RoomDirectorySharedActionViewModel::class.java) - setupRecyclerView() - } - private fun setupRecyclerView() { val layoutManager = LinearLayoutManager(context) diff --git a/vector/src/main/java/im/vector/riotx/features/roomdirectory/roompreview/RoomPreviewNoPreviewFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomdirectory/roompreview/RoomPreviewNoPreviewFragment.kt index 38e74c035a..9003421dc7 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomdirectory/roompreview/RoomPreviewNoPreviewFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomdirectory/roompreview/RoomPreviewNoPreviewFragment.kt @@ -45,16 +45,11 @@ class RoomPreviewNoPreviewFragment @Inject constructor( private val roomPreviewViewModel: RoomPreviewViewModel by fragmentViewModel() private val roomPreviewData: RoomPreviewData by args() - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - setupToolbar(roomPreviewNoPreviewToolbar) - } - override fun getLayoutResId() = R.layout.fragment_room_preview_no_preview override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - + setupToolbar(roomPreviewNoPreviewToolbar) // Toolbar avatarRenderer.render(roomPreviewData.avatarUrl, roomPreviewData.roomId, roomPreviewData.roomName, roomPreviewNoPreviewToolbarAvatar) roomPreviewNoPreviewToolbarTitle.text = roomPreviewData.roomName diff --git a/vector/src/main/java/im/vector/riotx/features/share/IncomingShareActivity.kt b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareActivity.kt index 1ec020e1a0..33b0744d4f 100644 --- a/vector/src/main/java/im/vector/riotx/features/share/IncomingShareActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareActivity.kt @@ -31,6 +31,7 @@ import im.vector.riotx.core.extensions.replaceFragment import im.vector.riotx.core.platform.VectorBaseActivity import im.vector.riotx.features.attachments.AttachmentsHelper import im.vector.riotx.features.home.LoadingFragment +import im.vector.riotx.features.home.RoomListDisplayMode import im.vector.riotx.features.home.room.list.RoomListFragment import im.vector.riotx.features.home.room.list.RoomListParams import im.vector.riotx.features.login.LoginActivity @@ -97,7 +98,7 @@ class IncomingShareActivity : } override fun onContentAttachmentsReady(attachments: List) { - val roomListParams = RoomListParams(RoomListFragment.DisplayMode.SHARE, sharedData = SharedData.Attachments(attachments)) + val roomListParams = RoomListParams(RoomListDisplayMode.SHARE, sharedData = SharedData.Attachments(attachments)) replaceFragment(R.id.shareRoomListFragmentContainer, RoomListFragment::class.java, roomListParams) } @@ -116,7 +117,7 @@ class IncomingShareActivity : return if (sharedText.isNullOrEmpty()) { false } else { - val roomListParams = RoomListParams(RoomListFragment.DisplayMode.SHARE, sharedData = SharedData.Text(sharedText)) + val roomListParams = RoomListParams(RoomListDisplayMode.SHARE, sharedData = SharedData.Text(sharedText)) replaceFragment(R.id.shareRoomListFragmentContainer, RoomListFragment::class.java, roomListParams) true } diff --git a/vector/src/main/java/im/vector/riotx/features/ui/SharedPreferencesUiStateRepository.kt b/vector/src/main/java/im/vector/riotx/features/ui/SharedPreferencesUiStateRepository.kt index 85051a0137..f850d12422 100644 --- a/vector/src/main/java/im/vector/riotx/features/ui/SharedPreferencesUiStateRepository.kt +++ b/vector/src/main/java/im/vector/riotx/features/ui/SharedPreferencesUiStateRepository.kt @@ -18,7 +18,7 @@ package im.vector.riotx.features.ui import android.content.SharedPreferences import androidx.core.content.edit -import im.vector.riotx.features.home.room.list.RoomListFragment +import im.vector.riotx.features.home.RoomListDisplayMode import javax.inject.Inject /** @@ -26,21 +26,21 @@ import javax.inject.Inject */ class SharedPreferencesUiStateRepository @Inject constructor(private val sharedPreferences: SharedPreferences) : UiStateRepository { - override fun getDisplayMode(): RoomListFragment.DisplayMode { + override fun getDisplayMode(): RoomListDisplayMode { return when (sharedPreferences.getInt(KEY_DISPLAY_MODE, VALUE_DISPLAY_MODE_CATCHUP)) { - VALUE_DISPLAY_MODE_PEOPLE -> RoomListFragment.DisplayMode.PEOPLE - VALUE_DISPLAY_MODE_ROOMS -> RoomListFragment.DisplayMode.ROOMS - else -> RoomListFragment.DisplayMode.HOME + VALUE_DISPLAY_MODE_PEOPLE -> RoomListDisplayMode.PEOPLE + VALUE_DISPLAY_MODE_ROOMS -> RoomListDisplayMode.ROOMS + else -> RoomListDisplayMode.HOME } } - override fun storeDisplayMode(displayMode: RoomListFragment.DisplayMode) { + override fun storeDisplayMode(displayMode: RoomListDisplayMode) { sharedPreferences.edit { putInt(KEY_DISPLAY_MODE, when (displayMode) { - RoomListFragment.DisplayMode.PEOPLE -> VALUE_DISPLAY_MODE_PEOPLE - RoomListFragment.DisplayMode.ROOMS -> VALUE_DISPLAY_MODE_ROOMS - else -> VALUE_DISPLAY_MODE_CATCHUP + RoomListDisplayMode.PEOPLE -> VALUE_DISPLAY_MODE_PEOPLE + RoomListDisplayMode.ROOMS -> VALUE_DISPLAY_MODE_ROOMS + else -> VALUE_DISPLAY_MODE_CATCHUP }) } } diff --git a/vector/src/main/java/im/vector/riotx/features/ui/UiStateRepository.kt b/vector/src/main/java/im/vector/riotx/features/ui/UiStateRepository.kt index 1c59a22892..feac6a64ed 100644 --- a/vector/src/main/java/im/vector/riotx/features/ui/UiStateRepository.kt +++ b/vector/src/main/java/im/vector/riotx/features/ui/UiStateRepository.kt @@ -16,14 +16,14 @@ package im.vector.riotx.features.ui -import im.vector.riotx.features.home.room.list.RoomListFragment +import im.vector.riotx.features.home.RoomListDisplayMode /** * This interface is used to persist UI state across application restart */ interface UiStateRepository { - fun getDisplayMode(): RoomListFragment.DisplayMode + fun getDisplayMode(): RoomListDisplayMode - fun storeDisplayMode(displayMode: RoomListFragment.DisplayMode) + fun storeDisplayMode(displayMode: RoomListDisplayMode) } diff --git a/vector/src/main/res/layout/constraint_set_composer_layout_expanded.xml b/vector/src/main/res/layout/constraint_set_composer_layout_expanded.xml index 3ce38c425d..4c9225dba7 100644 --- a/vector/src/main/res/layout/constraint_set_composer_layout_expanded.xml +++ b/vector/src/main/res/layout/constraint_set_composer_layout_expanded.xml @@ -90,10 +90,8 @@ - @@ -30,7 +30,7 @@ android:layout_marginEnd="16dp" android:layout_marginRight="16dp" android:layout_marginBottom="16dp" - android:accessibilityTraversalBefore="@+id/roomListEpoxyRecyclerView" + android:accessibilityTraversalBefore="@+id/roomListView" android:contentDescription="@string/a11y_create_direct_message" android:scaleType="center" android:src="@drawable/ic_fab_add_chat" @@ -47,7 +47,7 @@ android:layout_marginEnd="16dp" android:layout_marginRight="16dp" android:layout_marginBottom="16dp" - android:accessibilityTraversalBefore="@+id/roomListEpoxyRecyclerView" + android:accessibilityTraversalBefore="@+id/roomListView" android:contentDescription="@string/a11y_create_room" android:src="@drawable/ic_fab_add_room" android:visibility="gone" diff --git a/vector/src/main/res/layout/motion_fab_menu_merge.xml b/vector/src/main/res/layout/motion_fab_menu_merge.xml index 02ba4341c6..104db206cd 100644 --- a/vector/src/main/res/layout/motion_fab_menu_merge.xml +++ b/vector/src/main/res/layout/motion_fab_menu_merge.xml @@ -24,7 +24,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" - android:accessibilityTraversalBefore="@+id/roomListEpoxyRecyclerView" + android:accessibilityTraversalBefore="@+id/roomListView" android:contentDescription="@string/a11y_create_room" android:src="@drawable/ic_fab_add_room" app:backgroundTint="#FFFFFF"