mirror of
https://github.com/element-hq/element-android
synced 2024-11-27 20:06:51 +03:00
Refactor Account Data
Auto stash before rebase of "develop"
This commit is contained in:
parent
a250a895fe
commit
bf06b57bad
27 changed files with 690 additions and 20 deletions
|
@ -31,6 +31,7 @@ import im.vector.matrix.android.api.util.JsonDict
|
|||
import im.vector.matrix.android.api.util.Optional
|
||||
import im.vector.matrix.android.api.util.toOptional
|
||||
import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo
|
||||
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountData
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.Single
|
||||
|
||||
|
@ -121,6 +122,13 @@ class RxSession(private val session: Session) {
|
|||
session.getCrossSigningService().getUserCrossSigningKeys(userId).toOptional()
|
||||
}
|
||||
}
|
||||
|
||||
fun liveAccountData(filter: List<String>): Observable<List<UserAccountData>> {
|
||||
return session.getLiveAccountData(filter).asObservable()
|
||||
.startWithCallable {
|
||||
session.getAccountData(filter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Session.rx(): RxSession {
|
||||
|
|
|
@ -21,6 +21,7 @@ import androidx.lifecycle.LiveData
|
|||
import im.vector.matrix.android.api.auth.data.SessionParams
|
||||
import im.vector.matrix.android.api.failure.GlobalError
|
||||
import im.vector.matrix.android.api.pushrules.PushRuleService
|
||||
import im.vector.matrix.android.api.session.accountdata.AccountDataService
|
||||
import im.vector.matrix.android.api.session.cache.CacheService
|
||||
import im.vector.matrix.android.api.session.content.ContentUploadStateTracker
|
||||
import im.vector.matrix.android.api.session.content.ContentUrlResolver
|
||||
|
@ -57,7 +58,8 @@ interface Session :
|
|||
PushersService,
|
||||
InitialSyncProgressService,
|
||||
HomeServerCapabilitiesService,
|
||||
SecureStorageService {
|
||||
SecureStorageService,
|
||||
AccountDataService {
|
||||
|
||||
/**
|
||||
* The params associated to the session
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright (c) 2020 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.accountdata
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.util.Optional
|
||||
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountData
|
||||
|
||||
interface AccountDataService {
|
||||
|
||||
fun getAccountData(type: String): UserAccountData?
|
||||
|
||||
fun getLiveAccountData(type: String): LiveData<Optional<UserAccountData>>
|
||||
|
||||
fun getAccountData(filterType: List<String>): List<UserAccountData>
|
||||
|
||||
fun getLiveAccountData(filterType: List<String>): LiveData<List<UserAccountData>>
|
||||
|
||||
fun updateAccountData(type: String, data: Any, callback: MatrixCallback<Unit>? = null)
|
||||
}
|
|
@ -66,6 +66,9 @@ object EventType {
|
|||
const val ROOM_KEY_REQUEST = "m.room_key_request"
|
||||
const val FORWARDED_ROOM_KEY = "m.forwarded_room_key"
|
||||
|
||||
const val REQUEST_SECRET = "m.secret.request"
|
||||
const val SEND_SECRET = "m.secret.send"
|
||||
|
||||
// Interactive key verification
|
||||
const val KEY_VERIFICATION_START = "m.key.verification.start"
|
||||
const val KEY_VERIFICATION_ACCEPT = "m.key.verification.accept"
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* 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.securestorage
|
||||
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
|
||||
/**
|
||||
* Some features may require clients to store encrypted data on the server so that it can be shared securely between clients.
|
||||
* Clients may also wish to securely send such data directly to each other.
|
||||
* For example, key backups (MSC1219) can store the decryption key for the backups on the server, or cross-signing (MSC1756) can store the signing keys.
|
||||
*
|
||||
* https://github.com/matrix-org/matrix-doc/pull/1946
|
||||
*
|
||||
*/
|
||||
|
||||
interface SharedSecretStorageService {
|
||||
|
||||
/**
|
||||
* Add a key for encrypting secrets.
|
||||
*
|
||||
* @param algorithm the algorithm used by the key.
|
||||
* @param opts the options for the algorithm. The properties used
|
||||
* depend on the algorithm given.
|
||||
* @param keyId the ID of the key
|
||||
*
|
||||
* @return {string} the ID of the key
|
||||
*/
|
||||
fun addKey(algorithm: String, opts: Map<String, Any>, keyId: String, callback: MatrixCallback<String>)
|
||||
|
||||
/**
|
||||
* Check whether we have a key with a given ID.
|
||||
*
|
||||
* @param keyId The ID of the key to check
|
||||
* @return Whether we have the key.
|
||||
*/
|
||||
fun hasKey(keyId: String): Boolean
|
||||
|
||||
/**
|
||||
* Store an encrypted secret on the server
|
||||
*
|
||||
* @param name The name of the secret
|
||||
* @param secret The secret contents.
|
||||
* @param keys The IDs of the keys to use to encrypt the secret or null to use the default key.
|
||||
*/
|
||||
fun storeSecret(name: String, secretBase64: String, keys: List<String>?, callback: MatrixCallback<Unit>)
|
||||
|
||||
|
||||
/**
|
||||
* Get an encrypted secret from the shared storage
|
||||
*
|
||||
* @param name The name of the secret
|
||||
* @param keyId The id of the key that should be used to decrypt
|
||||
* @param privateKey the passphrase/secret
|
||||
*
|
||||
* @return The decrypted value
|
||||
*/
|
||||
fun getSecret(name: String, keyId: String, privateKey: String) : String
|
||||
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright (c) 2020 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.crypto.secrets
|
||||
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.session.securestorage.SharedSecretStorageService
|
||||
|
||||
internal class DefaultSharedSecureStorage : SharedSecretStorageService {
|
||||
|
||||
override fun addKey(algorithm: String, opts: Map<String, Any>, keyId: String, callback: MatrixCallback<String>) {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun hasKey(keyId: String): Boolean {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun storeSecret(name: String, secretBase64: String, keys: List<String>?, callback: MatrixCallback<Unit>) {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun getSecret(name: String, keyId: String, privateKey: String): String {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
}
|
|
@ -53,6 +53,7 @@ import io.realm.annotations.RealmModule
|
|||
DraftEntity::class,
|
||||
HomeServerCapabilitiesEntity::class,
|
||||
RoomMemberSummaryEntity::class,
|
||||
CurrentStateEventEntity::class
|
||||
CurrentStateEventEntity::class,
|
||||
UserAccountDataEntity::class
|
||||
])
|
||||
internal class SessionRealmModule
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (c) 2020 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
|
||||
import io.realm.annotations.Index
|
||||
|
||||
/**
|
||||
* Clients can store custom config data for their account on their homeserver.
|
||||
* This account data will be synced between different devices and can persist across installations on a particular device.
|
||||
* Users may only view the account data for their own accountThe account_data may be either global or scoped to a particular rooms.
|
||||
*/
|
||||
internal open class UserAccountDataEntity(
|
||||
@Index var type: String? = null,
|
||||
var contentStr: String? = null
|
||||
) : RealmObject() {
|
||||
|
||||
companion object
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (c) 2020 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.network.parsing
|
||||
|
||||
import com.squareup.moshi.JsonAdapter
|
||||
import com.squareup.moshi.Moshi
|
||||
import java.lang.reflect.Type
|
||||
|
||||
class AccountDataJsonAdapterFactory<UserAccountData> : JsonAdapter.Factory {
|
||||
|
||||
|
||||
override fun create(type: Type, annotations: MutableSet<out Annotation>, moshi: Moshi): JsonAdapter<*>? {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
}
|
|
@ -25,6 +25,7 @@ import im.vector.matrix.android.api.failure.GlobalError
|
|||
import im.vector.matrix.android.api.pushrules.PushRuleService
|
||||
import im.vector.matrix.android.api.session.InitialSyncProgressService
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.api.session.accountdata.AccountDataService
|
||||
import im.vector.matrix.android.api.session.cache.CacheService
|
||||
import im.vector.matrix.android.api.session.content.ContentUploadStateTracker
|
||||
import im.vector.matrix.android.api.session.content.ContentUrlResolver
|
||||
|
@ -91,6 +92,7 @@ internal class DefaultSession @Inject constructor(
|
|||
private val contentUploadProgressTracker: ContentUploadStateTracker,
|
||||
private val initialSyncProgressService: Lazy<InitialSyncProgressService>,
|
||||
private val homeServerCapabilitiesService: Lazy<HomeServerCapabilitiesService>,
|
||||
private val accountDataService: Lazy<AccountDataService>,
|
||||
private val shieldTrustUpdater: ShieldTrustUpdater)
|
||||
: Session,
|
||||
RoomService by roomService.get(),
|
||||
|
@ -106,7 +108,8 @@ internal class DefaultSession @Inject constructor(
|
|||
InitialSyncProgressService by initialSyncProgressService.get(),
|
||||
SecureStorageService by secureStorageService.get(),
|
||||
HomeServerCapabilitiesService by homeServerCapabilitiesService.get(),
|
||||
ProfileService by profileService.get() {
|
||||
ProfileService by profileService.get(),
|
||||
AccountDataService by accountDataService.get() {
|
||||
|
||||
private var isOpen = false
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ import im.vector.matrix.android.api.auth.data.sessionId
|
|||
import im.vector.matrix.android.api.crypto.MXCryptoConfig
|
||||
import im.vector.matrix.android.api.session.InitialSyncProgressService
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.api.session.accountdata.AccountDataService
|
||||
import im.vector.matrix.android.api.session.homeserver.HomeServerCapabilitiesService
|
||||
import im.vector.matrix.android.api.session.securestorage.SecureStorageService
|
||||
import im.vector.matrix.android.internal.crypto.verification.VerificationMessageLiveObserver
|
||||
|
@ -61,6 +62,7 @@ import im.vector.matrix.android.internal.session.room.create.RoomCreateEventLive
|
|||
import im.vector.matrix.android.internal.session.room.prune.EventsPruner
|
||||
import im.vector.matrix.android.internal.session.room.tombstone.RoomTombstoneEventLiveObserver
|
||||
import im.vector.matrix.android.internal.session.securestorage.DefaultSecureStorageService
|
||||
import im.vector.matrix.android.internal.session.user.accountdata.DefaultAccountDataService
|
||||
import im.vector.matrix.android.internal.util.md5
|
||||
import io.realm.RealmConfiguration
|
||||
import okhttp3.OkHttpClient
|
||||
|
@ -263,4 +265,7 @@ internal abstract class SessionModule {
|
|||
|
||||
@Binds
|
||||
abstract fun bindHomeServerCapabilitiesService(homeServerCapabilitiesService: DefaultHomeServerCapabilitiesService): HomeServerCapabilitiesService
|
||||
|
||||
@Binds
|
||||
abstract fun bindAccountDataServiceService(accountDataService: DefaultAccountDataService): AccountDataService
|
||||
}
|
||||
|
|
|
@ -19,9 +19,12 @@ package im.vector.matrix.android.internal.session.sync
|
|||
import com.zhuinden.monarchy.Monarchy
|
||||
import im.vector.matrix.android.api.pushrules.RuleScope
|
||||
import im.vector.matrix.android.api.pushrules.RuleSetKey
|
||||
import im.vector.matrix.android.api.session.events.model.Content
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
import im.vector.matrix.android.api.session.events.model.toModel
|
||||
import im.vector.matrix.android.api.session.room.model.RoomMemberContent
|
||||
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||
import im.vector.matrix.android.internal.database.mapper.ContentMapper
|
||||
import im.vector.matrix.android.internal.database.mapper.PushRulesMapper
|
||||
import im.vector.matrix.android.internal.database.mapper.asDomain
|
||||
import im.vector.matrix.android.internal.database.model.BreadcrumbsEntity
|
||||
|
@ -29,15 +32,18 @@ import im.vector.matrix.android.internal.database.model.IgnoredUserEntity
|
|||
import im.vector.matrix.android.internal.database.model.PushRulesEntity
|
||||
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
|
||||
import im.vector.matrix.android.internal.database.model.RoomSummaryEntityFields
|
||||
import im.vector.matrix.android.internal.database.model.UserAccountDataEntity
|
||||
import im.vector.matrix.android.internal.database.model.UserAccountDataEntityFields
|
||||
import im.vector.matrix.android.internal.database.query.getDirectRooms
|
||||
import im.vector.matrix.android.internal.database.query.getOrCreate
|
||||
import im.vector.matrix.android.internal.database.query.where
|
||||
import im.vector.matrix.android.internal.di.MoshiProvider
|
||||
import im.vector.matrix.android.internal.di.UserId
|
||||
import im.vector.matrix.android.internal.session.room.membership.RoomMemberHelper
|
||||
import im.vector.matrix.android.internal.session.sync.model.InvitedRoomSync
|
||||
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountData
|
||||
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataBreadcrumbs
|
||||
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
|
||||
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataSync
|
||||
|
@ -45,6 +51,7 @@ import im.vector.matrix.android.internal.session.user.accountdata.DirectChatsHel
|
|||
import im.vector.matrix.android.internal.session.user.accountdata.UpdateUserAccountDataTask
|
||||
import io.realm.Realm
|
||||
import io.realm.RealmList
|
||||
import io.realm.kotlin.where
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -56,21 +63,23 @@ internal class UserAccountDataSyncHandler @Inject constructor(
|
|||
|
||||
fun handle(realm: Realm, accountData: UserAccountDataSync?) {
|
||||
accountData?.list?.forEach {
|
||||
when (it) {
|
||||
is UserAccountDataDirectMessages -> handleDirectChatRooms(realm, it)
|
||||
is UserAccountDataPushRules -> handlePushRules(realm, it)
|
||||
is UserAccountDataIgnoredUsers -> handleIgnoredUsers(realm, it)
|
||||
is UserAccountDataBreadcrumbs -> handleBreadcrumbs(realm, it)
|
||||
is UserAccountDataFallback -> Timber.d("Receive account data of unhandled type ${it.type}")
|
||||
else -> error("Missing code here!")
|
||||
// Generic handling, just save in base
|
||||
handleGenericAccountData(realm, it.type, it.content)
|
||||
|
||||
// Didn't want to break too much thing, so i re-serialize to jsonString before reparsing
|
||||
// TODO would be better to have a mapper?
|
||||
val toJson = MoshiProvider.providesMoshi().adapter(Event::class.java).toJson(it)
|
||||
val model = toJson?.let { json ->
|
||||
MoshiProvider.providesMoshi().adapter(UserAccountData::class.java).fromJson(json)
|
||||
}
|
||||
// Specific parsing
|
||||
when (model) {
|
||||
is UserAccountDataDirectMessages -> handleDirectChatRooms(realm, model)
|
||||
is UserAccountDataPushRules -> handlePushRules(realm, model)
|
||||
is UserAccountDataIgnoredUsers -> handleIgnoredUsers(realm, model)
|
||||
is UserAccountDataBreadcrumbs -> handleBreadcrumbs(realm, model)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Store all account data, app can be interested of it
|
||||
// accountData?.list?.forEach {
|
||||
// it.toString()
|
||||
// MoshiProvider.providesMoshi()
|
||||
// }
|
||||
}
|
||||
|
||||
// If we get some direct chat invites, we synchronize the user account data including those.
|
||||
|
@ -200,4 +209,18 @@ internal class UserAccountDataSyncHandler @Inject constructor(
|
|||
?.breadcrumbsIndex = index
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleGenericAccountData(realm: Realm, type: String, content: Content?) {
|
||||
val existing = realm.where<UserAccountDataEntity>().equalTo(UserAccountDataEntityFields.TYPE, type)
|
||||
.findFirst()
|
||||
if (existing != null) {
|
||||
// Update current value
|
||||
existing.contentStr = ContentMapper.map(content)
|
||||
} else {
|
||||
realm.createObject(UserAccountDataEntity::class.java).let { accountDataEntity ->
|
||||
accountDataEntity.type = type
|
||||
accountDataEntity.contentStr = ContentMapper.map(content)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ package im.vector.matrix.android.internal.session.sync.model.accountdata
|
|||
|
||||
import com.squareup.moshi.Json
|
||||
|
||||
internal abstract class UserAccountData {
|
||||
abstract class UserAccountData {
|
||||
|
||||
@Json(name = "type") abstract val type: String
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ import com.squareup.moshi.Json
|
|||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class UserAccountDataFallback(
|
||||
data class UserAccountDataFallback(
|
||||
@Json(name = "type") override val type: String,
|
||||
@Json(name = "content") val content: Map<String, Any>
|
||||
) : UserAccountData()
|
||||
|
|
|
@ -18,8 +18,9 @@ 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.session.events.model.Event
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class UserAccountDataSync(
|
||||
@Json(name = "events") val list: List<UserAccountData> = emptyList()
|
||||
@Json(name = "events") val list: List<Event> = emptyList()
|
||||
)
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* Copyright (c) 2020 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 androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.Transformations
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.session.accountdata.AccountDataService
|
||||
import im.vector.matrix.android.api.util.JSON_DICT_PARAMETERIZED_TYPE
|
||||
import im.vector.matrix.android.api.util.Optional
|
||||
import im.vector.matrix.android.api.util.toOptional
|
||||
import im.vector.matrix.android.internal.database.model.UserAccountDataEntity
|
||||
import im.vector.matrix.android.internal.database.model.UserAccountDataEntityFields
|
||||
import im.vector.matrix.android.internal.di.MoshiProvider
|
||||
import im.vector.matrix.android.internal.di.SessionId
|
||||
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountData
|
||||
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataFallback
|
||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||
import im.vector.matrix.android.internal.task.configureWith
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class DefaultAccountDataService @Inject constructor(
|
||||
private val monarchy: Monarchy,
|
||||
@SessionId private val sessionId: String,
|
||||
private val updateUserAccountDataTask: UpdateUserAccountDataTask,
|
||||
private val taskExecutor: TaskExecutor
|
||||
) : AccountDataService {
|
||||
|
||||
private val moshi = MoshiProvider.providesMoshi()
|
||||
private val adapter = moshi.adapter<Map<String, Any>>(JSON_DICT_PARAMETERIZED_TYPE)
|
||||
|
||||
override fun getAccountData(type: String): UserAccountData? {
|
||||
return getAccountData(listOf(type)).firstOrNull()
|
||||
}
|
||||
|
||||
override fun getLiveAccountData(type: String): LiveData<Optional<UserAccountData>> {
|
||||
return Transformations.map(getLiveAccountData(listOf(type))) {
|
||||
it.firstOrNull()?.toOptional()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getAccountData(filterType: List<String>): List<UserAccountData> {
|
||||
return monarchy.fetchAllCopiedSync { realm ->
|
||||
realm.where(UserAccountDataEntity::class.java)
|
||||
.apply {
|
||||
if (filterType.isNotEmpty()) {
|
||||
`in`(UserAccountDataEntityFields.TYPE, filterType.toTypedArray())
|
||||
}
|
||||
}
|
||||
}?.mapNotNull { entity ->
|
||||
entity.type?.let { type ->
|
||||
UserAccountDataFallback(
|
||||
type = type,
|
||||
content = entity.contentStr?.let { adapter.fromJson(it) } ?: emptyMap()
|
||||
)
|
||||
}
|
||||
} ?: emptyList()
|
||||
}
|
||||
|
||||
override fun getLiveAccountData(filterType: List<String>): LiveData<List<UserAccountData>> {
|
||||
return monarchy.findAllMappedWithChanges({ realm ->
|
||||
realm.where(UserAccountDataEntity::class.java)
|
||||
.apply {
|
||||
if (filterType.isNotEmpty()) {
|
||||
`in`(UserAccountDataEntityFields.TYPE, filterType.toTypedArray())
|
||||
}
|
||||
}
|
||||
}, { entity ->
|
||||
UserAccountDataFallback(
|
||||
type = entity.type ?: "",
|
||||
content = entity.contentStr?.let { adapter.fromJson(it) } ?: emptyMap()
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
override fun updateAccountData(type: String, data: Any, callback: MatrixCallback<Unit>?) {
|
||||
updateUserAccountDataTask.configureWith(UpdateUserAccountDataTask.AnyParams(
|
||||
type = type,
|
||||
any = data
|
||||
)) {
|
||||
this.retryCount = 5
|
||||
callback?.let { this.callback = it }
|
||||
}
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
}
|
|
@ -49,6 +49,14 @@ internal interface UpdateUserAccountDataTask : Task<UpdateUserAccountDataTask.Pa
|
|||
return breadcrumbsContent
|
||||
}
|
||||
}
|
||||
|
||||
data class AnyParams(override val type: String,
|
||||
private val any: Any
|
||||
) : Params {
|
||||
override fun getData(): Any {
|
||||
return any
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class DefaultUpdateUserAccountDataTask @Inject constructor(
|
||||
|
|
|
@ -354,6 +354,9 @@ dependencies {
|
|||
compileOnly 'com.squareup.inject:assisted-inject-annotations-dagger2:0.5.0'
|
||||
kapt 'com.squareup.inject:assisted-inject-processor-dagger2:0.5.0'
|
||||
|
||||
// Json viewer
|
||||
implementation "com.yuyh.json:jsonviewer:1.0.6"
|
||||
|
||||
// gplay flavor only
|
||||
// Warning: due to the exclude, Android Studio does not propose to upgrade. Uncomment next line to be proposed to upgrade
|
||||
// implementation 'com.google.firebase:firebase-messaging:20.0.0'
|
||||
|
|
|
@ -374,6 +374,11 @@ SOFTWARE.
|
|||
<br/>
|
||||
Copyright (c) 2014 Dushyanth Maguluru
|
||||
</li>
|
||||
<li>
|
||||
<b>JsonViewer</b>
|
||||
<br/>
|
||||
Copyright 2017 smuyyh, All right reserved.
|
||||
</li>
|
||||
</ul>
|
||||
<pre>
|
||||
Apache License
|
||||
|
|
|
@ -72,6 +72,7 @@ import im.vector.riotx.features.settings.VectorSettingsPreferencesFragment
|
|||
import im.vector.riotx.features.settings.VectorSettingsSecurityPrivacyFragment
|
||||
import im.vector.riotx.features.settings.crosssigning.CrossSigningSettingsFragment
|
||||
import im.vector.riotx.features.settings.devices.VectorSettingsDevicesFragment
|
||||
import im.vector.riotx.features.settings.devtools.AccountDataFragment
|
||||
import im.vector.riotx.features.settings.ignored.VectorSettingsIgnoredUsersFragment
|
||||
import im.vector.riotx.features.settings.push.PushGatewaysFragment
|
||||
import im.vector.riotx.features.signout.soft.SoftLogoutFragment
|
||||
|
@ -348,4 +349,9 @@ interface FragmentModule {
|
|||
@IntoMap
|
||||
@FragmentKey(CrossSigningSettingsFragment::class)
|
||||
fun bindCrossSigningSettingsFragment(fragment: CrossSigningSettingsFragment): Fragment
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FragmentKey(AccountDataFragment::class)
|
||||
fun bindAccountDataFragment(fragment: AccountDataFragment): Fragment
|
||||
}
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* Copyright (c) 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.riotx.features.settings.devtools
|
||||
|
||||
import android.view.View
|
||||
import com.airbnb.epoxy.TypedEpoxyController
|
||||
import com.airbnb.mvrx.Fail
|
||||
import com.airbnb.mvrx.Loading
|
||||
import com.airbnb.mvrx.Success
|
||||
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountData
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.epoxy.loadingItem
|
||||
import im.vector.riotx.core.resources.StringProvider
|
||||
import im.vector.riotx.core.ui.list.genericFooterItem
|
||||
import im.vector.riotx.core.ui.list.genericItemWithValue
|
||||
import im.vector.riotx.core.utils.DebouncedClickListener
|
||||
import javax.inject.Inject
|
||||
|
||||
class AccountDataEpoxyController @Inject constructor(
|
||||
private val stringProvider: StringProvider
|
||||
) : TypedEpoxyController<AccountDataViewState>() {
|
||||
|
||||
interface InteractionListener {
|
||||
fun didTap(data: UserAccountData)
|
||||
}
|
||||
|
||||
var interactionListener: InteractionListener? = null
|
||||
|
||||
override fun buildModels(data: AccountDataViewState?) {
|
||||
if (data == null) return
|
||||
when (data.accountData) {
|
||||
is Loading -> {
|
||||
loadingItem {
|
||||
id("loading")
|
||||
loadingText(stringProvider.getString(R.string.loading))
|
||||
}
|
||||
}
|
||||
is Fail -> {
|
||||
genericFooterItem {
|
||||
id("fail")
|
||||
text(data.accountData.error.localizedMessage)
|
||||
}
|
||||
}
|
||||
is Success -> {
|
||||
val dataList = data.accountData.invoke()
|
||||
if (dataList.isEmpty()) {
|
||||
genericFooterItem {
|
||||
id("noResults")
|
||||
text(stringProvider.getString(R.string.no_result_placeholder))
|
||||
}
|
||||
} else {
|
||||
dataList.forEach { accountData ->
|
||||
genericItemWithValue {
|
||||
id(accountData.type)
|
||||
title(accountData.type)
|
||||
itemClickAction(DebouncedClickListener(View.OnClickListener {
|
||||
interactionListener?.didTap(accountData)
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright (c) 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.riotx.features.settings.devtools
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import com.airbnb.mvrx.fragmentViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import im.vector.matrix.android.internal.di.MoshiProvider
|
||||
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountData
|
||||
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataFallback
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.extensions.configureWith
|
||||
import im.vector.riotx.core.platform.VectorBaseActivity
|
||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||
import kotlinx.android.synthetic.main.fragment_generic_recycler.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class AccountDataFragment @Inject constructor(
|
||||
val viewModelFactory: AccountDataViewModel.Factory,
|
||||
private val epoxyController: AccountDataEpoxyController
|
||||
) : VectorBaseFragment(), AccountDataEpoxyController.InteractionListener {
|
||||
|
||||
override fun getLayoutResId() = R.layout.fragment_generic_recycler
|
||||
|
||||
private val viewModel: AccountDataViewModel by fragmentViewModel(AccountDataViewModel::class)
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
(activity as? VectorBaseActivity)?.supportActionBar?.setTitle(R.string.settings_account_data)
|
||||
}
|
||||
|
||||
override fun invalidate() = withState(viewModel) { state ->
|
||||
epoxyController.setData(state)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
recyclerView.configureWith(epoxyController, showDivider = true)
|
||||
epoxyController.interactionListener = this
|
||||
}
|
||||
|
||||
override fun didTap(data: UserAccountData) {
|
||||
val fb = data as? UserAccountDataFallback ?: return
|
||||
val jsonString = MoshiProvider.providesMoshi()
|
||||
.adapter(UserAccountDataFallback::class.java)
|
||||
.toJson(fb)
|
||||
JsonViewerBottomSheetDialog.newInstance(jsonString)
|
||||
.show(childFragmentManager, "JSON_VIEWER")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright (c) 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.riotx.features.settings.devtools
|
||||
|
||||
import com.airbnb.mvrx.Async
|
||||
import com.airbnb.mvrx.FragmentViewModelContext
|
||||
import com.airbnb.mvrx.MvRxState
|
||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||
import com.airbnb.mvrx.Uninitialized
|
||||
import com.airbnb.mvrx.ViewModelContext
|
||||
import com.squareup.inject.assisted.Assisted
|
||||
import com.squareup.inject.assisted.AssistedInject
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountData
|
||||
import im.vector.matrix.rx.rx
|
||||
import im.vector.riotx.core.platform.EmptyAction
|
||||
import im.vector.riotx.core.platform.EmptyViewEvents
|
||||
import im.vector.riotx.core.platform.VectorViewModel
|
||||
|
||||
data class AccountDataViewState(
|
||||
val accountData: Async<List<UserAccountData>> = Uninitialized
|
||||
) : MvRxState
|
||||
|
||||
class AccountDataViewModel @AssistedInject constructor(@Assisted initialState: AccountDataViewState,
|
||||
private val session: Session)
|
||||
: VectorViewModel<AccountDataViewState, EmptyAction, EmptyViewEvents>(initialState) {
|
||||
|
||||
|
||||
init {
|
||||
session.rx().liveAccountData(emptyList())
|
||||
.execute {
|
||||
copy(accountData = it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun handle(action: EmptyAction) {}
|
||||
|
||||
@AssistedInject.Factory
|
||||
interface Factory {
|
||||
fun create(initialState: AccountDataViewState): AccountDataViewModel
|
||||
}
|
||||
|
||||
companion object : MvRxViewModelFactory<AccountDataViewModel, AccountDataViewState> {
|
||||
|
||||
@JvmStatic
|
||||
override fun create(viewModelContext: ViewModelContext, state: AccountDataViewState): AccountDataViewModel? {
|
||||
val fragment: AccountDataFragment = (viewModelContext as FragmentViewModelContext).fragment()
|
||||
return fragment.viewModelFactory.create(state)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright (c) 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.riotx.features.settings.devtools
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.core.content.ContextCompat
|
||||
import butterknife.BindView
|
||||
import com.airbnb.mvrx.MvRx
|
||||
import com.yuyh.jsonviewer.library.JsonRecyclerView
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.platform.VectorBaseBottomSheetDialogFragment
|
||||
import im.vector.riotx.features.themes.ThemeUtils
|
||||
|
||||
class JsonViewerBottomSheetDialog : VectorBaseBottomSheetDialogFragment() {
|
||||
|
||||
override fun getLayoutResId() = R.layout.fragment_jsonviewer
|
||||
|
||||
@BindView(R.id.rv_json)
|
||||
lateinit var jsonRecyclerView: JsonRecyclerView
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
jsonRecyclerView.setKeyColor(ThemeUtils.getColor(requireContext(), R.attr.colorAccent))
|
||||
jsonRecyclerView.setValueTextColor(ContextCompat.getColor(requireContext(), R.color.riotx_notice_secondary))
|
||||
jsonRecyclerView.setValueNumberColor(ContextCompat.getColor(requireContext(), R.color.riotx_notice_secondary))
|
||||
jsonRecyclerView.setValueUrlColor(ThemeUtils.getColor(requireContext(), android.R.attr.textColorLink))
|
||||
jsonRecyclerView.setValueNullColor(ContextCompat.getColor(requireContext(), R.color.riotx_notice_secondary))
|
||||
jsonRecyclerView.setBracesColor(ThemeUtils.getColor(requireContext(), R.attr.riotx_text_primary))
|
||||
|
||||
val jsonString = arguments?.getString(MvRx.KEY_ARG)
|
||||
jsonRecyclerView.bindJson(jsonString)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun newInstance(jsonString: String): JsonViewerBottomSheetDialog {
|
||||
return JsonViewerBottomSheetDialog().apply {
|
||||
setArguments(Bundle().apply { putString(MvRx.KEY_ARG, jsonString) })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
16
vector/src/main/res/layout/fragment_jsonviewer.xml
Normal file
16
vector/src/main/res/layout/fragment_jsonviewer.xml
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<HorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingBottom="16dp"
|
||||
android:fillViewport="true"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.yuyh.jsonviewer.library.JsonRecyclerView
|
||||
android:id="@+id/rv_json"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
tools:ignore="ScrollViewSize" />
|
||||
</HorizontalScrollView>
|
|
@ -6,6 +6,8 @@
|
|||
<!-- Sections has been created to avoid merge conflict. Let's see if it's better -->
|
||||
|
||||
<!-- BEGIN Strings added by Valere -->
|
||||
<string name="settings_dev_tools">Dev Tools</string>
|
||||
<string name="settings_account_data">Account Data</string>
|
||||
<plurals name="poll_info">
|
||||
<item quantity="zero">%d vote</item>
|
||||
<item quantity="other">%d votes</item>
|
||||
|
|
|
@ -63,6 +63,14 @@
|
|||
android:persistent="false"
|
||||
android:title="@string/settings_push_rules"
|
||||
app:fragment="im.vector.riotx.features.settings.push.PushRulesFragment" />
|
||||
</im.vector.riotx.core.preference.VectorPreferenceCategory>
|
||||
|
||||
<im.vector.riotx.core.preference.VectorPreferenceCategory android:title="@string/settings_dev_tools">
|
||||
|
||||
<im.vector.riotx.core.preference.VectorPreference
|
||||
android:persistent="false"
|
||||
android:title="@string/settings_account_data"
|
||||
app:fragment="im.vector.riotx.features.settings.devtools.AccountDataFragment" />
|
||||
|
||||
</im.vector.riotx.core.preference.VectorPreferenceCategory>
|
||||
|
||||
|
|
Loading…
Reference in a new issue