Merge branch 'develop' into feature/perf_again

This commit is contained in:
ganfra 2019-11-08 17:09:22 +01:00
commit 1bd2c0d220
176 changed files with 3114 additions and 1353 deletions

View file

@ -2,7 +2,8 @@ Changes in RiotX 0.8.0 (2019-XX-XX)
===================================================
Features ✨:
-
- Handle long click on room in the room list (#395)
- Ignore/UnIgnore users, and display list of ignored users (#542, #617)
Improvements 🙌:
- Search reaction by name or keyword in emoji picker
@ -38,6 +39,7 @@ Improvements:
- Attachments: start using system pickers (#52)
- Mark all messages as read (#396)
Other changes:
- Accessibility improvements to read receipts in the room timeline and reactions emoji chooser

View file

@ -20,6 +20,7 @@ import im.vector.matrix.android.api.session.room.Room
import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary
import im.vector.matrix.android.api.session.room.model.ReadReceipt
import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.android.api.session.room.notification.RoomNotificationState
import im.vector.matrix.android.api.session.room.send.UserDraft
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.matrix.android.api.util.Optional
@ -67,6 +68,10 @@ class RxRoom(private val room: Room) {
fun liveDrafts(): Observable<List<UserDraft>> {
return room.getDraftsLive().asObservable()
}
fun liveNotificationState(): Observable<RoomNotificationState> {
return room.getLiveRoomNotificationState().asObservable()
}
}
fun Room.rx(): RxRoom {

View file

@ -54,6 +54,10 @@ class RxSession(private val session: Session) {
return session.liveUsers().asObservable()
}
fun liveIgnoredUsers(): Observable<List<User>> {
return session.liveIgnoredUsers().asObservable()
}
fun livePagedUsers(filter: String? = null): Observable<PagedList<User>> {
return session.livePagedUsers(filter).asObservable()
}

View file

@ -21,7 +21,7 @@ import timber.log.Timber
sealed class Action {
object Notify : Action()
object DoNotNotify : Action()
data class Sound(val sound: String) : Action()
data class Sound(val sound: String = ACTION_OBJECT_VALUE_VALUE_DEFAULT) : Action()
data class Highlight(val highlight: Boolean) : Action()
}
@ -63,6 +63,29 @@ private const val ACTION_OBJECT_VALUE_VALUE_DEFAULT = "default"
*
* </pre>
*/
@Suppress("IMPLICIT_CAST_TO_ANY")
fun List<Action>.toJson(): List<Any> {
return map { action ->
when (action) {
is Action.Notify -> ACTION_NOTIFY
is Action.DoNotNotify -> ACTION_DONT_NOTIFY
is Action.Sound -> {
mapOf(
ACTION_OBJECT_SET_TWEAK_KEY to ACTION_OBJECT_SET_TWEAK_VALUE_SOUND,
ACTION_OBJECT_VALUE_KEY to action.sound
)
}
is Action.Highlight -> {
mapOf(
ACTION_OBJECT_SET_TWEAK_KEY to ACTION_OBJECT_SET_TWEAK_VALUE_HIGHLIGHT,
ACTION_OBJECT_VALUE_KEY to action.highlight
)
}
}
}
}
fun PushRule.getActions(): List<Action> {
val result = ArrayList<Action>()

View file

@ -34,6 +34,10 @@ interface PushRuleService {
fun updatePushRuleEnableStatus(kind: RuleKind, pushRule: PushRule, enabled: Boolean, callback: MatrixCallback<Unit>): Cancelable
fun addPushRule(kind: RuleKind, pushRule: PushRule, callback: MatrixCallback<Unit>): Cancelable
fun removePushRule(kind: RuleKind, pushRule: PushRule, callback: MatrixCallback<Unit>): Cancelable
fun addPushRuleListener(listener: PushRuleListener)
fun removePushRuleListener(listener: PushRuleListener)

View file

@ -19,7 +19,7 @@ package im.vector.matrix.android.api.session.cache
import im.vector.matrix.android.api.MatrixCallback
/**
* This interface defines a method to sign out. It's implemented at the session level.
* This interface defines a method to clear the cache. It's implemented at the session level.
*/
interface CacheService {

View file

@ -21,6 +21,7 @@ import im.vector.matrix.android.api.session.room.crypto.RoomCryptoService
import im.vector.matrix.android.api.session.room.members.MembershipService
import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.android.api.session.room.model.relation.RelationService
import im.vector.matrix.android.api.session.room.notification.RoomPushRuleService
import im.vector.matrix.android.api.session.room.reporting.ReportingService
import im.vector.matrix.android.api.session.room.read.ReadService
import im.vector.matrix.android.api.session.room.send.DraftService
@ -41,7 +42,8 @@ interface Room :
StateService,
ReportingService,
RelationService,
RoomCryptoService {
RoomCryptoService,
RoomPushRuleService {
/**
* The roomId of this room

View file

@ -0,0 +1,42 @@
/*
* 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.room.notification
/**
* Defines the room notification state
*/
enum class RoomNotificationState {
/**
* All the messages will trigger a noisy notification
*/
ALL_MESSAGES_NOISY,
/**
* All the messages will trigger a notification
*/
ALL_MESSAGES,
/**
* Only the messages with user display name / user name will trigger notifications
*/
MENTIONS_ONLY,
/**
* No notifications
*/
MUTE
}

View file

@ -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.api.session.room.notification
import androidx.lifecycle.LiveData
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.util.Cancelable
interface RoomPushRuleService {
fun getLiveRoomNotificationState(): LiveData<RoomNotificationState>
fun setRoomNotificationState(roomNotificationState: RoomNotificationState, matrixCallback: MatrixCallback<Unit>): Cancelable
}

View file

@ -64,4 +64,19 @@ interface UserService {
* @return a Livedata of users
*/
fun livePagedUsers(filter: String? = null): LiveData<PagedList<User>>
/**
* Get list of ignored users
*/
fun liveIgnoredUsers(): LiveData<List<User>>
/**
* Ignore users
*/
fun ignoreUserIds(userIds: List<String>, callback: MatrixCallback<Unit>): Cancelable
/**
* Un-ignore some users
*/
fun unIgnoreUserIds(userIds: List<String>, callback: MatrixCallback<Unit>): Cancelable
}

View file

@ -21,4 +21,6 @@ import java.lang.reflect.ParameterizedType
typealias JsonDict = Map<String, @JvmSuppressWildcards Any>
val emptyJsonDict = emptyMap<String, Any>()
internal val JSON_DICT_PARAMETERIZED_TYPE: ParameterizedType = Types.newParameterizedType(Map::class.java, String::class.java, Any::class.java)

View file

@ -0,0 +1,24 @@
/*
* 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
internal open class IgnoredUserEntity(var userId: String = "") : RealmObject() {
companion object
}

View file

@ -17,6 +17,8 @@ package im.vector.matrix.android.internal.database.model
import io.realm.RealmList
import io.realm.RealmObject
import io.realm.RealmResults
import io.realm.annotations.LinkingObjects
internal open class PushRuleEntity(
// Required. The actions to perform when this rule is matched.
@ -33,5 +35,8 @@ internal open class PushRuleEntity(
var pattern: String? = null
) : RealmObject() {
@LinkingObjects("pushRules")
val parent: RealmResults<PushRulesEntity>? = null
companion object
}

View file

@ -35,6 +35,7 @@ import io.realm.annotations.RealmModule
RoomTagEntity::class,
SyncEntity::class,
UserEntity::class,
IgnoredUserEntity::class,
EventAnnotationsSummaryEntity::class,
ReactionAggregatedSummaryEntity::class,
EditAggregatedSummaryEntity::class,

View file

@ -16,10 +16,10 @@
package im.vector.matrix.android.internal.database.query
import im.vector.matrix.android.api.pushrules.RuleKind
import im.vector.matrix.android.internal.database.model.*
import im.vector.matrix.android.internal.database.model.PushRuleEntity
import im.vector.matrix.android.internal.database.model.PushRulesEntity
import im.vector.matrix.android.internal.database.model.PushRulesEntityFields
import im.vector.matrix.android.internal.database.model.PusherEntity
import im.vector.matrix.android.internal.database.model.PusherEntityFields
import io.realm.Realm
import io.realm.RealmQuery
import io.realm.kotlin.where
@ -41,3 +41,11 @@ internal fun PushRulesEntity.Companion.where(realm: Realm,
.equalTo(PushRulesEntityFields.SCOPE, scope)
.equalTo(PushRulesEntityFields.KIND_STR, kind.name)
}
internal fun PushRuleEntity.Companion.where(realm: Realm,
scope: String,
ruleId: String): RealmQuery<PushRuleEntity> {
return realm.where<PushRuleEntity>()
.equalTo("${PushRuleEntityFields.PARENT}.${PushRulesEntityFields.SCOPE}", scope)
.equalTo(PushRuleEntityFields.RULE_ID, ruleId)
}

View file

@ -20,10 +20,11 @@ import com.squareup.moshi.Moshi
import im.vector.matrix.android.api.session.room.model.message.*
import im.vector.matrix.android.internal.network.parsing.RuntimeJsonAdapterFactory
import im.vector.matrix.android.internal.network.parsing.UriMoshiAdapter
import im.vector.matrix.android.internal.session.sync.model.UserAccountData
import im.vector.matrix.android.internal.session.sync.model.UserAccountDataDirectMessages
import im.vector.matrix.android.internal.session.sync.model.UserAccountDataFallback
import im.vector.matrix.android.internal.session.sync.model.UserAccountDataPushRules
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountData
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataDirectMessages
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataFallback
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataIgnoredUsers
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataPushRules
object MoshiProvider {
@ -31,6 +32,7 @@ object MoshiProvider {
.add(UriMoshiAdapter())
.add(RuntimeJsonAdapterFactory.of(UserAccountData::class.java, "type", UserAccountDataFallback::class.java)
.registerSubtype(UserAccountDataDirectMessages::class.java, UserAccountData.TYPE_DIRECT_MESSAGES)
.registerSubtype(UserAccountDataIgnoredUsers::class.java, UserAccountData.TYPE_IGNORED_USER_LIST)
.registerSubtype(UserAccountDataPushRules::class.java, UserAccountData.TYPE_PUSH_RULES)
)
.add(RuntimeJsonAdapterFactory.of(MessageContent::class.java, "msgtype", MessageDefaultContent::class.java)

View file

@ -28,7 +28,9 @@ import im.vector.matrix.android.internal.database.mapper.PushRulesMapper
import im.vector.matrix.android.internal.database.model.PushRulesEntity
import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.session.SessionScope
import im.vector.matrix.android.internal.session.pushers.AddPushRuleTask
import im.vector.matrix.android.internal.session.pushers.GetPushRulesTask
import im.vector.matrix.android.internal.session.pushers.RemovePushRuleTask
import im.vector.matrix.android.internal.session.pushers.UpdatePushRuleEnableStatusTask
import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.task.configureWith
@ -38,6 +40,8 @@ import javax.inject.Inject
@SessionScope
internal class DefaultPushRuleService @Inject constructor(private val getPushRulesTask: GetPushRulesTask,
private val updatePushRuleEnableStatusTask: UpdatePushRuleEnableStatusTask,
private val addPushRuleTask: AddPushRuleTask,
private val removePushRuleTask: RemovePushRuleTask,
private val taskExecutor: TaskExecutor,
private val monarchy: Monarchy
) : PushRuleService {
@ -98,6 +102,22 @@ internal class DefaultPushRuleService @Inject constructor(private val getPushRul
.executeBy(taskExecutor)
}
override fun addPushRule(kind: RuleKind, pushRule: PushRule, callback: MatrixCallback<Unit>): Cancelable {
return addPushRuleTask
.configureWith(AddPushRuleTask.Params(kind, pushRule)) {
this.callback = callback
}
.executeBy(taskExecutor)
}
override fun removePushRule(kind: RuleKind, pushRule: PushRule, callback: MatrixCallback<Unit>): Cancelable {
return removePushRuleTask
.configureWith(RemovePushRuleTask.Params(kind, pushRule)) {
this.callback = callback
}
.executeBy(taskExecutor)
}
override fun removePushRuleListener(listener: PushRuleService.PushRuleListener) {
synchronized(listeners) {
listeners.remove(listener)

View file

@ -0,0 +1,39 @@
/*
* 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.pushers
import im.vector.matrix.android.api.pushrules.RuleKind
import im.vector.matrix.android.api.pushrules.rest.PushRule
import im.vector.matrix.android.internal.network.executeRequest
import im.vector.matrix.android.internal.task.Task
import javax.inject.Inject
internal interface AddPushRuleTask : Task<AddPushRuleTask.Params, Unit> {
data class Params(
val kind: RuleKind,
val pushRule: PushRule
)
}
internal class DefaultAddPushRuleTask @Inject constructor(private val pushRulesApi: PushRulesApi)
: AddPushRuleTask {
override suspend fun execute(params: AddPushRuleTask.Params) {
return executeRequest {
apiCall = pushRulesApi.addRule(params.kind.value, params.pushRule.ruleId, params.pushRule)
}
}
}

View file

@ -25,6 +25,8 @@ import im.vector.matrix.android.api.session.pushers.PushersService
import im.vector.matrix.android.internal.session.notification.DefaultProcessEventForPushTask
import im.vector.matrix.android.internal.session.notification.DefaultPushRuleService
import im.vector.matrix.android.internal.session.notification.ProcessEventForPushTask
import im.vector.matrix.android.internal.session.room.notification.DefaultSetRoomNotificationStateTask
import im.vector.matrix.android.internal.session.room.notification.SetRoomNotificationStateTask
import retrofit2.Retrofit
@Module
@ -67,6 +69,15 @@ internal abstract class PushersModule {
@Binds
abstract fun bindUpdatePushRuleEnableStatusTask(updatePushRuleEnableStatusTask: DefaultUpdatePushRuleEnableStatusTask): UpdatePushRuleEnableStatusTask
@Binds
abstract fun bindAddPushRuleTask(addPushRuleTask: DefaultAddPushRuleTask): AddPushRuleTask
@Binds
abstract fun bindRemovePushRuleTask(removePushRuleTask: DefaultRemovePushRuleTask): RemovePushRuleTask
@Binds
abstract fun bindSetRoomNotificationStateTask(setRoomNotificationStateTask: DefaultSetRoomNotificationStateTask): SetRoomNotificationStateTask
@Binds
abstract fun bindPushRuleService(pushRuleService: DefaultPushRuleService): PushRuleService

View file

@ -0,0 +1,39 @@
/*
* 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.pushers
import im.vector.matrix.android.api.pushrules.RuleKind
import im.vector.matrix.android.api.pushrules.rest.PushRule
import im.vector.matrix.android.internal.network.executeRequest
import im.vector.matrix.android.internal.task.Task
import javax.inject.Inject
internal interface RemovePushRuleTask : Task<RemovePushRuleTask.Params, Unit> {
data class Params(
val kind: RuleKind,
val pushRule: PushRule
)
}
internal class DefaultRemovePushRuleTask @Inject constructor(private val pushRulesApi: PushRulesApi)
: RemovePushRuleTask {
override suspend fun execute(params: RemovePushRuleTask.Params) {
return executeRequest {
apiCall = pushRulesApi.deleteRule(params.kind.value, params.pushRule.ruleId)
}
}
}

View file

@ -24,6 +24,7 @@ import im.vector.matrix.android.api.session.room.Room
import im.vector.matrix.android.api.session.room.members.MembershipService
import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.android.api.session.room.model.relation.RelationService
import im.vector.matrix.android.api.session.room.notification.RoomPushRuleService
import im.vector.matrix.android.api.session.room.reporting.ReportingService
import im.vector.matrix.android.api.session.room.read.ReadService
import im.vector.matrix.android.api.session.room.send.DraftService
@ -49,7 +50,8 @@ internal class DefaultRoom @Inject constructor(override val roomId: String,
private val readService: ReadService,
private val cryptoService: CryptoService,
private val relationService: RelationService,
private val roomMembersService: MembershipService) :
private val roomMembersService: MembershipService,
private val roomPushRuleService: RoomPushRuleService) :
Room,
TimelineService by timelineService,
SendService by sendService,
@ -58,7 +60,8 @@ internal class DefaultRoom @Inject constructor(override val roomId: String,
ReportingService by reportingService,
ReadService by readService,
RelationService by relationService,
MembershipService by roomMembersService {
MembershipService by roomMembersService,
RoomPushRuleService by roomPushRuleService {
override fun getRoomSummaryLive(): LiveData<Optional<RoomSummary>> {
val liveData = monarchy.findAllMappedWithChanges(

View file

@ -22,6 +22,7 @@ import im.vector.matrix.android.api.session.room.Room
import im.vector.matrix.android.internal.database.mapper.RoomSummaryMapper
import im.vector.matrix.android.internal.session.room.draft.DefaultDraftService
import im.vector.matrix.android.internal.session.room.membership.DefaultMembershipService
import im.vector.matrix.android.internal.session.room.notification.DefaultRoomPushRuleService
import im.vector.matrix.android.internal.session.room.read.DefaultReadService
import im.vector.matrix.android.internal.session.room.relation.DefaultRelationService
import im.vector.matrix.android.internal.session.room.reporting.DefaultReportingService
@ -44,7 +45,8 @@ internal class DefaultRoomFactory @Inject constructor(private val monarchy: Mona
private val reportingServiceFactory: DefaultReportingService.Factory,
private val readServiceFactory: DefaultReadService.Factory,
private val relationServiceFactory: DefaultRelationService.Factory,
private val membershipServiceFactory: DefaultMembershipService.Factory) :
private val membershipServiceFactory: DefaultMembershipService.Factory,
private val roomPushRuleServiceFactory: DefaultRoomPushRuleService.Factory) :
RoomFactory {
override fun create(roomId: String): Room {
@ -60,7 +62,8 @@ internal class DefaultRoomFactory @Inject constructor(private val monarchy: Mona
readServiceFactory.create(roomId),
cryptoService,
relationServiceFactory.create(roomId),
membershipServiceFactory.create(roomId)
membershipServiceFactory.create(roomId),
roomPushRuleServiceFactory.create(roomId)
)
}
}

View file

@ -0,0 +1,72 @@
/*
* 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.room.notification
import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.pushrules.RuleScope
import im.vector.matrix.android.api.session.room.notification.RoomNotificationState
import im.vector.matrix.android.api.session.room.notification.RoomPushRuleService
import im.vector.matrix.android.api.util.Cancelable
import im.vector.matrix.android.internal.database.model.PushRuleEntity
import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.task.configureWith
internal class DefaultRoomPushRuleService @AssistedInject constructor(@Assisted private val roomId: String,
private val setRoomNotificationStateTask: SetRoomNotificationStateTask,
private val monarchy: Monarchy,
private val taskExecutor: TaskExecutor)
: RoomPushRuleService {
@AssistedInject.Factory
interface Factory {
fun create(roomId: String): RoomPushRuleService
}
override fun getLiveRoomNotificationState(): LiveData<RoomNotificationState> {
return Transformations.map(getPushRuleForRoom()) {
it?.toRoomNotificationState() ?: RoomNotificationState.ALL_MESSAGES
}
}
override fun setRoomNotificationState(roomNotificationState: RoomNotificationState, matrixCallback: MatrixCallback<Unit>): Cancelable {
return setRoomNotificationStateTask
.configureWith(SetRoomNotificationStateTask.Params(roomId, roomNotificationState)) {
this.callback = callback
}
.executeBy(taskExecutor)
}
private fun getPushRuleForRoom(): LiveData<RoomPushRule?> {
val liveData = monarchy.findAllMappedWithChanges(
{ realm ->
PushRuleEntity.where(realm, scope = RuleScope.GLOBAL, ruleId = roomId)
},
{ result ->
result.toRoomPushRule()
}
)
return Transformations.map(liveData) { results ->
results.firstOrNull()
}
}
}

View file

@ -0,0 +1,25 @@
/*
* 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.room.notification
import im.vector.matrix.android.api.pushrules.RuleKind
import im.vector.matrix.android.api.pushrules.rest.PushRule
internal data class RoomPushRule(
val kind: RuleKind,
val rule: PushRule
)

View file

@ -0,0 +1,101 @@
/*
* 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.room.notification
import im.vector.matrix.android.api.pushrules.*
import im.vector.matrix.android.api.pushrules.rest.PushCondition
import im.vector.matrix.android.api.pushrules.rest.PushRule
import im.vector.matrix.android.api.session.room.notification.RoomNotificationState
import im.vector.matrix.android.internal.database.mapper.PushRulesMapper
import im.vector.matrix.android.internal.database.model.PushRuleEntity
internal fun PushRuleEntity.toRoomPushRule(): RoomPushRule? {
val kind = parent?.firstOrNull()?.kind
val pushRule = when (kind) {
RuleSetKey.OVERRIDE -> {
PushRulesMapper.map(this)
}
RuleSetKey.ROOM -> {
PushRulesMapper.mapRoomRule(this)
}
else -> null
}
return if (pushRule == null || kind == null) {
null
} else {
RoomPushRule(kind, pushRule)
}
}
internal fun RoomNotificationState.toRoomPushRule(roomId: String): RoomPushRule? {
return when {
this == RoomNotificationState.ALL_MESSAGES -> null
this == RoomNotificationState.ALL_MESSAGES_NOISY -> {
val rule = PushRule(
actions = listOf(Action.Notify, Action.Sound()).toJson(),
enabled = true,
ruleId = roomId
)
return RoomPushRule(RuleSetKey.ROOM, rule)
}
else -> {
val condition = PushCondition(
kind = Condition.Kind.event_match.value,
key = "room_id",
pattern = roomId
)
val rule = PushRule(
actions = listOf(Action.DoNotNotify).toJson(),
enabled = true,
ruleId = roomId,
conditions = listOf(condition)
)
val kind = if (this == RoomNotificationState.MUTE) {
RuleSetKey.OVERRIDE
} else {
RuleSetKey.ROOM
}
return RoomPushRule(kind, rule)
}
}
}
internal fun RoomPushRule.toRoomNotificationState(): RoomNotificationState {
return if (rule.enabled) {
val actions = rule.getActions()
if (actions.contains(Action.DoNotNotify)) {
if (kind == RuleSetKey.OVERRIDE) {
RoomNotificationState.MUTE
} else {
RoomNotificationState.MENTIONS_ONLY
}
} else if (actions.contains(Action.Notify)) {
val hasSoundAction = actions.find {
it is Action.Sound
} != null
if (hasSoundAction) {
RoomNotificationState.ALL_MESSAGES_NOISY
} else {
RoomNotificationState.ALL_MESSAGES
}
} else {
RoomNotificationState.ALL_MESSAGES
}
} else {
RoomNotificationState.ALL_MESSAGES
}
}

View file

@ -0,0 +1,54 @@
/*
* 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.room.notification
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.pushrules.RuleScope
import im.vector.matrix.android.api.session.room.notification.RoomNotificationState
import im.vector.matrix.android.internal.database.model.PushRuleEntity
import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.session.pushers.AddPushRuleTask
import im.vector.matrix.android.internal.session.pushers.RemovePushRuleTask
import im.vector.matrix.android.internal.task.Task
import io.realm.Realm
import javax.inject.Inject
internal interface SetRoomNotificationStateTask : Task<SetRoomNotificationStateTask.Params, Unit> {
data class Params(
val roomId: String,
val roomNotificationState: RoomNotificationState
)
}
internal class DefaultSetRoomNotificationStateTask @Inject constructor(private val monarchy: Monarchy,
private val removePushRuleTask: RemovePushRuleTask,
private val addPushRuleTask: AddPushRuleTask)
: SetRoomNotificationStateTask {
override suspend fun execute(params: SetRoomNotificationStateTask.Params) {
val currentRoomPushRule = Realm.getInstance(monarchy.realmConfiguration).use {
PushRuleEntity.where(it, scope = RuleScope.GLOBAL, ruleId = params.roomId).findFirst()?.toRoomPushRule()
}
if (currentRoomPushRule != null) {
removePushRuleTask.execute(RemovePushRuleTask.Params(currentRoomPushRule.kind, currentRoomPushRule.rule))
}
val newRoomPushRule = params.roomNotificationState.toRoomPushRule(params.roomId)
if (newRoomPushRule != null) {
addPushRuleTask.execute(AddPushRuleTask.Params(newRoomPushRule.kind, newRoomPushRule.rule))
}
}
}

View file

@ -27,10 +27,9 @@ import im.vector.matrix.android.internal.di.UserId
import im.vector.matrix.android.internal.session.pushers.SavePushRulesTask
import im.vector.matrix.android.internal.session.room.membership.RoomMembers
import im.vector.matrix.android.internal.session.sync.model.InvitedRoomSync
import im.vector.matrix.android.internal.session.sync.model.UserAccountDataDirectMessages
import im.vector.matrix.android.internal.session.sync.model.UserAccountDataPushRules
import im.vector.matrix.android.internal.session.sync.model.UserAccountDataSync
import im.vector.matrix.android.internal.session.sync.model.accountdata.*
import im.vector.matrix.android.internal.session.user.accountdata.DirectChatsHelper
import im.vector.matrix.android.internal.session.user.accountdata.SaveIgnoredUsersTask
import im.vector.matrix.android.internal.session.user.accountdata.UpdateUserAccountDataTask
import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.task.configureWith
@ -44,6 +43,7 @@ internal class UserAccountDataSyncHandler @Inject constructor(private val monarc
private val directChatsHelper: DirectChatsHelper,
private val updateUserAccountDataTask: UpdateUserAccountDataTask,
private val savePushRulesTask: SavePushRulesTask,
private val saveIgnoredUsersTask: SaveIgnoredUsersTask,
private val taskExecutor: TaskExecutor) {
suspend fun handle(accountData: UserAccountDataSync?, invites: Map<String, InvitedRoomSync>?) {
@ -51,9 +51,18 @@ internal class UserAccountDataSyncHandler @Inject constructor(private val monarc
when (it) {
is UserAccountDataDirectMessages -> handleDirectChatRooms(it)
is UserAccountDataPushRules -> handlePushRules(it)
else -> return@forEach
is UserAccountDataIgnoredUsers -> handleIgnoredUsers(it)
is UserAccountDataFallback -> Timber.d("Receive account data of unhandled type ${it.type}")
else -> error("Missing code here!")
}
}
// TODO Store all account data, app can be interested of it
// accountData?.list?.forEach {
// it.toString()
// MoshiProvider.providesMoshi()
// }
monarchy.doWithRealm { realm ->
synchronizeWithServerIfNeeded(realm, invites)
}
@ -114,4 +123,11 @@ internal class UserAccountDataSyncHandler @Inject constructor(private val monarc
updateUserAccountDataTask.configureWith(updateUserAccountParams).executeBy(taskExecutor)
}
}
private fun handleIgnoredUsers(userAccountDataIgnoredUsers: UserAccountDataIgnoredUsers) {
saveIgnoredUsersTask
.configureWith(SaveIgnoredUsersTask.Params(userAccountDataIgnoredUsers.content.ignoredUsers.keys.toList()))
.executeBy(taskExecutor)
// TODO If not initial sync, we should execute a init sync
}
}

View file

@ -18,6 +18,7 @@ package im.vector.matrix.android.internal.session.sync.model
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataSync
// SyncResponse represents the request response for server sync v2.
@JsonClass(generateAdapter = true)

View file

@ -0,0 +1,39 @@
/*
* 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.sync.model.accountdata
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import im.vector.matrix.android.api.util.JsonDict
import im.vector.matrix.android.api.util.emptyJsonDict
@JsonClass(generateAdapter = true)
internal data class IgnoredUsersContent(
/**
* Required. The map of users to ignore. UserId -> empty object for future enhancement
*/
@Json(name = "ignored_users") val ignoredUsers: Map<String, JsonDict>
) {
companion object {
fun createWithUserIds(userIds: List<String>): IgnoredUsersContent {
return IgnoredUsersContent(
ignoredUsers = userIds.associateWith { emptyJsonDict }
)
}
}
}

View file

@ -14,9 +14,13 @@
* limitations under the License.
*/
package im.vector.matrix.android.internal.session.sync.model
package im.vector.matrix.android.internal.session.sync.model.accountdata
internal interface UserAccountData {
import com.squareup.moshi.Json
internal abstract class UserAccountData {
@Json(name = "type") abstract val type: String
companion object {
const val TYPE_IGNORED_USER_LIST = "m.ignored_user_list"

View file

@ -14,12 +14,13 @@
* limitations under the License.
*/
package im.vector.matrix.android.internal.session.sync.model
package im.vector.matrix.android.internal.session.sync.model.accountdata
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
internal data class UserAccountDataDirectMessages(
@Json(name = "type") override val type: String = TYPE_DIRECT_MESSAGES,
@Json(name = "content") val content: Map<String, List<String>>
) : UserAccountData
) : UserAccountData()

View file

@ -14,12 +14,13 @@
* limitations under the License.
*/
package im.vector.matrix.android.internal.session.sync.model
package im.vector.matrix.android.internal.session.sync.model.accountdata
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
internal data class UserAccountDataFallback(
@Json(name = "type") override val type: String,
@Json(name = "content") val content: Map<String, Any>
) : UserAccountData
) : UserAccountData()

View file

@ -0,0 +1,26 @@
/*
* 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.sync.model.accountdata
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
internal data class UserAccountDataIgnoredUsers(
@Json(name = "type") override val type: String = TYPE_IGNORED_USER_LIST,
@Json(name = "content") val content: IgnoredUsersContent
) : UserAccountData()

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
package im.vector.matrix.android.internal.session.sync.model
package im.vector.matrix.android.internal.session.sync.model.accountdata
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@ -22,5 +22,6 @@ import im.vector.matrix.android.api.pushrules.rest.GetPushRulesResponse
@JsonClass(generateAdapter = true)
internal data class UserAccountDataPushRules(
@Json(name = "type") override val type: String = TYPE_PUSH_RULES,
@Json(name = "content") val content: GetPushRulesResponse
) : UserAccountData
) : UserAccountData()

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
package im.vector.matrix.android.internal.session.sync.model
package im.vector.matrix.android.internal.session.sync.model.accountdata
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass

View file

@ -29,9 +29,12 @@ import im.vector.matrix.android.api.util.Cancelable
import im.vector.matrix.android.api.util.Optional
import im.vector.matrix.android.api.util.toOptional
import im.vector.matrix.android.internal.database.mapper.asDomain
import im.vector.matrix.android.internal.database.model.IgnoredUserEntity
import im.vector.matrix.android.internal.database.model.IgnoredUserEntityFields
import im.vector.matrix.android.internal.database.model.UserEntity
import im.vector.matrix.android.internal.database.model.UserEntityFields
import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.session.user.accountdata.UpdateIgnoredUserIdsTask
import im.vector.matrix.android.internal.session.user.model.SearchUserTask
import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.task.configureWith
@ -40,8 +43,8 @@ import javax.inject.Inject
internal class DefaultUserService @Inject constructor(private val monarchy: Monarchy,
private val searchUserTask: SearchUserTask,
private val updateIgnoredUserIdsTask: UpdateIgnoredUserIdsTask,
private val taskExecutor: TaskExecutor) : UserService {
private val realmDataSourceFactory: Monarchy.RealmDataSourceFactory<UserEntity> by lazy {
monarchy.createDataSourceFactory { realm ->
realm.where(UserEntity::class.java)
@ -62,7 +65,7 @@ internal class DefaultUserService @Inject constructor(private val monarchy: Mona
override fun getUser(userId: String): User? {
val userEntity = monarchy.fetchCopied { UserEntity.where(it, userId).findFirst() }
?: return null
?: return null
return userEntity.asDomain()
}
@ -117,4 +120,33 @@ internal class DefaultUserService @Inject constructor(private val monarchy: Mona
}
.executeBy(taskExecutor)
}
override fun liveIgnoredUsers(): LiveData<List<User>> {
return monarchy.findAllMappedWithChanges(
{ realm ->
realm.where(IgnoredUserEntity::class.java)
.isNotEmpty(IgnoredUserEntityFields.USER_ID)
.sort(IgnoredUserEntityFields.USER_ID)
},
{ getUser(it.userId) ?: User(userId = it.userId) }
)
}
override fun ignoreUserIds(userIds: List<String>, callback: MatrixCallback<Unit>): Cancelable {
val params = UpdateIgnoredUserIdsTask.Params(userIdsToIgnore = userIds.toList())
return updateIgnoredUserIdsTask
.configureWith(params) {
this.callback = callback
}
.executeBy(taskExecutor)
}
override fun unIgnoreUserIds(userIds: List<String>, callback: MatrixCallback<Unit>): Cancelable {
val params = UpdateIgnoredUserIdsTask.Params(userIdsToUnIgnore = userIds.toList())
return updateIgnoredUserIdsTask
.configureWith(params) {
this.callback = callback
}
.executeBy(taskExecutor)
}
}

View file

@ -21,6 +21,10 @@ import dagger.Module
import dagger.Provides
import im.vector.matrix.android.api.session.user.UserService
import im.vector.matrix.android.internal.session.SessionScope
import im.vector.matrix.android.internal.session.user.accountdata.DefaultSaveIgnoredUsersTask
import im.vector.matrix.android.internal.session.user.accountdata.DefaultUpdateIgnoredUserIdsTask
import im.vector.matrix.android.internal.session.user.accountdata.SaveIgnoredUsersTask
import im.vector.matrix.android.internal.session.user.accountdata.UpdateIgnoredUserIdsTask
import im.vector.matrix.android.internal.session.user.model.DefaultSearchUserTask
import im.vector.matrix.android.internal.session.user.model.SearchUserTask
import retrofit2.Retrofit
@ -43,4 +47,10 @@ internal abstract class UserModule {
@Binds
abstract fun bindSearchUserTask(searchUserTask: DefaultSearchUserTask): SearchUserTask
@Binds
abstract fun bindSaveIgnoredUsersTask(task: DefaultSaveIgnoredUsersTask): SaveIgnoredUsersTask
@Binds
abstract fun bindUpdateIgnoredUserIdsTask(task: DefaultUpdateIgnoredUserIdsTask): UpdateIgnoredUserIdsTask
}

View file

@ -0,0 +1,46 @@
/*
* 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.user.accountdata
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.internal.database.model.IgnoredUserEntity
import im.vector.matrix.android.internal.task.Task
import im.vector.matrix.android.internal.util.awaitTransaction
import javax.inject.Inject
/**
* Save the ignored users list in DB
*/
internal interface SaveIgnoredUsersTask : Task<SaveIgnoredUsersTask.Params, Unit> {
data class Params(
val userIds: List<String>
)
}
internal class DefaultSaveIgnoredUsersTask @Inject constructor(private val monarchy: Monarchy) : SaveIgnoredUsersTask {
override suspend fun execute(params: SaveIgnoredUsersTask.Params) {
monarchy.awaitTransaction { realm ->
// clear current ignored users
realm.where(IgnoredUserEntity::class.java)
.findAll()
.deleteAllFromRealm()
// And save the new received list
params.userIds.forEach { realm.createObject(IgnoredUserEntity::class.java).apply { userId = it } }
}
}
}

View file

@ -0,0 +1,68 @@
/*
* 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.user.accountdata
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.internal.database.model.IgnoredUserEntity
import im.vector.matrix.android.internal.di.UserId
import im.vector.matrix.android.internal.network.executeRequest
import im.vector.matrix.android.internal.session.sync.model.accountdata.IgnoredUsersContent
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountData
import im.vector.matrix.android.internal.task.Task
import javax.inject.Inject
internal interface UpdateIgnoredUserIdsTask : Task<UpdateIgnoredUserIdsTask.Params, Unit> {
data class Params(
val userIdsToIgnore: List<String> = emptyList(),
val userIdsToUnIgnore: List<String> = emptyList()
)
}
internal class DefaultUpdateIgnoredUserIdsTask @Inject constructor(private val accountDataApi: AccountDataAPI,
private val monarchy: Monarchy,
private val saveIgnoredUsersTask: SaveIgnoredUsersTask,
@UserId private val userId: String) : UpdateIgnoredUserIdsTask {
override suspend fun execute(params: UpdateIgnoredUserIdsTask.Params) {
// Get current list
val ignoredUserIds = monarchy.fetchAllMappedSync(
{ realm -> realm.where(IgnoredUserEntity::class.java) },
{ it.userId }
).toMutableSet()
val original = ignoredUserIds.toList()
ignoredUserIds.removeAll { it in params.userIdsToUnIgnore }
ignoredUserIds.addAll(params.userIdsToIgnore)
if (original == ignoredUserIds) {
// No change
return
}
val list = ignoredUserIds.toList()
val body = IgnoredUsersContent.createWithUserIds(list)
executeRequest<Unit> {
apiCall = accountDataApi.setAccountData(userId, UserAccountData.TYPE_IGNORED_USER_LIST, body)
}
// Update the DB right now (do not wait for the sync to come back with updated data, for a faster UI update)
saveIgnoredUsersTask.execute(SaveIgnoredUsersTask.Params(list))
}
}

View file

@ -18,7 +18,7 @@ package im.vector.matrix.android.internal.session.user.accountdata
import im.vector.matrix.android.internal.di.UserId
import im.vector.matrix.android.internal.network.executeRequest
import im.vector.matrix.android.internal.session.sync.model.UserAccountData
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountData
import im.vector.matrix.android.internal.task.Task
import javax.inject.Inject
@ -29,6 +29,7 @@ internal interface UpdateUserAccountDataTask : Task<UpdateUserAccountDataTask.Pa
fun getData(): Any
}
// TODO Use [UserAccountDataDirectMessages] class?
data class DirectChatParams(override val type: String = UserAccountData.TYPE_DIRECT_MESSAGES,
private val directMessages: Map<String, List<String>>
) : Params {

View file

@ -16,23 +16,15 @@
package im.vector.matrix.android.api.pushrules
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.content.ContentAttachmentData
import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.events.model.toContent
import im.vector.matrix.android.api.session.room.Room
import im.vector.matrix.android.api.session.room.RoomService
import im.vector.matrix.android.api.session.room.model.*
import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams
import im.vector.matrix.android.api.session.room.model.Membership
import im.vector.matrix.android.api.session.room.model.RoomMember
import im.vector.matrix.android.api.session.room.model.message.MessageTextContent
import im.vector.matrix.android.api.session.room.send.UserDraft
import im.vector.matrix.android.api.session.room.timeline.Timeline
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.matrix.android.api.session.room.timeline.TimelineSettings
import im.vector.matrix.android.api.util.Cancelable
import im.vector.matrix.android.api.util.Optional
import io.mockk.every
import io.mockk.mockk
import org.junit.Assert
import org.junit.Test
@ -133,17 +125,20 @@ class PushrulesConditionTest {
val conditionEqual3Bis = RoomMemberCountCondition("==3")
val conditionLessThan3 = RoomMemberCountCondition("<3")
val session = MockRoomService()
val room2JoinedId = "2joined"
val room3JoinedId = "3joined"
Event(
type = "m.room.message",
eventId = "mx0",
content = MessageTextContent("m.text", "A").toContent(),
originServerTs = 0,
roomId = "2joined").also {
Assert.assertFalse("This room does not have 3 members", conditionEqual3.isSatisfied(it, session))
Assert.assertFalse("This room does not have 3 members", conditionEqual3Bis.isSatisfied(it, session))
Assert.assertTrue("This room has less than 3 members", conditionLessThan3.isSatisfied(it, session))
val roomStub2Joined = mockk<Room> {
every { getNumberOfJoinedMembers() } returns 2
}
val roomStub3Joined = mockk<Room> {
every { getNumberOfJoinedMembers() } returns 3
}
val sessionStub = mockk<RoomService> {
every { getRoom(room2JoinedId) } returns roomStub2Joined
every { getRoom(room3JoinedId) } returns roomStub3Joined
}
Event(
@ -151,10 +146,21 @@ class PushrulesConditionTest {
eventId = "mx0",
content = MessageTextContent("m.text", "A").toContent(),
originServerTs = 0,
roomId = "3joined").also {
Assert.assertTrue("This room has 3 members", conditionEqual3.isSatisfied(it, session))
Assert.assertTrue("This room has 3 members", conditionEqual3Bis.isSatisfied(it, session))
Assert.assertFalse("This room has more than 3 members", conditionLessThan3.isSatisfied(it, session))
roomId = room2JoinedId).also {
Assert.assertFalse("This room does not have 3 members", conditionEqual3.isSatisfied(it, sessionStub))
Assert.assertFalse("This room does not have 3 members", conditionEqual3Bis.isSatisfied(it, sessionStub))
Assert.assertTrue("This room has less than 3 members", conditionLessThan3.isSatisfied(it, sessionStub))
}
Event(
type = "m.room.message",
eventId = "mx0",
content = MessageTextContent("m.text", "A").toContent(),
originServerTs = 0,
roomId = room3JoinedId).also {
Assert.assertTrue("This room has 3 members", conditionEqual3.isSatisfied(it, sessionStub))
Assert.assertTrue("This room has 3 members", conditionEqual3Bis.isSatisfied(it, sessionStub))
Assert.assertFalse("This room has more than 3 members", conditionLessThan3.isSatisfied(it, sessionStub))
}
}
@ -171,212 +177,4 @@ class PushrulesConditionTest {
Assert.assertTrue("Notice", conditionEqual.isSatisfied(it))
}
}
class MockRoomService() : RoomService {
override fun createRoom(createRoomParams: CreateRoomParams, callback: MatrixCallback<String>): Cancelable {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun joinRoom(roomId: String, viaServers: List<String>, callback: MatrixCallback<Unit>): Cancelable {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun getRoom(roomId: String): Room? {
return when (roomId) {
"2joined" -> MockRoom(roomId, 2)
"3joined" -> MockRoom(roomId, 3)
else -> null
}
}
override fun liveRoomSummaries(): LiveData<List<RoomSummary>> {
return MutableLiveData()
}
override fun markAllAsRead(roomIds: List<String>, callback: MatrixCallback<Unit>): Cancelable {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
}
class MockRoom(override val roomId: String, val _numberOfJoinedMembers: Int) : Room {
override fun reportContent(eventId: String, score: Int, reason: String, callback: MatrixCallback<Unit>): Cancelable {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun getReadMarkerLive(): LiveData<Optional<String>> {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun getMyReadReceiptLive(): LiveData<Optional<String>> {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun resendTextMessage(localEcho: TimelineEvent): Cancelable? {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun resendMediaMessage(localEcho: TimelineEvent): Cancelable? {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun deleteFailedEcho(localEcho: TimelineEvent) {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun clearSendingQueue() {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun resendAllFailedMessages() {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun saveDraft(draft: UserDraft) {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun deleteDraft() {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun getDraftsLive(): LiveData<List<UserDraft>> {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun getEventReadReceiptsLive(eventId: String): LiveData<List<ReadReceipt>> {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun getStateEvent(eventType: String): Event? {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun editReply(replyToEdit: TimelineEvent, originalTimelineEvent: TimelineEvent, newBodyText: String, compatibilityBodyText: String): Cancelable {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun fetchEditHistory(eventId: String, callback: MatrixCallback<List<Event>>) {
}
override fun getTimeLineEventLive(eventId: String): LiveData<Optional<TimelineEvent>> {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun getNumberOfJoinedMembers(): Int {
return _numberOfJoinedMembers
}
override fun getRoomSummaryLive(): LiveData<Optional<RoomSummary>> {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun roomSummary(): RoomSummary? {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun createTimeline(eventId: String?, settings: TimelineSettings): Timeline {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun getTimeLineEvent(eventId: String): TimelineEvent? {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun sendTextMessage(text: String, msgType: String, autoMarkdown: Boolean): Cancelable {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun sendFormattedTextMessage(text: String, formattedText: String): Cancelable {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun sendMedia(attachment: ContentAttachmentData): Cancelable {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun sendMedias(attachments: List<ContentAttachmentData>): Cancelable {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun redactEvent(event: Event, reason: String?): Cancelable {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun markAllAsRead(callback: MatrixCallback<Unit>) {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun setReadReceipt(eventId: String, callback: MatrixCallback<Unit>) {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun setReadMarker(fullyReadEventId: String, callback: MatrixCallback<Unit>) {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun isEventRead(eventId: String): Boolean {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun loadRoomMembersIfNeeded(matrixCallback: MatrixCallback<Unit>): Cancelable {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun getRoomMember(userId: String): RoomMember? {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun getRoomMemberIdsLive(): LiveData<List<String>> {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun invite(userId: String, callback: MatrixCallback<Unit>): Cancelable {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun join(viaServers: List<String>, callback: MatrixCallback<Unit>): Cancelable {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun leave(callback: MatrixCallback<Unit>): Cancelable {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun updateTopic(topic: String, callback: MatrixCallback<Unit>) {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun sendReaction(targetEventId: String, reaction: String): Cancelable {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun undoReaction(targetEventId: String, reaction: String): Cancelable {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun editTextMessage(targetEventId: String, msgType: String, newBodyText: String,
newBodyAutoMarkdown: Boolean, compatibilityBodyText: String): Cancelable {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun replyToMessage(eventReplied: TimelineEvent, replyText: String, autoMarkdown: Boolean): Cancelable? {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun getEventSummaryLive(eventId: String): LiveData<Optional<EventAnnotationsSummary>> {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun isEncrypted(): Boolean {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun encryptionAlgorithm(): String? {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun shouldEncryptForInvitedMembers(): Boolean {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
}
}

View file

@ -217,6 +217,7 @@ android {
dependencies {
def epoxy_version = '3.8.0'
def fragment_version = '1.2.0-rc01'
def arrow_version = "0.8.2"
def coroutines_version = "1.3.2"
def markwon_version = '4.1.2'
@ -234,6 +235,8 @@ dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation "androidx.fragment:fragment:$fragment_version"
implementation "androidx.fragment:fragment-ktx:$fragment_version"
//Do not use beta2 at the moment, as it breaks things
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta1'
implementation 'androidx.core:core-ktx:1.1.0'

View file

@ -19,9 +19,9 @@ package im.vector.riotx
import arrow.core.Option
import im.vector.matrix.android.api.session.Session
import im.vector.riotx.core.utils.RxStore
import im.vector.riotx.core.utils.BehaviorDataSource
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class ActiveSessionObservableStore @Inject constructor() : RxStore<Option<Session>>()
class ActiveSessionDataSource @Inject constructor() : BehaviorDataSource<Option<Session>>()

View file

@ -23,9 +23,9 @@ import arrow.core.Option
import im.vector.matrix.android.api.session.group.model.GroupSummary
import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.rx.rx
import im.vector.riotx.features.home.HomeRoomListObservableStore
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.SelectedGroupStore
import im.vector.riotx.features.home.group.SelectedGroupDataSource
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
@ -41,9 +41,9 @@ import javax.inject.Singleton
*/
@Singleton
class AppStateHandler @Inject constructor(
private val sessionObservableStore: ActiveSessionObservableStore,
private val homeRoomListObservableStore: HomeRoomListObservableStore,
private val selectedGroupStore: SelectedGroupStore) : LifecycleObserver {
private val sessionDataSource: ActiveSessionDataSource,
private val homeRoomListDataSource: HomeRoomListDataSource,
private val selectedGroupDataSource: SelectedGroupDataSource) : LifecycleObserver {
private val compositeDisposable = CompositeDisposable()
@ -60,14 +60,14 @@ class AppStateHandler @Inject constructor(
private fun observeRoomsAndGroup() {
Observable
.combineLatest<List<RoomSummary>, Option<GroupSummary>, List<RoomSummary>>(
sessionObservableStore.observe()
sessionDataSource.observe()
.observeOn(AndroidSchedulers.mainThread())
.switchMap {
it.orNull()?.rx()?.liveRoomSummaries()
?: Observable.just(emptyList())
}
.throttleLast(300, TimeUnit.MILLISECONDS),
selectedGroupStore.observe(),
selectedGroupDataSource.observe(),
BiFunction { rooms, selectedGroupOption ->
val selectedGroup = selectedGroupOption.orNull()
val filteredDirectRooms = rooms
@ -92,7 +92,7 @@ class AppStateHandler @Inject constructor(
}
)
.subscribe {
homeRoomListObservableStore.post(it)
homeRoomListDataSource.post(it)
}
.addTo(compositeDisposable)
}

View file

@ -19,7 +19,7 @@ package im.vector.riotx.core.di
import arrow.core.Option
import im.vector.matrix.android.api.auth.Authenticator
import im.vector.matrix.android.api.session.Session
import im.vector.riotx.ActiveSessionObservableStore
import im.vector.riotx.ActiveSessionDataSource
import im.vector.riotx.features.crypto.keysrequest.KeyRequestHandler
import im.vector.riotx.features.crypto.verification.IncomingVerificationRequestHandler
import java.util.concurrent.atomic.AtomicReference
@ -28,7 +28,7 @@ import javax.inject.Singleton
@Singleton
class ActiveSessionHolder @Inject constructor(private val authenticator: Authenticator,
private val sessionObservableStore: ActiveSessionObservableStore,
private val sessionObservableStore: ActiveSessionDataSource,
private val keyRequestHandler: KeyRequestHandler,
private val incomingVerificationRequestHandler: IncomingVerificationRequestHandler
) {

View file

@ -0,0 +1,27 @@
/*
* 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.core.di
import androidx.fragment.app.Fragment
import dagger.MapKey
import kotlin.reflect.KClass
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
@Retention(AnnotationRetention.RUNTIME)
@MapKey
annotation class FragmentKey(val value: KClass<out Fragment>)

View file

@ -0,0 +1,197 @@
/*
* 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.core.di
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentFactory
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoMap
import im.vector.riotx.features.crypto.keysbackup.settings.KeysBackupSettingsFragment
import im.vector.riotx.features.crypto.verification.SASVerificationIncomingFragment
import im.vector.riotx.features.crypto.verification.SASVerificationShortCodeFragment
import im.vector.riotx.features.crypto.verification.SASVerificationStartFragment
import im.vector.riotx.features.crypto.verification.SASVerificationVerifiedFragment
import im.vector.riotx.features.home.HomeDetailFragment
import im.vector.riotx.features.home.HomeDrawerFragment
import im.vector.riotx.features.home.LoadingFragment
import im.vector.riotx.features.home.createdirect.CreateDirectRoomDirectoryUsersFragment
import im.vector.riotx.features.home.createdirect.CreateDirectRoomKnownUsersFragment
import im.vector.riotx.features.home.group.GroupListFragment
import im.vector.riotx.features.home.room.detail.RoomDetailFragment
import im.vector.riotx.features.home.room.list.RoomListFragment
import im.vector.riotx.features.login.LoginFragment
import im.vector.riotx.features.login.LoginSsoFallbackFragment
import im.vector.riotx.features.reactions.EmojiSearchResultFragment
import im.vector.riotx.features.roomdirectory.PublicRoomsFragment
import im.vector.riotx.features.roomdirectory.createroom.CreateRoomFragment
import im.vector.riotx.features.roomdirectory.picker.RoomDirectoryPickerFragment
import im.vector.riotx.features.roomdirectory.roompreview.RoomPreviewNoPreviewFragment
import im.vector.riotx.features.settings.*
import im.vector.riotx.features.settings.ignored.VectorSettingsIgnoredUsersFragment
import im.vector.riotx.features.settings.push.PushGatewaysFragment
@Module
interface FragmentModule {
/**
* Fragments with @IntoMap will be injected by this factory
*/
@Binds
fun bindFragmentFactory(factory: VectorFragmentFactory): FragmentFactory
@Binds
@IntoMap
@FragmentKey(RoomListFragment::class)
fun bindRoomListFragment(fragment: RoomListFragment): Fragment
@Binds
@IntoMap
@FragmentKey(GroupListFragment::class)
fun bindGroupListFragment(fragment: GroupListFragment): Fragment
@Binds
@IntoMap
@FragmentKey(RoomDetailFragment::class)
fun bindRoomDetailFragment(fragment: RoomDetailFragment): Fragment
@Binds
@IntoMap
@FragmentKey(RoomDirectoryPickerFragment::class)
fun bindRoomDirectoryPickerFragment(fragment: RoomDirectoryPickerFragment): Fragment
@Binds
@IntoMap
@FragmentKey(CreateRoomFragment::class)
fun bindCreateRoomFragment(fragment: CreateRoomFragment): Fragment
@Binds
@IntoMap
@FragmentKey(RoomPreviewNoPreviewFragment::class)
fun bindRoomPreviewNoPreviewFragment(fragment: RoomPreviewNoPreviewFragment): Fragment
@Binds
@IntoMap
@FragmentKey(KeysBackupSettingsFragment::class)
fun bindKeysBackupSettingsFragment(fragment: KeysBackupSettingsFragment): Fragment
@Binds
@IntoMap
@FragmentKey(LoadingFragment::class)
fun bindLoadingFragment(fragment: LoadingFragment): Fragment
@Binds
@IntoMap
@FragmentKey(HomeDrawerFragment::class)
fun bindHomeDrawerFragment(fragment: HomeDrawerFragment): Fragment
@Binds
@IntoMap
@FragmentKey(HomeDetailFragment::class)
fun bindHomeDetailFragment(fragment: HomeDetailFragment): Fragment
@Binds
@IntoMap
@FragmentKey(EmojiSearchResultFragment::class)
fun bindEmojiSearchResultFragment(fragment: EmojiSearchResultFragment): Fragment
@Binds
@IntoMap
@FragmentKey(LoginFragment::class)
fun bindLoginFragment(fragment: LoginFragment): Fragment
@Binds
@IntoMap
@FragmentKey(LoginSsoFallbackFragment::class)
fun bindLoginSsoFallbackFragment(fragment: LoginSsoFallbackFragment): Fragment
@Binds
@IntoMap
@FragmentKey(CreateDirectRoomDirectoryUsersFragment::class)
fun bindCreateDirectRoomDirectoryUsersFragment(fragment: CreateDirectRoomDirectoryUsersFragment): Fragment
@Binds
@IntoMap
@FragmentKey(CreateDirectRoomKnownUsersFragment::class)
fun bindCreateDirectRoomKnownUsersFragment(fragment: CreateDirectRoomKnownUsersFragment): Fragment
@Binds
@IntoMap
@FragmentKey(PushGatewaysFragment::class)
fun bindPushGatewaysFragment(fragment: PushGatewaysFragment): Fragment
@Binds
@IntoMap
@FragmentKey(VectorSettingsNotificationsTroubleshootFragment::class)
fun bindVectorSettingsNotificationsTroubleshootFragment(fragment: VectorSettingsNotificationsTroubleshootFragment): Fragment
@Binds
@IntoMap
@FragmentKey(VectorSettingsAdvancedNotificationPreferenceFragment::class)
fun bindVectorSettingsAdvancedNotificationPreferenceFragment(fragment: VectorSettingsAdvancedNotificationPreferenceFragment): Fragment
@Binds
@IntoMap
@FragmentKey(VectorSettingsNotificationPreferenceFragment::class)
fun bindVectorSettingsNotificationPreferenceFragment(fragment: VectorSettingsNotificationPreferenceFragment): Fragment
@Binds
@IntoMap
@FragmentKey(VectorSettingsPreferencesFragment::class)
fun bindVectorSettingsPreferencesFragment(fragment: VectorSettingsPreferencesFragment): Fragment
@Binds
@IntoMap
@FragmentKey(VectorSettingsSecurityPrivacyFragment::class)
fun bindVectorSettingsSecurityPrivacyFragment(fragment: VectorSettingsSecurityPrivacyFragment): Fragment
@Binds
@IntoMap
@FragmentKey(VectorSettingsHelpAboutFragment::class)
fun bindVectorSettingsHelpAboutFragment(fragment: VectorSettingsHelpAboutFragment): Fragment
@Binds
@IntoMap
@FragmentKey(VectorSettingsIgnoredUsersFragment::class)
fun bindVectorSettingsIgnoredUsersFragment(fragment: VectorSettingsIgnoredUsersFragment): Fragment
@Binds
@IntoMap
@FragmentKey(SASVerificationIncomingFragment::class)
fun bindSASVerificationIncomingFragment(fragment: SASVerificationIncomingFragment): Fragment
@Binds
@IntoMap
@FragmentKey(SASVerificationShortCodeFragment::class)
fun bindSASVerificationShortCodeFragment(fragment: SASVerificationShortCodeFragment): Fragment
@Binds
@IntoMap
@FragmentKey(SASVerificationVerifiedFragment::class)
fun bindSASVerificationVerifiedFragment(fragment: SASVerificationVerifiedFragment): Fragment
@Binds
@IntoMap
@FragmentKey(SASVerificationStartFragment::class)
fun bindSASVerificationStartFragment(fragment: SASVerificationStartFragment): Fragment
@Binds
@IntoMap
@FragmentKey(PublicRoomsFragment::class)
fun bindPublicRoomsFragment(fragment: PublicRoomsFragment): Fragment
}

View file

@ -17,41 +17,26 @@
package im.vector.riotx.core.di
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.FragmentFactory
import androidx.lifecycle.ViewModelProvider
import dagger.BindsInstance
import dagger.Component
import im.vector.fragments.keysbackup.restore.KeysBackupRestoreFromPassphraseFragment
import im.vector.riotx.core.preference.UserAvatarPreference
import im.vector.riotx.features.MainActivity
import im.vector.riotx.features.crypto.keysbackup.restore.KeysBackupRestoreFromKeyFragment
import im.vector.riotx.features.crypto.keysbackup.restore.KeysBackupRestoreSuccessFragment
import im.vector.riotx.features.crypto.keysbackup.settings.KeysBackupManageActivity
import im.vector.riotx.features.crypto.keysbackup.settings.KeysBackupSettingsFragment
import im.vector.riotx.features.crypto.keysbackup.setup.KeysBackupSetupStep1Fragment
import im.vector.riotx.features.crypto.keysbackup.setup.KeysBackupSetupStep2Fragment
import im.vector.riotx.features.crypto.keysbackup.setup.KeysBackupSetupStep3Fragment
import im.vector.riotx.features.crypto.verification.SASVerificationIncomingFragment
import im.vector.riotx.features.home.HomeActivity
import im.vector.riotx.features.home.HomeDetailFragment
import im.vector.riotx.features.home.HomeDrawerFragment
import im.vector.riotx.features.home.HomeModule
import im.vector.riotx.features.home.createdirect.CreateDirectRoomActivity
import im.vector.riotx.features.home.createdirect.CreateDirectRoomDirectoryUsersFragment
import im.vector.riotx.features.home.createdirect.CreateDirectRoomKnownUsersFragment
import im.vector.riotx.features.home.group.GroupListFragment
import im.vector.riotx.features.home.room.detail.RoomDetailFragment
import im.vector.riotx.features.home.room.detail.readreceipts.DisplayReadReceiptsBottomSheet
import im.vector.riotx.features.home.room.detail.timeline.action.MessageActionsBottomSheet
import im.vector.riotx.features.home.room.detail.timeline.edithistory.ViewEditHistoryBottomSheet
import im.vector.riotx.features.home.room.detail.timeline.reactions.ViewReactionsBottomSheet
import im.vector.riotx.features.home.room.filtered.FilteredRoomsActivity
import im.vector.riotx.features.home.room.list.RoomListFragment
import im.vector.riotx.features.home.room.list.actions.RoomListQuickActionsBottomSheet
import im.vector.riotx.features.home.room.list.RoomListModule
import im.vector.riotx.features.invite.VectorInviteView
import im.vector.riotx.features.link.LinkHandlerActivity
import im.vector.riotx.features.login.LoginActivity
import im.vector.riotx.features.login.LoginFragment
import im.vector.riotx.features.login.LoginSsoFallbackFragment
import im.vector.riotx.features.media.ImageMediaViewerActivity
import im.vector.riotx.features.media.VideoMediaViewerActivity
import im.vector.riotx.features.navigation.Navigator
@ -59,16 +44,10 @@ import im.vector.riotx.features.rageshake.BugReportActivity
import im.vector.riotx.features.rageshake.BugReporter
import im.vector.riotx.features.rageshake.RageShake
import im.vector.riotx.features.reactions.EmojiReactionPickerActivity
import im.vector.riotx.features.reactions.EmojiSearchResultFragment
import im.vector.riotx.features.reactions.widget.ReactionButton
import im.vector.riotx.features.roomdirectory.PublicRoomsFragment
import im.vector.riotx.features.roomdirectory.RoomDirectoryActivity
import im.vector.riotx.features.roomdirectory.createroom.CreateRoomActivity
import im.vector.riotx.features.roomdirectory.createroom.CreateRoomFragment
import im.vector.riotx.features.roomdirectory.picker.RoomDirectoryPickerFragment
import im.vector.riotx.features.roomdirectory.roompreview.RoomPreviewNoPreviewFragment
import im.vector.riotx.features.settings.*
import im.vector.riotx.features.settings.push.PushGatewaysFragment
import im.vector.riotx.features.share.IncomingShareActivity
import im.vector.riotx.features.ui.UiStateRepository
@ -79,6 +58,7 @@ import im.vector.riotx.features.ui.UiStateRepository
modules = [
AssistedInjectModule::class,
ViewModelModule::class,
FragmentModule::class,
HomeModule::class,
RoomListModule::class
]
@ -88,6 +68,8 @@ interface ScreenComponent {
fun activeSessionHolder(): ActiveSessionHolder
fun fragmentFactory(): FragmentFactory
fun viewModelFactory(): ViewModelProvider.Factory
fun bugReporter(): BugReporter
@ -100,22 +82,6 @@ interface ScreenComponent {
fun inject(activity: HomeActivity)
fun inject(roomDetailFragment: RoomDetailFragment)
fun inject(roomListFragment: RoomListFragment)
fun inject(groupListFragment: GroupListFragment)
fun inject(roomDirectoryPickerFragment: RoomDirectoryPickerFragment)
fun inject(roomPreviewNoPreviewFragment: RoomPreviewNoPreviewFragment)
fun inject(keysBackupSettingsFragment: KeysBackupSettingsFragment)
fun inject(homeDrawerFragment: HomeDrawerFragment)
fun inject(homeDetailFragment: HomeDetailFragment)
fun inject(messageActionsBottomSheet: MessageActionsBottomSheet)
fun inject(viewReactionsBottomSheet: ViewReactionsBottomSheet)
@ -124,30 +90,8 @@ interface ScreenComponent {
fun inject(vectorSettingsActivity: VectorSettingsActivity)
fun inject(createRoomFragment: CreateRoomFragment)
fun inject(keysBackupManageActivity: KeysBackupManageActivity)
fun inject(keysBackupRestoreFromKeyFragment: KeysBackupRestoreFromKeyFragment)
fun inject(keysBackupRestoreFromPassphraseFragment: KeysBackupRestoreFromPassphraseFragment)
fun inject(keysBackupRestoreSuccessFragment: KeysBackupRestoreSuccessFragment)
fun inject(keysBackupSetupStep1Fragment: KeysBackupSetupStep1Fragment)
fun inject(keysBackupSetupStep2Fragment: KeysBackupSetupStep2Fragment)
fun inject(keysBackupSetupStep3Fragment: KeysBackupSetupStep3Fragment)
fun inject(publicRoomsFragment: PublicRoomsFragment)
fun inject(loginFragment: LoginFragment)
fun inject(loginSsoFallbackFragment: LoginSsoFallbackFragment)
fun inject(sasVerificationIncomingFragment: SASVerificationIncomingFragment)
fun inject(emojiReactionPickerActivity: EmojiReactionPickerActivity)
fun inject(loginActivity: LoginActivity)
@ -170,26 +114,8 @@ interface ScreenComponent {
fun inject(videoMediaViewerActivity: VideoMediaViewerActivity)
fun inject(vectorSettingsNotificationPreferenceFragment: VectorSettingsNotificationPreferenceFragment)
fun inject(vectorSettingsPreferencesFragment: VectorSettingsPreferencesFragment)
fun inject(vectorSettingsAdvancedNotificationPreferenceFragment: VectorSettingsAdvancedNotificationPreferenceFragment)
fun inject(vectorSettingsSecurityPrivacyFragment: VectorSettingsSecurityPrivacyFragment)
fun inject(vectorSettingsHelpAboutFragment: VectorSettingsHelpAboutFragment)
fun inject(userAvatarPreference: UserAvatarPreference)
fun inject(vectorSettingsNotificationsTroubleshootFragment: VectorSettingsNotificationsTroubleshootFragment)
fun inject(pushGatewaysFragment: PushGatewaysFragment)
fun inject(createDirectRoomKnownUsersFragment: CreateDirectRoomKnownUsersFragment)
fun inject(createDirectRoomDirectoryUsersFragment: CreateDirectRoomDirectoryUsersFragment)
fun inject(createDirectRoomActivity: CreateDirectRoomActivity)
fun inject(displayReadReceiptsBottomSheet: DisplayReadReceiptsBottomSheet)
@ -198,7 +124,7 @@ interface ScreenComponent {
fun inject(incomingShareActivity: IncomingShareActivity)
fun inject(emojiSearchResultFragment: EmojiSearchResultFragment)
fun inject(roomListActionsBottomSheet: RoomListQuickActionsBottomSheet)
@Component.Factory
interface Factory {

View file

@ -23,7 +23,7 @@ import dagger.Component
import im.vector.matrix.android.api.Matrix
import im.vector.matrix.android.api.auth.Authenticator
import im.vector.matrix.android.api.session.Session
import im.vector.riotx.ActiveSessionObservableStore
import im.vector.riotx.ActiveSessionDataSource
import im.vector.riotx.EmojiCompatFontProvider
import im.vector.riotx.EmojiCompatWrapper
import im.vector.riotx.VectorApplication
@ -33,8 +33,8 @@ import im.vector.riotx.features.configuration.VectorConfiguration
import im.vector.riotx.features.crypto.keysrequest.KeyRequestHandler
import im.vector.riotx.features.crypto.verification.IncomingVerificationRequestHandler
import im.vector.riotx.features.home.AvatarRenderer
import im.vector.riotx.features.home.HomeRoomListObservableStore
import im.vector.riotx.features.home.group.SelectedGroupStore
import im.vector.riotx.features.home.HomeRoomListDataSource
import im.vector.riotx.features.home.group.SelectedGroupDataSource
import im.vector.riotx.features.html.EventHtmlRenderer
import im.vector.riotx.features.navigation.Navigator
import im.vector.riotx.features.notifications.*
@ -43,7 +43,7 @@ import im.vector.riotx.features.rageshake.VectorFileLogger
import im.vector.riotx.features.rageshake.VectorUncaughtExceptionHandler
import im.vector.riotx.features.session.SessionListener
import im.vector.riotx.features.settings.VectorPreferences
import im.vector.riotx.features.share.ShareRoomListObservableStore
import im.vector.riotx.features.share.ShareRoomListDataSource
import im.vector.riotx.features.ui.UiStateRepository
import javax.inject.Singleton
@ -85,13 +85,13 @@ interface VectorComponent {
fun navigator(): Navigator
fun homeRoomListObservableStore(): HomeRoomListObservableStore
fun homeRoomListObservableStore(): HomeRoomListDataSource
fun shareRoomListObservableStore(): ShareRoomListObservableStore
fun shareRoomListObservableStore(): ShareRoomListDataSource
fun selectedGroupStore(): SelectedGroupStore
fun selectedGroupStore(): SelectedGroupDataSource
fun activeSessionObservableStore(): ActiveSessionObservableStore
fun activeSessionObservableStore(): ActiveSessionDataSource
fun incomingVerificationRequestHandler(): IncomingVerificationRequestHandler

View file

@ -0,0 +1,43 @@
/*
* 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.core.di
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentFactory
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Provider
/**
* FragmentFactory which uses Dagger to create the instances.
*/
class VectorFragmentFactory @Inject constructor(
private val creators: @JvmSuppressWildcards Map<Class<out Fragment>, Provider<Fragment>>
) : FragmentFactory() {
override fun instantiate(classLoader: ClassLoader, className: String): Fragment {
val fragmentClass = loadFragmentClass(classLoader, className)
val creator: Provider<out Fragment>? = creators[fragmentClass]
return if (creator == null) {
Timber.v("Unknown model class: $className, fallback to default instance")
super.instantiate(classLoader, className)
} else {
creator.get()
}
}
}

View file

@ -27,10 +27,7 @@ import im.vector.riotx.features.crypto.keysbackup.restore.KeysBackupRestoreFromP
import im.vector.riotx.features.crypto.keysbackup.restore.KeysBackupRestoreSharedViewModel
import im.vector.riotx.features.crypto.keysbackup.setup.KeysBackupSetupSharedViewModel
import im.vector.riotx.features.crypto.verification.SasVerificationViewModel
import im.vector.riotx.features.home.HomeNavigationViewModel
import im.vector.riotx.features.home.createdirect.CreateDirectRoomNavigationViewModel
import im.vector.riotx.features.reactions.EmojiChooserViewModel
import im.vector.riotx.features.roomdirectory.RoomDirectoryNavigationViewModel
import im.vector.riotx.features.workers.signout.SignOutViewModel
@Module
@ -76,16 +73,6 @@ interface ViewModelModule {
@ViewModelKey(KeysBackupRestoreFromPassphraseViewModel::class)
fun bindKeysBackupRestoreFromPassphraseViewModel(viewModel: KeysBackupRestoreFromPassphraseViewModel): ViewModel
@Binds
@IntoMap
@ViewModelKey(RoomDirectoryNavigationViewModel::class)
fun bindRoomDirectoryNavigationViewModel(viewModel: RoomDirectoryNavigationViewModel): ViewModel
@Binds
@IntoMap
@ViewModelKey(HomeNavigationViewModel::class)
fun bindHomeNavigationViewModel(viewModel: HomeNavigationViewModel): ViewModel
@Binds
@IntoMap
@ViewModelKey(KeysBackupSetupSharedViewModel::class)
@ -95,9 +82,4 @@ interface ViewModelModule {
@IntoMap
@ViewModelKey(ConfigurationViewModel::class)
fun bindConfigurationViewModel(viewModel: ConfigurationViewModel): ViewModel
@Binds
@IntoMap
@ViewModelKey(CreateDirectRoomNavigationViewModel::class)
fun bindCreateDirectRoomNavigationViewModel(viewModel: CreateDirectRoomNavigationViewModel): ViewModel
}

View file

@ -5,26 +5,33 @@
* 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
* 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.room.detail.timeline.action
package im.vector.riotx.core.epoxy.bottomsheet
import android.content.res.ColorStateList
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.DrawableRes
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.DrawableCompat
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.core.widget.ImageViewCompat
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.riotx.R
import im.vector.riotx.core.epoxy.VectorEpoxyHolder
import im.vector.riotx.core.epoxy.VectorEpoxyModel
import im.vector.riotx.features.themes.ThemeUtils
/**
* A action for bottom sheet.
@ -42,28 +49,48 @@ abstract class BottomSheetItemAction : VectorEpoxyModel<BottomSheetItemAction.Ho
@EpoxyAttribute
var expanded = false
@EpoxyAttribute
var selected = false
@EpoxyAttribute
var subMenuItem = false
@EpoxyAttribute
var destructive = false
@EpoxyAttribute
lateinit var listener: View.OnClickListener
override fun bind(holder: Holder) {
holder.view.setOnClickListener {
listener.onClick(it)
}
holder.startSpace.isVisible = subMenuItem
val tintColor = if (destructive) {
ContextCompat.getColor(holder.view.context, R.color.riotx_notice)
} else {
ThemeUtils.getColor(holder.view.context, R.attr.riotx_text_secondary)
}
holder.icon.setImageResource(iconRes)
ImageViewCompat.setImageTintList(holder.icon, ColorStateList.valueOf(tintColor))
holder.text.setText(textRes)
holder.expand.isVisible = showExpand
holder.text.setTextColor(tintColor)
holder.selected.isInvisible = !selected
if (showExpand) {
holder.expand.setImageResource(if (expanded) R.drawable.ic_material_expand_less_black else R.drawable.ic_material_expand_more_black)
val expandDrawable = if (expanded) {
ContextCompat.getDrawable(holder.view.context, R.drawable.ic_material_expand_less_black)
} else {
ContextCompat.getDrawable(holder.view.context, R.drawable.ic_material_expand_more_black)
}
expandDrawable?.also {
DrawableCompat.setTint(it, tintColor)
}
holder.text.setCompoundDrawablesWithIntrinsicBounds(null, null, expandDrawable, null)
} else {
holder.text.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null)
}
}
class Holder : VectorEpoxyHolder() {
val startSpace by bind<View>(R.id.action_start_space)
val icon by bind<ImageView>(R.id.action_icon)
val text by bind<TextView>(R.id.action_title)
val expand by bind<ImageView>(R.id.action_expand)
val startSpace by bind<View>(R.id.actionStartSpace)
val icon by bind<ImageView>(R.id.actionIcon)
val text by bind<TextView>(R.id.actionTitle)
val selected by bind<ImageView>(R.id.actionSelected)
}
}

View file

@ -5,15 +5,16 @@
* 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
* 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.room.detail.timeline.action
package im.vector.riotx.core.epoxy.bottomsheet
import android.widget.ImageView
import android.widget.TextView
@ -24,7 +25,6 @@ import im.vector.riotx.core.epoxy.VectorEpoxyHolder
import im.vector.riotx.core.epoxy.VectorEpoxyModel
import im.vector.riotx.core.extensions.setTextOrHide
import im.vector.riotx.features.home.AvatarRenderer
import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData
/**
* A message preview for bottom sheet.
@ -35,7 +35,9 @@ abstract class BottomSheetItemMessagePreview : VectorEpoxyModel<BottomSheetItemM
@EpoxyAttribute
lateinit var avatarRenderer: AvatarRenderer
@EpoxyAttribute
lateinit var informationData: MessageInformationData
lateinit var avatarUrl: String
@EpoxyAttribute
lateinit var senderId: String
@EpoxyAttribute
var senderName: String? = null
@EpoxyAttribute
@ -44,7 +46,7 @@ abstract class BottomSheetItemMessagePreview : VectorEpoxyModel<BottomSheetItemM
var time: CharSequence? = null
override fun bind(holder: Holder) {
avatarRenderer.render(informationData.avatarUrl, informationData.senderId, senderName, holder.avatar)
avatarRenderer.render(avatarUrl, senderId, senderName, holder.avatar)
holder.sender.setTextOrHide(senderName)
holder.body.text = body
holder.timestamp.setTextOrHide(time)

View file

@ -5,15 +5,16 @@
* 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
* 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.room.detail.timeline.action
package im.vector.riotx.core.epoxy.bottomsheet
import android.graphics.Typeface
import android.widget.TextView

View file

@ -0,0 +1,57 @@
/*
* 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.core.epoxy.bottomsheet
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.riotx.R
import im.vector.riotx.core.epoxy.VectorEpoxyHolder
import im.vector.riotx.core.epoxy.VectorEpoxyModel
import im.vector.riotx.core.extensions.setTextOrHide
import im.vector.riotx.features.home.AvatarRenderer
/**
* A room preview for bottom sheet.
*/
@EpoxyModelClass(layout = R.layout.item_bottom_sheet_room_preview)
abstract class BottomSheetItemRoomPreview : VectorEpoxyModel<BottomSheetItemRoomPreview.Holder>() {
@EpoxyAttribute
lateinit var avatarRenderer: AvatarRenderer
@EpoxyAttribute
lateinit var avatarUrl: String
@EpoxyAttribute
lateinit var roomId: String
@EpoxyAttribute
var roomName: String? = null
@EpoxyAttribute var settingsClickListener: View.OnClickListener? = null
override fun bind(holder: Holder) {
avatarRenderer.render(avatarUrl, roomId, roomName, holder.avatar)
holder.roomName.setTextOrHide(roomName)
holder.roomSettings.setOnClickListener(settingsClickListener)
}
class Holder : VectorEpoxyHolder() {
val avatar by bind<ImageView>(R.id.bottomSheetRoomPreviewAvatar)
val roomName by bind<TextView>(R.id.bottomSheetRoomPreviewName)
val roomSettings by bind<View>(R.id.bottomSheetRoomPreviewSettings)
}
}

View file

@ -5,15 +5,16 @@
* 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
* 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.room.detail.timeline.action
package im.vector.riotx.core.epoxy.bottomsheet
import android.view.View
import android.widget.TextView

View file

@ -5,15 +5,16 @@
* 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
* 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.room.detail.timeline.action
package im.vector.riotx.core.epoxy.bottomsheet
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.riotx.R

View file

@ -16,21 +16,40 @@
package im.vector.riotx.core.extensions
import androidx.appcompat.app.AppCompatActivity
import android.os.Parcelable
import androidx.fragment.app.Fragment
import im.vector.riotx.core.platform.VectorBaseActivity
fun AppCompatActivity.addFragment(fragment: Fragment, frameId: Int) {
fun VectorBaseActivity.addFragment(frameId: Int, fragment: Fragment) {
supportFragmentManager.inTransaction { add(frameId, fragment) }
}
fun AppCompatActivity.replaceFragment(fragment: Fragment, frameId: Int, tag: String? = null) {
fun <T : Fragment> VectorBaseActivity.addFragment(frameId: Int, fragmentClass: Class<T>, params: Parcelable? = null, tag: String? = null) {
supportFragmentManager.inTransaction {
add(frameId, fragmentClass, params.toMvRxBundle(), tag)
}
}
fun VectorBaseActivity.replaceFragment(frameId: Int, fragment: Fragment, tag: String? = null) {
supportFragmentManager.inTransaction { replace(frameId, fragment, tag) }
}
fun AppCompatActivity.addFragmentToBackstack(fragment: Fragment, frameId: Int, tag: String? = null) {
fun <T : Fragment> VectorBaseActivity.replaceFragment(frameId: Int, fragmentClass: Class<T>, params: Parcelable? = null, tag: String? = null) {
supportFragmentManager.inTransaction {
replace(frameId, fragmentClass, params.toMvRxBundle(), tag)
}
}
fun VectorBaseActivity.addFragmentToBackstack(frameId: Int, fragment: Fragment, tag: String? = null) {
supportFragmentManager.inTransaction { replace(frameId, fragment).addToBackStack(tag) }
}
fun AppCompatActivity.hideKeyboard() {
fun <T : Fragment> VectorBaseActivity.addFragmentToBackstack(frameId: Int, fragmentClass: Class<T>, params: Parcelable? = null, tag: String? = null) {
supportFragmentManager.inTransaction {
replace(frameId, fragmentClass, params.toMvRxBundle(), tag).addToBackStack(tag)
}
}
fun VectorBaseActivity.hideKeyboard() {
currentFocus?.hideKeyboard()
}

View file

@ -16,28 +16,66 @@
package im.vector.riotx.core.extensions
import android.os.Parcelable
import androidx.fragment.app.Fragment
import im.vector.riotx.core.platform.VectorBaseFragment
fun Fragment.addFragment(fragment: Fragment, frameId: Int) {
fragmentManager?.inTransaction { add(frameId, fragment) }
fun VectorBaseFragment.addFragment(frameId: Int, fragment: Fragment) {
parentFragmentManager.inTransaction { add(frameId, fragment) }
}
fun Fragment.replaceFragment(fragment: Fragment, frameId: Int) {
fragmentManager?.inTransaction { replace(frameId, fragment) }
fun <T : Fragment> VectorBaseFragment.addFragment(frameId: Int, fragmentClass: Class<T>, params: Parcelable? = null, tag: String? = null) {
parentFragmentManager.inTransaction {
add(frameId, fragmentClass, params.toMvRxBundle(), tag)
}
}
fun Fragment.addFragmentToBackstack(fragment: Fragment, frameId: Int, tag: String? = null) {
fragmentManager?.inTransaction { replace(frameId, fragment).addToBackStack(tag) }
fun VectorBaseFragment.replaceFragment(frameId: Int, fragment: Fragment) {
parentFragmentManager.inTransaction { replace(frameId, fragment) }
}
fun Fragment.addChildFragment(fragment: Fragment, frameId: Int) {
fun <T : Fragment> VectorBaseFragment.replaceFragment(frameId: Int, fragmentClass: Class<T>, params: Parcelable? = null, tag: String? = null) {
parentFragmentManager.inTransaction {
replace(frameId, fragmentClass, params.toMvRxBundle(), tag)
}
}
fun VectorBaseFragment.addFragmentToBackstack(frameId: Int, fragment: Fragment, tag: String? = null) {
parentFragmentManager.inTransaction { replace(frameId, fragment).addToBackStack(tag) }
}
fun <T : Fragment> VectorBaseFragment.addFragmentToBackstack(frameId: Int, fragmentClass: Class<T>, params: Parcelable? = null, tag: String? = null) {
parentFragmentManager.inTransaction {
replace(frameId, fragmentClass, params.toMvRxBundle(), tag).addToBackStack(tag)
}
}
fun VectorBaseFragment.addChildFragment(frameId: Int, fragment: Fragment) {
childFragmentManager.inTransaction { add(frameId, fragment) }
}
fun Fragment.replaceChildFragment(fragment: Fragment, frameId: Int) {
fun <T : Fragment> VectorBaseFragment.addChildFragment(frameId: Int, fragmentClass: Class<T>, params: Parcelable? = null, tag: String? = null) {
childFragmentManager.inTransaction {
add(frameId, fragmentClass, params.toMvRxBundle(), tag)
}
}
fun VectorBaseFragment.replaceChildFragment(frameId: Int, fragment: Fragment) {
childFragmentManager.inTransaction { replace(frameId, fragment) }
}
fun Fragment.addChildFragmentToBackstack(fragment: Fragment, frameId: Int, tag: String? = null) {
fun <T : Fragment> VectorBaseFragment.replaceChildFragment(frameId: Int, fragmentClass: Class<T>, params: Parcelable? = null, tag: String? = null) {
childFragmentManager.inTransaction {
replace(frameId, fragmentClass, params.toMvRxBundle(), tag)
}
}
fun VectorBaseFragment.addChildFragmentToBackstack(frameId: Int, fragment: Fragment, tag: String? = null) {
childFragmentManager.inTransaction { replace(frameId, fragment).addToBackStack(tag) }
}
fun <T : Fragment> VectorBaseFragment.addChildFragmentToBackstack(frameId: Int, fragmentClass: Class<T>, params: Parcelable? = null, tag: String? = null) {
childFragmentManager.inTransaction {
replace(frameId, fragmentClass, params.toMvRxBundle(), tag).addToBackStack(tag)
}
}

View file

@ -1,34 +0,0 @@
/*
* 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.core.mvrx
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import im.vector.riotx.core.extensions.postLiveEvent
import im.vector.riotx.core.utils.LiveEvent
abstract class NavigationViewModel<NavigationClass> : ViewModel() {
private val _navigateTo = MutableLiveData<LiveEvent<NavigationClass>>()
val navigateTo: LiveData<LiveEvent<NavigationClass>>
get() = _navigateTo
fun goTo(navigation: NavigationClass) {
_navigateTo.postLiveEvent(navigation)
}
}

View file

@ -19,6 +19,7 @@ package im.vector.riotx.core.platform
import android.content.Context
import android.content.res.Configuration
import android.os.Bundle
import android.os.Parcelable
import android.view.Menu
import android.view.MenuItem
import android.view.View
@ -34,6 +35,7 @@ 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
import im.vector.riotx.BuildConfig
@ -125,7 +127,7 @@ abstract class VectorBaseActivity : BaseMvRxActivity(), HasScreenInjector {
}
Timber.v("Injecting dependencies into ${javaClass.simpleName} took $timeForInjection ms")
ThemeUtils.setActivityTheme(this, getOtherThemes())
supportFragmentManager.fragmentFactory = screenComponent.fragmentFactory()
super.onCreate(savedInstanceState)
viewModelFactory = screenComponent.viewModelFactory()
configurationViewModel = ViewModelProviders.of(this, viewModelFactory).get(ConfigurationViewModel::class.java)
@ -331,6 +333,10 @@ abstract class VectorBaseActivity : BaseMvRxActivity(), HasScreenInjector {
}
}
fun Parcelable?.toMvRxBundle(): Bundle? {
return this?.let { Bundle().apply { putParcelable(MvRx.KEY_ARG, it) } }
}
// ==============================================================================================
// Handle loading view (also called waiting view or spinner view)
// ==============================================================================================

View file

@ -63,6 +63,7 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), HasScreenInjector {
screenComponent = DaggerScreenComponent.factory().create(vectorBaseActivity.getVectorComponent(), vectorBaseActivity)
navigator = screenComponent.navigator()
viewModelFactory = screenComponent.viewModelFactory()
childFragmentManager.fragmentFactory = screenComponent.fragmentFactory()
injectWith(injector())
super.onAttach(context)
}
@ -134,7 +135,11 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), HasScreenInjector {
}
protected fun setArguments(args: Parcelable? = null) {
arguments = args?.let { Bundle().apply { putParcelable(MvRx.KEY_ARG, it) } }
arguments = args.toMvRxBundle()
}
fun Parcelable?.toMvRxBundle(): Bundle? {
return this?.let { Bundle().apply { putParcelable(MvRx.KEY_ARG, it) } }
}
@MainThread

View file

@ -1,54 +0,0 @@
/*
* 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.core.platform
import androidx.annotation.CallSuper
import androidx.preference.PreferenceFragmentCompat
import im.vector.riotx.R
import im.vector.riotx.core.utils.toast
import timber.log.Timber
abstract class VectorPreferenceFragment : PreferenceFragmentCompat() {
val vectorActivity: VectorBaseActivity by lazy {
activity as VectorBaseActivity
}
abstract var titleRes: Int
/* ==========================================================================================
* Life cycle
* ========================================================================================== */
@CallSuper
override fun onResume() {
super.onResume()
(activity as? VectorBaseActivity)?.supportActionBar?.setTitle(titleRes)
Timber.v("onResume Fragment ${this.javaClass.simpleName}")
}
/* ==========================================================================================
* Protected
* ========================================================================================== */
protected fun notImplemented() {
// Snackbar cannot be display on PreferenceFragment
// Snackbar.make(view!!, R.string.not_implemented, Snackbar.LENGTH_SHORT)
activity?.toast(R.string.not_implemented)
}
}

View file

@ -16,13 +16,21 @@
package im.vector.riotx.core.platform
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.airbnb.mvrx.*
import im.vector.riotx.core.utils.LiveEvent
import io.reactivex.Observable
import io.reactivex.Single
abstract class VectorViewModel<S : MvRxState>(initialState: S)
: BaseMvRxViewModel<S>(initialState, false) {
// Generic handling of any request error
protected val _requestErrorLiveData = MutableLiveData<LiveEvent<Throwable>>()
val requestErrorLiveData: LiveData<LiveEvent<Throwable>>
get() = _requestErrorLiveData
/**
* This method does the same thing as the execute function, but it doesn't subscribe to the stream
* so you can use this in a switchMap or a flatMap

View file

@ -17,18 +17,30 @@
package im.vector.riotx.core.utils
import com.jakewharton.rxrelay2.BehaviorRelay
import com.jakewharton.rxrelay2.PublishRelay
import io.reactivex.Observable
import io.reactivex.schedulers.Schedulers
open class RxStore<T>(private val defaultValue: T? = null) {
interface DataSource<T> {
fun observe(): Observable<T>
}
interface MutableDataSource<T> : DataSource<T> {
fun post(value: T)
}
/**
* This datasource emits the most recent value it has observed and all subsequent observed values to each subscriber.
*/
open class BehaviorDataSource<T>(private val defaultValue: T? = null) : MutableDataSource<T> {
private val storeRelay = createRelay()
fun observe(): Observable<T> {
override fun observe(): Observable<T> {
return storeRelay.hide().observeOn(Schedulers.computation())
}
fun post(value: T) {
override fun post(value: T) {
storeRelay.accept(value)
}
@ -40,3 +52,19 @@ open class RxStore<T>(private val defaultValue: T? = null) {
}
}
}
/**
* This datasource only emits all subsequent observed values to each subscriber.
*/
open class PublishDataSource<T> : MutableDataSource<T> {
private val storeRelay = PublishRelay.create<T>()
override fun observe(): Observable<T> {
return storeRelay.hide()
}
override fun post(value: T) {
storeRelay.accept(value)
}
}

View file

@ -22,9 +22,10 @@ import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import im.vector.fragments.keysbackup.restore.KeysBackupRestoreFromPassphraseFragment
import im.vector.riotx.R
import im.vector.riotx.core.extensions.addFragmentToBackstack
import im.vector.riotx.core.extensions.observeEvent
import im.vector.riotx.core.extensions.replaceFragment
import im.vector.riotx.core.platform.SimpleFragmentActivity
class KeysBackupRestoreActivity : SimpleFragmentActivity() {
@ -49,13 +50,9 @@ class KeysBackupRestoreActivity : SimpleFragmentActivity() {
if (keyVersion != null && supportFragmentManager.fragments.isEmpty()) {
val isBackupCreatedFromPassphrase = keyVersion.getAuthDataAsMegolmBackupAuthData()?.privateKeySalt != null
if (isBackupCreatedFromPassphrase) {
supportFragmentManager.beginTransaction()
.replace(R.id.container, KeysBackupRestoreFromPassphraseFragment.newInstance())
.commitNow()
replaceFragment(R.id.container, KeysBackupRestoreFromPassphraseFragment::class.java)
} else {
supportFragmentManager.beginTransaction()
.replace(R.id.container, KeysBackupRestoreFromKeyFragment.newInstance())
.commitNow()
replaceFragment(R.id.container, KeysBackupRestoreFromKeyFragment::class.java)
}
}
})
@ -80,16 +77,11 @@ class KeysBackupRestoreActivity : SimpleFragmentActivity() {
viewModel.navigateEvent.observeEvent(this) { uxStateEvent ->
when (uxStateEvent) {
KeysBackupRestoreSharedViewModel.NAVIGATE_TO_RECOVER_WITH_KEY -> {
supportFragmentManager.beginTransaction()
.replace(R.id.container, KeysBackupRestoreFromKeyFragment.newInstance())
.addToBackStack(null)
.commit()
addFragmentToBackstack(R.id.container, KeysBackupRestoreFromKeyFragment::class.java)
}
KeysBackupRestoreSharedViewModel.NAVIGATE_TO_SUCCESS -> {
supportFragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
supportFragmentManager.beginTransaction()
.replace(R.id.container, KeysBackupRestoreSuccessFragment.newInstance())
.commit()
replaceFragment(R.id.container, KeysBackupRestoreSuccessFragment::class.java)
}
}
}

View file

@ -28,15 +28,15 @@ import butterknife.OnClick
import butterknife.OnTextChanged
import com.google.android.material.textfield.TextInputLayout
import im.vector.riotx.R
import im.vector.riotx.core.di.ScreenComponent
import im.vector.riotx.core.platform.VectorBaseFragment
import im.vector.riotx.core.utils.startImportTextFromFileIntent
import timber.log.Timber
import javax.inject.Inject
class KeysBackupRestoreFromKeyFragment : VectorBaseFragment() {
class KeysBackupRestoreFromKeyFragment @Inject constructor()
: VectorBaseFragment() {
companion object {
fun newInstance() = KeysBackupRestoreFromKeyFragment()
private const val REQUEST_TEXT_FILE_GET = 1
}
@ -51,10 +51,6 @@ class KeysBackupRestoreFromKeyFragment : VectorBaseFragment() {
@BindView(R.id.keys_restore_key_enter_edittext)
lateinit var mKeyTextEdit: EditText
override fun injectWith(injector: ScreenComponent) {
injector.inject(this)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel = ViewModelProviders.of(this, viewModelFactory).get(KeysBackupRestoreFromKeyViewModel::class.java)
@ -72,7 +68,7 @@ class KeysBackupRestoreFromKeyFragment : VectorBaseFragment() {
}
mKeyInputLayout.error = viewModel.recoveryCodeErrorText.value
viewModel.recoveryCodeErrorText.observe(this, Observer { newValue ->
viewModel.recoveryCodeErrorText.observe(viewLifecycleOwner, Observer { newValue ->
mKeyInputLayout.error = newValue
})
}

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.fragments.keysbackup.restore
package im.vector.riotx.features.crypto.keysbackup.restore
import android.content.Context
import android.os.Bundle
@ -33,13 +33,11 @@ import butterknife.OnClick
import butterknife.OnTextChanged
import com.google.android.material.textfield.TextInputLayout
import im.vector.riotx.R
import im.vector.riotx.core.di.ScreenComponent
import im.vector.riotx.core.extensions.showPassword
import im.vector.riotx.core.platform.VectorBaseFragment
import im.vector.riotx.features.crypto.keysbackup.restore.KeysBackupRestoreFromPassphraseViewModel
import im.vector.riotx.features.crypto.keysbackup.restore.KeysBackupRestoreSharedViewModel
import javax.inject.Inject
class KeysBackupRestoreFromPassphraseFragment : VectorBaseFragment() {
class KeysBackupRestoreFromPassphraseFragment @Inject constructor(): VectorBaseFragment() {
override fun getLayoutResId() = R.layout.fragment_keys_backup_restore_from_passphrase
@ -63,14 +61,6 @@ class KeysBackupRestoreFromPassphraseFragment : VectorBaseFragment() {
viewModel.showPasswordMode.value = !(viewModel.showPasswordMode.value ?: false)
}
companion object {
fun newInstance() = KeysBackupRestoreFromPassphraseFragment()
}
override fun injectWith(injector: ScreenComponent) {
injector.inject(this)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
@ -79,13 +69,13 @@ class KeysBackupRestoreFromPassphraseFragment : VectorBaseFragment() {
ViewModelProviders.of(this, viewModelFactory).get(KeysBackupRestoreSharedViewModel::class.java)
} ?: throw Exception("Invalid Activity")
viewModel.passphraseErrorText.observe(this, Observer { newValue ->
viewModel.passphraseErrorText.observe(viewLifecycleOwner, Observer { newValue ->
mPassphraseInputLayout.error = newValue
})
helperTextWithLink.text = spannableStringForHelperText(context!!)
viewModel.showPasswordMode.observe(this, Observer {
viewModel.showPasswordMode.observe(viewLifecycleOwner, Observer {
val shouldBeVisible = it ?: false
mPassphraseTextEdit.showPassword(shouldBeVisible)
mPassphraseReveal.setImageResource(if (shouldBeVisible) R.drawable.ic_eye_closed_black else R.drawable.ic_eye_black)

View file

@ -21,11 +21,11 @@ import androidx.lifecycle.ViewModelProviders
import butterknife.BindView
import butterknife.OnClick
import im.vector.riotx.R
import im.vector.riotx.core.di.ScreenComponent
import im.vector.riotx.core.platform.VectorBaseFragment
import im.vector.riotx.core.utils.LiveEvent
import javax.inject.Inject
class KeysBackupRestoreSuccessFragment : VectorBaseFragment() {
class KeysBackupRestoreSuccessFragment @Inject constructor() : VectorBaseFragment() {
override fun getLayoutResId() = R.layout.fragment_keys_backup_restore_success
@ -36,10 +36,6 @@ class KeysBackupRestoreSuccessFragment : VectorBaseFragment() {
private lateinit var sharedViewModel: KeysBackupRestoreSharedViewModel
override fun injectWith(injector: ScreenComponent) {
injector.inject(this)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
sharedViewModel = activity?.run {
@ -62,8 +58,4 @@ class KeysBackupRestoreSuccessFragment : VectorBaseFragment() {
fun onDone() {
sharedViewModel.importRoomKeysFinishWithResult.value = LiveEvent(sharedViewModel.importKeyResult!!)
}
companion object {
fun newInstance() = KeysBackupRestoreSuccessFragment()
}
}

View file

@ -23,6 +23,7 @@ import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.viewModel
import im.vector.riotx.R
import im.vector.riotx.core.di.ScreenComponent
import im.vector.riotx.core.extensions.replaceFragment
import im.vector.riotx.core.platform.SimpleFragmentActivity
import im.vector.riotx.core.platform.WaitingViewData
import javax.inject.Inject
@ -49,10 +50,7 @@ class KeysBackupManageActivity : SimpleFragmentActivity() {
override fun initUiAndData() {
super.initUiAndData()
if (supportFragmentManager.fragments.isEmpty()) {
supportFragmentManager.beginTransaction()
.replace(R.id.container, KeysBackupSettingsFragment.newInstance())
.commitNow()
replaceFragment(R.id.container, KeysBackupSettingsFragment::class.java)
viewModel.init()
}

View file

@ -21,29 +21,20 @@ import androidx.appcompat.app.AlertDialog
import com.airbnb.mvrx.activityViewModel
import com.airbnb.mvrx.withState
import im.vector.riotx.R
import im.vector.riotx.core.di.ScreenComponent
import im.vector.riotx.core.platform.VectorBaseFragment
import im.vector.riotx.features.crypto.keysbackup.restore.KeysBackupRestoreActivity
import im.vector.riotx.features.crypto.keysbackup.setup.KeysBackupSetupActivity
import kotlinx.android.synthetic.main.fragment_keys_backup_settings.*
import javax.inject.Inject
class KeysBackupSettingsFragment : VectorBaseFragment(),
KeysBackupSettingsRecyclerViewController.Listener {
companion object {
fun newInstance() = KeysBackupSettingsFragment()
}
class KeysBackupSettingsFragment @Inject constructor(private val keysBackupSettingsRecyclerViewController: KeysBackupSettingsRecyclerViewController)
: VectorBaseFragment(),
KeysBackupSettingsRecyclerViewController.Listener {
override fun getLayoutResId() = R.layout.fragment_keys_backup_settings
@Inject lateinit var keysBackupSettingsRecyclerViewController: KeysBackupSettingsRecyclerViewController
private val viewModel: KeysBackupSettingsViewModel by activityViewModel()
override fun injectWith(injector: ScreenComponent) {
injector.inject(this)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

View file

@ -26,6 +26,7 @@ import im.vector.matrix.android.api.MatrixCallback
import im.vector.riotx.R
import im.vector.riotx.core.dialogs.ExportKeysDialog
import im.vector.riotx.core.extensions.observeEvent
import im.vector.riotx.core.extensions.replaceFragment
import im.vector.riotx.core.platform.SimpleFragmentActivity
import im.vector.riotx.core.utils.*
import im.vector.riotx.features.crypto.keys.KeysExporter
@ -39,9 +40,7 @@ class KeysBackupSetupActivity : SimpleFragmentActivity() {
override fun initUiAndData() {
super.initUiAndData()
if (isFirstCreation()) {
supportFragmentManager.beginTransaction()
.replace(R.id.container, KeysBackupSetupStep1Fragment.newInstance())
.commitNow()
replaceFragment(R.id.container, KeysBackupSetupStep1Fragment::class.java)
}
viewModel = ViewModelProviders.of(this, viewModelFactory).get(KeysBackupSetupSharedViewModel::class.java)
@ -67,15 +66,11 @@ class KeysBackupSetupActivity : SimpleFragmentActivity() {
when (uxStateEvent) {
KeysBackupSetupSharedViewModel.NAVIGATE_TO_STEP_2 -> {
supportFragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
supportFragmentManager.beginTransaction()
.replace(R.id.container, KeysBackupSetupStep2Fragment.newInstance())
.commit()
replaceFragment(R.id.container, KeysBackupSetupStep2Fragment::class.java)
}
KeysBackupSetupSharedViewModel.NAVIGATE_TO_STEP_3 -> {
supportFragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
supportFragmentManager.beginTransaction()
.replace(R.id.container, KeysBackupSetupStep3Fragment.newInstance())
.commit()
replaceFragment(R.id.container, KeysBackupSetupStep3Fragment::class.java)
}
KeysBackupSetupSharedViewModel.NAVIGATE_FINISH -> {
val resultIntent = Intent()

View file

@ -25,15 +25,11 @@ import androidx.lifecycle.ViewModelProviders
import butterknife.BindView
import butterknife.OnClick
import im.vector.riotx.R
import im.vector.riotx.core.di.ScreenComponent
import im.vector.riotx.core.platform.VectorBaseFragment
import im.vector.riotx.core.utils.LiveEvent
import javax.inject.Inject
class KeysBackupSetupStep1Fragment : VectorBaseFragment() {
companion object {
fun newInstance() = KeysBackupSetupStep1Fragment()
}
class KeysBackupSetupStep1Fragment @Inject constructor() : VectorBaseFragment() {
override fun getLayoutResId() = R.layout.fragment_keys_backup_setup_step1
@ -45,10 +41,6 @@ class KeysBackupSetupStep1Fragment : VectorBaseFragment() {
@BindView(R.id.keys_backup_setup_step1_manualExport)
lateinit var manualExportButton: Button
override fun injectWith(injector: ScreenComponent) {
injector.inject(this)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
@ -56,7 +48,7 @@ class KeysBackupSetupStep1Fragment : VectorBaseFragment() {
ViewModelProviders.of(this, viewModelFactory).get(KeysBackupSetupSharedViewModel::class.java)
} ?: throw Exception("Invalid Activity")
viewModel.showManualExport.observe(this, Observer {
viewModel.showManualExport.observe(viewLifecycleOwner, Observer {
val showOption = it ?: false
// Can't use isVisible because the kotlin compiler will crash with Back-end (JVM) Internal error: wrong code generated
advancedOptionText.visibility = if (showOption) View.VISIBLE else View.GONE

View file

@ -30,13 +30,13 @@ import butterknife.OnTextChanged
import com.google.android.material.textfield.TextInputLayout
import com.nulabinc.zxcvbn.Zxcvbn
import im.vector.riotx.R
import im.vector.riotx.core.di.ScreenComponent
import im.vector.riotx.core.extensions.showPassword
import im.vector.riotx.core.platform.VectorBaseFragment
import im.vector.riotx.core.ui.views.PasswordStrengthBar
import im.vector.riotx.features.settings.VectorLocale
import javax.inject.Inject
class KeysBackupSetupStep2Fragment : VectorBaseFragment() {
class KeysBackupSetupStep2Fragment @Inject constructor() : VectorBaseFragment() {
override fun getLayoutResId() = R.layout.fragment_keys_backup_setup_step2
@ -76,10 +76,6 @@ class KeysBackupSetupStep2Fragment : VectorBaseFragment() {
private lateinit var viewModel: KeysBackupSetupSharedViewModel
override fun injectWith(injector: ScreenComponent) {
injector.inject(this)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
@ -96,7 +92,7 @@ class KeysBackupSetupStep2Fragment : VectorBaseFragment() {
* ========================================================================================== */
private fun bindViewToViewModel() {
viewModel.passwordStrength.observe(this, Observer { strength ->
viewModel.passwordStrength.observe(viewLifecycleOwner, Observer { strength ->
if (strength == null) {
mPassphraseProgressLevel.strength = 0
mPassphraseInputLayout.error = null
@ -120,7 +116,7 @@ class KeysBackupSetupStep2Fragment : VectorBaseFragment() {
}
})
viewModel.passphrase.observe(this, Observer<String> { newValue ->
viewModel.passphrase.observe(viewLifecycleOwner, Observer<String> { newValue ->
if (newValue.isEmpty()) {
viewModel.passwordStrength.value = null
} else {
@ -135,21 +131,21 @@ class KeysBackupSetupStep2Fragment : VectorBaseFragment() {
mPassphraseTextEdit.setText(viewModel.passphrase.value)
viewModel.passphraseError.observe(this, Observer {
viewModel.passphraseError.observe(viewLifecycleOwner, Observer {
TransitionManager.beginDelayedTransition(rootGroup)
mPassphraseInputLayout.error = it
})
mPassphraseConfirmTextEdit.setText(viewModel.confirmPassphrase.value)
viewModel.showPasswordMode.observe(this, Observer {
viewModel.showPasswordMode.observe(viewLifecycleOwner, Observer {
val shouldBeVisible = it ?: false
mPassphraseTextEdit.showPassword(shouldBeVisible)
mPassphraseConfirmTextEdit.showPassword(shouldBeVisible)
mPassphraseReveal.setImageResource(if (shouldBeVisible) R.drawable.ic_eye_closed_black else R.drawable.ic_eye_black)
})
viewModel.confirmPassphraseError.observe(this, Observer {
viewModel.confirmPassphraseError.observe(viewLifecycleOwner, Observer {
TransitionManager.beginDelayedTransition(rootGroup)
mPassphraseConfirmInputLayout.error = it
})
@ -203,8 +199,4 @@ class KeysBackupSetupStep2Fragment : VectorBaseFragment() {
}
}
}
companion object {
fun newInstance() = KeysBackupSetupStep2Fragment()
}
}

View file

@ -30,7 +30,6 @@ import butterknife.BindView
import butterknife.OnClick
import com.google.android.material.bottomsheet.BottomSheetDialog
import im.vector.riotx.R
import im.vector.riotx.core.di.ScreenComponent
import im.vector.riotx.core.files.addEntryToDownloadManager
import im.vector.riotx.core.files.writeToFile
import im.vector.riotx.core.platform.VectorBaseFragment
@ -40,8 +39,9 @@ import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
import javax.inject.Inject
class KeysBackupSetupStep3Fragment : VectorBaseFragment() {
class KeysBackupSetupStep3Fragment @Inject constructor() : VectorBaseFragment() {
override fun getLayoutResId() = R.layout.fragment_keys_backup_setup_step3
@ -54,16 +54,8 @@ class KeysBackupSetupStep3Fragment : VectorBaseFragment() {
@BindView(R.id.keys_backup_setup_step3_line2_text)
lateinit var mRecoveryKeyLabel2TextView: TextView
companion object {
fun newInstance() = KeysBackupSetupStep3Fragment()
}
private lateinit var viewModel: KeysBackupSetupSharedViewModel
override fun injectWith(injector: ScreenComponent) {
injector.inject(this)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel = activity?.run {
@ -72,7 +64,7 @@ class KeysBackupSetupStep3Fragment : VectorBaseFragment() {
viewModel.shouldPromptOnBack = false
viewModel.passphrase.observe(this, Observer {
viewModel.passphrase.observe(viewLifecycleOwner, Observer {
if (it.isNullOrBlank()) {
// Recovery was generated, so show key and options to save
mRecoveryKeyLabel2TextView.text = getString(R.string.keys_backup_setup_step3_text_line2_no_passphrase)

View file

@ -27,6 +27,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.observeEvent
import im.vector.riotx.core.platform.SimpleFragmentActivity
import im.vector.riotx.core.platform.WaitingViewData
@ -102,23 +103,23 @@ class SASVerificationActivity : SimpleFragmentActivity() {
IncomingSasVerificationTransaction.UxState.SHOW_ACCEPT,
IncomingSasVerificationTransaction.UxState.WAIT_FOR_KEY_AGREEMENT -> {
supportActionBar?.setTitle(R.string.sas_incoming_request_title)
supportFragmentManager.beginTransaction()
.setCustomAnimations(R.anim.no_anim, R.anim.exit_fade_out)
.replace(R.id.container, SASVerificationIncomingFragment.newInstance())
.commitNow()
supportFragmentManager.inTransaction {
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.beginTransaction()
.setCustomAnimations(R.anim.no_anim, R.anim.exit_fade_out)
.replace(R.id.container, SASVerificationShortCodeFragment.newInstance())
.commitNow()
supportFragmentManager.inTransaction {
setCustomAnimations(R.anim.no_anim, R.anim.exit_fade_out)
replace(R.id.container, SASVerificationShortCodeFragment::class.java, null)
}
}
IncomingSasVerificationTransaction.UxState.VERIFIED -> {
supportFragmentManager.beginTransaction()
.setCustomAnimations(R.anim.no_anim, R.anim.exit_fade_out)
.replace(R.id.container, SASVerificationVerifiedFragment.newInstance())
.commitNow()
supportFragmentManager.inTransaction {
setCustomAnimations(R.anim.no_anim, R.anim.exit_fade_out)
replace(R.id.container, SASVerificationVerifiedFragment::class.java, null)
}
}
IncomingSasVerificationTransaction.UxState.CANCELLED_BY_ME,
IncomingSasVerificationTransaction.UxState.CANCELLED_BY_OTHER -> {
@ -133,23 +134,23 @@ class SASVerificationActivity : SimpleFragmentActivity() {
OutgoingSasVerificationRequest.UxState.UNKNOWN,
OutgoingSasVerificationRequest.UxState.WAIT_FOR_START,
OutgoingSasVerificationRequest.UxState.WAIT_FOR_KEY_AGREEMENT -> {
supportFragmentManager.beginTransaction()
.setCustomAnimations(R.anim.no_anim, R.anim.exit_fade_out)
.replace(R.id.container, SASVerificationStartFragment.newInstance())
.commitNow()
supportFragmentManager.inTransaction {
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.beginTransaction()
.setCustomAnimations(R.anim.no_anim, R.anim.exit_fade_out)
.replace(R.id.container, SASVerificationShortCodeFragment.newInstance())
.commitNow()
supportFragmentManager.inTransaction {
setCustomAnimations(R.anim.no_anim, R.anim.exit_fade_out)
replace(R.id.container, SASVerificationShortCodeFragment::class.java, null)
}
}
OutgoingSasVerificationRequest.UxState.VERIFIED -> {
supportFragmentManager.beginTransaction()
.setCustomAnimations(R.anim.no_anim, R.anim.exit_fade_out)
.replace(R.id.container, SASVerificationVerifiedFragment.newInstance())
.commitNow()
supportFragmentManager.inTransaction {
setCustomAnimations(R.anim.no_anim, R.anim.exit_fade_out)
replace(R.id.container, SASVerificationVerifiedFragment::class.java, null)
}
}
OutgoingSasVerificationRequest.UxState.CANCELLED_BY_ME,
OutgoingSasVerificationRequest.UxState.CANCELLED_BY_OTHER -> {
@ -172,16 +173,16 @@ class SASVerificationActivity : SimpleFragmentActivity() {
finish()
}
SasVerificationViewModel.NAVIGATE_SAS_DISPLAY -> {
supportFragmentManager.beginTransaction()
.setCustomAnimations(R.anim.enter_from_right, R.anim.exit_fade_out)
.replace(R.id.container, SASVerificationShortCodeFragment.newInstance())
.commitNow()
supportFragmentManager.inTransaction {
setCustomAnimations(R.anim.enter_from_right, R.anim.exit_fade_out)
replace(R.id.container, SASVerificationShortCodeFragment::class.java, null)
}
}
SasVerificationViewModel.NAVIGATE_SUCCESS -> {
supportFragmentManager.beginTransaction()
.setCustomAnimations(R.anim.enter_from_right, R.anim.exit_fade_out)
.replace(R.id.container, SASVerificationVerifiedFragment.newInstance())
.commitNow()
supportFragmentManager.inTransaction {
setCustomAnimations(R.anim.enter_from_right, R.anim.exit_fade_out)
replace(R.id.container, SASVerificationVerifiedFragment::class.java, null)
}
}
SasVerificationViewModel.NAVIGATE_CANCELLED -> {
val isCancelledByMe = viewModel.transaction?.state == SasVerificationTxState.Cancelled

View file

@ -24,16 +24,13 @@ import butterknife.BindView
import butterknife.OnClick
import im.vector.matrix.android.api.session.crypto.sas.IncomingSasVerificationTransaction
import im.vector.riotx.R
import im.vector.riotx.core.di.ScreenComponent
import im.vector.riotx.core.platform.VectorBaseFragment
import im.vector.riotx.features.home.AvatarRenderer
import javax.inject.Inject
class SASVerificationIncomingFragment : VectorBaseFragment() {
companion object {
fun newInstance() = SASVerificationIncomingFragment()
}
class SASVerificationIncomingFragment @Inject constructor(
private var avatarRenderer: AvatarRenderer
) : VectorBaseFragment() {
@BindView(R.id.sas_incoming_request_user_display_name)
lateinit var otherUserDisplayNameTextView: TextView
@ -49,13 +46,8 @@ class SASVerificationIncomingFragment : VectorBaseFragment() {
override fun getLayoutResId() = R.layout.fragment_sas_verification_incoming_request
@Inject lateinit var avatarRenderer: AvatarRenderer
private lateinit var viewModel: SasVerificationViewModel
override fun injectWith(injector: ScreenComponent) {
injector.inject(this)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
@ -74,7 +66,7 @@ class SASVerificationIncomingFragment : VectorBaseFragment() {
avatarRenderer.render(null, viewModel.otherUserId ?: "", viewModel.otherUserId, avatarImageView)
}
viewModel.transactionState.observe(this, Observer {
viewModel.transactionState.observe(viewLifecycleOwner, Observer {
val uxState = (viewModel.transaction as? IncomingSasVerificationTransaction)?.uxState
when (uxState) {
IncomingSasVerificationTransaction.UxState.SHOW_ACCEPT -> {

View file

@ -28,15 +28,12 @@ import im.vector.matrix.android.api.session.crypto.sas.IncomingSasVerificationTr
import im.vector.matrix.android.api.session.crypto.sas.OutgoingSasVerificationRequest
import im.vector.riotx.R
import im.vector.riotx.core.platform.VectorBaseFragment
import javax.inject.Inject
class SASVerificationShortCodeFragment : VectorBaseFragment() {
class SASVerificationShortCodeFragment @Inject constructor(): VectorBaseFragment() {
private lateinit var viewModel: SasVerificationViewModel
companion object {
fun newInstance() = SASVerificationShortCodeFragment()
}
@BindView(R.id.sas_decimal_code)
lateinit var decimalTextView: TextView
@ -120,7 +117,7 @@ class SASVerificationShortCodeFragment : VectorBaseFragment() {
}
}
viewModel.transactionState.observe(this, Observer {
viewModel.transactionState.observe(viewLifecycleOwner, Observer {
if (viewModel.transaction is IncomingSasVerificationTransaction) {
val uxState = (viewModel.transaction as IncomingSasVerificationTransaction).uxState
when (uxState) {

View file

@ -31,12 +31,9 @@ import im.vector.matrix.android.api.session.crypto.sas.OutgoingSasVerificationRe
import im.vector.riotx.R
import im.vector.riotx.core.platform.VectorBaseActivity
import im.vector.riotx.core.platform.VectorBaseFragment
import javax.inject.Inject
class SASVerificationStartFragment : VectorBaseFragment() {
companion object {
fun newInstance() = SASVerificationStartFragment()
}
class SASVerificationStartFragment @Inject constructor(): VectorBaseFragment() {
override fun getLayoutResId() = R.layout.fragment_sas_verification_start
@ -57,7 +54,7 @@ class SASVerificationStartFragment : VectorBaseFragment() {
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel = ViewModelProviders.of(vectorBaseActivity, viewModelFactory).get(SasVerificationViewModel::class.java)
viewModel.transactionState.observe(this, Observer {
viewModel.transactionState.observe(viewLifecycleOwner, Observer {
val uxState = (viewModel.transaction as? OutgoingSasVerificationRequest)?.uxState
when (uxState) {
OutgoingSasVerificationRequest.UxState.WAIT_FOR_KEY_AGREEMENT -> {

View file

@ -20,15 +20,12 @@ import androidx.lifecycle.ViewModelProviders
import butterknife.OnClick
import im.vector.riotx.R
import im.vector.riotx.core.platform.VectorBaseFragment
import javax.inject.Inject
class SASVerificationVerifiedFragment : VectorBaseFragment() {
class SASVerificationVerifiedFragment @Inject constructor() : VectorBaseFragment() {
override fun getLayoutResId() = R.layout.fragment_sas_verification_verified
companion object {
fun newInstance() = SASVerificationVerifiedFragment()
}
private lateinit var viewModel: SasVerificationViewModel
override fun onActivityCreated(savedInstanceState: Bundle?) {

View file

@ -31,7 +31,6 @@ import im.vector.riotx.R
import im.vector.riotx.core.di.ActiveSessionHolder
import im.vector.riotx.core.di.ScreenComponent
import im.vector.riotx.core.extensions.hideKeyboard
import im.vector.riotx.core.extensions.observeEvent
import im.vector.riotx.core.extensions.replaceFragment
import im.vector.riotx.core.platform.ToolbarConfigurable
import im.vector.riotx.core.platform.VectorBaseActivity
@ -79,22 +78,21 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
navigationViewModel = ViewModelProviders.of(this).get(HomeNavigationViewModel::class.java)
drawerLayout.addDrawerListener(drawerListener)
if (isFirstCreation()) {
val homeDrawerFragment = HomeDrawerFragment.newInstance()
val loadingDetail = LoadingFragment.newInstance()
replaceFragment(loadingDetail, R.id.homeDetailFragmentContainer)
replaceFragment(homeDrawerFragment, R.id.homeDrawerFragmentContainer)
replaceFragment(R.id.homeDetailFragmentContainer, LoadingFragment::class.java)
replaceFragment(R.id.homeDrawerFragmentContainer, HomeDrawerFragment::class.java)
}
navigationViewModel.navigateTo.observeEvent(this) { navigation ->
when (navigation) {
is Navigation.OpenDrawer -> drawerLayout.openDrawer(GravityCompat.START)
is Navigation.OpenGroup -> {
drawerLayout.closeDrawer(GravityCompat.START)
val homeDetailFragment = HomeDetailFragment.newInstance()
replaceFragment(homeDetailFragment, R.id.homeDetailFragmentContainer)
navigationViewModel.observe()
.subscribe { navigation ->
when (navigation) {
is Navigation.OpenDrawer -> drawerLayout.openDrawer(GravityCompat.START)
is Navigation.OpenGroup -> {
drawerLayout.closeDrawer(GravityCompat.START)
replaceFragment(R.id.homeDetailFragmentContainer, HomeDetailFragment::class.java)
}
}
}
}
}
.disposeOnDestroy()
if (intent.getBooleanExtra(EXTRA_CLEAR_EXISTING_NOTIFICATION, false)) {
notificationDrawerManager.clearAllEvents()

View file

@ -29,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.di.ScreenComponent
import im.vector.riotx.core.extensions.addChildFragmentToBackstack
import im.vector.riotx.core.platform.ToolbarConfigurable
import im.vector.riotx.core.platform.VectorBaseFragment
import im.vector.riotx.core.ui.views.KeysBackupBanner
@ -45,25 +45,21 @@ private const val INDEX_CATCHUP = 0
private const val INDEX_PEOPLE = 1
private const val INDEX_ROOMS = 2
class HomeDetailFragment : VectorBaseFragment(), KeysBackupBanner.Delegate {
class HomeDetailFragment @Inject constructor(
private val session: Session,
val homeDetailViewModelFactory: HomeDetailViewModel.Factory,
private val avatarRenderer: AvatarRenderer
) : VectorBaseFragment(), KeysBackupBanner.Delegate {
private val unreadCounterBadgeViews = arrayListOf<UnreadCounterBadgeView>()
private val viewModel: HomeDetailViewModel by fragmentViewModel()
private lateinit var navigationViewModel: HomeNavigationViewModel
@Inject lateinit var session: Session
@Inject lateinit var homeDetailViewModelFactory: HomeDetailViewModel.Factory
@Inject lateinit var avatarRenderer: AvatarRenderer
override fun getLayoutResId(): Int {
return R.layout.fragment_home_detail
}
override fun injectWith(injector: ScreenComponent) {
injector.inject(this)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
@ -99,7 +95,7 @@ class HomeDetailFragment : VectorBaseFragment(), KeysBackupBanner.Delegate {
model.init(session)
model.keysBackupState.observe(this, Observer { keysBackupState ->
model.keysBackupState.observe(viewLifecycleOwner, Observer { keysBackupState ->
when (keysBackupState) {
null ->
homeKeysBackupBanner.render(KeysBackupBanner.State.Hidden, false)
@ -133,7 +129,7 @@ class HomeDetailFragment : VectorBaseFragment(), KeysBackupBanner.Delegate {
}
groupToolbar.title = ""
groupToolbarAvatarImageView.setOnClickListener {
navigationViewModel.goTo(HomeActivity.Navigation.OpenDrawer)
navigationViewModel.post(HomeActivity.Navigation.OpenDrawer)
}
}
@ -172,14 +168,13 @@ class HomeDetailFragment : VectorBaseFragment(), KeysBackupBanner.Delegate {
private fun updateSelectedFragment(displayMode: RoomListFragment.DisplayMode) {
val fragmentTag = "FRAGMENT_TAG_${displayMode.name}"
var fragment = childFragmentManager.findFragmentByTag(fragmentTag)
val fragment = childFragmentManager.findFragmentByTag(fragmentTag)
if (fragment == null) {
fragment = RoomListFragment.newInstance(RoomListParams(displayMode))
val params = RoomListParams(displayMode)
addChildFragmentToBackstack(R.id.roomListContainer, RoomListFragment::class.java, params, fragmentTag)
} else {
addChildFragmentToBackstack(R.id.roomListContainer, fragment, fragmentTag)
}
childFragmentManager.beginTransaction()
.replace(R.id.roomListContainer, fragment, fragmentTag)
.addToBackStack(fragmentTag)
.commit()
}
/* ==========================================================================================
@ -201,11 +196,4 @@ class HomeDetailFragment : VectorBaseFragment(), KeysBackupBanner.Delegate {
unreadCounterBadgeViews[INDEX_ROOMS].render(UnreadCounterBadgeView.State(it.notificationCountRooms, it.notificationHighlightRooms))
syncStateView.render(it.syncState)
}
companion object {
fun newInstance(): HomeDetailFragment {
return HomeDetailFragment()
}
}
}

View file

@ -26,7 +26,7 @@ import im.vector.matrix.rx.rx
import im.vector.riotx.core.di.HasScreenInjector
import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.core.resources.StringProvider
import im.vector.riotx.features.home.group.SelectedGroupStore
import im.vector.riotx.features.home.group.SelectedGroupDataSource
import im.vector.riotx.features.home.room.list.RoomListFragment
import im.vector.riotx.features.ui.UiStateRepository
import io.reactivex.schedulers.Schedulers
@ -38,8 +38,8 @@ import io.reactivex.schedulers.Schedulers
class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: HomeDetailViewState,
private val session: Session,
private val uiStateRepository: UiStateRepository,
private val selectedGroupStore: SelectedGroupStore,
private val homeRoomListStore: HomeRoomListObservableStore,
private val selectedGroupStore: SelectedGroupDataSource,
private val homeRoomListStore: HomeRoomListDataSource,
private val stringProvider: StringProvider)
: VectorViewModel<HomeDetailViewState>(initialState) {

View file

@ -19,7 +19,6 @@ package im.vector.riotx.features.home
import android.os.Bundle
import im.vector.matrix.android.api.session.Session
import im.vector.riotx.R
import im.vector.riotx.core.di.ScreenComponent
import im.vector.riotx.core.extensions.observeK
import im.vector.riotx.core.extensions.replaceChildFragment
import im.vector.riotx.core.platform.VectorBaseFragment
@ -27,29 +26,17 @@ import im.vector.riotx.features.home.group.GroupListFragment
import kotlinx.android.synthetic.main.fragment_home_drawer.*
import javax.inject.Inject
class HomeDrawerFragment : VectorBaseFragment() {
companion object {
fun newInstance(): HomeDrawerFragment {
return HomeDrawerFragment()
}
}
@Inject lateinit var session: Session
@Inject lateinit var avatarRenderer: AvatarRenderer
class HomeDrawerFragment @Inject constructor(
private val session: Session,
private val avatarRenderer: AvatarRenderer
) : VectorBaseFragment() {
override fun getLayoutResId() = R.layout.fragment_home_drawer
override fun injectWith(injector: ScreenComponent) {
injector.inject(this)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
if (savedInstanceState == null) {
val groupListFragment = GroupListFragment.newInstance()
replaceChildFragment(groupListFragment, R.id.homeDrawerGroupListContainer)
replaceChildFragment(R.id.homeDrawerGroupListContainer, GroupListFragment::class.java)
}
session.liveUser(session.myUserId).observeK(this) { optionalUser ->
val user = optionalUser?.getOrNull()

View file

@ -16,7 +16,9 @@
package im.vector.riotx.features.home
import im.vector.riotx.core.mvrx.NavigationViewModel
import javax.inject.Inject
import androidx.lifecycle.ViewModel
import im.vector.riotx.core.utils.PublishDataSource
import im.vector.riotx.core.utils.MutableDataSource
class HomeNavigationViewModel @Inject constructor() : NavigationViewModel<HomeActivity.Navigation>()
class HomeNavigationViewModel(private val source: MutableDataSource<HomeActivity.Navigation> = PublishDataSource())
: ViewModel(), MutableDataSource<HomeActivity.Navigation> by source

View file

@ -17,9 +17,9 @@
package im.vector.riotx.features.home
import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.riotx.core.utils.RxStore
import im.vector.riotx.core.utils.BehaviorDataSource
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class HomeRoomListObservableStore @Inject constructor() : RxStore<List<RoomSummary>>()
class HomeRoomListDataSource @Inject constructor() : BehaviorDataSource<List<RoomSummary>>()

View file

@ -22,15 +22,9 @@ import android.view.View
import im.vector.riotx.R
import im.vector.riotx.core.platform.VectorBaseFragment
import kotlinx.android.synthetic.main.fragment_loading.*
import javax.inject.Inject
class LoadingFragment : VectorBaseFragment() {
companion object {
fun newInstance(): LoadingFragment {
return LoadingFragment()
}
}
class LoadingFragment @Inject constructor(): VectorBaseFragment() {
override fun getLayoutResId() = R.layout.fragment_loading

View file

@ -31,7 +31,6 @@ import im.vector.riotx.core.di.ScreenComponent
import im.vector.riotx.core.error.ErrorFormatter
import im.vector.riotx.core.extensions.addFragment
import im.vector.riotx.core.extensions.addFragmentToBackstack
import im.vector.riotx.core.extensions.observeEvent
import im.vector.riotx.core.platform.SimpleFragmentActivity
import im.vector.riotx.core.platform.WaitingViewData
import kotlinx.android.synthetic.main.activity.*
@ -59,15 +58,17 @@ class CreateDirectRoomActivity : SimpleFragmentActivity() {
super.onCreate(savedInstanceState)
toolbar.visibility = View.GONE
navigationViewModel = ViewModelProviders.of(this, viewModelFactory).get(CreateDirectRoomNavigationViewModel::class.java)
navigationViewModel.navigateTo.observeEvent(this) { navigation ->
when (navigation) {
is Navigation.UsersDirectory -> addFragmentToBackstack(CreateDirectRoomDirectoryUsersFragment(), R.id.container)
Navigation.Close -> finish()
Navigation.Previous -> onBackPressed()
}
}
navigationViewModel.observe()
.subscribe { navigation ->
when (navigation) {
is Navigation.UsersDirectory -> addFragmentToBackstack(R.id.container, CreateDirectRoomDirectoryUsersFragment::class.java)
Navigation.Close -> finish()
Navigation.Previous -> onBackPressed()
}
}
.disposeOnDestroy()
if (isFirstCreation()) {
addFragment(CreateDirectRoomKnownUsersFragment(), R.id.container)
addFragment(R.id.container, CreateDirectRoomKnownUsersFragment::class.java)
}
viewModel.selectSubscribe(this, CreateDirectRoomViewState::createAndInviteState) {
renderCreateAndInviteState(it)

View file

@ -25,26 +25,22 @@ import com.airbnb.mvrx.withState
import com.jakewharton.rxbinding3.widget.textChanges
import im.vector.matrix.android.api.session.user.model.User
import im.vector.riotx.R
import im.vector.riotx.core.di.ScreenComponent
import im.vector.riotx.core.extensions.hideKeyboard
import im.vector.riotx.core.extensions.setupAsSearch
import im.vector.riotx.core.platform.VectorBaseFragment
import kotlinx.android.synthetic.main.fragment_create_direct_room_directory_users.*
import javax.inject.Inject
class CreateDirectRoomDirectoryUsersFragment : VectorBaseFragment(), DirectoryUsersController.Callback {
class CreateDirectRoomDirectoryUsersFragment @Inject constructor(
private val directRoomController: DirectoryUsersController
) : VectorBaseFragment(), DirectoryUsersController.Callback {
override fun getLayoutResId() = R.layout.fragment_create_direct_room_directory_users
private val viewModel: CreateDirectRoomViewModel by activityViewModel()
@Inject lateinit var directRoomController: DirectoryUsersController
private lateinit var navigationViewModel: CreateDirectRoomNavigationViewModel
override fun injectWith(injector: ScreenComponent) {
injector.inject(this)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
navigationViewModel = ViewModelProviders.of(requireActivity(), viewModelFactory).get(CreateDirectRoomNavigationViewModel::class.java)
@ -74,7 +70,7 @@ class CreateDirectRoomDirectoryUsersFragment : VectorBaseFragment(), DirectoryUs
private fun setupCloseView() {
createDirectRoomClose.setOnClickListener {
navigationViewModel.goTo(CreateDirectRoomActivity.Navigation.Previous)
navigationViewModel.post(CreateDirectRoomActivity.Navigation.Previous)
}
}
@ -85,7 +81,7 @@ class CreateDirectRoomDirectoryUsersFragment : VectorBaseFragment(), DirectoryUs
override fun onItemClick(user: User) {
view?.hideKeyboard()
viewModel.handle(CreateDirectRoomActions.SelectUser(user))
navigationViewModel.goTo(CreateDirectRoomActivity.Navigation.Previous)
navigationViewModel.post(CreateDirectRoomActivity.Navigation.Previous)
}
override fun retryDirectoryUsersRequest() {

View file

@ -31,33 +31,26 @@ import com.google.android.material.chip.ChipGroup
import com.jakewharton.rxbinding3.widget.textChanges
import im.vector.matrix.android.api.session.user.model.User
import im.vector.riotx.R
import im.vector.riotx.core.di.ScreenComponent
import im.vector.riotx.core.extensions.hideKeyboard
import im.vector.riotx.core.extensions.observeEvent
import im.vector.riotx.core.extensions.setupAsSearch
import im.vector.riotx.core.platform.VectorBaseFragment
import im.vector.riotx.core.utils.DimensionConverter
import im.vector.riotx.features.home.AvatarRenderer
import kotlinx.android.synthetic.main.fragment_create_direct_room.*
import javax.inject.Inject
class CreateDirectRoomKnownUsersFragment : VectorBaseFragment(), KnownUsersController.Callback {
class CreateDirectRoomKnownUsersFragment @Inject constructor(
private val knownUsersController: KnownUsersController,
private val dimensionConverter: DimensionConverter
) : VectorBaseFragment(), KnownUsersController.Callback {
override fun getLayoutResId() = R.layout.fragment_create_direct_room
override fun getMenuRes() = R.menu.vector_create_direct_room
private val viewModel: CreateDirectRoomViewModel by activityViewModel()
@Inject lateinit var directRoomController: KnownUsersController
@Inject lateinit var avatarRenderer: AvatarRenderer
@Inject lateinit var dimensionConverter: DimensionConverter
private lateinit var navigationViewModel: CreateDirectRoomNavigationViewModel
override fun injectWith(injector: ScreenComponent) {
injector.inject(this)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
navigationViewModel = ViewModelProviders.of(requireActivity(), viewModelFactory).get(CreateDirectRoomNavigationViewModel::class.java)
@ -96,7 +89,7 @@ class CreateDirectRoomKnownUsersFragment : VectorBaseFragment(), KnownUsersContr
private fun setupAddByMatrixIdView() {
addByMatrixId.setOnClickListener {
navigationViewModel.goTo(CreateDirectRoomActivity.Navigation.UsersDirectory)
navigationViewModel.post(CreateDirectRoomActivity.Navigation.UsersDirectory)
}
}
@ -104,8 +97,8 @@ class CreateDirectRoomKnownUsersFragment : VectorBaseFragment(), KnownUsersContr
recyclerView.setHasFixedSize(true)
// Don't activate animation as we might have way to much item animation when filtering
recyclerView.itemAnimator = null
directRoomController.callback = this
recyclerView.setController(directRoomController)
knownUsersController.callback = this
recyclerView.setController(knownUsersController)
}
private fun setupFilterView() {
@ -134,7 +127,7 @@ class CreateDirectRoomKnownUsersFragment : VectorBaseFragment(), KnownUsersContr
}
override fun invalidate() = withState(viewModel) {
directRoomController.setData(it)
knownUsersController.setData(it)
}
private fun updateChipsView(data: SelectUserAction) {

View file

@ -16,7 +16,9 @@
package im.vector.riotx.features.home.createdirect
import im.vector.riotx.core.mvrx.NavigationViewModel
import javax.inject.Inject
import androidx.lifecycle.ViewModel
import im.vector.riotx.core.utils.PublishDataSource
import im.vector.riotx.core.utils.MutableDataSource
class CreateDirectRoomNavigationViewModel @Inject constructor(): NavigationViewModel<CreateDirectRoomActivity.Navigation>()
class CreateDirectRoomNavigationViewModel(private val dataSource: MutableDataSource<CreateDirectRoomActivity.Navigation> = PublishDataSource())
: ViewModel(), MutableDataSource<CreateDirectRoomActivity.Navigation> by dataSource

View file

@ -23,7 +23,6 @@ import com.airbnb.mvrx.Success
import com.airbnb.mvrx.fragmentViewModel
import im.vector.matrix.android.api.session.group.model.GroupSummary
import im.vector.riotx.R
import im.vector.riotx.core.di.ScreenComponent
import im.vector.riotx.core.extensions.observeEvent
import im.vector.riotx.core.platform.StateView
import im.vector.riotx.core.platform.VectorBaseFragment
@ -32,26 +31,16 @@ import im.vector.riotx.features.home.HomeNavigationViewModel
import kotlinx.android.synthetic.main.fragment_group_list.*
import javax.inject.Inject
class GroupListFragment : VectorBaseFragment(), GroupSummaryController.Callback {
companion object {
fun newInstance(): GroupListFragment {
return GroupListFragment()
}
}
class GroupListFragment @Inject constructor(
val groupListViewModelFactory: GroupListViewModel.Factory,
private val groupController: GroupSummaryController
) : VectorBaseFragment(), GroupSummaryController.Callback {
private lateinit var navigationViewModel: HomeNavigationViewModel
private val viewModel: GroupListViewModel by fragmentViewModel()
@Inject lateinit var groupListViewModelFactory: GroupListViewModel.Factory
@Inject lateinit var groupController: GroupSummaryController
override fun getLayoutResId() = R.layout.fragment_group_list
override fun injectWith(injector: ScreenComponent) {
injector.inject(this)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
navigationViewModel = ViewModelProviders.of(requireActivity()).get(HomeNavigationViewModel::class.java)
@ -60,7 +49,7 @@ class GroupListFragment : VectorBaseFragment(), GroupSummaryController.Callback
groupListEpoxyRecyclerView.setController(groupController)
viewModel.subscribe { renderState(it) }
viewModel.openGroupLiveData.observeEvent(this) {
navigationViewModel.goTo(HomeActivity.Navigation.OpenGroup)
navigationViewModel.post(HomeActivity.Navigation.OpenGroup)
}
}

View file

@ -39,7 +39,7 @@ import io.reactivex.functions.BiFunction
const val ALL_COMMUNITIES_GROUP_ID = "ALL_COMMUNITIES_GROUP_ID"
class GroupListViewModel @AssistedInject constructor(@Assisted initialState: GroupListViewState,
private val selectedGroupStore: SelectedGroupStore,
private val selectedGroupStore: SelectedGroupDataSource,
private val session: Session,
private val stringProvider: StringProvider
) : VectorViewModel<GroupListViewState>(initialState) {

View file

@ -18,9 +18,9 @@ package im.vector.riotx.features.home.group
import arrow.core.Option
import im.vector.matrix.android.api.session.group.model.GroupSummary
import im.vector.riotx.core.utils.RxStore
import im.vector.riotx.core.utils.BehaviorDataSource
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class SelectedGroupStore @Inject constructor() : RxStore<Option<GroupSummary>>(Option.empty())
class SelectedGroupDataSource @Inject constructor() : BehaviorDataSource<Option<GroupSummary>>(Option.empty())

View file

@ -50,7 +50,14 @@ sealed class RoomDetailActions {
data class ResendMessage(val eventId: String) : RoomDetailActions()
data class RemoveFailedEcho(val eventId: String) : RoomDetailActions()
data class ReportContent(val eventId: String, val reason: String, val spam: Boolean = false, val inappropriate: Boolean = false) : RoomDetailActions()
data class ReportContent(
val eventId: String,
val senderId: String?,
val reason: String,
val spam: Boolean = false,
val inappropriate: Boolean = false) : RoomDetailActions()
data class IgnoreUser(val userId: String?) : RoomDetailActions()
object ClearSendQueue : RoomDetailActions()
object ResendAll : RoomDetailActions()

View file

@ -38,8 +38,7 @@ class RoomDetailActivity : VectorBaseActivity(), ToolbarConfigurable {
if (isFirstCreation()) {
val roomDetailArgs: RoomDetailArgs = intent?.extras?.getParcelable(EXTRA_ROOM_DETAIL_ARGS)
?: return
val roomDetailFragment = RoomDetailFragment.newInstance(roomDetailArgs)
replaceFragment(roomDetailFragment, R.id.roomDetailContainer)
replaceFragment(R.id.roomDetailContainer, RoomDetailFragment::class.java, roomDetailArgs)
}
}

View file

@ -68,7 +68,6 @@ import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.matrix.android.api.session.room.timeline.getLastMessageContent
import im.vector.matrix.android.api.session.user.model.User
import im.vector.riotx.R
import im.vector.riotx.core.di.ScreenComponent
import im.vector.riotx.core.dialogs.withColoredButton
import im.vector.riotx.core.epoxy.LayoutManagerStateRestorer
import im.vector.riotx.core.error.ErrorFormatter
@ -98,8 +97,8 @@ import im.vector.riotx.features.home.room.detail.composer.TextComposerViewModel
import im.vector.riotx.features.home.room.detail.composer.TextComposerViewState
import im.vector.riotx.features.home.room.detail.readreceipts.DisplayReadReceiptsBottomSheet
import im.vector.riotx.features.home.room.detail.timeline.TimelineEventController
import im.vector.riotx.features.home.room.detail.timeline.action.ActionsHandler
import im.vector.riotx.features.home.room.detail.timeline.action.MessageActionsBottomSheet
import im.vector.riotx.features.home.room.detail.timeline.action.MessageActionsDispatcher
import im.vector.riotx.features.home.room.detail.timeline.action.SimpleAction
import im.vector.riotx.features.home.room.detail.timeline.edithistory.ViewEditHistoryBottomSheet
import im.vector.riotx.features.home.room.detail.timeline.item.*
@ -134,7 +133,22 @@ data class RoomDetailArgs(
private const val REACTION_SELECT_REQUEST_CODE = 0
class RoomDetailFragment :
class RoomDetailFragment @Inject constructor(
private val session: Session,
private val avatarRenderer: AvatarRenderer,
private val timelineEventController: TimelineEventController,
private val commandAutocompletePolicy: CommandAutocompletePolicy,
private val autocompleteCommandPresenter: AutocompleteCommandPresenter,
private val autocompleteUserPresenter: AutocompleteUserPresenter,
private val permalinkHandler: PermalinkHandler,
private val notificationDrawerManager: NotificationDrawerManager,
val roomDetailViewModelFactory: RoomDetailViewModel.Factory,
val textComposerViewModelFactory: TextComposerViewModel.Factory,
private val errorFormatter: ErrorFormatter,
private val eventHtmlRenderer: EventHtmlRenderer,
private val vectorPreferences: VectorPreferences,
private val readMarkerHelper: ReadMarkerHelper
) :
VectorBaseFragment(),
TimelineEventController.Callback,
AutocompleteUserPresenter.Callback,
@ -145,12 +159,6 @@ class RoomDetailFragment :
companion object {
fun newInstance(args: RoomDetailArgs): RoomDetailFragment {
return RoomDetailFragment().apply {
setArguments(args)
}
}
/**x
* Sanitize the display name.
*
@ -178,21 +186,6 @@ class RoomDetailFragment :
private val debouncer = Debouncer(createUIHandler())
@Inject lateinit var session: Session
@Inject lateinit var avatarRenderer: AvatarRenderer
@Inject lateinit var timelineEventController: TimelineEventController
@Inject lateinit var commandAutocompletePolicy: CommandAutocompletePolicy
@Inject lateinit var autocompleteCommandPresenter: AutocompleteCommandPresenter
@Inject lateinit var autocompleteUserPresenter: AutocompleteUserPresenter
@Inject lateinit var permalinkHandler: PermalinkHandler
@Inject lateinit var notificationDrawerManager: NotificationDrawerManager
@Inject lateinit var roomDetailViewModelFactory: RoomDetailViewModel.Factory
@Inject lateinit var textComposerViewModelFactory: TextComposerViewModel.Factory
@Inject lateinit var errorFormatter: ErrorFormatter
@Inject lateinit var eventHtmlRenderer: EventHtmlRenderer
@Inject lateinit var vectorPreferences: VectorPreferences
@Inject lateinit var readMarkerHelper: ReadMarkerHelper
private lateinit var scrollOnNewMessageCallback: ScrollOnNewMessageCallback
private lateinit var scrollOnHighlightedEventCallback: ScrollOnHighlightedEventCallback
@ -200,7 +193,7 @@ class RoomDetailFragment :
override fun getMenuRes() = R.menu.menu_timeline
private lateinit var actionViewModel: ActionsHandler
private lateinit var messageActionsDispatcher: MessageActionsDispatcher
private lateinit var layoutManager: LinearLayoutManager
private lateinit var attachmentsHelper: AttachmentsHelper
private lateinit var keyboardStateUtils: KeyboardStateUtils
@ -211,13 +204,9 @@ class RoomDetailFragment :
private var lockSendButton = false
override fun injectWith(injector: ScreenComponent) {
injector.inject(this)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
actionViewModel = ViewModelProviders.of(requireActivity()).get(ActionsHandler::class.java)
messageActionsDispatcher = ViewModelProviders.of(requireActivity()).get(MessageActionsDispatcher::class.java)
attachmentsHelper = AttachmentsHelper.create(this, this).register()
keyboardStateUtils = KeyboardStateUtils(requireActivity())
setupToolbar(roomToolbar)
@ -235,9 +224,12 @@ class RoomDetailFragment :
val message = requireContext().getString(pair.first, *pair.second.toTypedArray())
showSnackWithMessage(message, Snackbar.LENGTH_LONG)
}
actionViewModel.actionCommandEvent.observeEvent(this) {
handleActions(it)
}
messageActionsDispatcher
.observe()
.subscribe {
handleActions(it)
}
.disposeOnDestroy()
roomDetailViewModel.navigateToEvent.observeEvent(this) {
val scrollPosition = timelineEventController.searchPositionOfEvent(it)
@ -767,7 +759,7 @@ class RoomDetailFragment :
.setView(layout)
.setPositiveButton(R.string.report_content_custom_submit) { _, _ ->
val reason = input.text.toString()
roomDetailViewModel.process(RoomDetailActions.ReportContent(action.eventId, reason))
roomDetailViewModel.process(RoomDetailActions.ReportContent(action.eventId, action.senderId, reason))
}
.setNegativeButton(R.string.cancel, null)
.show()
@ -791,7 +783,9 @@ class RoomDetailFragment :
.setTitle(R.string.content_reported_as_spam_title)
.setMessage(R.string.content_reported_as_spam_content)
.setPositiveButton(R.string.ok, null)
.setNegativeButton(R.string.block_user) { _, _ -> vectorBaseActivity.notImplemented("block user") }
.setNegativeButton(R.string.block_user) { _, _ ->
roomDetailViewModel.process(RoomDetailActions.IgnoreUser(data.senderId))
}
.show()
.withColoredButton(DialogInterface.BUTTON_NEGATIVE)
}
@ -800,7 +794,9 @@ class RoomDetailFragment :
.setTitle(R.string.content_reported_as_inappropriate_title)
.setMessage(R.string.content_reported_as_inappropriate_content)
.setPositiveButton(R.string.ok, null)
.setNegativeButton(R.string.block_user) { _, _ -> vectorBaseActivity.notImplemented("block user") }
.setNegativeButton(R.string.block_user) { _, _ ->
roomDetailViewModel.process(RoomDetailActions.IgnoreUser(data.senderId))
}
.show()
.withColoredButton(DialogInterface.BUTTON_NEGATIVE)
}
@ -809,7 +805,9 @@ class RoomDetailFragment :
.setTitle(R.string.content_reported_title)
.setMessage(R.string.content_reported_content)
.setPositiveButton(R.string.ok, null)
.setNegativeButton(R.string.block_user) { _, _ -> vectorBaseActivity.notImplemented("block user") }
.setNegativeButton(R.string.block_user) { _, _ ->
roomDetailViewModel.process(RoomDetailActions.IgnoreUser(data.senderId))
}
.show()
.withColoredButton(DialogInterface.BUTTON_NEGATIVE)
}
@ -958,6 +956,7 @@ class RoomDetailFragment :
val roomId = roomDetailArgs.roomId
this.view?.hideKeyboard()
MessageActionsBottomSheet
.newInstance(roomId, informationData)
.show(requireActivity().supportFragmentManager, "MESSAGE_CONTEXTUAL_ACTIONS")
@ -1133,10 +1132,12 @@ class RoomDetailFragment :
roomDetailViewModel.process(RoomDetailActions.RemoveFailedEcho(action.eventId))
}
is SimpleAction.ReportContentSpam -> {
roomDetailViewModel.process(RoomDetailActions.ReportContent(action.eventId, "This message is spam", spam = true))
roomDetailViewModel.process(RoomDetailActions.ReportContent(
action.eventId, action.senderId, "This message is spam", spam = true))
}
is SimpleAction.ReportContentInappropriate -> {
roomDetailViewModel.process(RoomDetailActions.ReportContent(action.eventId, "This message is inappropriate", inappropriate = true))
roomDetailViewModel.process(RoomDetailActions.ReportContent(
action.eventId, action.senderId, "This message is inappropriate", inappropriate = true))
}
is SimpleAction.ReportContentCustom -> {
promptReasonToReportContent(action)

View file

@ -157,6 +157,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
is RoomDetailActions.SetReadMarkerAction -> handleSetReadMarkerAction(action)
is RoomDetailActions.MarkAllAsRead -> handleMarkAllAsRead()
is RoomDetailActions.ReportContent -> handleReportContent(action)
is RoomDetailActions.IgnoreUser -> handleIgnoreUser(action)
}
}
@ -712,6 +713,22 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
})
}
private fun handleIgnoreUser(action: RoomDetailActions.IgnoreUser) {
if (action.userId.isNullOrEmpty()) {
return
}
session.ignoreUserIds(listOf(action.userId), object : MatrixCallback<Unit> {
override fun onSuccess(data: Unit) {
_requestLiveData.postValue(LiveEvent(Success(action)))
}
override fun onFailure(failure: Throwable) {
_requestLiveData.postValue(LiveEvent(Fail(failure)))
}
})
}
private fun observeSyncState() {
session.rx()
.liveSyncState()

View file

@ -47,7 +47,7 @@ class MessageActionsBottomSheet : VectorBaseBottomSheetDialogFragment(), Message
override val showExpanded = true
private lateinit var actionHandlerModel: ActionsHandler
private lateinit var messageActionsStore: MessageActionsDispatcher
override fun injectWith(screenComponent: ScreenComponent) {
screenComponent.inject(this)
@ -61,7 +61,7 @@ class MessageActionsBottomSheet : VectorBaseBottomSheetDialogFragment(), Message
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
actionHandlerModel = ViewModelProviders.of(requireActivity()).get(ActionsHandler::class.java)
messageActionsStore = ViewModelProviders.of(requireActivity()).get(MessageActionsDispatcher::class.java)
recyclerView.layoutManager = LinearLayoutManager(requireContext(), RecyclerView.VERTICAL, false)
recyclerView.adapter = messageActionsEpoxyController.adapter
// Disable item animation
@ -74,7 +74,7 @@ class MessageActionsBottomSheet : VectorBaseBottomSheetDialogFragment(), Message
// Toggle report menu
viewModel.toggleReportMenu()
} else {
actionHandlerModel.fireAction(simpleAction)
messageActionsStore.post(simpleAction)
dismiss()
}
}

View file

@ -15,20 +15,13 @@
*/
package im.vector.riotx.features.home.room.detail.timeline.action
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import im.vector.riotx.core.extensions.postLiveEvent
import im.vector.riotx.core.utils.LiveEvent
import javax.inject.Inject
import im.vector.riotx.core.utils.PublishDataSource
import im.vector.riotx.core.utils.MutableDataSource
/**
* Activity shared view model to handle message actions
*/
class ActionsHandler @Inject constructor() : ViewModel() {
val actionCommandEvent = MutableLiveData<LiveEvent<SimpleAction>>()
fun fireAction(action: SimpleAction) {
actionCommandEvent.postLiveEvent(action)
}
}
class MessageActionsDispatcher constructor(
private val dataSource: MutableDataSource<SimpleAction> = PublishDataSource()
) : ViewModel(), MutableDataSource<SimpleAction> by dataSource

Some files were not shown because too many files have changed in this diff Show more