Merge pull request #3355 from vector-im/feature/fga/voip_virtual_room
Feature/fga/voip virtual room
This commit is contained in:
commit
72b1bd354e
@ -24,7 +24,7 @@ import kotlinx.coroutines.rx2.rxSingle
|
|||||||
import org.matrix.android.sdk.api.extensions.orFalse
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
import org.matrix.android.sdk.api.query.QueryStringValue
|
import org.matrix.android.sdk.api.query.QueryStringValue
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent
|
import org.matrix.android.sdk.api.session.accountdata.AccountDataEvent
|
||||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME
|
import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME
|
||||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME
|
import org.matrix.android.sdk.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME
|
||||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo
|
import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo
|
||||||
@ -177,10 +177,10 @@ class RxSession(private val session: Session) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun liveAccountData(types: Set<String>): Observable<List<UserAccountDataEvent>> {
|
fun liveUserAccountData(types: Set<String>): Observable<List<AccountDataEvent>> {
|
||||||
return session.getLiveAccountDataEvents(types).asObservable()
|
return session.userAccountDataService().getLiveAccountDataEvents(types).asObservable()
|
||||||
.startWithCallable {
|
.startWithCallable {
|
||||||
session.getAccountDataEvents(types)
|
session.userAccountDataService().getAccountDataEvents(types)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,8 +201,8 @@ class RxSession(private val session: Session) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun liveSecretSynchronisationInfo(): Observable<SecretsSynchronisationInfo> {
|
fun liveSecretSynchronisationInfo(): Observable<SecretsSynchronisationInfo> {
|
||||||
return Observable.combineLatest<List<UserAccountDataEvent>, Optional<MXCrossSigningInfo>, Optional<PrivateKeysInfo>, SecretsSynchronisationInfo>(
|
return Observable.combineLatest<List<AccountDataEvent>, Optional<MXCrossSigningInfo>, Optional<PrivateKeysInfo>, SecretsSynchronisationInfo>(
|
||||||
liveAccountData(setOf(MASTER_KEY_SSSS_NAME, USER_SIGNING_KEY_SSSS_NAME, SELF_SIGNING_KEY_SSSS_NAME, KEYBACKUP_SECRET_SSSS_NAME)),
|
liveUserAccountData(setOf(MASTER_KEY_SSSS_NAME, USER_SIGNING_KEY_SSSS_NAME, SELF_SIGNING_KEY_SSSS_NAME, KEYBACKUP_SECRET_SSSS_NAME)),
|
||||||
liveCrossSigningInfo(session.myUserId),
|
liveCrossSigningInfo(session.myUserId),
|
||||||
liveCrossSigningPrivateKeys(),
|
liveCrossSigningPrivateKeys(),
|
||||||
Function3 { _, crossSigningInfo, pInfo ->
|
Function3 { _, crossSigningInfo, pInfo ->
|
||||||
|
@ -33,7 +33,7 @@ import org.matrix.android.sdk.common.TestConstants
|
|||||||
import org.matrix.android.sdk.internal.crypto.SSSS_ALGORITHM_AES_HMAC_SHA2
|
import org.matrix.android.sdk.internal.crypto.SSSS_ALGORITHM_AES_HMAC_SHA2
|
||||||
import org.matrix.android.sdk.internal.crypto.crosssigning.toBase64NoPadding
|
import org.matrix.android.sdk.internal.crypto.crosssigning.toBase64NoPadding
|
||||||
import org.matrix.android.sdk.internal.crypto.secrets.DefaultSharedSecretStorageService
|
import org.matrix.android.sdk.internal.crypto.secrets.DefaultSharedSecretStorageService
|
||||||
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent
|
import org.matrix.android.sdk.api.session.accountdata.AccountDataEvent
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@ -73,12 +73,12 @@ class QuadSTests : InstrumentedTest {
|
|||||||
|
|
||||||
// Assert Account data is updated
|
// Assert Account data is updated
|
||||||
val accountDataLock = CountDownLatch(1)
|
val accountDataLock = CountDownLatch(1)
|
||||||
var accountData: UserAccountDataEvent? = null
|
var accountData: AccountDataEvent? = null
|
||||||
|
|
||||||
val liveAccountData = runBlocking(Dispatchers.Main) {
|
val liveAccountData = runBlocking(Dispatchers.Main) {
|
||||||
aliceSession.getLiveAccountDataEvent("${DefaultSharedSecretStorageService.KEY_ID_BASE}.$TEST_KEY_ID")
|
aliceSession.userAccountDataService().getLiveAccountDataEvent("${DefaultSharedSecretStorageService.KEY_ID_BASE}.$TEST_KEY_ID")
|
||||||
}
|
}
|
||||||
val accountDataObserver = Observer<Optional<UserAccountDataEvent>?> { t ->
|
val accountDataObserver = Observer<Optional<AccountDataEvent>?> { t ->
|
||||||
if (t?.getOrNull()?.type == "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$TEST_KEY_ID") {
|
if (t?.getOrNull()?.type == "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$TEST_KEY_ID") {
|
||||||
accountData = t.getOrNull()
|
accountData = t.getOrNull()
|
||||||
accountDataLock.countDown()
|
accountDataLock.countDown()
|
||||||
@ -100,13 +100,13 @@ class QuadSTests : InstrumentedTest {
|
|||||||
quadS.setDefaultKey(TEST_KEY_ID)
|
quadS.setDefaultKey(TEST_KEY_ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
var defaultKeyAccountData: UserAccountDataEvent? = null
|
var defaultKeyAccountData: AccountDataEvent? = null
|
||||||
val defaultDataLock = CountDownLatch(1)
|
val defaultDataLock = CountDownLatch(1)
|
||||||
|
|
||||||
val liveDefAccountData = runBlocking(Dispatchers.Main) {
|
val liveDefAccountData = runBlocking(Dispatchers.Main) {
|
||||||
aliceSession.getLiveAccountDataEvent(DefaultSharedSecretStorageService.DEFAULT_KEY_ID)
|
aliceSession.userAccountDataService().getLiveAccountDataEvent(DefaultSharedSecretStorageService.DEFAULT_KEY_ID)
|
||||||
}
|
}
|
||||||
val accountDefDataObserver = Observer<Optional<UserAccountDataEvent>?> { t ->
|
val accountDefDataObserver = Observer<Optional<AccountDataEvent>?> { t ->
|
||||||
if (t?.getOrNull()?.type == DefaultSharedSecretStorageService.DEFAULT_KEY_ID) {
|
if (t?.getOrNull()?.type == DefaultSharedSecretStorageService.DEFAULT_KEY_ID) {
|
||||||
defaultKeyAccountData = t.getOrNull()!!
|
defaultKeyAccountData = t.getOrNull()!!
|
||||||
defaultDataLock.countDown()
|
defaultDataLock.countDown()
|
||||||
@ -206,7 +206,7 @@ class QuadSTests : InstrumentedTest {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val accountDataEvent = aliceSession.getAccountDataEvent("my.secret")
|
val accountDataEvent = aliceSession.userAccountDataService().getAccountDataEvent("my.secret")
|
||||||
val encryptedContent = accountDataEvent?.content?.get("encrypted") as? Map<*, *>
|
val encryptedContent = accountDataEvent?.content?.get("encrypted") as? Map<*, *>
|
||||||
|
|
||||||
assertEquals("Content should contains two encryptions", 2, encryptedContent?.keys?.size ?: 0)
|
assertEquals("Content should contains two encryptions", 2, encryptedContent?.keys?.size ?: 0)
|
||||||
@ -275,14 +275,14 @@ class QuadSTests : InstrumentedTest {
|
|||||||
mTestHelper.signOutAndClose(aliceSession)
|
mTestHelper.signOutAndClose(aliceSession)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assertAccountData(session: Session, type: String): UserAccountDataEvent {
|
private fun assertAccountData(session: Session, type: String): AccountDataEvent {
|
||||||
val accountDataLock = CountDownLatch(1)
|
val accountDataLock = CountDownLatch(1)
|
||||||
var accountData: UserAccountDataEvent? = null
|
var accountData: AccountDataEvent? = null
|
||||||
|
|
||||||
val liveAccountData = runBlocking(Dispatchers.Main) {
|
val liveAccountData = runBlocking(Dispatchers.Main) {
|
||||||
session.getLiveAccountDataEvent(type)
|
session.userAccountDataService().getLiveAccountDataEvent(type)
|
||||||
}
|
}
|
||||||
val accountDataObserver = Observer<Optional<UserAccountDataEvent>?> { t ->
|
val accountDataObserver = Observer<Optional<AccountDataEvent>?> { t ->
|
||||||
if (t?.getOrNull()?.type == type) {
|
if (t?.getOrNull()?.type == type) {
|
||||||
accountData = t.getOrNull()
|
accountData = t.getOrNull()
|
||||||
accountDataLock.countDown()
|
accountDataLock.countDown()
|
||||||
|
@ -78,7 +78,6 @@ interface Session :
|
|||||||
InitialSyncProgressService,
|
InitialSyncProgressService,
|
||||||
HomeServerCapabilitiesService,
|
HomeServerCapabilitiesService,
|
||||||
SecureStorageService,
|
SecureStorageService,
|
||||||
AccountDataService,
|
|
||||||
AccountService {
|
AccountService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -239,6 +238,11 @@ interface Session :
|
|||||||
*/
|
*/
|
||||||
fun openIdService(): OpenIdService
|
fun openIdService(): OpenIdService
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the user account data service associated with the session
|
||||||
|
*/
|
||||||
|
fun userAccountDataService(): AccountDataService
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a listener to the session.
|
* Add a listener to the session.
|
||||||
* @param listener the listener to add.
|
* @param listener the listener to add.
|
||||||
@ -262,12 +266,17 @@ interface Session :
|
|||||||
* A global session listener to get notified for some events.
|
* A global session listener to get notified for some events.
|
||||||
*/
|
*/
|
||||||
interface Listener : SessionLifecycleObserver {
|
interface Listener : SessionLifecycleObserver {
|
||||||
|
/**
|
||||||
|
* Called when the session received new invites to room so the client can react to it once.
|
||||||
|
*/
|
||||||
|
fun onNewInvitedRoom(session: Session, roomId: String) = Unit
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Possible cases:
|
* Possible cases:
|
||||||
* - The access token is not valid anymore,
|
* - The access token is not valid anymore,
|
||||||
* - a M_CONSENT_NOT_GIVEN error has been received from the homeserver
|
* - a M_CONSENT_NOT_GIVEN error has been received from the homeserver
|
||||||
*/
|
*/
|
||||||
fun onGlobalError(session: Session, globalError: GlobalError)
|
fun onGlobalError(session: Session, globalError: GlobalError) = Unit
|
||||||
}
|
}
|
||||||
|
|
||||||
val sharedSecretStorageService: SharedSecretStorageService
|
val sharedSecretStorageService: SharedSecretStorageService
|
||||||
|
@ -25,7 +25,7 @@ import org.matrix.android.sdk.api.session.events.model.Content
|
|||||||
* Currently used types are defined in [UserAccountDataTypes].
|
* Currently used types are defined in [UserAccountDataTypes].
|
||||||
*/
|
*/
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
data class UserAccountDataEvent(
|
data class AccountDataEvent(
|
||||||
@Json(name = "type") val type: String,
|
@Json(name = "type") val type: String,
|
||||||
@Json(name = "content") val content: Content
|
@Json(name = "content") val content: Content
|
||||||
)
|
)
|
@ -20,28 +20,31 @@ import androidx.lifecycle.LiveData
|
|||||||
import org.matrix.android.sdk.api.session.events.model.Content
|
import org.matrix.android.sdk.api.session.events.model.Content
|
||||||
import org.matrix.android.sdk.api.util.Optional
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This service can be attached globally to the session so it represents user data or attached to a single room.
|
||||||
|
*/
|
||||||
interface AccountDataService {
|
interface AccountDataService {
|
||||||
/**
|
/**
|
||||||
* Retrieve the account data with the provided type or null if not found
|
* Retrieve the account data with the provided type or null if not found
|
||||||
*/
|
*/
|
||||||
fun getAccountDataEvent(type: String): UserAccountDataEvent?
|
fun getAccountDataEvent(type: String): AccountDataEvent?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Observe the account data with the provided type
|
* Observe the account data with the provided type
|
||||||
*/
|
*/
|
||||||
fun getLiveAccountDataEvent(type: String): LiveData<Optional<UserAccountDataEvent>>
|
fun getLiveAccountDataEvent(type: String): LiveData<Optional<AccountDataEvent>>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the account data with the provided types. The return list can have a different size that
|
* Retrieve the account data with the provided types. The return list can have a different size that
|
||||||
* the size of the types set, because some AccountData may not exist.
|
* the size of the types set, because some AccountData may not exist.
|
||||||
* If an empty set is provided, all the AccountData are retrieved
|
* If an empty set is provided, all the AccountData are retrieved
|
||||||
*/
|
*/
|
||||||
fun getAccountDataEvents(types: Set<String>): List<UserAccountDataEvent>
|
fun getAccountDataEvents(types: Set<String>): List<AccountDataEvent>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Observe the account data with the provided types. If an empty set is provided, all the AccountData are observed
|
* Observe the account data with the provided types. If an empty set is provided, all the AccountData are observed
|
||||||
*/
|
*/
|
||||||
fun getLiveAccountDataEvents(types: Set<String>): LiveData<List<UserAccountDataEvent>>
|
fun getLiveAccountDataEvents(types: Set<String>): LiveData<List<AccountDataEvent>>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the account data with the provided type and the provided account data content
|
* Update the account data with the provided type and the provided account data content
|
||||||
|
@ -20,8 +20,6 @@ interface CallSignalingService {
|
|||||||
|
|
||||||
suspend fun getTurnServer(): TurnServerResponse
|
suspend fun getTurnServer(): TurnServerResponse
|
||||||
|
|
||||||
fun getPSTNProtocolChecker(): PSTNProtocolChecker
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an outgoing call
|
* Create an outgoing call
|
||||||
*/
|
*/
|
||||||
|
@ -1,98 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2021 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* 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 org.matrix.android.sdk.api.session.call
|
|
||||||
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
|
||||||
import org.matrix.android.sdk.api.session.room.model.thirdparty.ThirdPartyProtocol
|
|
||||||
import org.matrix.android.sdk.internal.session.SessionScope
|
|
||||||
import org.matrix.android.sdk.internal.session.thirdparty.GetThirdPartyProtocolsTask
|
|
||||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
|
||||||
import timber.log.Timber
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
private const val PSTN_VECTOR_KEY = "im.vector.protocol.pstn"
|
|
||||||
private const val PSTN_MATRIX_KEY = "m.protocol.pstn"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class is responsible for checking if the HS support the PSTN protocol.
|
|
||||||
* As long as the request succeed, it'll check only once by session.
|
|
||||||
*/
|
|
||||||
@SessionScope
|
|
||||||
class PSTNProtocolChecker @Inject internal constructor(private val taskExecutor: TaskExecutor,
|
|
||||||
private val getThirdPartyProtocolsTask: GetThirdPartyProtocolsTask) {
|
|
||||||
|
|
||||||
interface Listener {
|
|
||||||
fun onPSTNSupportUpdated()
|
|
||||||
}
|
|
||||||
|
|
||||||
private var alreadyChecked = AtomicBoolean(false)
|
|
||||||
|
|
||||||
private val pstnSupportListeners = mutableListOf<Listener>()
|
|
||||||
|
|
||||||
fun addListener(listener: Listener) {
|
|
||||||
pstnSupportListeners.add(listener)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun removeListener(listener: Listener) {
|
|
||||||
pstnSupportListeners.remove(listener)
|
|
||||||
}
|
|
||||||
|
|
||||||
var supportedPSTNProtocol: String? = null
|
|
||||||
private set
|
|
||||||
|
|
||||||
fun checkForPSTNSupportIfNeeded() {
|
|
||||||
if (alreadyChecked.get()) return
|
|
||||||
taskExecutor.executorScope.checkForPSTNSupport()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun CoroutineScope.checkForPSTNSupport() = launch {
|
|
||||||
try {
|
|
||||||
supportedPSTNProtocol = getSupportedPSTN(3)
|
|
||||||
alreadyChecked.set(true)
|
|
||||||
if (supportedPSTNProtocol != null) {
|
|
||||||
pstnSupportListeners.forEach {
|
|
||||||
tryOrNull { it.onPSTNSupportUpdated() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (failure: Throwable) {
|
|
||||||
Timber.v("Fail to get supported PSTN, will check again next time.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun getSupportedPSTN(maxTries: Int): String? {
|
|
||||||
val thirdPartyProtocols: Map<String, ThirdPartyProtocol> = try {
|
|
||||||
getThirdPartyProtocolsTask.execute(Unit)
|
|
||||||
} catch (failure: Throwable) {
|
|
||||||
if (maxTries == 1) {
|
|
||||||
throw failure
|
|
||||||
} else {
|
|
||||||
// Wait for 10s before trying again
|
|
||||||
delay(10_000L)
|
|
||||||
return getSupportedPSTN(maxTries - 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return when {
|
|
||||||
thirdPartyProtocols.containsKey(PSTN_VECTOR_KEY) -> PSTN_VECTOR_KEY
|
|
||||||
thirdPartyProtocols.containsKey(PSTN_MATRIX_KEY) -> PSTN_MATRIX_KEY
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -31,9 +31,7 @@ object EventType {
|
|||||||
const val TYPING = "m.typing"
|
const val TYPING = "m.typing"
|
||||||
const val REDACTION = "m.room.redaction"
|
const val REDACTION = "m.room.redaction"
|
||||||
const val RECEIPT = "m.receipt"
|
const val RECEIPT = "m.receipt"
|
||||||
const val TAG = "m.tag"
|
|
||||||
const val ROOM_KEY = "m.room_key"
|
const val ROOM_KEY = "m.room_key"
|
||||||
const val FULLY_READ = "m.fully_read"
|
|
||||||
const val PLUMBING = "m.room.plumbing"
|
const val PLUMBING = "m.room.plumbing"
|
||||||
const val BOT_OPTIONS = "m.room.bot.options"
|
const val BOT_OPTIONS = "m.room.bot.options"
|
||||||
const val PREVIEW_URLS = "org.matrix.room.preview_urls"
|
const val PREVIEW_URLS = "org.matrix.room.preview_urls"
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package org.matrix.android.sdk.api.session.room
|
package org.matrix.android.sdk.api.session.room
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
|
import org.matrix.android.sdk.api.session.accountdata.AccountDataService
|
||||||
import org.matrix.android.sdk.api.session.room.alias.AliasService
|
import org.matrix.android.sdk.api.session.room.alias.AliasService
|
||||||
import org.matrix.android.sdk.api.session.room.call.RoomCallService
|
import org.matrix.android.sdk.api.session.room.call.RoomCallService
|
||||||
import org.matrix.android.sdk.api.session.room.crypto.RoomCryptoService
|
import org.matrix.android.sdk.api.session.room.crypto.RoomCryptoService
|
||||||
@ -55,7 +56,8 @@ interface Room :
|
|||||||
RoomCallService,
|
RoomCallService,
|
||||||
RelationService,
|
RelationService,
|
||||||
RoomCryptoService,
|
RoomCryptoService,
|
||||||
RoomPushRuleService {
|
RoomPushRuleService,
|
||||||
|
AccountDataService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The roomId of this room
|
* The roomId of this room
|
||||||
@ -86,12 +88,12 @@ interface Room :
|
|||||||
* @return The search result
|
* @return The search result
|
||||||
*/
|
*/
|
||||||
suspend fun search(searchTerm: String,
|
suspend fun search(searchTerm: String,
|
||||||
nextBatch: String?,
|
nextBatch: String?,
|
||||||
orderByRecent: Boolean,
|
orderByRecent: Boolean,
|
||||||
limit: Int,
|
limit: Int,
|
||||||
beforeLimit: Int,
|
beforeLimit: Int,
|
||||||
afterLimit: Int,
|
afterLimit: Int,
|
||||||
includeProfile: Boolean): SearchResult
|
includeProfile: Boolean): SearchResult
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use this room as a Space, if the type is correct.
|
* Use this room as a Space, if the type is correct.
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* 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 org.matrix.android.sdk.api.session.room.accountdata
|
||||||
|
|
||||||
|
object RoomAccountDataTypes {
|
||||||
|
const val EVENT_TYPE_VIRTUAL_ROOM = "im.vector.is_virtual_room"
|
||||||
|
const val EVENT_TYPE_TAG = "m.tag"
|
||||||
|
const val EVENT_TYPE_FULLY_READ = "m.fully_read"
|
||||||
|
}
|
@ -44,7 +44,7 @@ data class CallAnswerContent(
|
|||||||
* Capability advertisement.
|
* Capability advertisement.
|
||||||
*/
|
*/
|
||||||
@Json(name = "capabilities") val capabilities: CallCapabilities? = null
|
@Json(name = "capabilities") val capabilities: CallCapabilities? = null
|
||||||
): CallSignallingContent {
|
): CallSignalingContent {
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
data class Answer(
|
data class Answer(
|
||||||
|
@ -41,4 +41,4 @@ data class CallCandidatesContent(
|
|||||||
* Required. The version of the VoIP specification this messages adheres to.
|
* Required. The version of the VoIP specification this messages adheres to.
|
||||||
*/
|
*/
|
||||||
@Json(name = "version") override val version: String?
|
@Json(name = "version") override val version: String?
|
||||||
): CallSignallingContent
|
): CallSignalingContent
|
||||||
|
@ -44,7 +44,7 @@ data class CallHangupContent(
|
|||||||
* One of: ["ice_failed", "invite_timeout"]
|
* One of: ["ice_failed", "invite_timeout"]
|
||||||
*/
|
*/
|
||||||
@Json(name = "reason") val reason: Reason? = null
|
@Json(name = "reason") val reason: Reason? = null
|
||||||
) : CallSignallingContent {
|
) : CallSignalingContent {
|
||||||
@JsonClass(generateAdapter = false)
|
@JsonClass(generateAdapter = false)
|
||||||
enum class Reason {
|
enum class Reason {
|
||||||
@Json(name = "ice_failed")
|
@Json(name = "ice_failed")
|
||||||
|
@ -55,7 +55,7 @@ data class CallInviteContent(
|
|||||||
*/
|
*/
|
||||||
@Json(name = "capabilities") val capabilities: CallCapabilities? = null
|
@Json(name = "capabilities") val capabilities: CallCapabilities? = null
|
||||||
|
|
||||||
): CallSignallingContent {
|
): CallSignalingContent {
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
data class Offer(
|
data class Offer(
|
||||||
/**
|
/**
|
||||||
|
@ -47,7 +47,7 @@ data class CallNegotiateContent(
|
|||||||
*/
|
*/
|
||||||
@Json(name = "version") override val version: String?
|
@Json(name = "version") override val version: String?
|
||||||
|
|
||||||
): CallSignallingContent {
|
): CallSignalingContent {
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
data class Description(
|
data class Description(
|
||||||
/**
|
/**
|
||||||
|
@ -37,4 +37,4 @@ data class CallRejectContent(
|
|||||||
* Required. The version of the VoIP specification this message adheres to.
|
* Required. The version of the VoIP specification this message adheres to.
|
||||||
*/
|
*/
|
||||||
@Json(name = "version") override val version: String?
|
@Json(name = "version") override val version: String?
|
||||||
) : CallSignallingContent
|
) : CallSignalingContent
|
||||||
|
@ -61,7 +61,7 @@ data class CallReplacesContent(
|
|||||||
* Required. The version of the VoIP specification this messages adheres to.
|
* Required. The version of the VoIP specification this messages adheres to.
|
||||||
*/
|
*/
|
||||||
@Json(name = "version") override val version: String?
|
@Json(name = "version") override val version: String?
|
||||||
): CallSignallingContent {
|
): CallSignalingContent {
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
data class TargetUser(
|
data class TargetUser(
|
||||||
|
@ -41,4 +41,4 @@ data class CallSelectAnswerContent(
|
|||||||
* Required. The version of the VoIP specification this message adheres to.
|
* Required. The version of the VoIP specification this message adheres to.
|
||||||
*/
|
*/
|
||||||
@Json(name = "version") override val version: String?
|
@Json(name = "version") override val version: String?
|
||||||
): CallSignallingContent
|
): CallSignalingContent
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
package org.matrix.android.sdk.api.session.room.model.call
|
package org.matrix.android.sdk.api.session.room.model.call
|
||||||
|
|
||||||
interface CallSignallingContent {
|
interface CallSignalingContent {
|
||||||
/**
|
/**
|
||||||
* Required. A unique identifier for the call.
|
* Required. A unique identifier for the call.
|
||||||
*/
|
*/
|
@ -19,6 +19,7 @@ package org.matrix.android.sdk.internal.database
|
|||||||
import io.realm.DynamicRealm
|
import io.realm.DynamicRealm
|
||||||
import io.realm.FieldAttribute
|
import io.realm.FieldAttribute
|
||||||
import io.realm.RealmMigration
|
import io.realm.RealmMigration
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.VersioningState
|
||||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesContent
|
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent
|
import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent
|
||||||
@ -30,6 +31,7 @@ import org.matrix.android.sdk.internal.database.model.EventEntityFields
|
|||||||
import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEntityFields
|
import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEntityFields
|
||||||
import org.matrix.android.sdk.internal.database.model.PendingThreePidEntityFields
|
import org.matrix.android.sdk.internal.database.model.PendingThreePidEntityFields
|
||||||
import org.matrix.android.sdk.internal.database.model.PreviewUrlCacheEntityFields
|
import org.matrix.android.sdk.internal.database.model.PreviewUrlCacheEntityFields
|
||||||
|
import org.matrix.android.sdk.internal.database.model.RoomAccountDataEntityFields
|
||||||
import org.matrix.android.sdk.internal.database.model.RoomEntityFields
|
import org.matrix.android.sdk.internal.database.model.RoomEntityFields
|
||||||
import org.matrix.android.sdk.internal.database.model.RoomMembersLoadStatusType
|
import org.matrix.android.sdk.internal.database.model.RoomMembersLoadStatusType
|
||||||
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields
|
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields
|
||||||
@ -44,7 +46,7 @@ import javax.inject.Inject
|
|||||||
class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
|
class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val SESSION_STORE_SCHEMA_VERSION = 13L
|
const val SESSION_STORE_SCHEMA_VERSION = 14L
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) {
|
override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) {
|
||||||
@ -63,6 +65,7 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
|
|||||||
if (oldVersion <= 10) migrateTo11(realm)
|
if (oldVersion <= 10) migrateTo11(realm)
|
||||||
if (oldVersion <= 11) migrateTo12(realm)
|
if (oldVersion <= 11) migrateTo12(realm)
|
||||||
if (oldVersion <= 12) migrateTo13(realm)
|
if (oldVersion <= 12) migrateTo13(realm)
|
||||||
|
if (oldVersion <= 13) migrateTo14(realm)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun migrateTo1(realm: DynamicRealm) {
|
private fun migrateTo1(realm: DynamicRealm) {
|
||||||
@ -278,11 +281,29 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
|
|||||||
|
|
||||||
private fun migrateTo13(realm: DynamicRealm) {
|
private fun migrateTo13(realm: DynamicRealm) {
|
||||||
Timber.d("Step 12 -> 13")
|
Timber.d("Step 12 -> 13")
|
||||||
|
|
||||||
// Fix issue with the nightly build. Eventually play again the migration which has been included in migrateTo12()
|
// Fix issue with the nightly build. Eventually play again the migration which has been included in migrateTo12()
|
||||||
realm.schema.get("SpaceChildSummaryEntity")
|
realm.schema.get("SpaceChildSummaryEntity")
|
||||||
?.takeIf { !it.hasField(SpaceChildSummaryEntityFields.SUGGESTED) }
|
?.takeIf { !it.hasField(SpaceChildSummaryEntityFields.SUGGESTED) }
|
||||||
?.addField(SpaceChildSummaryEntityFields.SUGGESTED, Boolean::class.java)
|
?.addField(SpaceChildSummaryEntityFields.SUGGESTED, Boolean::class.java)
|
||||||
?.setNullable(SpaceChildSummaryEntityFields.SUGGESTED, true)
|
?.setNullable(SpaceChildSummaryEntityFields.SUGGESTED, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun migrateTo14(realm: DynamicRealm) {
|
||||||
|
Timber.d("Step 13 -> 14")
|
||||||
|
val roomAccountDataSchema = realm.schema.create("RoomAccountDataEntity")
|
||||||
|
.addField(RoomAccountDataEntityFields.CONTENT_STR, String::class.java)
|
||||||
|
.addField(RoomAccountDataEntityFields.TYPE, String::class.java, FieldAttribute.INDEXED)
|
||||||
|
|
||||||
|
realm.schema.get("RoomEntity")
|
||||||
|
?.addRealmListField(RoomEntityFields.ACCOUNT_DATA.`$`, roomAccountDataSchema)
|
||||||
|
|
||||||
|
realm.schema.get("RoomSummaryEntity")
|
||||||
|
?.addField(RoomSummaryEntityFields.IS_HIDDEN_FROM_USER, Boolean::class.java, FieldAttribute.INDEXED)
|
||||||
|
?.transform {
|
||||||
|
val isHiddenFromUser = it.getString(RoomSummaryEntityFields.VERSIONING_STATE_STR) == VersioningState.UPGRADED_ROOM_JOINED.name
|
||||||
|
it.setBoolean(RoomSummaryEntityFields.IS_HIDDEN_FROM_USER, isHiddenFromUser)
|
||||||
|
}
|
||||||
|
|
||||||
|
roomAccountDataSchema.isEmbedded = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,17 +17,25 @@
|
|||||||
package org.matrix.android.sdk.internal.database.mapper
|
package org.matrix.android.sdk.internal.database.mapper
|
||||||
|
|
||||||
import com.squareup.moshi.Moshi
|
import com.squareup.moshi.Moshi
|
||||||
|
import org.matrix.android.sdk.api.session.accountdata.AccountDataEvent
|
||||||
import org.matrix.android.sdk.api.util.JSON_DICT_PARAMETERIZED_TYPE
|
import org.matrix.android.sdk.api.util.JSON_DICT_PARAMETERIZED_TYPE
|
||||||
|
import org.matrix.android.sdk.internal.database.model.RoomAccountDataEntity
|
||||||
import org.matrix.android.sdk.internal.database.model.UserAccountDataEntity
|
import org.matrix.android.sdk.internal.database.model.UserAccountDataEntity
|
||||||
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class AccountDataMapper @Inject constructor(moshi: Moshi) {
|
internal class AccountDataMapper @Inject constructor(moshi: Moshi) {
|
||||||
|
|
||||||
private val adapter = moshi.adapter<Map<String, Any>>(JSON_DICT_PARAMETERIZED_TYPE)
|
private val adapter = moshi.adapter<Map<String, Any>>(JSON_DICT_PARAMETERIZED_TYPE)
|
||||||
|
|
||||||
fun map(entity: UserAccountDataEntity): UserAccountDataEvent {
|
fun map(entity: UserAccountDataEntity): AccountDataEvent {
|
||||||
return UserAccountDataEvent(
|
return AccountDataEvent(
|
||||||
|
type = entity.type ?: "",
|
||||||
|
content = entity.contentStr?.let { adapter.fromJson(it) }.orEmpty()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun map(entity: RoomAccountDataEntity): AccountDataEvent {
|
||||||
|
return AccountDataEvent(
|
||||||
type = entity.type ?: "",
|
type = entity.type ?: "",
|
||||||
content = entity.contentStr?.let { adapter.fromJson(it) }.orEmpty()
|
content = entity.contentStr?.let { adapter.fromJson(it) }.orEmpty()
|
||||||
)
|
)
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* 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 org.matrix.android.sdk.internal.database.model
|
||||||
|
|
||||||
|
import io.realm.RealmObject
|
||||||
|
import io.realm.annotations.Index
|
||||||
|
import io.realm.annotations.RealmClass
|
||||||
|
|
||||||
|
@RealmClass(embedded = true)
|
||||||
|
internal open class RoomAccountDataEntity(
|
||||||
|
@Index var type: String? = null,
|
||||||
|
var contentStr: String? = null
|
||||||
|
) : RealmObject()
|
@ -23,7 +23,8 @@ import io.realm.annotations.PrimaryKey
|
|||||||
|
|
||||||
internal open class RoomEntity(@PrimaryKey var roomId: String = "",
|
internal open class RoomEntity(@PrimaryKey var roomId: String = "",
|
||||||
var chunks: RealmList<ChunkEntity> = RealmList(),
|
var chunks: RealmList<ChunkEntity> = RealmList(),
|
||||||
var sendingTimelineEvents: RealmList<TimelineEventEntity> = RealmList()
|
var sendingTimelineEvents: RealmList<TimelineEventEntity> = RealmList(),
|
||||||
|
var accountData: RealmList<RoomAccountDataEntity> = RealmList()
|
||||||
) : RealmObject() {
|
) : RealmObject() {
|
||||||
|
|
||||||
private var membershipStr: String = Membership.NONE.name
|
private var membershipStr: String = Membership.NONE.name
|
||||||
|
@ -232,6 +232,12 @@ internal open class RoomSummaryEntity(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Index
|
||||||
|
var isHiddenFromUser: Boolean = false
|
||||||
|
set(value) {
|
||||||
|
if (value != field) field = value
|
||||||
|
}
|
||||||
|
|
||||||
@Index
|
@Index
|
||||||
private var versioningStateStr: String = VersioningState.NONE.name
|
private var versioningStateStr: String = VersioningState.NONE.name
|
||||||
var versioningState: VersioningState
|
var versioningState: VersioningState
|
||||||
|
@ -62,6 +62,7 @@ import io.realm.annotations.RealmModule
|
|||||||
UserAccountDataEntity::class,
|
UserAccountDataEntity::class,
|
||||||
ScalarTokenEntity::class,
|
ScalarTokenEntity::class,
|
||||||
WellknownIntegrationManagerConfigEntity::class,
|
WellknownIntegrationManagerConfigEntity::class,
|
||||||
|
RoomAccountDataEntity::class,
|
||||||
SpaceChildSummaryEntity::class,
|
SpaceChildSummaryEntity::class,
|
||||||
SpaceParentSummaryEntity::class
|
SpaceParentSummaryEntity::class
|
||||||
])
|
])
|
||||||
|
@ -29,6 +29,10 @@ internal fun RoomEntity.Companion.where(realm: Realm, roomId: String): RealmQuer
|
|||||||
.equalTo(RoomEntityFields.ROOM_ID, roomId)
|
.equalTo(RoomEntityFields.ROOM_ID, roomId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun RoomEntity.Companion.getOrCreate(realm: Realm, roomId: String): RoomEntity {
|
||||||
|
return where(realm, roomId).findFirst() ?: realm.createObject(RoomEntity::class.java, roomId)
|
||||||
|
}
|
||||||
|
|
||||||
internal fun RoomEntity.Companion.where(realm: Realm, membership: Membership? = null): RealmQuery<RoomEntity> {
|
internal fun RoomEntity.Companion.where(realm: Realm, membership: Membership? = null): RealmQuery<RoomEntity> {
|
||||||
val query = realm.where<RoomEntity>()
|
val query = realm.where<RoomEntity>()
|
||||||
if (membership != null) {
|
if (membership != null) {
|
||||||
|
@ -16,13 +16,13 @@
|
|||||||
|
|
||||||
package org.matrix.android.sdk.internal.network
|
package org.matrix.android.sdk.internal.network
|
||||||
|
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import org.matrix.android.sdk.api.failure.GlobalError
|
import org.matrix.android.sdk.api.failure.GlobalError
|
||||||
import org.matrix.android.sdk.internal.auth.SessionParamsStore
|
import org.matrix.android.sdk.internal.auth.SessionParamsStore
|
||||||
import org.matrix.android.sdk.internal.di.SessionId
|
import org.matrix.android.sdk.internal.di.SessionId
|
||||||
import org.matrix.android.sdk.internal.session.SessionScope
|
import org.matrix.android.sdk.internal.session.SessionScope
|
||||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
import org.matrix.android.sdk.internal.task.TaskExecutor
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -44,7 +44,6 @@ internal class GlobalErrorHandler @Inject constructor(
|
|||||||
sessionParamsStore.setTokenInvalid(sessionId)
|
sessionParamsStore.setTokenInvalid(sessionId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
listener?.onGlobalError(globalError)
|
listener?.onGlobalError(globalError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,6 +74,7 @@ import org.matrix.android.sdk.internal.session.identity.DefaultIdentityService
|
|||||||
import org.matrix.android.sdk.internal.session.sync.SyncTokenStore
|
import org.matrix.android.sdk.internal.session.sync.SyncTokenStore
|
||||||
import org.matrix.android.sdk.internal.session.sync.job.SyncThread
|
import org.matrix.android.sdk.internal.session.sync.job.SyncThread
|
||||||
import org.matrix.android.sdk.internal.session.sync.job.SyncWorker
|
import org.matrix.android.sdk.internal.session.sync.job.SyncWorker
|
||||||
|
import org.matrix.android.sdk.internal.session.user.accountdata.UserAccountDataService
|
||||||
import org.matrix.android.sdk.internal.util.createUIHandler
|
import org.matrix.android.sdk.internal.util.createUIHandler
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -117,7 +118,7 @@ internal class DefaultSession @Inject constructor(
|
|||||||
private val contentDownloadStateTracker: ContentDownloadStateTracker,
|
private val contentDownloadStateTracker: ContentDownloadStateTracker,
|
||||||
private val initialSyncProgressService: Lazy<InitialSyncProgressService>,
|
private val initialSyncProgressService: Lazy<InitialSyncProgressService>,
|
||||||
private val homeServerCapabilitiesService: Lazy<HomeServerCapabilitiesService>,
|
private val homeServerCapabilitiesService: Lazy<HomeServerCapabilitiesService>,
|
||||||
private val accountDataService: Lazy<AccountDataService>,
|
private val accountDataService: Lazy<UserAccountDataService>,
|
||||||
private val _sharedSecretStorageService: Lazy<SharedSecretStorageService>,
|
private val _sharedSecretStorageService: Lazy<SharedSecretStorageService>,
|
||||||
private val accountService: Lazy<AccountService>,
|
private val accountService: Lazy<AccountService>,
|
||||||
private val eventService: Lazy<EventService>,
|
private val eventService: Lazy<EventService>,
|
||||||
@ -130,6 +131,7 @@ internal class DefaultSession @Inject constructor(
|
|||||||
@UnauthenticatedWithCertificate
|
@UnauthenticatedWithCertificate
|
||||||
private val unauthenticatedWithCertificateOkHttpClient: Lazy<OkHttpClient>
|
private val unauthenticatedWithCertificateOkHttpClient: Lazy<OkHttpClient>
|
||||||
) : Session,
|
) : Session,
|
||||||
|
GlobalErrorHandler.Listener,
|
||||||
RoomService by roomService.get(),
|
RoomService by roomService.get(),
|
||||||
RoomDirectoryService by roomDirectoryService.get(),
|
RoomDirectoryService by roomDirectoryService.get(),
|
||||||
GroupService by groupService.get(),
|
GroupService by groupService.get(),
|
||||||
@ -144,9 +146,7 @@ internal class DefaultSession @Inject constructor(
|
|||||||
SecureStorageService by secureStorageService.get(),
|
SecureStorageService by secureStorageService.get(),
|
||||||
HomeServerCapabilitiesService by homeServerCapabilitiesService.get(),
|
HomeServerCapabilitiesService by homeServerCapabilitiesService.get(),
|
||||||
ProfileService by profileService.get(),
|
ProfileService by profileService.get(),
|
||||||
AccountDataService by accountDataService.get(),
|
AccountService by accountService.get() {
|
||||||
AccountService by accountService.get(),
|
|
||||||
GlobalErrorHandler.Listener {
|
|
||||||
|
|
||||||
override val sharedSecretStorageService: SharedSecretStorageService
|
override val sharedSecretStorageService: SharedSecretStorageService
|
||||||
get() = _sharedSecretStorageService.get()
|
get() = _sharedSecretStorageService.get()
|
||||||
@ -164,16 +164,16 @@ internal class DefaultSession @Inject constructor(
|
|||||||
override fun open() {
|
override fun open() {
|
||||||
assert(!isOpen)
|
assert(!isOpen)
|
||||||
isOpen = true
|
isOpen = true
|
||||||
|
globalErrorHandler.listener = this
|
||||||
cryptoService.get().ensureDevice()
|
cryptoService.get().ensureDevice()
|
||||||
uiHandler.post {
|
uiHandler.post {
|
||||||
lifecycleObservers.forEach {
|
lifecycleObservers.forEach {
|
||||||
it.onSessionStarted(this)
|
it.onSessionStarted(this)
|
||||||
}
|
}
|
||||||
sessionListeners.dispatch {
|
sessionListeners.dispatch { _, listener ->
|
||||||
it.onSessionStarted(this)
|
listener.onSessionStarted(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
globalErrorHandler.listener = this
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun requireBackgroundSync() {
|
override fun requireBackgroundSync() {
|
||||||
@ -213,13 +213,13 @@ internal class DefaultSession @Inject constructor(
|
|||||||
// timelineEventDecryptor.destroy()
|
// timelineEventDecryptor.destroy()
|
||||||
uiHandler.post {
|
uiHandler.post {
|
||||||
lifecycleObservers.forEach { it.onSessionStopped(this) }
|
lifecycleObservers.forEach { it.onSessionStopped(this) }
|
||||||
sessionListeners.dispatch {
|
sessionListeners.dispatch { _, listener ->
|
||||||
it.onSessionStopped(this)
|
listener.onSessionStopped(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cryptoService.get().close()
|
cryptoService.get().close()
|
||||||
isOpen = false
|
|
||||||
globalErrorHandler.listener = null
|
globalErrorHandler.listener = null
|
||||||
|
isOpen = false
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getSyncStateLive() = getSyncThread().liveState()
|
override fun getSyncStateLive() = getSyncThread().liveState()
|
||||||
@ -243,8 +243,8 @@ internal class DefaultSession @Inject constructor(
|
|||||||
lifecycleObservers.forEach {
|
lifecycleObservers.forEach {
|
||||||
it.onClearCache(this)
|
it.onClearCache(this)
|
||||||
}
|
}
|
||||||
sessionListeners.dispatch {
|
sessionListeners.dispatch { _, listener ->
|
||||||
it.onClearCache(this)
|
listener.onClearCache(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
withContext(NonCancellable) {
|
withContext(NonCancellable) {
|
||||||
@ -254,8 +254,8 @@ internal class DefaultSession @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onGlobalError(globalError: GlobalError) {
|
override fun onGlobalError(globalError: GlobalError) {
|
||||||
sessionListeners.dispatch {
|
sessionListeners.dispatch { _, listener ->
|
||||||
it.onGlobalError(this, globalError)
|
listener.onGlobalError(this, globalError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -293,6 +293,8 @@ internal class DefaultSession @Inject constructor(
|
|||||||
|
|
||||||
override fun openIdService(): OpenIdService = openIdService.get()
|
override fun openIdService(): OpenIdService = openIdService.get()
|
||||||
|
|
||||||
|
override fun userAccountDataService(): AccountDataService = accountDataService.get()
|
||||||
|
|
||||||
override fun getOkHttpClient(): OkHttpClient {
|
override fun getOkHttpClient(): OkHttpClient {
|
||||||
return unauthenticatedWithCertificateOkHttpClient.get()
|
return unauthenticatedWithCertificateOkHttpClient.get()
|
||||||
}
|
}
|
||||||
|
@ -16,10 +16,16 @@
|
|||||||
|
|
||||||
package org.matrix.android.sdk.internal.session
|
package org.matrix.android.sdk.internal.session
|
||||||
|
|
||||||
|
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
|
import org.matrix.android.sdk.internal.SessionManager
|
||||||
|
import org.matrix.android.sdk.internal.di.SessionId
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class SessionListeners @Inject constructor() {
|
@SessionScope
|
||||||
|
internal class SessionListeners @Inject constructor(
|
||||||
|
@SessionId private val sessionId: String,
|
||||||
|
private val sessionManager: SessionManager) {
|
||||||
|
|
||||||
private val listeners = mutableSetOf<Session.Listener>()
|
private val listeners = mutableSetOf<Session.Listener>()
|
||||||
|
|
||||||
@ -35,11 +41,17 @@ internal class SessionListeners @Inject constructor() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun dispatch(block: (Session.Listener) -> Unit) {
|
fun dispatch(block: (Session, Session.Listener) -> Unit) {
|
||||||
synchronized(listeners) {
|
synchronized(listeners) {
|
||||||
|
val session = getSession()
|
||||||
listeners.forEach {
|
listeners.forEach {
|
||||||
block(it)
|
tryOrNull { block(session, it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getSession(): Session {
|
||||||
|
return sessionManager.getSessionComponent(sessionId)?.session()
|
||||||
|
?: throw IllegalStateException("No session found with this id.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,7 +93,7 @@ import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProces
|
|||||||
import org.matrix.android.sdk.internal.session.room.tombstone.RoomTombstoneEventProcessor
|
import org.matrix.android.sdk.internal.session.room.tombstone.RoomTombstoneEventProcessor
|
||||||
import org.matrix.android.sdk.internal.session.securestorage.DefaultSecureStorageService
|
import org.matrix.android.sdk.internal.session.securestorage.DefaultSecureStorageService
|
||||||
import org.matrix.android.sdk.internal.session.typing.DefaultTypingUsersTracker
|
import org.matrix.android.sdk.internal.session.typing.DefaultTypingUsersTracker
|
||||||
import org.matrix.android.sdk.internal.session.user.accountdata.DefaultAccountDataService
|
import org.matrix.android.sdk.internal.session.user.accountdata.UserAccountDataService
|
||||||
import org.matrix.android.sdk.internal.session.widgets.DefaultWidgetURLFormatter
|
import org.matrix.android.sdk.internal.session.widgets.DefaultWidgetURLFormatter
|
||||||
import org.matrix.android.sdk.internal.util.md5
|
import org.matrix.android.sdk.internal.util.md5
|
||||||
import retrofit2.Retrofit
|
import retrofit2.Retrofit
|
||||||
@ -364,7 +364,7 @@ internal abstract class SessionModule {
|
|||||||
abstract fun bindHomeServerCapabilitiesService(service: DefaultHomeServerCapabilitiesService): HomeServerCapabilitiesService
|
abstract fun bindHomeServerCapabilitiesService(service: DefaultHomeServerCapabilitiesService): HomeServerCapabilitiesService
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun bindAccountDataService(service: DefaultAccountDataService): AccountDataService
|
abstract fun bindAccountDataService(service: UserAccountDataService): AccountDataService
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun bindEventService(service: DefaultEventService): EventService
|
abstract fun bindEventService(service: DefaultEventService): EventService
|
||||||
|
@ -30,7 +30,7 @@ import org.matrix.android.sdk.api.session.room.model.call.CallInviteContent
|
|||||||
import org.matrix.android.sdk.api.session.room.model.call.CallNegotiateContent
|
import org.matrix.android.sdk.api.session.room.model.call.CallNegotiateContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.call.CallRejectContent
|
import org.matrix.android.sdk.api.session.room.model.call.CallRejectContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.call.CallSelectAnswerContent
|
import org.matrix.android.sdk.api.session.room.model.call.CallSelectAnswerContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.call.CallSignallingContent
|
import org.matrix.android.sdk.api.session.room.model.call.CallSignalingContent
|
||||||
import org.matrix.android.sdk.api.util.Optional
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
import org.matrix.android.sdk.internal.di.UserId
|
import org.matrix.android.sdk.internal.di.UserId
|
||||||
import org.matrix.android.sdk.internal.session.SessionScope
|
import org.matrix.android.sdk.internal.session.SessionScope
|
||||||
@ -210,11 +210,11 @@ internal class CallSignalingHandler @Inject constructor(private val activeCallHa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun MxCall.partyIdsMatches(contentSignallingContent: CallSignallingContent): Boolean {
|
private fun MxCall.partyIdsMatches(contentSignalingContent: CallSignalingContent): Boolean {
|
||||||
return opponentPartyId?.getOrNull() == contentSignallingContent.partyId
|
return opponentPartyId?.getOrNull() == contentSignalingContent.partyId
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CallSignallingContent.getCall(): MxCall? {
|
private fun CallSignalingContent.getCall(): MxCall? {
|
||||||
val currentCall = callId?.let {
|
val currentCall = callId?.let {
|
||||||
activeCallHandler.getCallWithId(it)
|
activeCallHandler.getCallWithId(it)
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,6 @@ package org.matrix.android.sdk.internal.session.call
|
|||||||
import org.matrix.android.sdk.api.session.call.CallListener
|
import org.matrix.android.sdk.api.session.call.CallListener
|
||||||
import org.matrix.android.sdk.api.session.call.CallSignalingService
|
import org.matrix.android.sdk.api.session.call.CallSignalingService
|
||||||
import org.matrix.android.sdk.api.session.call.MxCall
|
import org.matrix.android.sdk.api.session.call.MxCall
|
||||||
import org.matrix.android.sdk.api.session.call.PSTNProtocolChecker
|
|
||||||
import org.matrix.android.sdk.api.session.call.TurnServerResponse
|
import org.matrix.android.sdk.api.session.call.TurnServerResponse
|
||||||
import org.matrix.android.sdk.internal.session.SessionScope
|
import org.matrix.android.sdk.internal.session.SessionScope
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
@ -30,18 +29,13 @@ internal class DefaultCallSignalingService @Inject constructor(
|
|||||||
private val callSignalingHandler: CallSignalingHandler,
|
private val callSignalingHandler: CallSignalingHandler,
|
||||||
private val mxCallFactory: MxCallFactory,
|
private val mxCallFactory: MxCallFactory,
|
||||||
private val activeCallHandler: ActiveCallHandler,
|
private val activeCallHandler: ActiveCallHandler,
|
||||||
private val turnServerDataSource: TurnServerDataSource,
|
private val turnServerDataSource: TurnServerDataSource
|
||||||
private val pstnProtocolChecker: PSTNProtocolChecker
|
|
||||||
) : CallSignalingService {
|
) : CallSignalingService {
|
||||||
|
|
||||||
override suspend fun getTurnServer(): TurnServerResponse {
|
override suspend fun getTurnServer(): TurnServerResponse {
|
||||||
return turnServerDataSource.getTurnServer()
|
return turnServerDataSource.getTurnServer()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getPSTNProtocolChecker(): PSTNProtocolChecker {
|
|
||||||
return pstnProtocolChecker
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun createOutgoingCall(roomId: String, otherUserId: String, isVideoCall: Boolean): MxCall {
|
override fun createOutgoingCall(roomId: String, otherUserId: String, isVideoCall: Boolean): MxCall {
|
||||||
return mxCallFactory.createOutgoingCall(roomId, otherUserId, isVideoCall).also {
|
return mxCallFactory.createOutgoingCall(roomId, otherUserId, isVideoCall).also {
|
||||||
activeCallHandler.addCall(it)
|
activeCallHandler.addCall(it)
|
||||||
|
@ -44,7 +44,7 @@ import org.matrix.android.sdk.internal.session.profile.BindThreePidsTask
|
|||||||
import org.matrix.android.sdk.internal.session.profile.UnbindThreePidsTask
|
import org.matrix.android.sdk.internal.session.profile.UnbindThreePidsTask
|
||||||
import org.matrix.android.sdk.internal.session.sync.model.accountdata.IdentityServerContent
|
import org.matrix.android.sdk.internal.session.sync.model.accountdata.IdentityServerContent
|
||||||
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes
|
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes
|
||||||
import org.matrix.android.sdk.internal.session.user.accountdata.AccountDataDataSource
|
import org.matrix.android.sdk.internal.session.user.accountdata.UserAccountDataDataSource
|
||||||
import org.matrix.android.sdk.internal.session.user.accountdata.UpdateUserAccountDataTask
|
import org.matrix.android.sdk.internal.session.user.accountdata.UpdateUserAccountDataTask
|
||||||
import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
|
import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
|
||||||
import org.matrix.android.sdk.internal.util.ensureProtocol
|
import org.matrix.android.sdk.internal.util.ensureProtocol
|
||||||
@ -77,7 +77,7 @@ internal class DefaultIdentityService @Inject constructor(
|
|||||||
private val submitTokenForBindingTask: IdentitySubmitTokenForBindingTask,
|
private val submitTokenForBindingTask: IdentitySubmitTokenForBindingTask,
|
||||||
private val unbindThreePidsTask: UnbindThreePidsTask,
|
private val unbindThreePidsTask: UnbindThreePidsTask,
|
||||||
private val identityApiProvider: IdentityApiProvider,
|
private val identityApiProvider: IdentityApiProvider,
|
||||||
private val accountDataDataSource: AccountDataDataSource,
|
private val accountDataDataSource: UserAccountDataDataSource,
|
||||||
private val homeServerCapabilitiesService: HomeServerCapabilitiesService,
|
private val homeServerCapabilitiesService: HomeServerCapabilitiesService,
|
||||||
private val sessionParams: SessionParams
|
private val sessionParams: SessionParams
|
||||||
) : IdentityService, SessionLifecycleObserver {
|
) : IdentityService, SessionLifecycleObserver {
|
||||||
|
@ -33,8 +33,8 @@ import org.matrix.android.sdk.internal.extensions.observeNotNull
|
|||||||
import org.matrix.android.sdk.api.session.SessionLifecycleObserver
|
import org.matrix.android.sdk.api.session.SessionLifecycleObserver
|
||||||
import org.matrix.android.sdk.internal.session.SessionScope
|
import org.matrix.android.sdk.internal.session.SessionScope
|
||||||
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes
|
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes
|
||||||
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent
|
import org.matrix.android.sdk.api.session.accountdata.AccountDataEvent
|
||||||
import org.matrix.android.sdk.internal.session.user.accountdata.AccountDataDataSource
|
import org.matrix.android.sdk.internal.session.user.accountdata.UserAccountDataDataSource
|
||||||
import org.matrix.android.sdk.internal.session.user.accountdata.UpdateUserAccountDataTask
|
import org.matrix.android.sdk.internal.session.user.accountdata.UpdateUserAccountDataTask
|
||||||
import org.matrix.android.sdk.internal.session.widgets.helper.WidgetFactory
|
import org.matrix.android.sdk.internal.session.widgets.helper.WidgetFactory
|
||||||
import org.matrix.android.sdk.internal.session.widgets.helper.extractWidgetSequence
|
import org.matrix.android.sdk.internal.session.widgets.helper.extractWidgetSequence
|
||||||
@ -57,7 +57,7 @@ import javax.inject.Inject
|
|||||||
internal class IntegrationManager @Inject constructor(matrixConfiguration: MatrixConfiguration,
|
internal class IntegrationManager @Inject constructor(matrixConfiguration: MatrixConfiguration,
|
||||||
@SessionDatabase private val monarchy: Monarchy,
|
@SessionDatabase private val monarchy: Monarchy,
|
||||||
private val updateUserAccountDataTask: UpdateUserAccountDataTask,
|
private val updateUserAccountDataTask: UpdateUserAccountDataTask,
|
||||||
private val accountDataDataSource: AccountDataDataSource,
|
private val accountDataDataSource: UserAccountDataDataSource,
|
||||||
private val widgetFactory: WidgetFactory)
|
private val widgetFactory: WidgetFactory)
|
||||||
: SessionLifecycleObserver {
|
: SessionLifecycleObserver {
|
||||||
|
|
||||||
@ -240,7 +240,7 @@ internal class IntegrationManager @Inject constructor(matrixConfiguration: Matri
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun UserAccountDataEvent.asIntegrationManagerWidgetContent(): WidgetContent? {
|
private fun AccountDataEvent.asIntegrationManagerWidgetContent(): WidgetContent? {
|
||||||
return extractWidgetSequence(widgetFactory)
|
return extractWidgetSequence(widgetFactory)
|
||||||
.filter {
|
.filter {
|
||||||
WidgetType.IntegrationManager == it.type
|
WidgetType.IntegrationManager == it.type
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package org.matrix.android.sdk.internal.session.room
|
package org.matrix.android.sdk.internal.session.room
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
|
import org.matrix.android.sdk.api.session.accountdata.AccountDataService
|
||||||
import org.matrix.android.sdk.api.session.crypto.CryptoService
|
import org.matrix.android.sdk.api.session.crypto.CryptoService
|
||||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||||
import org.matrix.android.sdk.api.session.room.Room
|
import org.matrix.android.sdk.api.session.room.Room
|
||||||
@ -41,34 +42,35 @@ import org.matrix.android.sdk.api.session.space.Space
|
|||||||
import org.matrix.android.sdk.api.util.Optional
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
|
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
|
||||||
import org.matrix.android.sdk.internal.session.permalinks.ViaParameterFinder
|
import org.matrix.android.sdk.internal.session.permalinks.ViaParameterFinder
|
||||||
|
import org.matrix.android.sdk.internal.session.room.accountdata.RoomAccountDataService
|
||||||
import org.matrix.android.sdk.internal.session.room.state.SendStateTask
|
import org.matrix.android.sdk.internal.session.room.state.SendStateTask
|
||||||
import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryDataSource
|
import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryDataSource
|
||||||
import org.matrix.android.sdk.internal.session.search.SearchTask
|
import org.matrix.android.sdk.internal.session.search.SearchTask
|
||||||
import org.matrix.android.sdk.internal.session.space.DefaultSpace
|
import org.matrix.android.sdk.internal.session.space.DefaultSpace
|
||||||
import org.matrix.android.sdk.internal.util.awaitCallback
|
import org.matrix.android.sdk.internal.util.awaitCallback
|
||||||
import java.security.InvalidParameterException
|
import java.security.InvalidParameterException
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
internal class DefaultRoom @Inject constructor(override val roomId: String,
|
internal class DefaultRoom(override val roomId: String,
|
||||||
private val roomSummaryDataSource: RoomSummaryDataSource,
|
private val roomSummaryDataSource: RoomSummaryDataSource,
|
||||||
private val timelineService: TimelineService,
|
private val timelineService: TimelineService,
|
||||||
private val sendService: SendService,
|
private val sendService: SendService,
|
||||||
private val draftService: DraftService,
|
private val draftService: DraftService,
|
||||||
private val stateService: StateService,
|
private val stateService: StateService,
|
||||||
private val uploadsService: UploadsService,
|
private val uploadsService: UploadsService,
|
||||||
private val reportingService: ReportingService,
|
private val reportingService: ReportingService,
|
||||||
private val roomCallService: RoomCallService,
|
private val roomCallService: RoomCallService,
|
||||||
private val readService: ReadService,
|
private val readService: ReadService,
|
||||||
private val typingService: TypingService,
|
private val typingService: TypingService,
|
||||||
private val aliasService: AliasService,
|
private val aliasService: AliasService,
|
||||||
private val tagsService: TagsService,
|
private val tagsService: TagsService,
|
||||||
private val cryptoService: CryptoService,
|
private val cryptoService: CryptoService,
|
||||||
private val relationService: RelationService,
|
private val relationService: RelationService,
|
||||||
private val roomMembersService: MembershipService,
|
private val roomMembersService: MembershipService,
|
||||||
private val roomPushRuleService: RoomPushRuleService,
|
private val roomPushRuleService: RoomPushRuleService,
|
||||||
private val sendStateTask: SendStateTask,
|
private val roomAccountDataService: RoomAccountDataService,
|
||||||
private val viaParameterFinder: ViaParameterFinder,
|
private val sendStateTask: SendStateTask,
|
||||||
private val searchTask: SearchTask) :
|
private val viaParameterFinder: ViaParameterFinder,
|
||||||
|
private val searchTask: SearchTask) :
|
||||||
Room,
|
Room,
|
||||||
TimelineService by timelineService,
|
TimelineService by timelineService,
|
||||||
SendService by sendService,
|
SendService by sendService,
|
||||||
@ -83,7 +85,8 @@ internal class DefaultRoom @Inject constructor(override val roomId: String,
|
|||||||
TagsService by tagsService,
|
TagsService by tagsService,
|
||||||
RelationService by relationService,
|
RelationService by relationService,
|
||||||
MembershipService by roomMembersService,
|
MembershipService by roomMembersService,
|
||||||
RoomPushRuleService by roomPushRuleService {
|
RoomPushRuleService by roomPushRuleService,
|
||||||
|
AccountDataService by roomAccountDataService {
|
||||||
|
|
||||||
override fun getRoomSummaryLive(): LiveData<Optional<RoomSummary>> {
|
override fun getRoomSummaryLive(): LiveData<Optional<RoomSummary>> {
|
||||||
return roomSummaryDataSource.getRoomSummaryLive(roomId)
|
return roomSummaryDataSource.getRoomSummaryLive(roomId)
|
||||||
|
@ -360,4 +360,13 @@ internal interface RoomAPI {
|
|||||||
suspend fun deleteTag(@Path("userId") userId: String,
|
suspend fun deleteTag(@Path("userId") userId: String,
|
||||||
@Path("roomId") roomId: String,
|
@Path("roomId") roomId: String,
|
||||||
@Path("tag") tag: String)
|
@Path("tag") tag: String)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set an AccountData event to the room.
|
||||||
|
*/
|
||||||
|
@PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "user/{userId}/rooms/{roomId}/account_data/{type}")
|
||||||
|
suspend fun setRoomAccountData(@Path("userId") userId: String,
|
||||||
|
@Path("roomId") roomId: String,
|
||||||
|
@Path("type") type: String,
|
||||||
|
@Body content: JsonDict)
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ package org.matrix.android.sdk.internal.session.room
|
|||||||
import org.matrix.android.sdk.api.session.crypto.CryptoService
|
import org.matrix.android.sdk.api.session.crypto.CryptoService
|
||||||
import org.matrix.android.sdk.api.session.room.Room
|
import org.matrix.android.sdk.api.session.room.Room
|
||||||
import org.matrix.android.sdk.internal.session.SessionScope
|
import org.matrix.android.sdk.internal.session.SessionScope
|
||||||
|
import org.matrix.android.sdk.internal.session.room.accountdata.RoomAccountDataService
|
||||||
import org.matrix.android.sdk.internal.session.permalinks.ViaParameterFinder
|
import org.matrix.android.sdk.internal.session.permalinks.ViaParameterFinder
|
||||||
import org.matrix.android.sdk.internal.session.room.alias.DefaultAliasService
|
import org.matrix.android.sdk.internal.session.room.alias.DefaultAliasService
|
||||||
import org.matrix.android.sdk.internal.session.room.call.DefaultRoomCallService
|
import org.matrix.android.sdk.internal.session.room.call.DefaultRoomCallService
|
||||||
@ -60,6 +61,7 @@ internal class DefaultRoomFactory @Inject constructor(private val cryptoService:
|
|||||||
private val relationServiceFactory: DefaultRelationService.Factory,
|
private val relationServiceFactory: DefaultRelationService.Factory,
|
||||||
private val membershipServiceFactory: DefaultMembershipService.Factory,
|
private val membershipServiceFactory: DefaultMembershipService.Factory,
|
||||||
private val roomPushRuleServiceFactory: DefaultRoomPushRuleService.Factory,
|
private val roomPushRuleServiceFactory: DefaultRoomPushRuleService.Factory,
|
||||||
|
private val roomAccountDataServiceFactory: RoomAccountDataService.Factory,
|
||||||
private val sendStateTask: SendStateTask,
|
private val sendStateTask: SendStateTask,
|
||||||
private val viaParameterFinder: ViaParameterFinder,
|
private val viaParameterFinder: ViaParameterFinder,
|
||||||
private val searchTask: SearchTask) :
|
private val searchTask: SearchTask) :
|
||||||
@ -84,6 +86,7 @@ internal class DefaultRoomFactory @Inject constructor(private val cryptoService:
|
|||||||
relationService = relationServiceFactory.create(roomId),
|
relationService = relationServiceFactory.create(roomId),
|
||||||
roomMembersService = membershipServiceFactory.create(roomId),
|
roomMembersService = membershipServiceFactory.create(roomId),
|
||||||
roomPushRuleService = roomPushRuleServiceFactory.create(roomId),
|
roomPushRuleService = roomPushRuleServiceFactory.create(roomId),
|
||||||
|
roomAccountDataService = roomAccountDataServiceFactory.create(roomId),
|
||||||
sendStateTask = sendStateTask,
|
sendStateTask = sendStateTask,
|
||||||
searchTask = searchTask,
|
searchTask = searchTask,
|
||||||
viaParameterFinder = viaParameterFinder
|
viaParameterFinder = viaParameterFinder
|
||||||
|
@ -28,6 +28,8 @@ import org.matrix.android.sdk.api.session.space.SpaceService
|
|||||||
import org.matrix.android.sdk.internal.session.DefaultFileService
|
import org.matrix.android.sdk.internal.session.DefaultFileService
|
||||||
import org.matrix.android.sdk.internal.session.SessionScope
|
import org.matrix.android.sdk.internal.session.SessionScope
|
||||||
import org.matrix.android.sdk.internal.session.directory.DirectoryAPI
|
import org.matrix.android.sdk.internal.session.directory.DirectoryAPI
|
||||||
|
import org.matrix.android.sdk.internal.session.room.accountdata.DefaultUpdateRoomAccountDataTask
|
||||||
|
import org.matrix.android.sdk.internal.session.room.accountdata.UpdateRoomAccountDataTask
|
||||||
import org.matrix.android.sdk.internal.session.room.alias.AddRoomAliasTask
|
import org.matrix.android.sdk.internal.session.room.alias.AddRoomAliasTask
|
||||||
import org.matrix.android.sdk.internal.session.room.alias.DefaultAddRoomAliasTask
|
import org.matrix.android.sdk.internal.session.room.alias.DefaultAddRoomAliasTask
|
||||||
import org.matrix.android.sdk.internal.session.room.alias.DefaultDeleteRoomAliasTask
|
import org.matrix.android.sdk.internal.session.room.alias.DefaultDeleteRoomAliasTask
|
||||||
@ -236,6 +238,9 @@ internal abstract class RoomModule {
|
|||||||
@Binds
|
@Binds
|
||||||
abstract fun bindPeekRoomTask(task: DefaultPeekRoomTask): PeekRoomTask
|
abstract fun bindPeekRoomTask(task: DefaultPeekRoomTask): PeekRoomTask
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
abstract fun bindUpdateRoomAccountDataTask(task: DefaultUpdateRoomAccountDataTask): UpdateRoomAccountDataTask
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun bindGetEventTask(task: DefaultGetEventTask): GetEventTask
|
abstract fun bindGetEventTask(task: DefaultGetEventTask): GetEventTask
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* 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 org.matrix.android.sdk.internal.session.room.accountdata
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MediatorLiveData
|
||||||
|
import androidx.lifecycle.Transformations
|
||||||
|
import com.zhuinden.monarchy.Monarchy
|
||||||
|
import org.matrix.android.sdk.api.session.accountdata.AccountDataEvent
|
||||||
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
|
import org.matrix.android.sdk.api.util.toOptional
|
||||||
|
import org.matrix.android.sdk.internal.database.RealmSessionProvider
|
||||||
|
import org.matrix.android.sdk.internal.database.mapper.AccountDataMapper
|
||||||
|
import org.matrix.android.sdk.internal.database.model.RoomAccountDataEntityFields
|
||||||
|
import org.matrix.android.sdk.internal.database.model.RoomEntity
|
||||||
|
import org.matrix.android.sdk.internal.database.query.where
|
||||||
|
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
internal class RoomAccountDataDataSource @Inject constructor(@SessionDatabase private val monarchy: Monarchy,
|
||||||
|
private val realmSessionProvider: RealmSessionProvider,
|
||||||
|
private val accountDataMapper: AccountDataMapper) {
|
||||||
|
|
||||||
|
fun getAccountDataEvent(roomId: String, type: String): AccountDataEvent? {
|
||||||
|
return getAccountDataEvents(roomId, setOf(type)).firstOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getLiveAccountDataEvent(roomId: String, type: String): LiveData<Optional<AccountDataEvent>> {
|
||||||
|
return Transformations.map(getLiveAccountDataEvents(roomId, setOf(type))) {
|
||||||
|
it.firstOrNull()?.toOptional()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAccountDataEvents(roomId: String, types: Set<String>): List<AccountDataEvent> {
|
||||||
|
return realmSessionProvider.withRealm { realm ->
|
||||||
|
val roomEntity = RoomEntity.where(realm, roomId).findFirst() ?: return@withRealm emptyList()
|
||||||
|
roomEntity.accountDataEvents(types)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getLiveAccountDataEvents(roomId: String, types: Set<String>): LiveData<List<AccountDataEvent>> {
|
||||||
|
val liveRoomEntity = monarchy.findAllManagedWithChanges { RoomEntity.where(it, roomId) }
|
||||||
|
val resultLiveData = MediatorLiveData<List<AccountDataEvent>>()
|
||||||
|
resultLiveData.addSource(liveRoomEntity) {
|
||||||
|
val roomEntity = it.realmResults.firstOrNull()
|
||||||
|
if (roomEntity == null) {
|
||||||
|
resultLiveData.postValue(emptyList())
|
||||||
|
} else {
|
||||||
|
val mappedResult = roomEntity.accountDataEvents(types)
|
||||||
|
resultLiveData.postValue(mappedResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resultLiveData
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun RoomEntity.accountDataEvents(types: Set<String>): List<AccountDataEvent> {
|
||||||
|
val query = accountData.where()
|
||||||
|
if (types.isNotEmpty()) {
|
||||||
|
query.`in`(RoomAccountDataEntityFields.TYPE, types.toTypedArray())
|
||||||
|
}
|
||||||
|
return query.findAll().map { accountDataMapper.map(it) }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* 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 org.matrix.android.sdk.internal.session.room.accountdata
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import dagger.assisted.Assisted
|
||||||
|
import dagger.assisted.AssistedFactory
|
||||||
|
import dagger.assisted.AssistedInject
|
||||||
|
import org.matrix.android.sdk.api.session.accountdata.AccountDataEvent
|
||||||
|
import org.matrix.android.sdk.api.session.accountdata.AccountDataService
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.Content
|
||||||
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
|
|
||||||
|
internal class RoomAccountDataService @AssistedInject constructor(@Assisted private val roomId: String,
|
||||||
|
private val dataSource: RoomAccountDataDataSource,
|
||||||
|
private val updateRoomAccountDataTask: UpdateRoomAccountDataTask
|
||||||
|
) : AccountDataService {
|
||||||
|
|
||||||
|
@AssistedFactory
|
||||||
|
interface Factory {
|
||||||
|
fun create(roomId: String): RoomAccountDataService
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getAccountDataEvent(type: String): AccountDataEvent? {
|
||||||
|
return dataSource.getAccountDataEvent(roomId, type)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getLiveAccountDataEvent(type: String): LiveData<Optional<AccountDataEvent>> {
|
||||||
|
return dataSource.getLiveAccountDataEvent(roomId, type)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getAccountDataEvents(types: Set<String>): List<AccountDataEvent> {
|
||||||
|
return dataSource.getAccountDataEvents(roomId, types)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getLiveAccountDataEvents(types: Set<String>): LiveData<List<AccountDataEvent>> {
|
||||||
|
return dataSource.getLiveAccountDataEvents(roomId, types)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun updateAccountData(type: String, content: Content) {
|
||||||
|
val params = UpdateRoomAccountDataTask.Params(roomId, type, content)
|
||||||
|
return updateRoomAccountDataTask.execute(params)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* 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 org.matrix.android.sdk.internal.session.room.accountdata
|
||||||
|
|
||||||
|
import org.matrix.android.sdk.api.util.JsonDict
|
||||||
|
import org.matrix.android.sdk.internal.di.UserId
|
||||||
|
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
|
||||||
|
import org.matrix.android.sdk.internal.network.executeRequest
|
||||||
|
import org.matrix.android.sdk.internal.session.room.RoomAPI
|
||||||
|
import org.matrix.android.sdk.internal.task.Task
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
internal interface UpdateRoomAccountDataTask : Task<UpdateRoomAccountDataTask.Params, Unit> {
|
||||||
|
|
||||||
|
data class Params(
|
||||||
|
val roomId: String,
|
||||||
|
val type: String,
|
||||||
|
val content: JsonDict
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class DefaultUpdateRoomAccountDataTask @Inject constructor(
|
||||||
|
private val roomApi: RoomAPI,
|
||||||
|
@UserId private val userId: String,
|
||||||
|
private val globalErrorReceiver: GlobalErrorReceiver
|
||||||
|
) : UpdateRoomAccountDataTask {
|
||||||
|
|
||||||
|
override suspend fun execute(params: UpdateRoomAccountDataTask.Params) {
|
||||||
|
return executeRequest(globalErrorReceiver) {
|
||||||
|
roomApi.setRoomAccountData(userId, params.roomId, params.type, params.content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -37,6 +37,7 @@ internal class RoomCreateEventProcessor @Inject constructor() : EventInsertLiveP
|
|||||||
val predecessorRoomSummary = RoomSummaryEntity.where(realm, predecessorRoomId).findFirst()
|
val predecessorRoomSummary = RoomSummaryEntity.where(realm, predecessorRoomId).findFirst()
|
||||||
?: RoomSummaryEntity(predecessorRoomId)
|
?: RoomSummaryEntity(predecessorRoomId)
|
||||||
predecessorRoomSummary.versioningState = VersioningState.UPGRADED_ROOM_JOINED
|
predecessorRoomSummary.versioningState = VersioningState.UPGRADED_ROOM_JOINED
|
||||||
|
predecessorRoomSummary.isHiddenFromUser = true
|
||||||
realm.insertOrUpdate(predecessorRoomSummary)
|
realm.insertOrUpdate(predecessorRoomSummary)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,7 +36,6 @@ import org.matrix.android.sdk.api.session.room.UpdatableLivePageResult
|
|||||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomType
|
import org.matrix.android.sdk.api.session.room.model.RoomType
|
||||||
import org.matrix.android.sdk.api.session.room.model.VersioningState
|
|
||||||
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
||||||
import org.matrix.android.sdk.api.session.room.spaceSummaryQueryParams
|
import org.matrix.android.sdk.api.session.room.spaceSummaryQueryParams
|
||||||
import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount
|
import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount
|
||||||
@ -244,7 +243,7 @@ internal class RoomSummaryDataSource @Inject constructor(@SessionDatabase privat
|
|||||||
query.process(RoomSummaryEntityFields.DISPLAY_NAME, queryParams.displayName)
|
query.process(RoomSummaryEntityFields.DISPLAY_NAME, queryParams.displayName)
|
||||||
query.process(RoomSummaryEntityFields.CANONICAL_ALIAS, queryParams.canonicalAlias)
|
query.process(RoomSummaryEntityFields.CANONICAL_ALIAS, queryParams.canonicalAlias)
|
||||||
query.process(RoomSummaryEntityFields.MEMBERSHIP_STR, queryParams.memberships)
|
query.process(RoomSummaryEntityFields.MEMBERSHIP_STR, queryParams.memberships)
|
||||||
query.notEqualTo(RoomSummaryEntityFields.VERSIONING_STATE_STR, VersioningState.UPGRADED_ROOM_JOINED.name)
|
query.equalTo(RoomSummaryEntityFields.IS_HIDDEN_FROM_USER, false)
|
||||||
|
|
||||||
queryParams.roomCategoryFilter?.let {
|
queryParams.roomCategoryFilter?.let {
|
||||||
when (it) {
|
when (it) {
|
||||||
|
@ -21,6 +21,7 @@ import io.realm.kotlin.createObject
|
|||||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||||
|
import org.matrix.android.sdk.api.session.room.accountdata.RoomAccountDataTypes
|
||||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomAliasesContent
|
import org.matrix.android.sdk.api.session.room.model.RoomAliasesContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomCanonicalAliasContent
|
import org.matrix.android.sdk.api.session.room.model.RoomCanonicalAliasContent
|
||||||
@ -28,6 +29,7 @@ import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesContent
|
|||||||
import org.matrix.android.sdk.api.session.room.model.RoomNameContent
|
import org.matrix.android.sdk.api.session.room.model.RoomNameContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomTopicContent
|
import org.matrix.android.sdk.api.session.room.model.RoomTopicContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomType
|
import org.matrix.android.sdk.api.session.room.model.RoomType
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.VersioningState
|
||||||
import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent
|
import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent
|
||||||
import org.matrix.android.sdk.api.session.room.send.SendState
|
import org.matrix.android.sdk.api.session.room.send.SendState
|
||||||
import org.matrix.android.sdk.internal.crypto.EventDecryptor
|
import org.matrix.android.sdk.internal.crypto.EventDecryptor
|
||||||
@ -55,10 +57,10 @@ import org.matrix.android.sdk.internal.di.UserId
|
|||||||
import org.matrix.android.sdk.internal.extensions.clearWith
|
import org.matrix.android.sdk.internal.extensions.clearWith
|
||||||
import org.matrix.android.sdk.internal.query.process
|
import org.matrix.android.sdk.internal.query.process
|
||||||
import org.matrix.android.sdk.internal.session.room.RoomAvatarResolver
|
import org.matrix.android.sdk.internal.session.room.RoomAvatarResolver
|
||||||
|
import org.matrix.android.sdk.internal.session.room.accountdata.RoomAccountDataDataSource
|
||||||
import org.matrix.android.sdk.internal.session.room.membership.RoomDisplayNameResolver
|
import org.matrix.android.sdk.internal.session.room.membership.RoomDisplayNameResolver
|
||||||
import org.matrix.android.sdk.internal.session.room.membership.RoomMemberHelper
|
import org.matrix.android.sdk.internal.session.room.membership.RoomMemberHelper
|
||||||
import org.matrix.android.sdk.internal.session.room.relationship.RoomChildRelationInfo
|
import org.matrix.android.sdk.internal.session.room.relationship.RoomChildRelationInfo
|
||||||
import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource
|
|
||||||
import org.matrix.android.sdk.internal.session.sync.model.RoomSyncSummary
|
import org.matrix.android.sdk.internal.session.sync.model.RoomSyncSummary
|
||||||
import org.matrix.android.sdk.internal.session.sync.model.RoomSyncUnreadNotifications
|
import org.matrix.android.sdk.internal.session.sync.model.RoomSyncUnreadNotifications
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
@ -71,7 +73,7 @@ internal class RoomSummaryUpdater @Inject constructor(
|
|||||||
private val roomAvatarResolver: RoomAvatarResolver,
|
private val roomAvatarResolver: RoomAvatarResolver,
|
||||||
private val eventDecryptor: EventDecryptor,
|
private val eventDecryptor: EventDecryptor,
|
||||||
private val crossSigningService: DefaultCrossSigningService,
|
private val crossSigningService: DefaultCrossSigningService,
|
||||||
private val stateEventDataSource: StateEventDataSource) {
|
private val roomAccountDataDataSource: RoomAccountDataDataSource) {
|
||||||
|
|
||||||
fun update(realm: Realm,
|
fun update(realm: Realm,
|
||||||
roomId: String,
|
roomId: String,
|
||||||
@ -100,6 +102,10 @@ internal class RoomSummaryUpdater @Inject constructor(
|
|||||||
roomSummaryEntity.membership = membership
|
roomSummaryEntity.membership = membership
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hard to filter from the app now we use PagedList...
|
||||||
|
roomSummaryEntity.isHiddenFromUser = roomSummaryEntity.versioningState == VersioningState.UPGRADED_ROOM_JOINED
|
||||||
|
|| roomAccountDataDataSource.getAccountDataEvent(roomId, RoomAccountDataTypes.EVENT_TYPE_VIRTUAL_ROOM) != null
|
||||||
|
|
||||||
val lastNameEvent = CurrentStateEventEntity.getOrNull(realm, roomId, type = EventType.STATE_ROOM_NAME, stateKey = "")?.root
|
val lastNameEvent = CurrentStateEventEntity.getOrNull(realm, roomId, type = EventType.STATE_ROOM_NAME, stateKey = "")?.root
|
||||||
val lastTopicEvent = CurrentStateEventEntity.getOrNull(realm, roomId, type = EventType.STATE_ROOM_TOPIC, stateKey = "")?.root
|
val lastTopicEvent = CurrentStateEventEntity.getOrNull(realm, roomId, type = EventType.STATE_ROOM_TOPIC, stateKey = "")?.root
|
||||||
val lastCanonicalAliasEvent = CurrentStateEventEntity.getOrNull(realm, roomId, type = EventType.STATE_ROOM_CANONICAL_ALIAS, stateKey = "")?.root
|
val lastCanonicalAliasEvent = CurrentStateEventEntity.getOrNull(realm, roomId, type = EventType.STATE_ROOM_CANONICAL_ALIAS, stateKey = "")?.root
|
||||||
@ -297,7 +303,7 @@ internal class RoomSummaryUpdater @Inject constructor(
|
|||||||
// Timber.v("## SPACES: lookup map ${lookupMap.map { it.key.name to it.value.map { it.name } }.toMap()}")
|
// Timber.v("## SPACES: lookup map ${lookupMap.map { it.key.name to it.value.map { it.name } }.toMap()}")
|
||||||
|
|
||||||
lookupMap.entries
|
lookupMap.entries
|
||||||
.filter { it.key.roomType == RoomType.SPACE && it.key.membership == Membership.JOIN }
|
.filter { it.key.roomType == RoomType.SPACE && it.key.membership == Membership.JOIN }
|
||||||
.forEach { entry ->
|
.forEach { entry ->
|
||||||
val parent = RoomSummaryEntity.where(realm, entry.key.roomId).findFirst()
|
val parent = RoomSummaryEntity.where(realm, entry.key.roomId).findFirst()
|
||||||
if (parent != null) {
|
if (parent != null) {
|
||||||
|
@ -25,7 +25,6 @@ import org.matrix.android.sdk.api.session.events.model.toModel
|
|||||||
import org.matrix.android.sdk.api.session.initsync.InitSyncStep
|
import org.matrix.android.sdk.api.session.initsync.InitSyncStep
|
||||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomMemberContent
|
import org.matrix.android.sdk.api.session.room.model.RoomMemberContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.tag.RoomTagContent
|
|
||||||
import org.matrix.android.sdk.api.session.room.send.SendState
|
import org.matrix.android.sdk.api.session.room.send.SendState
|
||||||
import org.matrix.android.sdk.internal.crypto.DefaultCryptoService
|
import org.matrix.android.sdk.internal.crypto.DefaultCryptoService
|
||||||
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
|
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
|
||||||
@ -55,7 +54,6 @@ import org.matrix.android.sdk.internal.session.initsync.mapWithProgress
|
|||||||
import org.matrix.android.sdk.internal.session.initsync.reportSubtask
|
import org.matrix.android.sdk.internal.session.initsync.reportSubtask
|
||||||
import org.matrix.android.sdk.internal.session.room.membership.RoomChangeMembershipStateDataSource
|
import org.matrix.android.sdk.internal.session.room.membership.RoomChangeMembershipStateDataSource
|
||||||
import org.matrix.android.sdk.internal.session.room.membership.RoomMemberEventHandler
|
import org.matrix.android.sdk.internal.session.room.membership.RoomMemberEventHandler
|
||||||
import org.matrix.android.sdk.internal.session.room.read.FullyReadContent
|
|
||||||
import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryUpdater
|
import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryUpdater
|
||||||
import org.matrix.android.sdk.internal.session.room.timeline.PaginationDirection
|
import org.matrix.android.sdk.internal.session.room.timeline.PaginationDirection
|
||||||
import org.matrix.android.sdk.internal.session.room.timeline.TimelineInput
|
import org.matrix.android.sdk.internal.session.room.timeline.TimelineInput
|
||||||
@ -63,16 +61,15 @@ import org.matrix.android.sdk.internal.session.room.typing.TypingEventContent
|
|||||||
import org.matrix.android.sdk.internal.session.sync.model.InvitedRoomSync
|
import org.matrix.android.sdk.internal.session.sync.model.InvitedRoomSync
|
||||||
import org.matrix.android.sdk.internal.session.sync.model.LazyRoomSyncEphemeral
|
import org.matrix.android.sdk.internal.session.sync.model.LazyRoomSyncEphemeral
|
||||||
import org.matrix.android.sdk.internal.session.sync.model.RoomSync
|
import org.matrix.android.sdk.internal.session.sync.model.RoomSync
|
||||||
import org.matrix.android.sdk.internal.session.sync.model.RoomSyncAccountData
|
|
||||||
import org.matrix.android.sdk.internal.session.sync.model.RoomsSyncResponse
|
import org.matrix.android.sdk.internal.session.sync.model.RoomsSyncResponse
|
||||||
|
import org.matrix.android.sdk.internal.session.sync.parsing.RoomSyncAccountDataHandler
|
||||||
import org.matrix.android.sdk.internal.util.computeBestChunkSize
|
import org.matrix.android.sdk.internal.util.computeBestChunkSize
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class RoomSyncHandler @Inject constructor(private val readReceiptHandler: ReadReceiptHandler,
|
internal class RoomSyncHandler @Inject constructor(private val readReceiptHandler: ReadReceiptHandler,
|
||||||
private val roomSummaryUpdater: RoomSummaryUpdater,
|
private val roomSummaryUpdater: RoomSummaryUpdater,
|
||||||
private val roomTagHandler: RoomTagHandler,
|
private val roomAccountDataHandler: RoomSyncAccountDataHandler,
|
||||||
private val roomFullyReadHandler: RoomFullyReadHandler,
|
|
||||||
private val cryptoService: DefaultCryptoService,
|
private val cryptoService: DefaultCryptoService,
|
||||||
private val roomMemberEventHandler: RoomMemberEventHandler,
|
private val roomMemberEventHandler: RoomMemberEventHandler,
|
||||||
private val roomTypingUsersHandler: RoomTypingUsersHandler,
|
private val roomTypingUsersHandler: RoomTypingUsersHandler,
|
||||||
@ -198,11 +195,11 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
|
|||||||
?.takeIf { it.isNotEmpty() }
|
?.takeIf { it.isNotEmpty() }
|
||||||
?.let { handleEphemeral(realm, roomId, it, insertType == EventInsertType.INITIAL_SYNC, aggregator) }
|
?.let { handleEphemeral(realm, roomId, it, insertType == EventInsertType.INITIAL_SYNC, aggregator) }
|
||||||
|
|
||||||
if (roomSync.accountData?.events?.isNotEmpty() == true) {
|
if (roomSync.accountData != null) {
|
||||||
handleRoomAccountDataEvents(realm, roomId, roomSync.accountData)
|
roomAccountDataHandler.handle(realm, roomId, roomSync.accountData)
|
||||||
}
|
}
|
||||||
|
|
||||||
val roomEntity = RoomEntity.where(realm, roomId).findFirst() ?: realm.createObject(roomId)
|
val roomEntity = RoomEntity.getOrCreate(realm, roomId)
|
||||||
|
|
||||||
if (roomEntity.membership == Membership.INVITE) {
|
if (roomEntity.membership == Membership.INVITE) {
|
||||||
roomEntity.chunks.deleteAllFromRealm()
|
roomEntity.chunks.deleteAllFromRealm()
|
||||||
@ -265,7 +262,7 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
|
|||||||
insertType: EventInsertType,
|
insertType: EventInsertType,
|
||||||
syncLocalTimestampMillis: Long): RoomEntity {
|
syncLocalTimestampMillis: Long): RoomEntity {
|
||||||
Timber.v("Handle invited sync for room $roomId")
|
Timber.v("Handle invited sync for room $roomId")
|
||||||
val roomEntity = RoomEntity.where(realm, roomId).findFirst() ?: realm.createObject(roomId)
|
val roomEntity = RoomEntity.getOrCreate(realm, roomId)
|
||||||
roomEntity.membership = Membership.INVITE
|
roomEntity.membership = Membership.INVITE
|
||||||
if (roomSync.inviteState != null && roomSync.inviteState.events.isNotEmpty()) {
|
if (roomSync.inviteState != null && roomSync.inviteState.events.isNotEmpty()) {
|
||||||
roomSync.inviteState.events.forEach { event ->
|
roomSync.inviteState.events.forEach { event ->
|
||||||
@ -294,7 +291,7 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
|
|||||||
roomSync: RoomSync,
|
roomSync: RoomSync,
|
||||||
insertType: EventInsertType,
|
insertType: EventInsertType,
|
||||||
syncLocalTimestampMillis: Long): RoomEntity {
|
syncLocalTimestampMillis: Long): RoomEntity {
|
||||||
val roomEntity = RoomEntity.where(realm, roomId).findFirst() ?: realm.createObject(roomId)
|
val roomEntity = RoomEntity.getOrCreate(realm, roomId)
|
||||||
for (event in roomSync.state?.events.orEmpty()) {
|
for (event in roomSync.state?.events.orEmpty()) {
|
||||||
if (event.eventId == null || event.stateKey == null || event.type == null) {
|
if (event.eventId == null || event.stateKey == null || event.type == null) {
|
||||||
continue
|
continue
|
||||||
@ -460,17 +457,4 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleRoomAccountDataEvents(realm: Realm, roomId: String, accountData: RoomSyncAccountData) {
|
|
||||||
accountData.events?.forEach { event ->
|
|
||||||
val eventType = event.getClearType()
|
|
||||||
if (eventType == EventType.TAG) {
|
|
||||||
val content = event.getClearContent().toModel<RoomTagContent>()
|
|
||||||
roomTagHandler.handle(realm, roomId, content)
|
|
||||||
} else if (eventType == EventType.FULLY_READ) {
|
|
||||||
val content = event.getClearContent().toModel<FullyReadContent>()
|
|
||||||
roomFullyReadHandler.handle(realm, roomId, content)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ import org.matrix.android.sdk.internal.crypto.DefaultCryptoService
|
|||||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||||
import org.matrix.android.sdk.internal.di.SessionId
|
import org.matrix.android.sdk.internal.di.SessionId
|
||||||
import org.matrix.android.sdk.internal.di.WorkManagerProvider
|
import org.matrix.android.sdk.internal.di.WorkManagerProvider
|
||||||
|
import org.matrix.android.sdk.internal.session.SessionListeners
|
||||||
import org.matrix.android.sdk.internal.session.group.GetGroupDataWorker
|
import org.matrix.android.sdk.internal.session.group.GetGroupDataWorker
|
||||||
import org.matrix.android.sdk.internal.session.initsync.ProgressReporter
|
import org.matrix.android.sdk.internal.session.initsync.ProgressReporter
|
||||||
import org.matrix.android.sdk.internal.session.initsync.reportSubtask
|
import org.matrix.android.sdk.internal.session.initsync.reportSubtask
|
||||||
@ -44,6 +45,7 @@ private const val GET_GROUP_DATA_WORKER = "GET_GROUP_DATA_WORKER"
|
|||||||
internal class SyncResponseHandler @Inject constructor(
|
internal class SyncResponseHandler @Inject constructor(
|
||||||
@SessionDatabase private val monarchy: Monarchy,
|
@SessionDatabase private val monarchy: Monarchy,
|
||||||
@SessionId private val sessionId: String,
|
@SessionId private val sessionId: String,
|
||||||
|
private val sessionListeners: SessionListeners,
|
||||||
private val workManagerProvider: WorkManagerProvider,
|
private val workManagerProvider: WorkManagerProvider,
|
||||||
private val roomSyncHandler: RoomSyncHandler,
|
private val roomSyncHandler: RoomSyncHandler,
|
||||||
private val userAccountDataSyncHandler: UserAccountDataSyncHandler,
|
private val userAccountDataSyncHandler: UserAccountDataSyncHandler,
|
||||||
@ -125,6 +127,7 @@ internal class SyncResponseHandler @Inject constructor(
|
|||||||
syncResponse.rooms?.let {
|
syncResponse.rooms?.let {
|
||||||
checkPushRules(it, isInitialSync)
|
checkPushRules(it, isInitialSync)
|
||||||
userAccountDataSyncHandler.synchronizeWithServerIfNeeded(it.invite)
|
userAccountDataSyncHandler.synchronizeWithServerIfNeeded(it.invite)
|
||||||
|
dispatchInvitedRoom(it)
|
||||||
}
|
}
|
||||||
syncResponse.groups?.let {
|
syncResponse.groups?.let {
|
||||||
scheduleGroupDataFetchingIfNeeded(it)
|
scheduleGroupDataFetchingIfNeeded(it)
|
||||||
@ -139,6 +142,13 @@ internal class SyncResponseHandler @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun dispatchInvitedRoom(roomsSyncResponse: RoomsSyncResponse) {
|
||||||
|
roomsSyncResponse.invite.keys.forEach { roomId ->
|
||||||
|
sessionListeners.dispatch { session, listener ->
|
||||||
|
listener.onNewInvitedRoom(session, roomId) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* At the moment we don't get any group data through the sync, so we poll where every hour.
|
* At the moment we don't get any group data through the sync, so we poll where every hour.
|
||||||
* You can also force to refetch group data using [Group] API.
|
* You can also force to refetch group data using [Group] API.
|
||||||
|
@ -23,7 +23,7 @@ import io.realm.kotlin.where
|
|||||||
import org.matrix.android.sdk.api.pushrules.RuleScope
|
import org.matrix.android.sdk.api.pushrules.RuleScope
|
||||||
import org.matrix.android.sdk.api.pushrules.RuleSetKey
|
import org.matrix.android.sdk.api.pushrules.RuleSetKey
|
||||||
import org.matrix.android.sdk.api.pushrules.rest.GetPushRulesResponse
|
import org.matrix.android.sdk.api.pushrules.rest.GetPushRulesResponse
|
||||||
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent
|
import org.matrix.android.sdk.api.session.accountdata.AccountDataEvent
|
||||||
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes
|
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes
|
||||||
import org.matrix.android.sdk.api.session.events.model.Content
|
import org.matrix.android.sdk.api.session.events.model.Content
|
||||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||||
@ -113,7 +113,7 @@ internal class UserAccountDataSyncHandler @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handlePushRules(realm: Realm, event: UserAccountDataEvent) {
|
private fun handlePushRules(realm: Realm, event: AccountDataEvent) {
|
||||||
val pushRules = event.content.toModel<GetPushRulesResponse>() ?: return
|
val pushRules = event.content.toModel<GetPushRulesResponse>() ?: return
|
||||||
realm.where(PushRulesEntity::class.java)
|
realm.where(PushRulesEntity::class.java)
|
||||||
.findAll()
|
.findAll()
|
||||||
@ -155,7 +155,7 @@ internal class UserAccountDataSyncHandler @Inject constructor(
|
|||||||
realm.insertOrUpdate(underrides)
|
realm.insertOrUpdate(underrides)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleDirectChatRooms(realm: Realm, event: UserAccountDataEvent) {
|
private fun handleDirectChatRooms(realm: Realm, event: AccountDataEvent) {
|
||||||
val content = event.content.toModel<DirectMessagesContent>() ?: return
|
val content = event.content.toModel<DirectMessagesContent>() ?: return
|
||||||
content.forEach { (userId, roomIds) ->
|
content.forEach { (userId, roomIds) ->
|
||||||
roomIds.forEach { roomId ->
|
roomIds.forEach { roomId ->
|
||||||
@ -181,7 +181,7 @@ internal class UserAccountDataSyncHandler @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleIgnoredUsers(realm: Realm, event: UserAccountDataEvent) {
|
private fun handleIgnoredUsers(realm: Realm, event: AccountDataEvent) {
|
||||||
val userIds = event.content.toModel<IgnoredUsersContent>()?.ignoredUsers?.keys ?: return
|
val userIds = event.content.toModel<IgnoredUsersContent>()?.ignoredUsers?.keys ?: return
|
||||||
realm.where(IgnoredUserEntity::class.java)
|
realm.where(IgnoredUserEntity::class.java)
|
||||||
.findAll()
|
.findAll()
|
||||||
@ -191,7 +191,7 @@ internal class UserAccountDataSyncHandler @Inject constructor(
|
|||||||
// TODO If not initial sync, we should execute a init sync
|
// TODO If not initial sync, we should execute a init sync
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleBreadcrumbs(realm: Realm, event: UserAccountDataEvent) {
|
private fun handleBreadcrumbs(realm: Realm, event: AccountDataEvent) {
|
||||||
val recentRoomIds = event.content.toModel<BreadcrumbsContent>()?.recentRoomIds ?: return
|
val recentRoomIds = event.content.toModel<BreadcrumbsContent>()?.recentRoomIds ?: return
|
||||||
val entity = BreadcrumbsEntity.getOrCreate(realm)
|
val entity = BreadcrumbsEntity.getOrCreate(realm)
|
||||||
|
|
||||||
|
@ -18,9 +18,9 @@ package org.matrix.android.sdk.internal.session.sync.model.accountdata
|
|||||||
|
|
||||||
import com.squareup.moshi.Json
|
import com.squareup.moshi.Json
|
||||||
import com.squareup.moshi.JsonClass
|
import com.squareup.moshi.JsonClass
|
||||||
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent
|
import org.matrix.android.sdk.api.session.accountdata.AccountDataEvent
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
internal data class UserAccountDataSync(
|
internal data class UserAccountDataSync(
|
||||||
@Json(name = "events") val list: List<UserAccountDataEvent> = emptyList()
|
@Json(name = "events") val list: List<AccountDataEvent> = emptyList()
|
||||||
)
|
)
|
||||||
|
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* 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 org.matrix.android.sdk.internal.session.sync.parsing
|
||||||
|
|
||||||
|
import io.realm.Realm
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||||
|
import org.matrix.android.sdk.api.session.room.accountdata.RoomAccountDataTypes
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.tag.RoomTagContent
|
||||||
|
import org.matrix.android.sdk.api.util.JsonDict
|
||||||
|
import org.matrix.android.sdk.internal.database.mapper.ContentMapper
|
||||||
|
import org.matrix.android.sdk.internal.database.model.RoomAccountDataEntity
|
||||||
|
import org.matrix.android.sdk.internal.database.model.RoomAccountDataEntityFields
|
||||||
|
import org.matrix.android.sdk.internal.database.model.RoomEntity
|
||||||
|
import org.matrix.android.sdk.internal.database.query.getOrCreate
|
||||||
|
import org.matrix.android.sdk.internal.session.room.read.FullyReadContent
|
||||||
|
import org.matrix.android.sdk.internal.session.sync.RoomFullyReadHandler
|
||||||
|
import org.matrix.android.sdk.internal.session.sync.RoomTagHandler
|
||||||
|
import org.matrix.android.sdk.internal.session.sync.model.RoomSyncAccountData
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
internal class RoomSyncAccountDataHandler @Inject constructor(private val roomTagHandler: RoomTagHandler,
|
||||||
|
private val roomFullyReadHandler: RoomFullyReadHandler) {
|
||||||
|
|
||||||
|
fun handle(realm: Realm, roomId: String, accountData: RoomSyncAccountData) {
|
||||||
|
if (accountData.events.isNullOrEmpty()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val roomEntity = RoomEntity.getOrCreate(realm, roomId)
|
||||||
|
for (event in accountData.events) {
|
||||||
|
val eventType = event.getClearType()
|
||||||
|
handleGeneric(roomEntity, event.getClearContent(), eventType)
|
||||||
|
if (eventType == RoomAccountDataTypes.EVENT_TYPE_TAG) {
|
||||||
|
val content = event.getClearContent().toModel<RoomTagContent>()
|
||||||
|
roomTagHandler.handle(realm, roomId, content)
|
||||||
|
} else if (eventType == RoomAccountDataTypes.EVENT_TYPE_FULLY_READ) {
|
||||||
|
val content = event.getClearContent().toModel<FullyReadContent>()
|
||||||
|
roomFullyReadHandler.handle(realm, roomId, content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleGeneric(roomEntity: RoomEntity, content: JsonDict?, eventType: String) {
|
||||||
|
val existing = roomEntity.accountData.where().equalTo(RoomAccountDataEntityFields.TYPE, eventType).findFirst()
|
||||||
|
if (existing != null) {
|
||||||
|
// Update current value
|
||||||
|
existing.contentStr = ContentMapper.map(content)
|
||||||
|
} else {
|
||||||
|
val roomAccountData = RoomAccountDataEntity(
|
||||||
|
type = eventType,
|
||||||
|
contentStr = ContentMapper.map(content)
|
||||||
|
)
|
||||||
|
roomEntity.accountData.add(roomAccountData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -30,7 +30,7 @@ import org.matrix.android.sdk.internal.session.identity.IdentityAuthAPI
|
|||||||
import org.matrix.android.sdk.internal.session.identity.IdentityRegisterTask
|
import org.matrix.android.sdk.internal.session.identity.IdentityRegisterTask
|
||||||
import org.matrix.android.sdk.internal.session.openid.GetOpenIdTokenTask
|
import org.matrix.android.sdk.internal.session.openid.GetOpenIdTokenTask
|
||||||
import org.matrix.android.sdk.internal.session.sync.model.accountdata.AcceptedTermsContent
|
import org.matrix.android.sdk.internal.session.sync.model.accountdata.AcceptedTermsContent
|
||||||
import org.matrix.android.sdk.internal.session.user.accountdata.AccountDataDataSource
|
import org.matrix.android.sdk.internal.session.user.accountdata.UserAccountDataDataSource
|
||||||
import org.matrix.android.sdk.internal.session.user.accountdata.UpdateUserAccountDataTask
|
import org.matrix.android.sdk.internal.session.user.accountdata.UpdateUserAccountDataTask
|
||||||
import org.matrix.android.sdk.internal.util.ensureTrailingSlash
|
import org.matrix.android.sdk.internal.util.ensureTrailingSlash
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -38,7 +38,7 @@ import javax.inject.Inject
|
|||||||
internal class DefaultTermsService @Inject constructor(
|
internal class DefaultTermsService @Inject constructor(
|
||||||
@UnauthenticatedWithCertificate
|
@UnauthenticatedWithCertificate
|
||||||
private val unauthenticatedOkHttpClient: Lazy<OkHttpClient>,
|
private val unauthenticatedOkHttpClient: Lazy<OkHttpClient>,
|
||||||
private val accountDataDataSource: AccountDataDataSource,
|
private val accountDataDataSource: UserAccountDataDataSource,
|
||||||
private val termsAPI: TermsAPI,
|
private val termsAPI: TermsAPI,
|
||||||
private val retrofitFactory: RetrofitFactory,
|
private val retrofitFactory: RetrofitFactory,
|
||||||
private val getOpenIdTokenTask: GetOpenIdTokenTask,
|
private val getOpenIdTokenTask: GetOpenIdTokenTask,
|
||||||
|
@ -38,7 +38,7 @@ internal interface ThirdPartyAPI {
|
|||||||
*
|
*
|
||||||
* Ref: https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-thirdparty-user-protocol
|
* Ref: https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-thirdparty-user-protocol
|
||||||
*/
|
*/
|
||||||
@GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "thirdparty/protocols/user/{protocol}")
|
@GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "thirdparty/user/{protocol}")
|
||||||
suspend fun getThirdPartyUser(@Path("protocol") protocol: String,
|
suspend fun getThirdPartyUser(@Path("protocol") protocol: String,
|
||||||
@QueryMap params: Map<String, String>?): List<ThirdPartyUser>
|
@QueryMap params: Map<String, String>?): List<ThirdPartyUser>
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ import androidx.lifecycle.Transformations
|
|||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
import io.realm.Realm
|
import io.realm.Realm
|
||||||
import io.realm.RealmQuery
|
import io.realm.RealmQuery
|
||||||
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent
|
import org.matrix.android.sdk.api.session.accountdata.AccountDataEvent
|
||||||
import org.matrix.android.sdk.api.util.Optional
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
import org.matrix.android.sdk.api.util.toOptional
|
import org.matrix.android.sdk.api.util.toOptional
|
||||||
import org.matrix.android.sdk.internal.database.RealmSessionProvider
|
import org.matrix.android.sdk.internal.database.RealmSessionProvider
|
||||||
@ -31,27 +31,27 @@ import org.matrix.android.sdk.internal.database.model.UserAccountDataEntityField
|
|||||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class AccountDataDataSource @Inject constructor(@SessionDatabase private val monarchy: Monarchy,
|
internal class UserAccountDataDataSource @Inject constructor(@SessionDatabase private val monarchy: Monarchy,
|
||||||
private val realmSessionProvider: RealmSessionProvider,
|
private val realmSessionProvider: RealmSessionProvider,
|
||||||
private val accountDataMapper: AccountDataMapper) {
|
private val accountDataMapper: AccountDataMapper) {
|
||||||
|
|
||||||
fun getAccountDataEvent(type: String): UserAccountDataEvent? {
|
fun getAccountDataEvent(type: String): AccountDataEvent? {
|
||||||
return getAccountDataEvents(setOf(type)).firstOrNull()
|
return getAccountDataEvents(setOf(type)).firstOrNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getLiveAccountDataEvent(type: String): LiveData<Optional<UserAccountDataEvent>> {
|
fun getLiveAccountDataEvent(type: String): LiveData<Optional<AccountDataEvent>> {
|
||||||
return Transformations.map(getLiveAccountDataEvents(setOf(type))) {
|
return Transformations.map(getLiveAccountDataEvents(setOf(type))) {
|
||||||
it.firstOrNull()?.toOptional()
|
it.firstOrNull()?.toOptional()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getAccountDataEvents(types: Set<String>): List<UserAccountDataEvent> {
|
fun getAccountDataEvents(types: Set<String>): List<AccountDataEvent> {
|
||||||
return realmSessionProvider.withRealm {
|
return realmSessionProvider.withRealm {
|
||||||
accountDataEventsQuery(it, types).findAll().map(accountDataMapper::map)
|
accountDataEventsQuery(it, types).findAll().map(accountDataMapper::map)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getLiveAccountDataEvents(types: Set<String>): LiveData<List<UserAccountDataEvent>> {
|
fun getLiveAccountDataEvents(types: Set<String>): LiveData<List<AccountDataEvent>> {
|
||||||
return monarchy.findAllMappedWithChanges(
|
return monarchy.findAllMappedWithChanges(
|
||||||
{ accountDataEventsQuery(it, types) },
|
{ accountDataEventsQuery(it, types) },
|
||||||
accountDataMapper::map
|
accountDataMapper::map
|
@ -23,33 +23,33 @@ import org.matrix.android.sdk.api.session.events.model.Content
|
|||||||
import org.matrix.android.sdk.api.util.Optional
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||||
import org.matrix.android.sdk.internal.session.sync.UserAccountDataSyncHandler
|
import org.matrix.android.sdk.internal.session.sync.UserAccountDataSyncHandler
|
||||||
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent
|
import org.matrix.android.sdk.api.session.accountdata.AccountDataEvent
|
||||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
import org.matrix.android.sdk.internal.task.TaskExecutor
|
||||||
import org.matrix.android.sdk.internal.task.configureWith
|
import org.matrix.android.sdk.internal.task.configureWith
|
||||||
import org.matrix.android.sdk.internal.util.awaitCallback
|
import org.matrix.android.sdk.internal.util.awaitCallback
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class DefaultAccountDataService @Inject constructor(
|
internal class UserAccountDataService @Inject constructor(
|
||||||
@SessionDatabase private val monarchy: Monarchy,
|
@SessionDatabase private val monarchy: Monarchy,
|
||||||
private val updateUserAccountDataTask: UpdateUserAccountDataTask,
|
private val updateUserAccountDataTask: UpdateUserAccountDataTask,
|
||||||
private val userAccountDataSyncHandler: UserAccountDataSyncHandler,
|
private val userAccountDataSyncHandler: UserAccountDataSyncHandler,
|
||||||
private val accountDataDataSource: AccountDataDataSource,
|
private val accountDataDataSource: UserAccountDataDataSource,
|
||||||
private val taskExecutor: TaskExecutor
|
private val taskExecutor: TaskExecutor
|
||||||
) : AccountDataService {
|
) : AccountDataService {
|
||||||
|
|
||||||
override fun getAccountDataEvent(type: String): UserAccountDataEvent? {
|
override fun getAccountDataEvent(type: String): AccountDataEvent? {
|
||||||
return accountDataDataSource.getAccountDataEvent(type)
|
return accountDataDataSource.getAccountDataEvent(type)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getLiveAccountDataEvent(type: String): LiveData<Optional<UserAccountDataEvent>> {
|
override fun getLiveAccountDataEvent(type: String): LiveData<Optional<AccountDataEvent>> {
|
||||||
return accountDataDataSource.getLiveAccountDataEvent(type)
|
return accountDataDataSource.getLiveAccountDataEvent(type)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getAccountDataEvents(types: Set<String>): List<UserAccountDataEvent> {
|
override fun getAccountDataEvents(types: Set<String>): List<AccountDataEvent> {
|
||||||
return accountDataDataSource.getAccountDataEvents(types)
|
return accountDataDataSource.getAccountDataEvents(types)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getLiveAccountDataEvents(types: Set<String>): LiveData<List<UserAccountDataEvent>> {
|
override fun getLiveAccountDataEvents(types: Set<String>): LiveData<List<AccountDataEvent>> {
|
||||||
return accountDataDataSource.getLiveAccountDataEvents(types)
|
return accountDataDataSource.getLiveAccountDataEvents(types)
|
||||||
}
|
}
|
||||||
|
|
@ -23,7 +23,7 @@ import androidx.lifecycle.LiveData
|
|||||||
import androidx.lifecycle.Transformations
|
import androidx.lifecycle.Transformations
|
||||||
import org.matrix.android.sdk.api.query.QueryStringValue
|
import org.matrix.android.sdk.api.query.QueryStringValue
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent
|
import org.matrix.android.sdk.api.session.accountdata.AccountDataEvent
|
||||||
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes
|
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes
|
||||||
import org.matrix.android.sdk.api.session.events.model.Content
|
import org.matrix.android.sdk.api.session.events.model.Content
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
@ -39,7 +39,7 @@ import org.matrix.android.sdk.api.session.SessionLifecycleObserver
|
|||||||
import org.matrix.android.sdk.internal.session.SessionScope
|
import org.matrix.android.sdk.internal.session.SessionScope
|
||||||
import org.matrix.android.sdk.internal.session.integrationmanager.IntegrationManager
|
import org.matrix.android.sdk.internal.session.integrationmanager.IntegrationManager
|
||||||
import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource
|
import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource
|
||||||
import org.matrix.android.sdk.internal.session.user.accountdata.AccountDataDataSource
|
import org.matrix.android.sdk.internal.session.user.accountdata.UserAccountDataDataSource
|
||||||
import org.matrix.android.sdk.internal.session.widgets.helper.WidgetFactory
|
import org.matrix.android.sdk.internal.session.widgets.helper.WidgetFactory
|
||||||
import org.matrix.android.sdk.internal.session.widgets.helper.extractWidgetSequence
|
import org.matrix.android.sdk.internal.session.widgets.helper.extractWidgetSequence
|
||||||
import java.util.HashMap
|
import java.util.HashMap
|
||||||
@ -47,7 +47,7 @@ import javax.inject.Inject
|
|||||||
|
|
||||||
@SessionScope
|
@SessionScope
|
||||||
internal class WidgetManager @Inject constructor(private val integrationManager: IntegrationManager,
|
internal class WidgetManager @Inject constructor(private val integrationManager: IntegrationManager,
|
||||||
private val accountDataDataSource: AccountDataDataSource,
|
private val accountDataDataSource: UserAccountDataDataSource,
|
||||||
private val stateEventDataSource: StateEventDataSource,
|
private val stateEventDataSource: StateEventDataSource,
|
||||||
private val createWidgetTask: CreateWidgetTask,
|
private val createWidgetTask: CreateWidgetTask,
|
||||||
private val widgetFactory: WidgetFactory,
|
private val widgetFactory: WidgetFactory,
|
||||||
@ -150,8 +150,8 @@ internal class WidgetManager @Inject constructor(private val integrationManager:
|
|||||||
return widgetsAccountData.mapToWidgets(widgetTypes, excludedTypes)
|
return widgetsAccountData.mapToWidgets(widgetTypes, excludedTypes)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun UserAccountDataEvent.mapToWidgets(widgetTypes: Set<String>? = null,
|
private fun AccountDataEvent.mapToWidgets(widgetTypes: Set<String>? = null,
|
||||||
excludedTypes: Set<String>? = null): List<Widget> {
|
excludedTypes: Set<String>? = null): List<Widget> {
|
||||||
return extractWidgetSequence(widgetFactory)
|
return extractWidgetSequence(widgetFactory)
|
||||||
.filter {
|
.filter {
|
||||||
val widgetType = it.widgetContent.type ?: return@filter false
|
val widgetType = it.widgetContent.type ?: return@filter false
|
||||||
|
@ -19,10 +19,10 @@ package org.matrix.android.sdk.internal.session.widgets.helper
|
|||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||||
import org.matrix.android.sdk.api.util.JsonDict
|
import org.matrix.android.sdk.api.util.JsonDict
|
||||||
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent
|
import org.matrix.android.sdk.api.session.accountdata.AccountDataEvent
|
||||||
import org.matrix.android.sdk.api.session.widgets.model.Widget
|
import org.matrix.android.sdk.api.session.widgets.model.Widget
|
||||||
|
|
||||||
internal fun UserAccountDataEvent.extractWidgetSequence(widgetFactory: WidgetFactory): Sequence<Widget> {
|
internal fun AccountDataEvent.extractWidgetSequence(widgetFactory: WidgetFactory): Sequence<Widget> {
|
||||||
return content.asSequence()
|
return content.asSequence()
|
||||||
.mapNotNull {
|
.mapNotNull {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
1
newsfragment/3355.feature
Normal file
1
newsfragment/3355.feature
Normal file
@ -0,0 +1 @@
|
|||||||
|
VoIP: support for virtual rooms
|
@ -104,7 +104,7 @@ class DefaultErrorFormatter @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is Failure.OtherServerError -> {
|
is Failure.OtherServerError -> {
|
||||||
when (throwable.httpCode) {
|
when (throwable.httpCode) {
|
||||||
HttpURLConnection.HTTP_NOT_FOUND ->
|
HttpURLConnection.HTTP_NOT_FOUND ->
|
||||||
// homeserver not found
|
// homeserver not found
|
||||||
@ -116,9 +116,9 @@ class DefaultErrorFormatter @Inject constructor(
|
|||||||
throwable.localizedMessage
|
throwable.localizedMessage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is DialPadLookup.Failure ->
|
is DialPadLookup.Failure ->
|
||||||
stringProvider.getString(R.string.call_dial_pad_lookup_error)
|
stringProvider.getString(R.string.call_dial_pad_lookup_error)
|
||||||
else -> throwable.localizedMessage
|
else -> throwable.localizedMessage
|
||||||
}
|
}
|
||||||
?: stringProvider.getString(R.string.unknown_error)
|
?: stringProvider.getString(R.string.unknown_error)
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@ fun Session.configureAndStart(context: Context) {
|
|||||||
setFilter(FilterService.FilterPreset.ElementFilter)
|
setFilter(FilterService.FilterPreset.ElementFilter)
|
||||||
startSyncing(context)
|
startSyncing(context)
|
||||||
refreshPushers()
|
refreshPushers()
|
||||||
|
context.vectorComponent().webRtcCallManager().checkForProtocolsSupportIfNeeded()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Session.startSyncing(context: Context) {
|
fun Session.startSyncing(context: Context) {
|
||||||
|
@ -32,12 +32,12 @@ import im.vector.app.features.call.VectorCallActivity
|
|||||||
import im.vector.app.features.call.telecom.CallConnection
|
import im.vector.app.features.call.telecom.CallConnection
|
||||||
import im.vector.app.features.call.webrtc.WebRtcCall
|
import im.vector.app.features.call.webrtc.WebRtcCall
|
||||||
import im.vector.app.features.call.webrtc.WebRtcCallManager
|
import im.vector.app.features.call.webrtc.WebRtcCallManager
|
||||||
|
import im.vector.app.features.call.webrtc.getOpponentAsMatrixItem
|
||||||
import im.vector.app.features.home.AvatarRenderer
|
import im.vector.app.features.home.AvatarRenderer
|
||||||
import im.vector.app.features.notifications.NotificationUtils
|
import im.vector.app.features.notifications.NotificationUtils
|
||||||
import im.vector.app.features.popup.IncomingCallAlert
|
import im.vector.app.features.popup.IncomingCallAlert
|
||||||
import im.vector.app.features.popup.PopupAlertManager
|
import im.vector.app.features.popup.PopupAlertManager
|
||||||
import org.matrix.android.sdk.api.util.MatrixItem
|
import org.matrix.android.sdk.api.util.MatrixItem
|
||||||
import org.matrix.android.sdk.api.util.toMatrixItem
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -176,7 +176,7 @@ class CallService : VectorService() {
|
|||||||
}
|
}
|
||||||
alertManager.postVectorAlert(incomingCallAlert)
|
alertManager.postVectorAlert(incomingCallAlert)
|
||||||
val notification = notificationUtils.buildIncomingCallNotification(
|
val notification = notificationUtils.buildIncomingCallNotification(
|
||||||
mxCall = call.mxCall,
|
call = call,
|
||||||
title = opponentMatrixItem?.getBestName() ?: call.mxCall.opponentUserId,
|
title = opponentMatrixItem?.getBestName() ?: call.mxCall.opponentUserId,
|
||||||
fromBg = fromBg
|
fromBg = fromBg
|
||||||
)
|
)
|
||||||
@ -207,7 +207,7 @@ class CallService : VectorService() {
|
|||||||
private fun showCallScreen(call: WebRtcCall, mode: String) {
|
private fun showCallScreen(call: WebRtcCall, mode: String) {
|
||||||
val intent = VectorCallActivity.newIntent(
|
val intent = VectorCallActivity.newIntent(
|
||||||
context = this,
|
context = this,
|
||||||
mxCall = call.mxCall,
|
call = call,
|
||||||
mode = mode
|
mode = mode
|
||||||
)
|
)
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
@ -221,7 +221,7 @@ class CallService : VectorService() {
|
|||||||
val opponentMatrixItem = getOpponentMatrixItem(call)
|
val opponentMatrixItem = getOpponentMatrixItem(call)
|
||||||
Timber.v("displayOutgoingCallNotification : display the dedicated notification")
|
Timber.v("displayOutgoingCallNotification : display the dedicated notification")
|
||||||
val notification = notificationUtils.buildOutgoingRingingCallNotification(
|
val notification = notificationUtils.buildOutgoingRingingCallNotification(
|
||||||
mxCall = call.mxCall,
|
call = call,
|
||||||
title = opponentMatrixItem?.getBestName() ?: call.mxCall.opponentUserId
|
title = opponentMatrixItem?.getBestName() ?: call.mxCall.opponentUserId
|
||||||
)
|
)
|
||||||
if (knownCalls.isEmpty()) {
|
if (knownCalls.isEmpty()) {
|
||||||
@ -244,7 +244,7 @@ class CallService : VectorService() {
|
|||||||
val opponentMatrixItem = getOpponentMatrixItem(call)
|
val opponentMatrixItem = getOpponentMatrixItem(call)
|
||||||
alertManager.cancelAlert(callId)
|
alertManager.cancelAlert(callId)
|
||||||
val notification = notificationUtils.buildPendingCallNotification(
|
val notification = notificationUtils.buildPendingCallNotification(
|
||||||
mxCall = call.mxCall,
|
call = call,
|
||||||
title = opponentMatrixItem?.getBestName() ?: call.mxCall.opponentUserId
|
title = opponentMatrixItem?.getBestName() ?: call.mxCall.opponentUserId
|
||||||
)
|
)
|
||||||
if (knownCalls.isEmpty()) {
|
if (knownCalls.isEmpty()) {
|
||||||
@ -275,7 +275,9 @@ class CallService : VectorService() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun getOpponentMatrixItem(call: WebRtcCall): MatrixItem? {
|
private fun getOpponentMatrixItem(call: WebRtcCall): MatrixItem? {
|
||||||
return vectorComponent().currentSession().getUser(call.mxCall.opponentUserId)?.toMatrixItem()
|
return vectorComponent().activeSessionHolder().getSafeActiveSession()?.let {
|
||||||
|
call.getOpponentAsMatrixItem(it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 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.app.features.call
|
||||||
|
|
||||||
|
import im.vector.app.features.call.lookup.CallProtocolsChecker
|
||||||
|
import im.vector.app.features.call.lookup.CallUserMapper
|
||||||
|
import im.vector.app.features.session.SessionScopedProperty
|
||||||
|
import org.matrix.android.sdk.api.session.Session
|
||||||
|
|
||||||
|
interface VectorCallService {
|
||||||
|
val protocolChecker: CallProtocolsChecker
|
||||||
|
val userMapper: CallUserMapper
|
||||||
|
}
|
||||||
|
|
||||||
|
val Session.vectorCallService: VectorCallService by SessionScopedProperty {
|
||||||
|
object : VectorCallService {
|
||||||
|
override val protocolChecker = CallProtocolsChecker(it)
|
||||||
|
override val userMapper = CallUserMapper(it, protocolChecker)
|
||||||
|
}
|
||||||
|
}
|
@ -46,6 +46,7 @@ import im.vector.app.databinding.ActivityCallBinding
|
|||||||
import im.vector.app.features.call.dialpad.CallDialPadBottomSheet
|
import im.vector.app.features.call.dialpad.CallDialPadBottomSheet
|
||||||
import im.vector.app.features.call.dialpad.DialPadFragment
|
import im.vector.app.features.call.dialpad.DialPadFragment
|
||||||
import im.vector.app.features.call.utils.EglUtils
|
import im.vector.app.features.call.utils.EglUtils
|
||||||
|
import im.vector.app.features.call.webrtc.WebRtcCall
|
||||||
import im.vector.app.features.call.webrtc.WebRtcCallManager
|
import im.vector.app.features.call.webrtc.WebRtcCallManager
|
||||||
import im.vector.app.features.home.AvatarRenderer
|
import im.vector.app.features.home.AvatarRenderer
|
||||||
import im.vector.app.features.home.room.detail.RoomDetailActivity
|
import im.vector.app.features.home.room.detail.RoomDetailActivity
|
||||||
@ -54,7 +55,6 @@ import io.reactivex.android.schedulers.AndroidSchedulers
|
|||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
import org.matrix.android.sdk.api.extensions.orFalse
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
import org.matrix.android.sdk.api.session.call.CallState
|
import org.matrix.android.sdk.api.session.call.CallState
|
||||||
import org.matrix.android.sdk.api.session.call.MxCallDetail
|
|
||||||
import org.matrix.android.sdk.api.session.call.MxPeerConnectionState
|
import org.matrix.android.sdk.api.session.call.MxPeerConnectionState
|
||||||
import org.matrix.android.sdk.api.session.call.TurnServerResponse
|
import org.matrix.android.sdk.api.session.call.TurnServerResponse
|
||||||
import org.webrtc.EglBase
|
import org.webrtc.EglBase
|
||||||
@ -64,7 +64,7 @@ import javax.inject.Inject
|
|||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class CallArgs(
|
data class CallArgs(
|
||||||
val roomId: String,
|
val signalingRoomId: String,
|
||||||
val callId: String,
|
val callId: String,
|
||||||
val participantUserId: String,
|
val participantUserId: String,
|
||||||
val isIncomingCall: Boolean,
|
val isIncomingCall: Boolean,
|
||||||
@ -276,7 +276,7 @@ class VectorCallActivity : VectorBaseActivity<ActivityCallBinding>(), CallContro
|
|||||||
views.otherKnownCallAvatarView.setOnClickListener {
|
views.otherKnownCallAvatarView.setOnClickListener {
|
||||||
withState(callViewModel) {
|
withState(callViewModel) {
|
||||||
val otherCall = callManager.getCallById(it.otherKnownCallInfo?.callId ?: "") ?: return@withState
|
val otherCall = callManager.getCallById(it.otherKnownCallInfo?.callId ?: "") ?: return@withState
|
||||||
startActivity(newIntent(this, otherCall.mxCall, null))
|
startActivity(newIntent(this, otherCall, null))
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -364,18 +364,18 @@ class VectorCallActivity : VectorBaseActivity<ActivityCallBinding>(), CallContro
|
|||||||
const val INCOMING_RINGING = "INCOMING_RINGING"
|
const val INCOMING_RINGING = "INCOMING_RINGING"
|
||||||
const val INCOMING_ACCEPT = "INCOMING_ACCEPT"
|
const val INCOMING_ACCEPT = "INCOMING_ACCEPT"
|
||||||
|
|
||||||
fun newIntent(context: Context, mxCall: MxCallDetail, mode: String?): Intent {
|
fun newIntent(context: Context, call: WebRtcCall, mode: String?): Intent {
|
||||||
return Intent(context, VectorCallActivity::class.java).apply {
|
return Intent(context, VectorCallActivity::class.java).apply {
|
||||||
// what could be the best flags?
|
// what could be the best flags?
|
||||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
putExtra(MvRx.KEY_ARG, CallArgs(mxCall.roomId, mxCall.callId, mxCall.opponentUserId, !mxCall.isOutgoing, mxCall.isVideoCall))
|
putExtra(MvRx.KEY_ARG, CallArgs(call.nativeRoomId, call.callId, call.mxCall.opponentUserId, !call.mxCall.isOutgoing, call.mxCall.isVideoCall))
|
||||||
putExtra(EXTRA_MODE, mode)
|
putExtra(EXTRA_MODE, mode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun newIntent(context: Context,
|
fun newIntent(context: Context,
|
||||||
callId: String,
|
callId: String,
|
||||||
roomId: String,
|
signalingRoomId: String,
|
||||||
otherUserId: String,
|
otherUserId: String,
|
||||||
isIncomingCall: Boolean,
|
isIncomingCall: Boolean,
|
||||||
isVideoCall: Boolean,
|
isVideoCall: Boolean,
|
||||||
@ -383,7 +383,7 @@ class VectorCallActivity : VectorBaseActivity<ActivityCallBinding>(), CallContro
|
|||||||
return Intent(context, VectorCallActivity::class.java).apply {
|
return Intent(context, VectorCallActivity::class.java).apply {
|
||||||
// what could be the best flags?
|
// what could be the best flags?
|
||||||
flags = FLAG_ACTIVITY_CLEAR_TOP
|
flags = FLAG_ACTIVITY_CLEAR_TOP
|
||||||
putExtra(MvRx.KEY_ARG, CallArgs(roomId, callId, otherUserId, isIncomingCall, isVideoCall))
|
putExtra(MvRx.KEY_ARG, CallArgs(signalingRoomId, callId, otherUserId, isIncomingCall, isVideoCall))
|
||||||
putExtra(EXTRA_MODE, mode)
|
putExtra(EXTRA_MODE, mode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -410,7 +410,7 @@ class VectorCallActivity : VectorBaseActivity<ActivityCallBinding>(), CallContro
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun returnToChat() {
|
override fun returnToChat() {
|
||||||
val args = RoomDetailArgs(callArgs.roomId)
|
val args = RoomDetailArgs(callArgs.signalingRoomId)
|
||||||
val intent = RoomDetailActivity.newIntent(this, args).apply {
|
val intent = RoomDetailActivity.newIntent(this, args).apply {
|
||||||
flags = FLAG_ACTIVITY_CLEAR_TOP
|
flags = FLAG_ACTIVITY_CLEAR_TOP
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ import im.vector.app.core.platform.VectorViewModel
|
|||||||
import im.vector.app.features.call.audio.CallAudioManager
|
import im.vector.app.features.call.audio.CallAudioManager
|
||||||
import im.vector.app.features.call.webrtc.WebRtcCall
|
import im.vector.app.features.call.webrtc.WebRtcCall
|
||||||
import im.vector.app.features.call.webrtc.WebRtcCallManager
|
import im.vector.app.features.call.webrtc.WebRtcCallManager
|
||||||
|
import im.vector.app.features.call.webrtc.getOpponentAsMatrixItem
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@ -38,8 +39,6 @@ import org.matrix.android.sdk.api.session.call.CallState
|
|||||||
import org.matrix.android.sdk.api.session.call.MxCall
|
import org.matrix.android.sdk.api.session.call.MxCall
|
||||||
import org.matrix.android.sdk.api.session.call.MxPeerConnectionState
|
import org.matrix.android.sdk.api.session.call.MxPeerConnectionState
|
||||||
import org.matrix.android.sdk.api.session.room.model.call.supportCallTransfer
|
import org.matrix.android.sdk.api.session.room.model.call.supportCallTransfer
|
||||||
import org.matrix.android.sdk.api.util.MatrixItem
|
|
||||||
import org.matrix.android.sdk.api.util.toMatrixItem
|
|
||||||
|
|
||||||
class VectorCallViewModel @AssistedInject constructor(
|
class VectorCallViewModel @AssistedInject constructor(
|
||||||
@Assisted initialState: VectorCallViewState,
|
@Assisted initialState: VectorCallViewState,
|
||||||
@ -152,7 +151,7 @@ class VectorCallViewModel @AssistedInject constructor(
|
|||||||
if (otherCall == null) {
|
if (otherCall == null) {
|
||||||
copy(otherKnownCallInfo = null)
|
copy(otherKnownCallInfo = null)
|
||||||
} else {
|
} else {
|
||||||
val otherUserItem: MatrixItem? = session.getUser(otherCall.mxCall.opponentUserId)?.toMatrixItem()
|
val otherUserItem = otherCall.getOpponentAsMatrixItem(session)
|
||||||
copy(otherKnownCallInfo = VectorCallViewState.CallInfo(otherCall.callId, otherUserItem))
|
copy(otherKnownCallInfo = VectorCallViewState.CallInfo(otherCall.callId, otherUserItem))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -167,7 +166,7 @@ class VectorCallViewModel @AssistedInject constructor(
|
|||||||
} else {
|
} else {
|
||||||
call = webRtcCall
|
call = webRtcCall
|
||||||
callManager.addCurrentCallListener(currentCallListener)
|
callManager.addCurrentCallListener(currentCallListener)
|
||||||
val item: MatrixItem? = session.getUser(webRtcCall.mxCall.opponentUserId)?.toMatrixItem()
|
val item = webRtcCall.getOpponentAsMatrixItem(session)
|
||||||
webRtcCall.addListener(callListener)
|
webRtcCall.addListener(callListener)
|
||||||
val currentSoundDevice = callManager.audioManager.selectedDevice
|
val currentSoundDevice = callManager.audioManager.selectedDevice
|
||||||
if (currentSoundDevice == CallAudioManager.Device.PHONE) {
|
if (currentSoundDevice == CallAudioManager.Device.PHONE) {
|
||||||
|
@ -51,7 +51,7 @@ data class VectorCallViewState(
|
|||||||
|
|
||||||
constructor(callArgs: CallArgs): this(
|
constructor(callArgs: CallArgs): this(
|
||||||
callId = callArgs.callId,
|
callId = callArgs.callId,
|
||||||
roomId = callArgs.roomId,
|
roomId = callArgs.signalingRoomId,
|
||||||
isVideoCall = callArgs.isVideoCall
|
isVideoCall = callArgs.isVideoCall
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -16,30 +16,24 @@
|
|||||||
|
|
||||||
package im.vector.app.features.call.dialpad
|
package im.vector.app.features.call.dialpad
|
||||||
|
|
||||||
|
import im.vector.app.features.call.lookup.pstnLookup
|
||||||
import im.vector.app.features.call.webrtc.WebRtcCallManager
|
import im.vector.app.features.call.webrtc.WebRtcCallManager
|
||||||
import im.vector.app.features.createdirect.DirectRoomHelper
|
import im.vector.app.features.createdirect.DirectRoomHelper
|
||||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
|
import java.lang.IllegalStateException
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class DialPadLookup @Inject constructor(
|
class DialPadLookup @Inject constructor(
|
||||||
private val session: Session,
|
private val session: Session,
|
||||||
private val directRoomHelper: DirectRoomHelper,
|
private val webRtcCallManager: WebRtcCallManager,
|
||||||
private val callManager: WebRtcCallManager
|
private val directRoomHelper: DirectRoomHelper
|
||||||
) {
|
) {
|
||||||
class Failure : Throwable()
|
class Failure : Throwable()
|
||||||
|
|
||||||
data class Result(val userId: String, val roomId: String)
|
data class Result(val userId: String, val roomId: String)
|
||||||
|
|
||||||
suspend fun lookupPhoneNumber(phoneNumber: String): Result {
|
suspend fun lookupPhoneNumber(phoneNumber: String): Result {
|
||||||
val supportedProtocolKey = callManager.supportedPSTNProtocol ?: throw Failure()
|
val thirdPartyUser = session.pstnLookup(phoneNumber, webRtcCallManager.supportedPSTNProtocol).firstOrNull() ?: throw IllegalStateException()
|
||||||
val thirdPartyUser = tryOrNull {
|
|
||||||
session.thirdPartyService().getThirdPartyUser(
|
|
||||||
protocol = supportedProtocolKey,
|
|
||||||
fields = mapOf("m.id.phone" to phoneNumber)
|
|
||||||
).firstOrNull()
|
|
||||||
} ?: throw Failure()
|
|
||||||
|
|
||||||
val roomId = directRoomHelper.ensureDMExists(thirdPartyUser.userId)
|
val roomId = directRoomHelper.ensureDMExists(thirdPartyUser.userId)
|
||||||
return Result(userId = thirdPartyUser.userId, roomId = roomId)
|
return Result(userId = thirdPartyUser.userId, roomId = roomId)
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,118 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 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.app.features.call.lookup
|
||||||
|
|
||||||
|
import im.vector.app.features.session.coroutineScope
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||||
|
import org.matrix.android.sdk.api.session.Session
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.thirdparty.ThirdPartyProtocol
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
|
||||||
|
const val PROTOCOL_PSTN_PREFIXED = "im.vector.protocol.pstn"
|
||||||
|
const val PROTOCOL_PSTN = "m.protocol.pstn"
|
||||||
|
const val PROTOCOL_SIP_NATIVE = "im.vector.protocol.sip_native"
|
||||||
|
const val PROTOCOL_SIP_VIRTUAL = "im.vector.protocol.sip_virtual"
|
||||||
|
|
||||||
|
class CallProtocolsChecker(private val session: Session) {
|
||||||
|
|
||||||
|
interface Listener {
|
||||||
|
fun onPSTNSupportUpdated() = Unit
|
||||||
|
fun onVirtualRoomSupportUpdated() = Unit
|
||||||
|
}
|
||||||
|
|
||||||
|
private val alreadyChecked = AtomicBoolean(false)
|
||||||
|
private val checking = AtomicBoolean(false)
|
||||||
|
|
||||||
|
private val listeners = mutableListOf<Listener>()
|
||||||
|
|
||||||
|
fun addListener(listener: Listener) {
|
||||||
|
listeners.add(listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeListener(listener: Listener) {
|
||||||
|
listeners.remove(listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
var supportedPSTNProtocol: String? = null
|
||||||
|
private set
|
||||||
|
|
||||||
|
var supportVirtualRooms: Boolean = false
|
||||||
|
private set
|
||||||
|
|
||||||
|
fun checkProtocols() {
|
||||||
|
session.coroutineScope.launch {
|
||||||
|
checkThirdPartyProtocols()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun awaitCheckProtocols() {
|
||||||
|
checkThirdPartyProtocols()
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun checkThirdPartyProtocols() {
|
||||||
|
if (alreadyChecked.get()) return
|
||||||
|
if (!checking.compareAndSet(false, true)) return
|
||||||
|
try {
|
||||||
|
val protocols = getThirdPartyProtocols(3)
|
||||||
|
alreadyChecked.set(true)
|
||||||
|
checking.set(false)
|
||||||
|
supportedPSTNProtocol = protocols.extractPSTN()
|
||||||
|
if (supportedPSTNProtocol != null) {
|
||||||
|
listeners.forEach {
|
||||||
|
tryOrNull { it.onPSTNSupportUpdated() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
supportVirtualRooms = protocols.supportsVirtualRooms()
|
||||||
|
if (supportVirtualRooms) {
|
||||||
|
listeners.forEach {
|
||||||
|
tryOrNull { it.onVirtualRoomSupportUpdated() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (failure: Throwable) {
|
||||||
|
Timber.v("Fail to get third party protocols, will check again next time.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Map<String, ThirdPartyProtocol>.extractPSTN(): String? {
|
||||||
|
return when {
|
||||||
|
containsKey(PROTOCOL_PSTN_PREFIXED) -> PROTOCOL_PSTN_PREFIXED
|
||||||
|
containsKey(PROTOCOL_PSTN) -> PROTOCOL_PSTN
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Map<String, ThirdPartyProtocol>.supportsVirtualRooms(): Boolean {
|
||||||
|
return containsKey(PROTOCOL_SIP_VIRTUAL) && containsKey(PROTOCOL_SIP_NATIVE)
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun getThirdPartyProtocols(maxTries: Int): Map<String, ThirdPartyProtocol> {
|
||||||
|
return try {
|
||||||
|
session.thirdPartyService().getThirdPartyProtocols()
|
||||||
|
} catch (failure: Throwable) {
|
||||||
|
if (maxTries == 1) {
|
||||||
|
throw failure
|
||||||
|
} else {
|
||||||
|
// Wait for 10s before trying again
|
||||||
|
delay(10_000L)
|
||||||
|
return getThirdPartyProtocols(maxTries - 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 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.app.features.call.lookup
|
||||||
|
|
||||||
|
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||||
|
import org.matrix.android.sdk.api.session.Session
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||||
|
import org.matrix.android.sdk.api.session.room.Room
|
||||||
|
import org.matrix.android.sdk.api.session.room.accountdata.RoomAccountDataTypes
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
||||||
|
|
||||||
|
class CallUserMapper(private val session: Session, private val protocolsChecker: CallProtocolsChecker) {
|
||||||
|
|
||||||
|
fun nativeRoomForVirtualRoom(roomId: String): String? {
|
||||||
|
val virtualRoom = session.getRoom(roomId) ?: return null
|
||||||
|
val virtualRoomEvent = virtualRoom.getAccountDataEvent(RoomAccountDataTypes.EVENT_TYPE_VIRTUAL_ROOM)
|
||||||
|
return virtualRoomEvent?.content?.toModel<RoomVirtualContent>()?.nativeRoomId
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun getOrCreateVirtualRoomForRoom(roomId: String, opponentUserId: String): String? {
|
||||||
|
protocolsChecker.awaitCheckProtocols()
|
||||||
|
if (!protocolsChecker.supportVirtualRooms) return null
|
||||||
|
val virtualUser = userToVirtualUser(opponentUserId) ?: return null
|
||||||
|
val virtualRoomId = tryOrNull {
|
||||||
|
ensureVirtualRoomExists(virtualUser, roomId)
|
||||||
|
} ?: return null
|
||||||
|
session.getRoom(virtualRoomId)?.markVirtual(roomId)
|
||||||
|
return virtualRoomId
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun onNewInvitedRoom(invitedRoomId: String) {
|
||||||
|
protocolsChecker.awaitCheckProtocols()
|
||||||
|
if (!protocolsChecker.supportVirtualRooms) return
|
||||||
|
val invitedRoom = session.getRoom(invitedRoomId) ?: return
|
||||||
|
val inviterId = invitedRoom.roomSummary()?.inviterId ?: return
|
||||||
|
val nativeLookup = session.sipNativeLookup(inviterId).firstOrNull() ?: return
|
||||||
|
if (nativeLookup.fields.containsKey("is_virtual")) {
|
||||||
|
val nativeUser = nativeLookup.userId
|
||||||
|
val nativeRoomId = session.getExistingDirectRoomWithUser(nativeUser)
|
||||||
|
if (nativeRoomId != null) {
|
||||||
|
// It's a virtual room with a matching native room, so set the room account data. This
|
||||||
|
// will make sure we know where how to map calls and also allow us know not to display
|
||||||
|
// it in the future.
|
||||||
|
invitedRoom.markVirtual(nativeRoomId)
|
||||||
|
// also auto-join the virtual room if we have a matching native room
|
||||||
|
// (possibly we should only join if we've also joined the native room, then we'd also have
|
||||||
|
// to make sure we joined virtual rooms on joining a native one)
|
||||||
|
session.joinRoom(invitedRoomId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun userToVirtualUser(userId: String): String? {
|
||||||
|
val results = session.sipVirtualLookup(userId)
|
||||||
|
return results.firstOrNull()?.userId
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun Room.markVirtual(nativeRoomId: String) {
|
||||||
|
val virtualRoomContent = RoomVirtualContent(nativeRoomId = nativeRoomId)
|
||||||
|
updateAccountData(RoomAccountDataTypes.EVENT_TYPE_VIRTUAL_ROOM, virtualRoomContent.toContent())
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun ensureVirtualRoomExists(userId: String, nativeRoomId: String): String {
|
||||||
|
val existingDMRoom = tryOrNull { session.getExistingDirectRoomWithUser(userId) }
|
||||||
|
val roomId: String
|
||||||
|
if (existingDMRoom != null) {
|
||||||
|
roomId = existingDMRoom
|
||||||
|
} else {
|
||||||
|
val roomParams = CreateRoomParams().apply {
|
||||||
|
invitedUserIds.add(userId)
|
||||||
|
setDirectMessage()
|
||||||
|
creationContent[RoomAccountDataTypes.EVENT_TYPE_VIRTUAL_ROOM] = nativeRoomId
|
||||||
|
}
|
||||||
|
roomId = session.createRoom(roomParams)
|
||||||
|
}
|
||||||
|
return roomId
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* 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.app.features.call.lookup
|
||||||
|
|
||||||
|
import com.squareup.moshi.Json
|
||||||
|
import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
data class RoomVirtualContent(
|
||||||
|
@Json(name = "native_room") val nativeRoomId: String
|
||||||
|
)
|
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 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.app.features.call.lookup
|
||||||
|
|
||||||
|
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||||
|
import org.matrix.android.sdk.api.session.Session
|
||||||
|
import org.matrix.android.sdk.api.session.thirdparty.model.ThirdPartyUser
|
||||||
|
|
||||||
|
suspend fun Session.pstnLookup(phoneNumber: String, protocol: String?): List<ThirdPartyUser> {
|
||||||
|
if (protocol == null) return emptyList()
|
||||||
|
return tryOrNull {
|
||||||
|
thirdPartyService().getThirdPartyUser(
|
||||||
|
protocol = protocol,
|
||||||
|
fields = mapOf("m.id.phone" to phoneNumber)
|
||||||
|
)
|
||||||
|
}.orEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun Session.sipVirtualLookup(nativeMxid: String): List<ThirdPartyUser> {
|
||||||
|
return tryOrNull {
|
||||||
|
thirdPartyService().getThirdPartyUser(
|
||||||
|
protocol = PROTOCOL_SIP_VIRTUAL,
|
||||||
|
fields = mapOf("native_mxid" to nativeMxid)
|
||||||
|
)
|
||||||
|
}.orEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun Session.sipNativeLookup(virtualMxid: String): List<ThirdPartyUser> {
|
||||||
|
return tryOrNull {
|
||||||
|
thirdPartyService().getThirdPartyUser(
|
||||||
|
protocol = PROTOCOL_SIP_NATIVE,
|
||||||
|
fields = mapOf("virtual_mxid" to virtualMxid)
|
||||||
|
)
|
||||||
|
}.orEmpty()
|
||||||
|
}
|
@ -86,6 +86,8 @@ private const val VIDEO_TRACK_ID = "ARDAMSv0"
|
|||||||
private val DEFAULT_AUDIO_CONSTRAINTS = MediaConstraints()
|
private val DEFAULT_AUDIO_CONSTRAINTS = MediaConstraints()
|
||||||
|
|
||||||
class WebRtcCall(val mxCall: MxCall,
|
class WebRtcCall(val mxCall: MxCall,
|
||||||
|
// This is where the call is placed from an ui perspective. In case of virtual room, it can differs from the signalingRoomId.
|
||||||
|
val nativeRoomId: String,
|
||||||
private val rootEglBase: EglBase?,
|
private val rootEglBase: EglBase?,
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val dispatcher: CoroutineContext,
|
private val dispatcher: CoroutineContext,
|
||||||
@ -116,7 +118,8 @@ class WebRtcCall(val mxCall: MxCall,
|
|||||||
}
|
}
|
||||||
|
|
||||||
val callId = mxCall.callId
|
val callId = mxCall.callId
|
||||||
val roomId = mxCall.roomId
|
// room where call signaling is placed. In case of virtual room it can differs from the nativeRoomId.
|
||||||
|
val signalingRoomId = mxCall.roomId
|
||||||
|
|
||||||
private var peerConnection: PeerConnection? = null
|
private var peerConnection: PeerConnection? = null
|
||||||
private var localAudioSource: AudioSource? = null
|
private var localAudioSource: AudioSource? = null
|
||||||
@ -385,6 +388,7 @@ class WebRtcCall(val mxCall: MxCall,
|
|||||||
peerConnection?.awaitSetRemoteDescription(offerSdp)
|
peerConnection?.awaitSetRemoteDescription(offerSdp)
|
||||||
} catch (failure: Throwable) {
|
} catch (failure: Throwable) {
|
||||||
Timber.v("Failure putting remote description")
|
Timber.v("Failure putting remote description")
|
||||||
|
endCall(true, CallHangupContent.Reason.UNKWOWN_ERROR)
|
||||||
return@withContext
|
return@withContext
|
||||||
}
|
}
|
||||||
// 2) Access camera + microphone, create local stream
|
// 2) Access camera + microphone, create local stream
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 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.app.features.call.webrtc
|
||||||
|
|
||||||
|
import org.matrix.android.sdk.api.session.Session
|
||||||
|
import org.matrix.android.sdk.api.util.MatrixItem
|
||||||
|
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||||
|
|
||||||
|
fun WebRtcCall.getOpponentAsMatrixItem(session: Session): MatrixItem? {
|
||||||
|
return session.getRoomSummary(nativeRoomId)?.otherMemberIds?.firstOrNull()?.let {
|
||||||
|
session.getUser(it)?.toMatrixItem()
|
||||||
|
}
|
||||||
|
}
|
@ -24,15 +24,18 @@ import im.vector.app.ActiveSessionDataSource
|
|||||||
import im.vector.app.core.services.CallService
|
import im.vector.app.core.services.CallService
|
||||||
import im.vector.app.features.call.VectorCallActivity
|
import im.vector.app.features.call.VectorCallActivity
|
||||||
import im.vector.app.features.call.audio.CallAudioManager
|
import im.vector.app.features.call.audio.CallAudioManager
|
||||||
|
import im.vector.app.features.call.lookup.CallProtocolsChecker
|
||||||
|
import im.vector.app.features.call.lookup.CallUserMapper
|
||||||
import im.vector.app.features.call.utils.EglUtils
|
import im.vector.app.features.call.utils.EglUtils
|
||||||
|
import im.vector.app.features.call.vectorCallService
|
||||||
import im.vector.app.push.fcm.FcmHelper
|
import im.vector.app.push.fcm.FcmHelper
|
||||||
import kotlinx.coroutines.asCoroutineDispatcher
|
import kotlinx.coroutines.asCoroutineDispatcher
|
||||||
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import org.matrix.android.sdk.api.session.call.CallListener
|
import org.matrix.android.sdk.api.session.call.CallListener
|
||||||
import org.matrix.android.sdk.api.session.call.CallState
|
import org.matrix.android.sdk.api.session.call.CallState
|
||||||
import org.matrix.android.sdk.api.session.call.MxCall
|
import org.matrix.android.sdk.api.session.call.MxCall
|
||||||
import org.matrix.android.sdk.api.session.call.PSTNProtocolChecker
|
|
||||||
import org.matrix.android.sdk.api.session.room.model.call.CallAnswerContent
|
import org.matrix.android.sdk.api.session.room.model.call.CallAnswerContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.call.CallCandidatesContent
|
import org.matrix.android.sdk.api.session.room.model.call.CallCandidatesContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.call.CallHangupContent
|
import org.matrix.android.sdk.api.session.room.model.call.CallHangupContent
|
||||||
@ -64,8 +67,11 @@ class WebRtcCallManager @Inject constructor(
|
|||||||
private val currentSession: Session?
|
private val currentSession: Session?
|
||||||
get() = activeSessionDataSource.currentValue?.orNull()
|
get() = activeSessionDataSource.currentValue?.orNull()
|
||||||
|
|
||||||
private val pstnProtocolChecker: PSTNProtocolChecker?
|
private val protocolsChecker: CallProtocolsChecker?
|
||||||
get() = currentSession?.callSignalingService()?.getPSTNProtocolChecker()
|
get() = currentSession?.vectorCallService?.protocolChecker
|
||||||
|
|
||||||
|
private val callUserMapper: CallUserMapper?
|
||||||
|
get() = currentSession?.vectorCallService?.userMapper
|
||||||
|
|
||||||
interface CurrentCallListener {
|
interface CurrentCallListener {
|
||||||
fun onCurrentCallChange(call: WebRtcCall?) {}
|
fun onCurrentCallChange(call: WebRtcCall?) {}
|
||||||
@ -73,17 +79,20 @@ class WebRtcCallManager @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val supportedPSTNProtocol: String?
|
val supportedPSTNProtocol: String?
|
||||||
get() = pstnProtocolChecker?.supportedPSTNProtocol
|
get() = protocolsChecker?.supportedPSTNProtocol
|
||||||
|
|
||||||
val supportsPSTNProtocol: Boolean
|
val supportsPSTNProtocol: Boolean
|
||||||
get() = supportedPSTNProtocol != null
|
get() = supportedPSTNProtocol != null
|
||||||
|
|
||||||
fun addPstnSupportListener(listener: PSTNProtocolChecker.Listener) {
|
val supportsVirtualRooms: Boolean
|
||||||
pstnProtocolChecker?.addListener(listener)
|
get() = protocolsChecker?.supportVirtualRooms.orFalse()
|
||||||
|
|
||||||
|
fun addProtocolsCheckerListener(listener: CallProtocolsChecker.Listener) {
|
||||||
|
protocolsChecker?.addListener(listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removePstnSupportListener(listener: PSTNProtocolChecker.Listener) {
|
fun removeProtocolsCheckerListener(listener: CallProtocolsChecker.Listener) {
|
||||||
pstnProtocolChecker?.removeListener(listener)
|
protocolsChecker?.removeListener(listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val currentCallsListeners = CopyOnWriteArrayList<CurrentCallListener>()
|
private val currentCallsListeners = CopyOnWriteArrayList<CurrentCallListener>()
|
||||||
@ -154,8 +163,8 @@ class WebRtcCallManager @Inject constructor(
|
|||||||
return callsByCallId.values.toList()
|
return callsByCallId.values.toList()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun checkForPSTNSupportIfNeeded() {
|
fun checkForProtocolsSupportIfNeeded() {
|
||||||
pstnProtocolChecker?.checkForPSTNSupportIfNeeded()
|
protocolsChecker?.checkProtocols()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -218,7 +227,8 @@ class WebRtcCallManager @Inject constructor(
|
|||||||
Timber.v("On call ended for unknown call $callId")
|
Timber.v("On call ended for unknown call $callId")
|
||||||
}
|
}
|
||||||
CallService.onCallTerminated(context, callId)
|
CallService.onCallTerminated(context, callId)
|
||||||
callsByRoomId[webRtcCall.roomId]?.remove(webRtcCall)
|
callsByRoomId[webRtcCall.signalingRoomId]?.remove(webRtcCall)
|
||||||
|
callsByRoomId[webRtcCall.nativeRoomId]?.remove(webRtcCall)
|
||||||
if (getCurrentCall()?.callId == callId) {
|
if (getCurrentCall()?.callId == callId) {
|
||||||
val otherCall = getCalls().lastOrNull()
|
val otherCall = getCalls().lastOrNull()
|
||||||
currentCall.setAndNotify(otherCall)
|
currentCall.setAndNotify(otherCall)
|
||||||
@ -245,9 +255,10 @@ class WebRtcCallManager @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun startOutgoingCall(signalingRoomId: String, otherUserId: String, isVideoCall: Boolean) {
|
suspend fun startOutgoingCall(nativeRoomId: String, otherUserId: String, isVideoCall: Boolean) {
|
||||||
|
val signalingRoomId = callUserMapper?.getOrCreateVirtualRoomForRoom(nativeRoomId, otherUserId) ?: nativeRoomId
|
||||||
Timber.v("## VOIP startOutgoingCall in room $signalingRoomId to $otherUserId isVideo $isVideoCall")
|
Timber.v("## VOIP startOutgoingCall in room $signalingRoomId to $otherUserId isVideo $isVideoCall")
|
||||||
if (getCallsByRoomId(signalingRoomId).isNotEmpty()) {
|
if (getCallsByRoomId(nativeRoomId).isNotEmpty()) {
|
||||||
Timber.w("## VOIP you already have a call in this room")
|
Timber.w("## VOIP you already have a call in this room")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -261,7 +272,7 @@ class WebRtcCallManager @Inject constructor(
|
|||||||
}
|
}
|
||||||
getCurrentCall()?.updateRemoteOnHold(onHold = true)
|
getCurrentCall()?.updateRemoteOnHold(onHold = true)
|
||||||
val mxCall = currentSession?.callSignalingService()?.createOutgoingCall(signalingRoomId, otherUserId, isVideoCall) ?: return
|
val mxCall = currentSession?.callSignalingService()?.createOutgoingCall(signalingRoomId, otherUserId, isVideoCall) ?: return
|
||||||
val webRtcCall = createWebRtcCall(mxCall)
|
val webRtcCall = createWebRtcCall(mxCall, nativeRoomId)
|
||||||
currentCall.setAndNotify(webRtcCall)
|
currentCall.setAndNotify(webRtcCall)
|
||||||
|
|
||||||
CallService.onOutgoingCallRinging(
|
CallService.onOutgoingCallRinging(
|
||||||
@ -269,7 +280,7 @@ class WebRtcCallManager @Inject constructor(
|
|||||||
callId = mxCall.callId)
|
callId = mxCall.callId)
|
||||||
|
|
||||||
// start the activity now
|
// start the activity now
|
||||||
context.startActivity(VectorCallActivity.newIntent(context, mxCall, VectorCallActivity.OUTGOING_CREATED))
|
context.startActivity(VectorCallActivity.newIntent(context, webRtcCall, VectorCallActivity.OUTGOING_CREATED))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCallIceCandidateReceived(mxCall: MxCall, iceCandidatesContent: CallCandidatesContent) {
|
override fun onCallIceCandidateReceived(mxCall: MxCall, iceCandidatesContent: CallCandidatesContent) {
|
||||||
@ -281,9 +292,10 @@ class WebRtcCallManager @Inject constructor(
|
|||||||
call.onCallIceCandidateReceived(iceCandidatesContent)
|
call.onCallIceCandidateReceived(iceCandidatesContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createWebRtcCall(mxCall: MxCall): WebRtcCall {
|
private fun createWebRtcCall(mxCall: MxCall, nativeRoomId: String): WebRtcCall {
|
||||||
val webRtcCall = WebRtcCall(
|
val webRtcCall = WebRtcCall(
|
||||||
mxCall = mxCall,
|
mxCall = mxCall,
|
||||||
|
nativeRoomId = nativeRoomId,
|
||||||
rootEglBase = rootEglBase,
|
rootEglBase = rootEglBase,
|
||||||
context = context,
|
context = context,
|
||||||
dispatcher = dispatcher,
|
dispatcher = dispatcher,
|
||||||
@ -297,6 +309,8 @@ class WebRtcCallManager @Inject constructor(
|
|||||||
)
|
)
|
||||||
advertisedCalls.add(mxCall.callId)
|
advertisedCalls.add(mxCall.callId)
|
||||||
callsByCallId[mxCall.callId] = webRtcCall
|
callsByCallId[mxCall.callId] = webRtcCall
|
||||||
|
callsByRoomId.getOrPut(nativeRoomId) { ArrayList(1) }
|
||||||
|
.add(webRtcCall)
|
||||||
callsByRoomId.getOrPut(mxCall.roomId) { ArrayList(1) }
|
callsByRoomId.getOrPut(mxCall.roomId) { ArrayList(1) }
|
||||||
.add(webRtcCall)
|
.add(webRtcCall)
|
||||||
if (getCurrentCall() == null) {
|
if (getCurrentCall() == null) {
|
||||||
@ -306,12 +320,13 @@ class WebRtcCallManager @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun endCallForRoom(roomId: String, originatedByMe: Boolean = true) {
|
fun endCallForRoom(roomId: String, originatedByMe: Boolean = true) {
|
||||||
callsByRoomId[roomId]?.forEach { it.endCall(originatedByMe) }
|
callsByRoomId[roomId]?.firstOrNull()?.endCall(originatedByMe)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCallInviteReceived(mxCall: MxCall, callInviteContent: CallInviteContent) {
|
override fun onCallInviteReceived(mxCall: MxCall, callInviteContent: CallInviteContent) {
|
||||||
Timber.v("## VOIP onCallInviteReceived callId ${mxCall.callId}")
|
Timber.v("## VOIP onCallInviteReceived callId ${mxCall.callId}")
|
||||||
if (getCallsByRoomId(mxCall.roomId).isNotEmpty()) {
|
val nativeRoomId = callUserMapper?.nativeRoomForVirtualRoom(mxCall.roomId) ?: mxCall.roomId
|
||||||
|
if (getCallsByRoomId(nativeRoomId).isNotEmpty()) {
|
||||||
Timber.w("## VOIP you already have a call in this room")
|
Timber.w("## VOIP you already have a call in this room")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -320,7 +335,7 @@ class WebRtcCallManager @Inject constructor(
|
|||||||
// Just ignore, maybe we could answer from other session?
|
// Just ignore, maybe we could answer from other session?
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
createWebRtcCall(mxCall).apply {
|
createWebRtcCall(mxCall, nativeRoomId).apply {
|
||||||
offerSdp = callInviteContent.offer
|
offerSdp = callInviteContent.offer
|
||||||
}
|
}
|
||||||
// Start background service with notification
|
// Start background service with notification
|
||||||
|
@ -252,7 +252,7 @@ class KeysBackupRestoreSharedViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun isBackupKeyInQuadS(): Boolean {
|
private fun isBackupKeyInQuadS(): Boolean {
|
||||||
val sssBackupSecret = session.getAccountDataEvent(KEYBACKUP_SECRET_SSSS_NAME)
|
val sssBackupSecret = session.userAccountDataService().getAccountDataEvent(KEYBACKUP_SECRET_SSSS_NAME)
|
||||||
?: return false
|
?: return false
|
||||||
|
|
||||||
// Some sanity ?
|
// Some sanity ?
|
||||||
|
@ -218,7 +218,7 @@ class SharedSecureStorageViewModel @AssistedInject constructor(
|
|||||||
|
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
args.requestedSecrets.forEach {
|
args.requestedSecrets.forEach {
|
||||||
if (session.getAccountDataEvent(it) != null) {
|
if (session.userAccountDataService().getAccountDataEvent(it) != null) {
|
||||||
val res = session.sharedSecretStorageService.getSecret(
|
val res = session.sharedSecretStorageService.getSecret(
|
||||||
name = it,
|
name = it,
|
||||||
keyId = keyInfo.id,
|
keyId = keyInfo.id,
|
||||||
@ -287,7 +287,7 @@ class SharedSecureStorageViewModel @AssistedInject constructor(
|
|||||||
|
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
args.requestedSecrets.forEach {
|
args.requestedSecrets.forEach {
|
||||||
if (session.getAccountDataEvent(it) != null) {
|
if (session.userAccountDataService().getAccountDataEvent(it) != null) {
|
||||||
val res = session.sharedSecretStorageService.getSecret(
|
val res = session.sharedSecretStorageService.getSecret(
|
||||||
name = it,
|
name = it,
|
||||||
keyId = keyInfo.id,
|
keyId = keyInfo.id,
|
||||||
|
@ -409,7 +409,7 @@ class HomeDetailFragment @Inject constructor(
|
|||||||
VectorCallActivity.newIntent(
|
VectorCallActivity.newIntent(
|
||||||
context = requireContext(),
|
context = requireContext(),
|
||||||
callId = call.callId,
|
callId = call.callId,
|
||||||
roomId = call.mxCall.roomId,
|
signalingRoomId = call.signalingRoomId,
|
||||||
otherUserId = call.mxCall.opponentUserId,
|
otherUserId = call.mxCall.opponentUserId,
|
||||||
isIncomingCall = !call.mxCall.isOutgoing,
|
isIncomingCall = !call.mxCall.isOutgoing,
|
||||||
isVideoCall = call.mxCall.isVideoCall,
|
isVideoCall = call.mxCall.isVideoCall,
|
||||||
|
@ -417,7 +417,7 @@ class RoomDetailFragment @Inject constructor(
|
|||||||
private fun acceptIncomingCall(event: RoomDetailViewEvents.DisplayAndAcceptCall) {
|
private fun acceptIncomingCall(event: RoomDetailViewEvents.DisplayAndAcceptCall) {
|
||||||
val intent = VectorCallActivity.newIntent(
|
val intent = VectorCallActivity.newIntent(
|
||||||
context = vectorBaseActivity,
|
context = vectorBaseActivity,
|
||||||
mxCall = event.call.mxCall,
|
call = event.call,
|
||||||
mode = VectorCallActivity.INCOMING_ACCEPT
|
mode = VectorCallActivity.INCOMING_ACCEPT
|
||||||
)
|
)
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
@ -2042,7 +2042,7 @@ class RoomDetailFragment @Inject constructor(
|
|||||||
VectorCallActivity.newIntent(
|
VectorCallActivity.newIntent(
|
||||||
context = requireContext(),
|
context = requireContext(),
|
||||||
callId = call.callId,
|
callId = call.callId,
|
||||||
roomId = call.roomId,
|
signalingRoomId = call.signalingRoomId,
|
||||||
otherUserId = call.mxCall.opponentUserId,
|
otherUserId = call.mxCall.opponentUserId,
|
||||||
isIncomingCall = !call.mxCall.isOutgoing,
|
isIncomingCall = !call.mxCall.isOutgoing,
|
||||||
isVideoCall = call.mxCall.isVideoCall,
|
isVideoCall = call.mxCall.isVideoCall,
|
||||||
|
@ -40,6 +40,7 @@ import im.vector.app.core.platform.VectorViewModel
|
|||||||
import im.vector.app.core.resources.StringProvider
|
import im.vector.app.core.resources.StringProvider
|
||||||
import im.vector.app.features.call.conference.JitsiService
|
import im.vector.app.features.call.conference.JitsiService
|
||||||
import im.vector.app.features.call.dialpad.DialPadLookup
|
import im.vector.app.features.call.dialpad.DialPadLookup
|
||||||
|
import im.vector.app.features.call.lookup.CallProtocolsChecker
|
||||||
import im.vector.app.features.call.webrtc.WebRtcCallManager
|
import im.vector.app.features.call.webrtc.WebRtcCallManager
|
||||||
import im.vector.app.features.command.CommandParser
|
import im.vector.app.features.command.CommandParser
|
||||||
import im.vector.app.features.command.ParsedCommand
|
import im.vector.app.features.command.ParsedCommand
|
||||||
@ -68,7 +69,6 @@ import org.matrix.android.sdk.api.MatrixPatterns
|
|||||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||||
import org.matrix.android.sdk.api.query.QueryStringValue
|
import org.matrix.android.sdk.api.query.QueryStringValue
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import org.matrix.android.sdk.api.session.call.PSTNProtocolChecker
|
|
||||||
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
|
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
|
||||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||||
import org.matrix.android.sdk.api.session.events.model.LocalEcho
|
import org.matrix.android.sdk.api.session.events.model.LocalEcho
|
||||||
@ -121,7 +121,7 @@ class RoomDetailViewModel @AssistedInject constructor(
|
|||||||
private val jitsiService: JitsiService,
|
private val jitsiService: JitsiService,
|
||||||
timelineSettingsFactory: TimelineSettingsFactory
|
timelineSettingsFactory: TimelineSettingsFactory
|
||||||
) : VectorViewModel<RoomDetailViewState, RoomDetailAction, RoomDetailViewEvents>(initialState),
|
) : VectorViewModel<RoomDetailViewState, RoomDetailAction, RoomDetailViewEvents>(initialState),
|
||||||
Timeline.Listener, ChatEffectManager.Delegate, PSTNProtocolChecker.Listener {
|
Timeline.Listener, ChatEffectManager.Delegate, CallProtocolsChecker.Listener {
|
||||||
|
|
||||||
private val room = session.getRoom(initialState.roomId)!!
|
private val room = session.getRoom(initialState.roomId)!!
|
||||||
private val eventId = initialState.eventId
|
private val eventId = initialState.eventId
|
||||||
@ -185,8 +185,8 @@ class RoomDetailViewModel @AssistedInject constructor(
|
|||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
tryOrNull { session.onRoomDisplayed(initialState.roomId) }
|
tryOrNull { session.onRoomDisplayed(initialState.roomId) }
|
||||||
}
|
}
|
||||||
callManager.addPstnSupportListener(this)
|
callManager.addProtocolsCheckerListener(this)
|
||||||
callManager.checkForPSTNSupportIfNeeded()
|
callManager.checkForProtocolsSupportIfNeeded()
|
||||||
chatEffectManager.delegate = this
|
chatEffectManager.delegate = this
|
||||||
|
|
||||||
// Ensure to share the outbound session keys with all members
|
// Ensure to share the outbound session keys with all members
|
||||||
@ -330,7 +330,7 @@ class RoomDetailViewModel @AssistedInject constructor(
|
|||||||
private fun handleStartCallWithPhoneNumber(action: RoomDetailAction.StartCallWithPhoneNumber) {
|
private fun handleStartCallWithPhoneNumber(action: RoomDetailAction.StartCallWithPhoneNumber) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
try {
|
try {
|
||||||
val result = DialPadLookup(session, directRoomHelper, callManager).lookupPhoneNumber(action.phoneNumber)
|
val result = DialPadLookup(session, callManager, directRoomHelper).lookupPhoneNumber(action.phoneNumber)
|
||||||
callManager.startOutgoingCall(result.roomId, result.userId, action.videoCall)
|
callManager.startOutgoingCall(result.roomId, result.userId, action.videoCall)
|
||||||
} catch (failure: Throwable) {
|
} catch (failure: Throwable) {
|
||||||
_viewEvents.post(RoomDetailViewEvents.ActionFailure(action, failure))
|
_viewEvents.post(RoomDetailViewEvents.ActionFailure(action, failure))
|
||||||
@ -391,8 +391,10 @@ class RoomDetailViewModel @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun handleStartCall(action: RoomDetailAction.StartCall) {
|
private fun handleStartCall(action: RoomDetailAction.StartCall) {
|
||||||
room.roomSummary()?.otherMemberIds?.firstOrNull()?.let {
|
viewModelScope.launch {
|
||||||
callManager.startOutgoingCall(room.roomId, it, action.isVideo)
|
room.roomSummary()?.otherMemberIds?.firstOrNull()?.let {
|
||||||
|
callManager.startOutgoingCall(room.roomId, it, action.isVideo)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1508,7 +1510,7 @@ class RoomDetailViewModel @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
chatEffectManager.delegate = null
|
chatEffectManager.delegate = null
|
||||||
chatEffectManager.dispose()
|
chatEffectManager.dispose()
|
||||||
callManager.removePstnSupportListener(this)
|
callManager.removeProtocolsCheckerListener(this)
|
||||||
super.onCleared()
|
super.onCleared()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,7 +103,7 @@ class StartCallActionsHandler(
|
|||||||
val currentCall = callManager.getCurrentCall()
|
val currentCall = callManager.getCurrentCall()
|
||||||
if (currentCall != null) {
|
if (currentCall != null) {
|
||||||
// resume existing if same room, if not prompt to kill and then restart new call?
|
// resume existing if same room, if not prompt to kill and then restart new call?
|
||||||
if (currentCall.roomId == roomId) {
|
if (currentCall.signalingRoomId == roomId) {
|
||||||
onTapToReturnToCall()
|
onTapToReturnToCall()
|
||||||
}
|
}
|
||||||
// else {
|
// else {
|
||||||
|
@ -32,7 +32,7 @@ import org.matrix.android.sdk.api.session.room.model.call.CallAnswerContent
|
|||||||
import org.matrix.android.sdk.api.session.room.model.call.CallHangupContent
|
import org.matrix.android.sdk.api.session.room.model.call.CallHangupContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.call.CallInviteContent
|
import org.matrix.android.sdk.api.session.room.model.call.CallInviteContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.call.CallRejectContent
|
import org.matrix.android.sdk.api.session.room.model.call.CallRejectContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.call.CallSignallingContent
|
import org.matrix.android.sdk.api.session.room.model.call.CallSignalingContent
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||||
import org.matrix.android.sdk.api.util.toMatrixItem
|
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -51,7 +51,7 @@ class CallItemFactory @Inject constructor(
|
|||||||
if (event.root.eventId == null) return null
|
if (event.root.eventId == null) return null
|
||||||
val roomId = event.roomId
|
val roomId = event.roomId
|
||||||
val informationData = messageInformationDataFactory.create(params)
|
val informationData = messageInformationDataFactory.create(params)
|
||||||
val callSignalingContent = event.getCallSignallingContent() ?: return null
|
val callSignalingContent = event.getCallSignalingContent() ?: return null
|
||||||
val callId = callSignalingContent.callId ?: return null
|
val callId = callSignalingContent.callId ?: return null
|
||||||
val call = callManager.getCallById(callId)
|
val call = callManager.getCallById(callId)
|
||||||
val callKind = when {
|
val callKind = when {
|
||||||
@ -112,7 +112,7 @@ class CallItemFactory @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun TimelineEvent.getCallSignallingContent(): CallSignallingContent? {
|
private fun TimelineEvent.getCallSignalingContent(): CallSignalingContent? {
|
||||||
return when (root.getClearType()) {
|
return when (root.getClearType()) {
|
||||||
EventType.CALL_INVITE -> root.getClearContent().toModel<CallInviteContent>()
|
EventType.CALL_INVITE -> root.getClearContent().toModel<CallInviteContent>()
|
||||||
EventType.CALL_HANGUP -> root.getClearContent().toModel<CallHangupContent>()
|
EventType.CALL_HANGUP -> root.getClearContent().toModel<CallHangupContent>()
|
||||||
|
@ -51,12 +51,12 @@ import im.vector.app.core.resources.StringProvider
|
|||||||
import im.vector.app.core.utils.startNotificationChannelSettingsIntent
|
import im.vector.app.core.utils.startNotificationChannelSettingsIntent
|
||||||
import im.vector.app.features.call.VectorCallActivity
|
import im.vector.app.features.call.VectorCallActivity
|
||||||
import im.vector.app.features.call.service.CallHeadsUpActionReceiver
|
import im.vector.app.features.call.service.CallHeadsUpActionReceiver
|
||||||
|
import im.vector.app.features.call.webrtc.WebRtcCall
|
||||||
import im.vector.app.features.home.HomeActivity
|
import im.vector.app.features.home.HomeActivity
|
||||||
import im.vector.app.features.home.room.detail.RoomDetailActivity
|
import im.vector.app.features.home.room.detail.RoomDetailActivity
|
||||||
import im.vector.app.features.home.room.detail.RoomDetailArgs
|
import im.vector.app.features.home.room.detail.RoomDetailArgs
|
||||||
import im.vector.app.features.settings.VectorPreferences
|
import im.vector.app.features.settings.VectorPreferences
|
||||||
import im.vector.app.features.settings.troubleshoot.TestNotificationReceiver
|
import im.vector.app.features.settings.troubleshoot.TestNotificationReceiver
|
||||||
import org.matrix.android.sdk.api.session.call.MxCall
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
@ -287,7 +287,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
|
|||||||
* @return the call notification.
|
* @return the call notification.
|
||||||
*/
|
*/
|
||||||
@SuppressLint("NewApi")
|
@SuppressLint("NewApi")
|
||||||
fun buildIncomingCallNotification(mxCall: MxCall,
|
fun buildIncomingCallNotification(call: WebRtcCall,
|
||||||
title: String,
|
title: String,
|
||||||
fromBg: Boolean): Notification {
|
fromBg: Boolean): Notification {
|
||||||
val accentColor = ContextCompat.getColor(context, R.color.notification_accent_color)
|
val accentColor = ContextCompat.getColor(context, R.color.notification_accent_color)
|
||||||
@ -295,7 +295,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
|
|||||||
val builder = NotificationCompat.Builder(context, notificationChannel)
|
val builder = NotificationCompat.Builder(context, notificationChannel)
|
||||||
.setContentTitle(ensureTitleNotEmpty(title))
|
.setContentTitle(ensureTitleNotEmpty(title))
|
||||||
.apply {
|
.apply {
|
||||||
if (mxCall.isVideoCall) {
|
if (call.mxCall.isVideoCall) {
|
||||||
setContentText(stringProvider.getString(R.string.incoming_video_call))
|
setContentText(stringProvider.getString(R.string.incoming_video_call))
|
||||||
} else {
|
} else {
|
||||||
setContentText(stringProvider.getString(R.string.incoming_voice_call))
|
setContentText(stringProvider.getString(R.string.incoming_voice_call))
|
||||||
@ -308,11 +308,11 @@ class NotificationUtils @Inject constructor(private val context: Context,
|
|||||||
|
|
||||||
val contentIntent = VectorCallActivity.newIntent(
|
val contentIntent = VectorCallActivity.newIntent(
|
||||||
context = context,
|
context = context,
|
||||||
mxCall = mxCall,
|
call = call,
|
||||||
mode = VectorCallActivity.INCOMING_RINGING
|
mode = VectorCallActivity.INCOMING_RINGING
|
||||||
).apply {
|
).apply {
|
||||||
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
|
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
|
||||||
data = Uri.parse("foobar://${mxCall.callId}")
|
data = Uri.parse("foobar://${call.callId}")
|
||||||
}
|
}
|
||||||
val contentPendingIntent = PendingIntent.getActivity(context, System.currentTimeMillis().toInt(), contentIntent, 0)
|
val contentPendingIntent = PendingIntent.getActivity(context, System.currentTimeMillis().toInt(), contentIntent, 0)
|
||||||
|
|
||||||
@ -320,12 +320,12 @@ class NotificationUtils @Inject constructor(private val context: Context,
|
|||||||
.addNextIntentWithParentStack(HomeActivity.newIntent(context))
|
.addNextIntentWithParentStack(HomeActivity.newIntent(context))
|
||||||
.addNextIntent(VectorCallActivity.newIntent(
|
.addNextIntent(VectorCallActivity.newIntent(
|
||||||
context = context,
|
context = context,
|
||||||
mxCall = mxCall,
|
call = call,
|
||||||
mode = VectorCallActivity.INCOMING_ACCEPT)
|
mode = VectorCallActivity.INCOMING_ACCEPT)
|
||||||
)
|
)
|
||||||
.getPendingIntent(System.currentTimeMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT)
|
.getPendingIntent(System.currentTimeMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT)
|
||||||
|
|
||||||
val rejectCallPendingIntent = buildRejectCallPendingIntent(mxCall.callId)
|
val rejectCallPendingIntent = buildRejectCallPendingIntent(call.callId)
|
||||||
|
|
||||||
builder.addAction(
|
builder.addAction(
|
||||||
NotificationCompat.Action(
|
NotificationCompat.Action(
|
||||||
@ -351,7 +351,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
|
|||||||
return builder.build()
|
return builder.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun buildOutgoingRingingCallNotification(mxCall: MxCall,
|
fun buildOutgoingRingingCallNotification(call: WebRtcCall,
|
||||||
title: String): Notification {
|
title: String): Notification {
|
||||||
val accentColor = ContextCompat.getColor(context, R.color.notification_accent_color)
|
val accentColor = ContextCompat.getColor(context, R.color.notification_accent_color)
|
||||||
val builder = NotificationCompat.Builder(context, SILENT_NOTIFICATION_CHANNEL_ID)
|
val builder = NotificationCompat.Builder(context, SILENT_NOTIFICATION_CHANNEL_ID)
|
||||||
@ -366,14 +366,14 @@ class NotificationUtils @Inject constructor(private val context: Context,
|
|||||||
|
|
||||||
val contentIntent = VectorCallActivity.newIntent(
|
val contentIntent = VectorCallActivity.newIntent(
|
||||||
context = context,
|
context = context,
|
||||||
mxCall = mxCall,
|
call = call,
|
||||||
mode = null).apply {
|
mode = null).apply {
|
||||||
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
|
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
|
||||||
data = Uri.parse("foobar://$mxCall.callId")
|
data = Uri.parse("foobar://$call.callId")
|
||||||
}
|
}
|
||||||
val contentPendingIntent = PendingIntent.getActivity(context, System.currentTimeMillis().toInt(), contentIntent, 0)
|
val contentPendingIntent = PendingIntent.getActivity(context, System.currentTimeMillis().toInt(), contentIntent, 0)
|
||||||
|
|
||||||
val rejectCallPendingIntent = buildRejectCallPendingIntent(mxCall.callId)
|
val rejectCallPendingIntent = buildRejectCallPendingIntent(call.callId)
|
||||||
|
|
||||||
builder.addAction(
|
builder.addAction(
|
||||||
NotificationCompat.Action(
|
NotificationCompat.Action(
|
||||||
@ -397,12 +397,12 @@ class NotificationUtils @Inject constructor(private val context: Context,
|
|||||||
* @return the call notification.
|
* @return the call notification.
|
||||||
*/
|
*/
|
||||||
@SuppressLint("NewApi")
|
@SuppressLint("NewApi")
|
||||||
fun buildPendingCallNotification(mxCall: MxCall,
|
fun buildPendingCallNotification(call: WebRtcCall,
|
||||||
title: String): Notification {
|
title: String): Notification {
|
||||||
val builder = NotificationCompat.Builder(context, SILENT_NOTIFICATION_CHANNEL_ID)
|
val builder = NotificationCompat.Builder(context, SILENT_NOTIFICATION_CHANNEL_ID)
|
||||||
.setContentTitle(ensureTitleNotEmpty(title))
|
.setContentTitle(ensureTitleNotEmpty(title))
|
||||||
.apply {
|
.apply {
|
||||||
if (mxCall.isVideoCall) {
|
if (call.mxCall.isVideoCall) {
|
||||||
setContentText(stringProvider.getString(R.string.video_call_in_progress))
|
setContentText(stringProvider.getString(R.string.video_call_in_progress))
|
||||||
} else {
|
} else {
|
||||||
setContentText(stringProvider.getString(R.string.call_in_progress))
|
setContentText(stringProvider.getString(R.string.call_in_progress))
|
||||||
@ -411,7 +411,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
|
|||||||
.setSmallIcon(R.drawable.incoming_call_notification_transparent)
|
.setSmallIcon(R.drawable.incoming_call_notification_transparent)
|
||||||
.setCategory(NotificationCompat.CATEGORY_CALL)
|
.setCategory(NotificationCompat.CATEGORY_CALL)
|
||||||
|
|
||||||
val rejectCallPendingIntent = buildRejectCallPendingIntent(mxCall.callId)
|
val rejectCallPendingIntent = buildRejectCallPendingIntent(call.callId)
|
||||||
|
|
||||||
builder.addAction(
|
builder.addAction(
|
||||||
NotificationCompat.Action(
|
NotificationCompat.Action(
|
||||||
@ -422,7 +422,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
|
|||||||
|
|
||||||
val contentPendingIntent = TaskStackBuilder.create(context)
|
val contentPendingIntent = TaskStackBuilder.create(context)
|
||||||
.addNextIntentWithParentStack(HomeActivity.newIntent(context))
|
.addNextIntentWithParentStack(HomeActivity.newIntent(context))
|
||||||
.addNextIntent(VectorCallActivity.newIntent(context, mxCall, null))
|
.addNextIntent(VectorCallActivity.newIntent(context, call, null))
|
||||||
.getPendingIntent(System.currentTimeMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT)
|
.getPendingIntent(System.currentTimeMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT)
|
||||||
|
|
||||||
builder.setContentIntent(contentPendingIntent)
|
builder.setContentIntent(contentPendingIntent)
|
||||||
|
@ -21,6 +21,8 @@ import androidx.lifecycle.MutableLiveData
|
|||||||
import im.vector.app.core.extensions.postLiveEvent
|
import im.vector.app.core.extensions.postLiveEvent
|
||||||
import im.vector.app.core.utils.LiveEvent
|
import im.vector.app.core.utils.LiveEvent
|
||||||
import kotlinx.coroutines.cancelChildren
|
import kotlinx.coroutines.cancelChildren
|
||||||
|
import im.vector.app.features.call.vectorCallService
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import org.matrix.android.sdk.api.failure.GlobalError
|
import org.matrix.android.sdk.api.failure.GlobalError
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -37,6 +39,12 @@ class SessionListener @Inject constructor() : Session.Listener {
|
|||||||
_globalErrorLiveData.postLiveEvent(globalError)
|
_globalErrorLiveData.postLiveEvent(globalError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onNewInvitedRoom(session: Session, roomId: String) {
|
||||||
|
session.coroutineScope.launch {
|
||||||
|
session.vectorCallService.userMapper.onNewInvitedRoom(roomId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onSessionStopped(session: Session) {
|
override fun onSessionStopped(session: Session) {
|
||||||
session.coroutineScope.coroutineContext.cancelChildren()
|
session.coroutineScope.coroutineContext.cancelChildren()
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 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.app.features.session
|
||||||
|
|
||||||
|
import org.matrix.android.sdk.api.session.Session
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a simple hack for having some Session scope dependencies.
|
||||||
|
* Probably a temporary solution waiting for refactoring the Dagger management of Session.
|
||||||
|
* You should use it with an extension property :
|
||||||
|
val Session.myProperty: MyProperty by SessionScopedProperty {
|
||||||
|
init code
|
||||||
|
}
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class SessionScopedProperty<T : Any>(val initializer: (Session) -> T) {
|
||||||
|
|
||||||
|
private val propertyBySessionId = HashMap<String, T>()
|
||||||
|
|
||||||
|
private val sessionListener = object : Session.Listener {
|
||||||
|
|
||||||
|
override fun onSessionStopped(session: Session) {
|
||||||
|
synchronized(propertyBySessionId) {
|
||||||
|
session.removeListener(this)
|
||||||
|
propertyBySessionId.remove(session.sessionId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun getValue(thisRef: Session, property: KProperty<*>): T = synchronized(propertyBySessionId) {
|
||||||
|
propertyBySessionId.getOrPut(thisRef.sessionId) {
|
||||||
|
thisRef.addListener(sessionListener)
|
||||||
|
initializer(thisRef)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -27,7 +27,7 @@ import im.vector.app.core.resources.StringProvider
|
|||||||
import im.vector.app.core.ui.list.genericFooterItem
|
import im.vector.app.core.ui.list.genericFooterItem
|
||||||
import im.vector.app.core.ui.list.genericItemWithValue
|
import im.vector.app.core.ui.list.genericItemWithValue
|
||||||
import im.vector.app.core.utils.DebouncedClickListener
|
import im.vector.app.core.utils.DebouncedClickListener
|
||||||
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent
|
import org.matrix.android.sdk.api.session.accountdata.AccountDataEvent
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class AccountDataEpoxyController @Inject constructor(
|
class AccountDataEpoxyController @Inject constructor(
|
||||||
@ -35,8 +35,8 @@ class AccountDataEpoxyController @Inject constructor(
|
|||||||
) : TypedEpoxyController<AccountDataViewState>() {
|
) : TypedEpoxyController<AccountDataViewState>() {
|
||||||
|
|
||||||
interface InteractionListener {
|
interface InteractionListener {
|
||||||
fun didTap(data: UserAccountDataEvent)
|
fun didTap(data: AccountDataEvent)
|
||||||
fun didLongTap(data: UserAccountDataEvent)
|
fun didLongTap(data: AccountDataEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
var interactionListener: InteractionListener? = null
|
var interactionListener: InteractionListener? = null
|
||||||
|
@ -35,7 +35,7 @@ import im.vector.app.core.utils.createJSonViewerStyleProvider
|
|||||||
import im.vector.app.databinding.FragmentGenericRecyclerBinding
|
import im.vector.app.databinding.FragmentGenericRecyclerBinding
|
||||||
|
|
||||||
import org.billcarsonfr.jsonviewer.JSonViewerDialog
|
import org.billcarsonfr.jsonviewer.JSonViewerDialog
|
||||||
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent
|
import org.matrix.android.sdk.api.session.accountdata.AccountDataEvent
|
||||||
import org.matrix.android.sdk.internal.di.MoshiProvider
|
import org.matrix.android.sdk.internal.di.MoshiProvider
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -73,9 +73,9 @@ class AccountDataFragment @Inject constructor(
|
|||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun didTap(data: UserAccountDataEvent) {
|
override fun didTap(data: AccountDataEvent) {
|
||||||
val jsonString = MoshiProvider.providesMoshi()
|
val jsonString = MoshiProvider.providesMoshi()
|
||||||
.adapter(UserAccountDataEvent::class.java)
|
.adapter(AccountDataEvent::class.java)
|
||||||
.toJson(data)
|
.toJson(data)
|
||||||
JSonViewerDialog.newInstance(
|
JSonViewerDialog.newInstance(
|
||||||
jsonString,
|
jsonString,
|
||||||
@ -84,7 +84,7 @@ class AccountDataFragment @Inject constructor(
|
|||||||
).show(childFragmentManager, "JSON_VIEWER")
|
).show(childFragmentManager, "JSON_VIEWER")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun didLongTap(data: UserAccountDataEvent) {
|
override fun didLongTap(data: AccountDataEvent) {
|
||||||
AlertDialog.Builder(requireActivity())
|
AlertDialog.Builder(requireActivity())
|
||||||
.setTitle(R.string.delete)
|
.setTitle(R.string.delete)
|
||||||
.setMessage(getString(R.string.delete_account_data_warning, data.type))
|
.setMessage(getString(R.string.delete_account_data_warning, data.type))
|
||||||
|
@ -31,11 +31,11 @@ import im.vector.app.core.platform.EmptyViewEvents
|
|||||||
import im.vector.app.core.platform.VectorViewModel
|
import im.vector.app.core.platform.VectorViewModel
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent
|
import org.matrix.android.sdk.api.session.accountdata.AccountDataEvent
|
||||||
import org.matrix.android.sdk.rx.rx
|
import org.matrix.android.sdk.rx.rx
|
||||||
|
|
||||||
data class AccountDataViewState(
|
data class AccountDataViewState(
|
||||||
val accountData: Async<List<UserAccountDataEvent>> = Uninitialized
|
val accountData: Async<List<AccountDataEvent>> = Uninitialized
|
||||||
) : MvRxState
|
) : MvRxState
|
||||||
|
|
||||||
class AccountDataViewModel @AssistedInject constructor(@Assisted initialState: AccountDataViewState,
|
class AccountDataViewModel @AssistedInject constructor(@Assisted initialState: AccountDataViewState,
|
||||||
@ -43,7 +43,7 @@ class AccountDataViewModel @AssistedInject constructor(@Assisted initialState: A
|
|||||||
: VectorViewModel<AccountDataViewState, AccountDataAction, EmptyViewEvents>(initialState) {
|
: VectorViewModel<AccountDataViewState, AccountDataAction, EmptyViewEvents>(initialState) {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
session.rx().liveAccountData(emptySet())
|
session.rx().liveUserAccountData(emptySet())
|
||||||
.execute {
|
.execute {
|
||||||
copy(accountData = it)
|
copy(accountData = it)
|
||||||
}
|
}
|
||||||
@ -57,7 +57,7 @@ class AccountDataViewModel @AssistedInject constructor(@Assisted initialState: A
|
|||||||
|
|
||||||
private fun handleDeleteAccountData(action: AccountDataAction.DeleteAccountData) {
|
private fun handleDeleteAccountData(action: AccountDataAction.DeleteAccountData) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
session.updateAccountData(action.type, emptyMap())
|
session.userAccountDataService().updateAccountData(action.type, emptyMap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -284,7 +284,7 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
launchWidgetAPIAction(widgetPostAPIMediator, eventData) {
|
launchWidgetAPIAction(widgetPostAPIMediator, eventData) {
|
||||||
session.updateAccountData(
|
session.userAccountDataService().updateAccountData(
|
||||||
type = UserAccountDataTypes.TYPE_WIDGETS,
|
type = UserAccountDataTypes.TYPE_WIDGETS,
|
||||||
content = addUserWidgetBody
|
content = addUserWidgetBody
|
||||||
)
|
)
|
||||||
|
@ -35,7 +35,7 @@ import io.reactivex.functions.Function4
|
|||||||
import io.reactivex.subjects.PublishSubject
|
import io.reactivex.subjects.PublishSubject
|
||||||
import org.matrix.android.sdk.api.extensions.orFalse
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent
|
import org.matrix.android.sdk.api.session.accountdata.AccountDataEvent
|
||||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME
|
import org.matrix.android.sdk.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME
|
||||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo
|
import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo
|
||||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME
|
import org.matrix.android.sdk.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME
|
||||||
@ -98,8 +98,8 @@ class ServerBackupStatusViewModel @AssistedInject constructor(@Assisted initialS
|
|||||||
|
|
||||||
keysBackupState.value = session.cryptoService().keysBackupService().state
|
keysBackupState.value = session.cryptoService().keysBackupService().state
|
||||||
|
|
||||||
Observable.combineLatest<List<UserAccountDataEvent>, Optional<MXCrossSigningInfo>, KeysBackupState, Optional<PrivateKeysInfo>, BannerState>(
|
Observable.combineLatest<List<AccountDataEvent>, Optional<MXCrossSigningInfo>, KeysBackupState, Optional<PrivateKeysInfo>, BannerState>(
|
||||||
session.rx().liveAccountData(setOf(MASTER_KEY_SSSS_NAME, USER_SIGNING_KEY_SSSS_NAME, SELF_SIGNING_KEY_SSSS_NAME)),
|
session.rx().liveUserAccountData(setOf(MASTER_KEY_SSSS_NAME, USER_SIGNING_KEY_SSSS_NAME, SELF_SIGNING_KEY_SSSS_NAME)),
|
||||||
session.rx().liveCrossSigningInfo(session.myUserId),
|
session.rx().liveCrossSigningInfo(session.myUserId),
|
||||||
keyBackupPublishSubject,
|
keyBackupPublishSubject,
|
||||||
session.rx().liveCrossSigningPrivateKeys(),
|
session.rx().liveCrossSigningPrivateKeys(),
|
||||||
|
@ -97,7 +97,7 @@ class SignoutCheckViewModel @AssistedInject constructor(@Assisted initialState:
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
session.rx().liveAccountData(setOf(MASTER_KEY_SSSS_NAME, USER_SIGNING_KEY_SSSS_NAME, SELF_SIGNING_KEY_SSSS_NAME))
|
session.rx().liveUserAccountData(setOf(MASTER_KEY_SSSS_NAME, USER_SIGNING_KEY_SSSS_NAME, SELF_SIGNING_KEY_SSSS_NAME))
|
||||||
.map {
|
.map {
|
||||||
session.sharedSecretStorageService.isRecoverySetup()
|
session.sharedSecretStorageService.isRecoverySetup()
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user