diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/Matrix.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/Matrix.kt index a737d792..015b95e2 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/Matrix.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/Matrix.kt @@ -53,6 +53,7 @@ class Matrix private constructor(context: Context) : MatrixKoinComponent { authenticator.getLastActiveSession()?.also { currentSession = it it.open() + it.startSync() } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt index 68fc07a8..bdf1d670 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt @@ -24,6 +24,7 @@ import im.vector.matrix.android.api.session.crypto.CryptoService import im.vector.matrix.android.api.session.group.GroupService import im.vector.matrix.android.api.session.room.RoomService import im.vector.matrix.android.api.session.signout.SignOutService +import im.vector.matrix.android.api.session.sync.FilterService import im.vector.matrix.android.api.session.user.UserService /** @@ -35,7 +36,8 @@ interface Session : GroupService, UserService, CryptoService, - SignOutService { + SignOutService, + FilterService { /** * The params associated to the session @@ -48,6 +50,18 @@ interface Session : @MainThread fun open() + /** + * This method start the sync thread. + */ + @MainThread + fun startSync() + + /** + * This method stop the sync thread. + */ + @MainThread + fun stopSync() + /** * This method allow to close a session. It does stop some services. */ diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/sync/FilterService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/sync/FilterService.kt new file mode 100644 index 00000000..4a29013d --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/sync/FilterService.kt @@ -0,0 +1,33 @@ +/* + * 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.matrix.android.api.session.sync + +interface FilterService { + + enum class FilterPreset { + NoFilter, + /** + * Filter for Riot, will include only known event type + */ + RiotFilter + } + + /** + * Configure the filter for the sync + */ + fun setFilter(filterPreset: FilterPreset) +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/AuthModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/AuthModule.kt index 8e7c6398..5a49c512 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/AuthModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/AuthModule.kt @@ -18,6 +18,7 @@ package im.vector.matrix.android.internal.auth import android.content.Context import im.vector.matrix.android.api.auth.Authenticator +import im.vector.matrix.android.internal.auth.db.AuthRealmModule import im.vector.matrix.android.internal.auth.db.RealmSessionParamsStore import im.vector.matrix.android.internal.auth.db.SessionParamsMapper import io.realm.RealmConfiguration @@ -43,6 +44,7 @@ class AuthModule { val mapper = SessionParamsMapper((get())) val realmConfiguration = RealmConfiguration.Builder() .name("matrix-sdk-auth.realm") + .modules(AuthRealmModule()) .deleteRealmIfMigrationNeeded() .build() RealmSessionParamsStore(mapper, realmConfiguration) as SessionParamsStore diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/AuthRealmModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/AuthRealmModule.kt new file mode 100644 index 00000000..dcc03935 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/AuthRealmModule.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.matrix.android.internal.auth.db + +import io.realm.annotations.RealmModule + +/** + * Realm module for authentication classes + */ +@RealmModule(library = true, + classes = [ + SessionParamsEntity::class + ]) +internal class AuthRealmModule diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/FilterEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/FilterEntity.kt new file mode 100644 index 00000000..13209cb9 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/FilterEntity.kt @@ -0,0 +1,36 @@ +/* + * 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.matrix.android.internal.database.model + +import io.realm.RealmObject + +/** + * Contain a map between Json filter string and filterId (from Homeserver) + * Currently there is only one object in this table + */ +internal open class FilterEntity( + // The serialized FilterBody + var filterBodyJson: String = "", + // The serialized room event filter for pagination + var roomEventFilterJson: String = "", + // the id server side of the filterBodyJson, can be used instead of filterBodyJson if not blank + var filterId: String = "" + +) : RealmObject() { + + companion object +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/SessionRealmModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/SessionRealmModule.kt new file mode 100644 index 00000000..a5042634 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/SessionRealmModule.kt @@ -0,0 +1,38 @@ +/* + * 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.matrix.android.internal.database.model + +import io.realm.annotations.RealmModule + +/** + * Realm module for Session + */ +@RealmModule(library = true, + classes = [ + ChunkEntity::class, + EventEntity::class, + FilterEntity::class, + GroupEntity::class, + GroupSummaryEntity::class, + ReadReceiptEntity::class, + RoomEntity::class, + RoomSummaryEntity::class, + RoomTagEntity::class, + SyncEntity::class, + UserEntity::class + ]) +internal class SessionRealmModule 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 new file mode 100644 index 00000000..d69fb34a --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/FilterEntityQueries.kt @@ -0,0 +1,44 @@ +/* + * 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.matrix.android.internal.database.query + +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 { + var filter = realm.where().findFirst() + + if (filter == null) { + realm.executeTransaction { + realm.createObject().apply { + filterBodyJson = FilterFactory.createDefaultFilterBody().toJSONString() + roomEventFilterJson = FilterFactory.createDefaultRoomFilter().toJSONString() + filterId = "" + } + } + + filter = realm.where().findFirst()!! + } + + return filter +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt index 1c4e12f8..8ac7811c 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt @@ -33,6 +33,7 @@ import im.vector.matrix.android.api.session.room.RoomService import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams import im.vector.matrix.android.api.session.signout.SignOutService +import im.vector.matrix.android.api.session.sync.FilterService import im.vector.matrix.android.api.session.user.UserService import im.vector.matrix.android.api.session.user.model.User import im.vector.matrix.android.internal.database.LiveEntityObserver @@ -63,6 +64,7 @@ internal class DefaultSession(override val sessionParams: SessionParams) : Sessi private val roomService by inject() private val groupService by inject() private val userService by inject() + private val filterService by inject() private val signOutService by inject() private val syncThread by inject() private val contentUrlResolver by inject() @@ -87,15 +89,24 @@ internal class DefaultSession(override val sessionParams: SessionParams) : Sessi monarchy.openManually() } liveEntityUpdaters.forEach { it.start() } + } + + @MainThread + override fun startSync() { + assert(isOpen) syncThread.start() } + @MainThread + override fun stopSync() { + assert(isOpen) + syncThread.kill() + } @MainThread override fun close() { assertMainThread() assert(isOpen) - syncThread.kill() liveEntityUpdaters.forEach { it.dispose() } if (monarchy.isMonarchyThreadOpen) { monarchy.closeManually() @@ -110,6 +121,7 @@ internal class DefaultSession(override val sessionParams: SessionParams) : Sessi return signOutService.signOut(object : MatrixCallback { override fun onSuccess(data: Unit) { // Close the session + stopSync() close() callback.onSuccess(data) @@ -167,6 +179,11 @@ internal class DefaultSession(override val sessionParams: SessionParams) : Sessi return groupService.liveGroupSummaries() } + override fun setFilter(filterPreset: FilterService.FilterPreset) { + assert(isOpen) + return filterService.setFilter(filterPreset) + } + // USER SERVICE override fun getUser(userId: String): User? { 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 76a55a4b..43e0b8a7 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 @@ -22,8 +22,11 @@ import im.vector.matrix.android.api.auth.data.SessionParams import im.vector.matrix.android.api.session.group.GroupService import im.vector.matrix.android.api.session.room.RoomService import im.vector.matrix.android.api.session.signout.SignOutService +import im.vector.matrix.android.api.session.sync.FilterService import im.vector.matrix.android.api.session.user.UserService import im.vector.matrix.android.internal.database.LiveEntityObserver +import im.vector.matrix.android.internal.database.model.SessionRealmModule +import im.vector.matrix.android.internal.session.filter.* import im.vector.matrix.android.internal.session.group.DefaultGroupService import im.vector.matrix.android.internal.session.group.GroupSummaryUpdater import im.vector.matrix.android.internal.session.room.DefaultRoomService @@ -61,6 +64,7 @@ internal class SessionModule(private val sessionParams: SessionParams) { RealmConfiguration.Builder() .directory(directory) .name("disk_store.realm") + .modules(SessionRealmModule()) .deleteRealmIfMigrationNeeded() .build() } @@ -87,7 +91,7 @@ internal class SessionModule(private val sessionParams: SessionParams) { } scope(DefaultSession.SCOPE) { - RoomAvatarResolver(get(), sessionParams.credentials) + RoomAvatarResolver(get(), get()) } scope(DefaultSession.SCOPE) { @@ -114,6 +118,23 @@ internal class SessionModule(private val sessionParams: SessionParams) { SessionListeners() } + scope(DefaultSession.SCOPE) { + DefaultFilterRepository(get()) as FilterRepository + } + + scope(DefaultSession.SCOPE) { + DefaultSaveFilterTask(get(), get(), get()) as SaveFilterTask + } + + scope(DefaultSession.SCOPE) { + DefaultFilterService(get(), get(), get()) as FilterService + } + + scope(DefaultSession.SCOPE) { + val retrofit: Retrofit = get() + retrofit.create(FilterApi::class.java) + } + scope(DefaultSession.SCOPE) { val groupSummaryUpdater = GroupSummaryUpdater(get()) val eventsPruner = EventsPruner(get()) 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 new file mode 100644 index 00000000..32d9541a --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/DefaultFilterRepository.kt @@ -0,0 +1,107 @@ +/* + * 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.matrix.android.internal.session.filter + +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 io.realm.Realm +import io.realm.RealmConfiguration +import io.realm.kotlin.where + +internal class DefaultFilterRepository(val realmConfiguration: RealmConfiguration) : FilterRepository { + + override fun storeFilter(filterBody: FilterBody, roomEventFilter: RoomEventFilter): Boolean { + val result: Boolean + + val realm = Realm.getInstance(realmConfiguration) + + 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 = "" + } + result = true + } else { + result = filter.filterId.isBlank() + } + + realm.close() + + return result + } + + override fun storeFilterId(filterBody: FilterBody, filterId: String) { + val realm = Realm.getInstance(realmConfiguration) + + realm.executeTransaction { + // We manage only one filter for now + val filterBodyJson = filterBody.toJSONString() + + // Update the filter id, only if the filter body matches + it.where() + .equalTo(FilterEntityFields.FILTER_BODY_JSON, filterBodyJson) + ?.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 + } + + 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 + } +} \ No newline at end of file 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 new file mode 100644 index 00000000..94c3f1cf --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/DefaultFilterService.kt @@ -0,0 +1,55 @@ +/* + * 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.matrix.android.internal.session.filter + +import im.vector.matrix.android.api.session.sync.FilterService +import im.vector.matrix.android.internal.task.TaskExecutor +import im.vector.matrix.android.internal.task.configureWith + +internal class DefaultFilterService(private val filterRepository: FilterRepository, + 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) + } + } +} \ No newline at end of file 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 new file mode 100644 index 00000000..c5ef1a35 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/DefaultSaveFilterTask.kt @@ -0,0 +1,52 @@ +/* + * 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.matrix.android.internal.session.filter + +import arrow.core.Try +import im.vector.matrix.android.api.auth.data.SessionParams +import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.task.Task + + +/** + * Save a filter to the server + */ +internal interface SaveFilterTask : Task { + + data class Params( + val filter: FilterBody + ) + +} + +internal class DefaultSaveFilterTask(private val sessionParams: SessionParams, + private val filterAPI: FilterApi, + private val filterRepository: FilterRepository +) : SaveFilterTask { + + override fun execute(params: SaveFilterTask.Params): Try { + return executeRequest { + // TODO auto retry + apiCall = filterAPI.uploadFilter(sessionParams.credentials.userId, params.filter) + }.flatMap { filterResponse -> + Try { + filterRepository.storeFilterId(params.filter, filterResponse.filterId) + } + } + } + +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/FilterApi.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/FilterApi.kt new file mode 100644 index 00000000..a10f654f --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/FilterApi.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2018 Matthias Kesler + * Copyright 2018 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.matrix.android.internal.session.filter + +import im.vector.matrix.android.internal.network.NetworkConstants +import retrofit2.Call +import retrofit2.http.Body +import retrofit2.http.GET +import retrofit2.http.POST +import retrofit2.http.Path + +internal interface FilterApi { + + /** + * Upload FilterBody to get a filter_id which can be used for /sync requests + * + * @param userId the user id + * @param body the Json representation of a FilterBody object + */ + @POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "user/{userId}/filter") + fun uploadFilter(@Path("userId") userId: String, @Body body: FilterBody): Call + + /** + * Gets a filter with a given filterId from the homeserver + * + * @param userId the user id + * @param filterId the filterID + * @return Filter + */ + @GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "user/{userId}/filter/{filterId}") + fun getFilterById(@Path("userId") userId: String, @Path("filterId") filterId: String): Call +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/FilterFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/FilterFactory.kt new file mode 100644 index 00000000..7c03ebbd --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/FilterFactory.kt @@ -0,0 +1,81 @@ +/* + * 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.matrix.android.internal.session.filter + +import im.vector.matrix.android.api.session.events.model.EventType + +internal object FilterFactory { + + fun createDefaultFilterBody(): FilterBody { + val filterBody = FilterBody() + FilterUtil.enableLazyLoading(filterBody, true) + + return filterBody + } + + fun createRiotFilterBody(): FilterBody { + val filterBody = FilterBody() + + filterBody.room = RoomFilter().apply { + timeline = createRiotTimelineFilter() + // TODO Enable this for optimization + // state = createRiotStateFilter() + } + + return filterBody + } + + fun createDefaultRoomFilter(): RoomEventFilter { + return RoomEventFilter().apply { + lazyLoadMembers = true + } + } + + fun createRiotRoomFilter(): RoomEventFilter { + return RoomEventFilter().apply { + lazyLoadMembers = true + // TODO Enable this for optimization + // types = (listOfSupportedEventTypes + listOfSupportedStateEventTypes).toMutableList() + } + } + + private fun createRiotTimelineFilter(): RoomEventFilter { + return RoomEventFilter().apply { + lazyLoadMembers = true + // TODO Enable this for optimization + // types = listOfSupportedEventTypes.toMutableList() + } + } + + private fun createRiotStateFilter(): RoomEventFilter { + return RoomEventFilter().apply { + types = listOfSupportedStateEventTypes.toMutableList() + } + } + + // Get only managed types by Riot + private val listOfSupportedEventTypes = listOf( + // TODO Complete the list + EventType.MESSAGE + ) + + // Get only managed types by Riot + private val listOfSupportedStateEventTypes = listOf( + // TODO Complete the list + EventType.STATE_ROOM_MEMBER + ) +} \ No newline at end of file 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 new file mode 100644 index 00000000..f48153e9 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/FilterRepository.kt @@ -0,0 +1,40 @@ +/* + * 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.matrix.android.internal.session.filter + +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 + + /** + * Set the filterId of this filter + */ + fun storeFilterId(filterBody: FilterBody, filterId: String) + + /** + * Return filter json or filter id + */ + fun getFilter(): String + + /** + * Return the room filter + */ + fun getRoomFilter(): String +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/FilterUtil.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/FilterUtil.kt similarity index 83% rename from matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/FilterUtil.kt rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/FilterUtil.kt index 5447a1d3..526191cf 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/FilterUtil.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/FilterUtil.kt @@ -14,12 +14,7 @@ * limitations under the License. */ -package im.vector.matrix.android.internal.util - -import im.vector.matrix.android.internal.session.filter.Filter -import im.vector.matrix.android.internal.session.filter.FilterBody -import im.vector.matrix.android.internal.session.filter.RoomEventFilter -import im.vector.matrix.android.internal.session.filter.RoomFilter +package im.vector.matrix.android.internal.session.filter internal object FilterUtil { @@ -121,21 +116,4 @@ internal object FilterUtil { } } } - - /** - * Create a RoomEventFilter - * - * @param withLazyLoading true when lazy loading is enabled - * @return a RoomEventFilter or null if lazy loading if OFF - */ - fun createRoomEventFilter(withLazyLoading: Boolean): RoomEventFilter? { - var roomEventFilter: RoomEventFilter? = null - - if (withLazyLoading) { - roomEventFilter = RoomEventFilter() - roomEventFilter.lazyLoadMembers = true - } - - return roomEventFilter - } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt index 1732fe69..973b7381 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt @@ -55,11 +55,11 @@ class RoomModule { } scope(DefaultSession.SCOPE) { - DefaultPaginationTask(get(), get()) as PaginationTask + DefaultPaginationTask(get(), get(), get()) as PaginationTask } scope(DefaultSession.SCOPE) { - DefaultGetContextOfEventTask(get(), get()) as GetContextOfEventTask + DefaultGetContextOfEventTask(get(), get(), get()) as GetContextOfEventTask } scope(DefaultSession.SCOPE) { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultGetContextOfEventTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultGetContextOfEventTask.kt index efe7509a..4f93dc36 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultGetContextOfEventTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultGetContextOfEventTask.kt @@ -18,9 +18,9 @@ package im.vector.matrix.android.internal.session.room.timeline import arrow.core.Try import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.session.filter.FilterRepository import im.vector.matrix.android.internal.session.room.RoomAPI import im.vector.matrix.android.internal.task.Task -import im.vector.matrix.android.internal.util.FilterUtil internal interface GetContextOfEventTask : Task { @@ -32,11 +32,12 @@ internal interface GetContextOfEventTask : Task { - val filter = FilterUtil.createRoomEventFilter(true)?.toJSONString() + val filter = filterRepository.getRoomFilter() return executeRequest { apiCall = roomAPI.getContextOfEvent(params.roomId, params.eventId, 0, filter) }.flatMap { response -> diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultPaginationTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultPaginationTask.kt index 9bebcf7d..12c1e361 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultPaginationTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultPaginationTask.kt @@ -18,9 +18,9 @@ package im.vector.matrix.android.internal.session.room.timeline import arrow.core.Try import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.session.filter.FilterRepository import im.vector.matrix.android.internal.session.room.RoomAPI import im.vector.matrix.android.internal.task.Task -import im.vector.matrix.android.internal.util.FilterUtil internal interface PaginationTask : Task { @@ -35,11 +35,12 @@ internal interface PaginationTask : Task { - val filter = FilterUtil.createRoomEventFilter(true)?.toJSONString() + val filter = filterRepository.getRoomFilter() return executeRequest { apiCall = roomAPI.getRoomMessagesFrom(params.roomId, params.from, params.direction.value, params.limit, filter) }.flatMap { chunk -> diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncModule.kt index 7995fff1..83272c1b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncModule.kt @@ -56,7 +56,7 @@ internal class SyncModule { } scope(DefaultSession.SCOPE) { - DefaultSyncTask(get(), get()) as SyncTask + DefaultSyncTask(get(), get(), get()) as SyncTask } scope(DefaultSession.SCOPE) { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncTask.kt index 3dec31ee..6c5a35cc 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncTask.kt @@ -17,11 +17,10 @@ package im.vector.matrix.android.internal.session.sync import arrow.core.Try -import im.vector.matrix.android.internal.task.Task import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.filter.FilterBody +import im.vector.matrix.android.internal.session.filter.FilterRepository import im.vector.matrix.android.internal.session.sync.model.SyncResponse -import im.vector.matrix.android.internal.util.FilterUtil +import im.vector.matrix.android.internal.task.Task internal interface SyncTask : Task { @@ -30,21 +29,20 @@ internal interface SyncTask : Task { } internal class DefaultSyncTask(private val syncAPI: SyncAPI, + private val filterRepository: FilterRepository, private val syncResponseHandler: SyncResponseHandler ) : SyncTask { override fun execute(params: SyncTask.Params): Try { val requestParams = HashMap() - val filterBody = FilterBody() - FilterUtil.enableLazyLoading(filterBody, true) var timeout = 0 if (params.token != null) { requestParams["since"] = params.token timeout = 30000 } requestParams["timeout"] = timeout.toString() - requestParams["filter"] = filterBody.toJSONString() + requestParams["filter"] = filterRepository.getFilter() return executeRequest { apiCall = syncAPI.sync(requestParams) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncThread.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncThread.kt index fb5868a3..d70b1b21 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncThread.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncThread.kt @@ -16,6 +16,7 @@ package im.vector.matrix.android.internal.session.sync.job +import com.squareup.moshi.JsonEncodingException import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.failure.Failure import im.vector.matrix.android.api.util.Cancelable @@ -118,7 +119,8 @@ internal class SyncThread(private val syncTask: SyncTask, Timber.e(failure) } - if (failure !is Failure.NetworkConnection) { + if (failure !is Failure.NetworkConnection + || failure.cause is JsonEncodingException) { // Wait 10s before retrying sleep(RETRY_WAIT_TIME_MS) } diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/HomeActivityViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/home/HomeActivityViewModel.kt index c40a488f..610f4119 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/HomeActivityViewModel.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/HomeActivityViewModel.kt @@ -25,6 +25,7 @@ import im.vector.matrix.android.api.Matrix import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams +import im.vector.matrix.android.api.session.sync.FilterService import im.vector.matrix.rx.rx import im.vector.riotredesign.core.platform.VectorViewModel import im.vector.riotredesign.core.utils.LiveEvent @@ -58,6 +59,9 @@ class HomeActivityViewModel(state: EmptyState, get() = _openRoomLiveData init { + // TODO Move this else where, it's too late when we are here to change the filter + session.setFilter(FilterService.FilterPreset.RiotFilter) + val lastSelectedRoomId = roomSelectionRepository.lastSelectedRoom() if (lastSelectedRoomId == null || session.getRoom(lastSelectedRoomId) == null) { getTheFirstRoomWhenAvailable() diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailFragment.kt index 59cf99ab..b4f9a4ed 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailFragment.kt @@ -30,9 +30,6 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.airbnb.epoxy.EpoxyVisibilityTracker import com.airbnb.mvrx.fragmentViewModel -import com.jaiselrahman.filepicker.activity.FilePickerActivity -import com.jaiselrahman.filepicker.config.Configurations -import com.jaiselrahman.filepicker.model.MediaFile import com.otaliastudios.autocomplete.Autocomplete import com.otaliastudios.autocomplete.AutocompleteCallback import com.otaliastudios.autocomplete.CharPolicy @@ -46,12 +43,7 @@ import im.vector.riotredesign.core.extensions.observeEvent import im.vector.riotredesign.core.glide.GlideApp import im.vector.riotredesign.core.platform.ToolbarConfigurable import im.vector.riotredesign.core.platform.VectorBaseFragment -import im.vector.riotredesign.core.utils.PERMISSIONS_FOR_TAKING_PHOTO -import im.vector.riotredesign.core.utils.PERMISSION_REQUEST_CODE_LAUNCH_CAMERA -import im.vector.riotredesign.core.utils.PERMISSION_REQUEST_CODE_LAUNCH_NATIVE_CAMERA -import im.vector.riotredesign.core.utils.PERMISSION_REQUEST_CODE_LAUNCH_NATIVE_VIDEO_CAMERA -import im.vector.riotredesign.core.utils.checkPermissions -import im.vector.riotredesign.core.utils.openCamera +import im.vector.riotredesign.core.utils.* import im.vector.riotredesign.features.autocomplete.command.AutocompleteCommandPresenter import im.vector.riotredesign.features.autocomplete.command.CommandAutocompletePolicy import im.vector.riotredesign.features.autocomplete.user.AutocompleteUserPresenter @@ -288,24 +280,27 @@ class RoomDetailFragment : VectorBaseFragment(), TimelineEventController.Callbac private fun onSendChoiceClicked(dialogListItem: DialogListItem) { Timber.v("On send choice clicked: $dialogListItem") when (dialogListItem) { - is DialogListItem.SendFile -> { + is DialogListItem.SendFile -> { // launchFileIntent } - is DialogListItem.SendVoice -> { + is DialogListItem.SendVoice -> { //launchAudioRecorderIntent() } - is DialogListItem.SendSticker -> { + is DialogListItem.SendSticker -> { //startStickerPickerActivity() } - is DialogListItem.TakePhotoVideo -> if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, requireActivity(), PERMISSION_REQUEST_CODE_LAUNCH_CAMERA)) { - // launchCamera() - } - is DialogListItem.TakePhoto -> if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, requireActivity(), PERMISSION_REQUEST_CODE_LAUNCH_NATIVE_CAMERA)) { - openCamera(requireActivity(), CAMERA_VALUE_TITLE, TAKE_IMAGE_REQUEST_CODE) - } - is DialogListItem.TakeVideo -> if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, requireActivity(), PERMISSION_REQUEST_CODE_LAUNCH_NATIVE_VIDEO_CAMERA)) { - // launchNativeVideoRecorder() - } + is DialogListItem.TakePhotoVideo -> + if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, requireActivity(), PERMISSION_REQUEST_CODE_LAUNCH_CAMERA)) { + // launchCamera() + } + is DialogListItem.TakePhoto -> + if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, requireActivity(), PERMISSION_REQUEST_CODE_LAUNCH_NATIVE_CAMERA)) { + openCamera(requireActivity(), CAMERA_VALUE_TITLE, TAKE_IMAGE_REQUEST_CODE) + } + is DialogListItem.TakeVideo -> + if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, requireActivity(), PERMISSION_REQUEST_CODE_LAUNCH_NATIVE_VIDEO_CAMERA)) { + // launchNativeVideoRecorder() + } } } @@ -339,20 +334,20 @@ class RoomDetailFragment : VectorBaseFragment(), TimelineEventController.Callbac private fun renderSendMessageResult(sendMessageResult: SendMessageResult) { when (sendMessageResult) { is SendMessageResult.MessageSent, - is SendMessageResult.SlashCommandHandled -> { + is SendMessageResult.SlashCommandHandled -> { // Clear composer composerEditText.text = null } - is SendMessageResult.SlashCommandError -> { + is SendMessageResult.SlashCommandError -> { displayCommandError(getString(R.string.command_problem_with_parameters, sendMessageResult.command.command)) } - is SendMessageResult.SlashCommandUnknown -> { + is SendMessageResult.SlashCommandUnknown -> { displayCommandError(getString(R.string.unrecognized_command, sendMessageResult.command)) } - is SendMessageResult.SlashCommandResultOk -> { + is SendMessageResult.SlashCommandResultOk -> { // Ignore } - is SendMessageResult.SlashCommandResultError -> { + is SendMessageResult.SlashCommandResultError -> { displayCommandError(sendMessageResult.throwable.localizedMessage) } is SendMessageResult.SlashCommandNotImplemented -> { diff --git a/vector/src/main/java/im/vector/riotredesign/features/login/LoginActivity.kt b/vector/src/main/java/im/vector/riotredesign/features/login/LoginActivity.kt index 0c79747a..dc6f3aeb 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/login/LoginActivity.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/login/LoginActivity.kt @@ -27,6 +27,7 @@ import im.vector.matrix.android.api.Matrix import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig import im.vector.matrix.android.api.session.Session +import im.vector.matrix.android.api.session.sync.FilterService import im.vector.riotredesign.R import im.vector.riotredesign.core.platform.VectorBaseActivity import im.vector.riotredesign.features.home.HomeActivity @@ -63,7 +64,11 @@ class LoginActivity : VectorBaseActivity() { progressBar.visibility = View.VISIBLE authenticator.authenticate(homeServerConnectionConfig, login, password, object : MatrixCallback { override fun onSuccess(data: Session) { - Matrix.getInstance().currentSession = data.apply { open() } + Matrix.getInstance().currentSession = data + data.open() + data.setFilter(FilterService.FilterPreset.RiotFilter) + data.startSync() + goToHome() }