mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-03-21 05:38:49 +03:00
Merge branch 'develop' into feature/bma/fix_redirection
This commit is contained in:
commit
1294d211d6
44 changed files with 724 additions and 584 deletions
.idea/dictionaries
CHANGES.mdmatrix-sdk-android/src
androidTest/java/org/matrix/android/sdk/account
main/java/org/matrix/android/sdk
api
failure
session
internal
tools/release
vector/src/main
java/im/vector/app
core/epoxy
features
auth
call
crypto/recover
home
HomeActivityViewModel.kt
room/detail
settings
account/deactivation
crosssigning
devices
threepids
res
1
.idea/dictionaries/bmarty.xml
generated
1
.idea/dictionaries/bmarty.xml
generated
|
@ -26,6 +26,7 @@
|
||||||
<w>pkcs</w>
|
<w>pkcs</w>
|
||||||
<w>previewable</w>
|
<w>previewable</w>
|
||||||
<w>previewables</w>
|
<w>previewables</w>
|
||||||
|
<w>pstn</w>
|
||||||
<w>riotx</w>
|
<w>riotx</w>
|
||||||
<w>signin</w>
|
<w>signin</w>
|
||||||
<w>signout</w>
|
<w>signout</w>
|
||||||
|
|
|
@ -15,6 +15,7 @@ Bugfix 🐛:
|
||||||
- Fix crash after initial sync on Dendrite
|
- Fix crash after initial sync on Dendrite
|
||||||
- Fix crash reported by PlayStore (#2707)
|
- Fix crash reported by PlayStore (#2707)
|
||||||
- Ignore url override from credential if it is not valid (#2822)
|
- Ignore url override from credential if it is not valid (#2822)
|
||||||
|
- Fix crash when deactivating an account
|
||||||
|
|
||||||
Translations 🗣:
|
Translations 🗣:
|
||||||
-
|
-
|
||||||
|
@ -31,6 +32,7 @@ Test:
|
||||||
Other changes:
|
Other changes:
|
||||||
- New Dev Tools panel for developers
|
- New Dev Tools panel for developers
|
||||||
- Fix typos in CHANGES.md (#2811)
|
- Fix typos in CHANGES.md (#2811)
|
||||||
|
- Colors rework: first step: merge file `colors_riot.xml` to file `colors_riotx.xml` and rename the file to `colors.xml`
|
||||||
|
|
||||||
Changes in Element 1.0.17 (2021-02-09)
|
Changes in Element 1.0.17 (2021-02-09)
|
||||||
===================================================
|
===================================================
|
||||||
|
|
|
@ -43,12 +43,13 @@ class DeactivateAccountTest : InstrumentedTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun deactivateAccountTest() {
|
fun deactivateAccountTest() {
|
||||||
val session = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(withInitialSync = false))
|
val session = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(withInitialSync = true))
|
||||||
|
|
||||||
// Deactivate the account
|
// Deactivate the account
|
||||||
commonTestHelper.runBlockingTest {
|
commonTestHelper.runBlockingTest {
|
||||||
session.deactivateAccount(
|
session.deactivateAccount(
|
||||||
object : UserInteractiveAuthInterceptor {
|
eraseAllData = false,
|
||||||
|
userInteractiveAuthInterceptor = object : UserInteractiveAuthInterceptor {
|
||||||
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
||||||
promise.resume(
|
promise.resume(
|
||||||
UserPasswordAuth(
|
UserPasswordAuth(
|
||||||
|
@ -58,7 +59,8 @@ class DeactivateAccountTest : InstrumentedTest {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}, false)
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to login on the previous account, it will fail (M_USER_DEACTIVATED)
|
// Try to login on the previous account, it will fail (M_USER_DEACTIVATED)
|
||||||
|
|
|
@ -53,22 +53,24 @@ fun Throwable.isInvalidUIAAuth(): Boolean {
|
||||||
* Try to convert to a RegistrationFlowResponse. Return null in the cases it's not possible
|
* Try to convert to a RegistrationFlowResponse. Return null in the cases it's not possible
|
||||||
*/
|
*/
|
||||||
fun Throwable.toRegistrationFlowResponse(): RegistrationFlowResponse? {
|
fun Throwable.toRegistrationFlowResponse(): RegistrationFlowResponse? {
|
||||||
return if (this is Failure.OtherServerError && this.httpCode == 401) {
|
return if (this is Failure.OtherServerError && httpCode == 401) {
|
||||||
tryOrNull {
|
tryOrNull {
|
||||||
MoshiProvider.providesMoshi()
|
MoshiProvider.providesMoshi()
|
||||||
.adapter(RegistrationFlowResponse::class.java)
|
.adapter(RegistrationFlowResponse::class.java)
|
||||||
.fromJson(this.errorBody)
|
.fromJson(errorBody)
|
||||||
}
|
}
|
||||||
} else if (this is Failure.ServerError && this.httpCode == 401 && this.error.code == MatrixError.M_FORBIDDEN) {
|
} else if (this is Failure.ServerError && httpCode == 401 && error.code == MatrixError.M_FORBIDDEN) {
|
||||||
// This happens when the submission for this stage was bad (like bad password)
|
// This happens when the submission for this stage was bad (like bad password)
|
||||||
if (this.error.session != null && this.error.flows != null) {
|
if (error.session != null && error.flows != null) {
|
||||||
RegistrationFlowResponse(
|
RegistrationFlowResponse(
|
||||||
flows = this.error.flows,
|
flows = error.flows,
|
||||||
session = this.error.session,
|
session = error.session,
|
||||||
completedStages = this.error.completedStages,
|
completedStages = error.completedStages,
|
||||||
params = this.error.params
|
params = error.params
|
||||||
)
|
)
|
||||||
} else null
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,8 @@ interface AccountService {
|
||||||
* @param password Current password.
|
* @param password Current password.
|
||||||
* @param newPassword New password
|
* @param newPassword New password
|
||||||
*/
|
*/
|
||||||
suspend fun changePassword(password: String, newPassword: String)
|
suspend fun changePassword(password: String,
|
||||||
|
newPassword: String)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deactivate the account.
|
* Deactivate the account.
|
||||||
|
@ -41,9 +42,10 @@ interface AccountService {
|
||||||
* be shared with any new or unregistered users, but registered users who already have access to these messages will still
|
* be shared with any new or unregistered users, but registered users who already have access to these messages will still
|
||||||
* have access to their copy.
|
* have access to their copy.
|
||||||
*
|
*
|
||||||
* @param password the account password
|
|
||||||
* @param eraseAllData set to true to forget all messages that have been sent. Warning: this will cause future users to see
|
* @param eraseAllData set to true to forget all messages that have been sent. Warning: this will cause future users to see
|
||||||
* an incomplete view of conversations
|
* an incomplete view of conversations
|
||||||
|
* @param userInteractiveAuthInterceptor see [UserInteractiveAuthInterceptor]
|
||||||
*/
|
*/
|
||||||
suspend fun deactivateAccount(userInteractiveAuthInterceptor: UserInteractiveAuthInterceptor, eraseAllData: Boolean)
|
suspend fun deactivateAccount(eraseAllData: Boolean,
|
||||||
|
userInteractiveAuthInterceptor: UserInteractiveAuthInterceptor)
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,12 +16,11 @@
|
||||||
|
|
||||||
package org.matrix.android.sdk.api.session.call
|
package org.matrix.android.sdk.api.session.call
|
||||||
|
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
|
||||||
import org.matrix.android.sdk.api.util.Cancelable
|
|
||||||
|
|
||||||
interface CallSignalingService {
|
interface CallSignalingService {
|
||||||
|
|
||||||
fun getTurnServer(callback: MatrixCallback<TurnServerResponse>): Cancelable
|
suspend fun getTurnServer(): TurnServerResponse
|
||||||
|
|
||||||
|
fun getPSTNProtocolChecker(): PSTNProtocolChecker
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an outgoing call
|
* Create an outgoing call
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -56,8 +56,6 @@ interface CryptoService {
|
||||||
|
|
||||||
fun deleteDevice(deviceId: String, userInteractiveAuthInterceptor: UserInteractiveAuthInterceptor, callback: MatrixCallback<Unit>)
|
fun deleteDevice(deviceId: String, userInteractiveAuthInterceptor: UserInteractiveAuthInterceptor, callback: MatrixCallback<Unit>)
|
||||||
|
|
||||||
fun deleteDeviceWithUserPassword(deviceId: String, authSession: String?, password: String, callback: MatrixCallback<Unit>)
|
|
||||||
|
|
||||||
fun getCryptoVersion(context: Context, longFormat: Boolean): String
|
fun getCryptoVersion(context: Context, longFormat: Boolean): String
|
||||||
|
|
||||||
fun isCryptoEnabled(): Boolean
|
fun isCryptoEnabled(): Boolean
|
||||||
|
|
|
@ -16,14 +16,25 @@
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.auth.registration
|
package org.matrix.android.sdk.internal.auth.registration
|
||||||
|
|
||||||
|
import org.matrix.android.sdk.api.auth.UIABaseAuth
|
||||||
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
||||||
import org.matrix.android.sdk.api.failure.Failure
|
import org.matrix.android.sdk.api.failure.Failure
|
||||||
import org.matrix.android.sdk.api.failure.toRegistrationFlowResponse
|
import org.matrix.android.sdk.api.failure.toRegistrationFlowResponse
|
||||||
import org.matrix.android.sdk.api.auth.UIABaseAuth
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import kotlin.coroutines.suspendCoroutine
|
import kotlin.coroutines.suspendCoroutine
|
||||||
|
|
||||||
internal suspend fun handleUIA(failure: Throwable, interceptor: UserInteractiveAuthInterceptor, retryBlock: suspend (UIABaseAuth) -> Unit): Boolean {
|
/**
|
||||||
|
* Handle a UIA challenge
|
||||||
|
*
|
||||||
|
* @param failure the failure to handle
|
||||||
|
* @param interceptor see doc in [UserInteractiveAuthInterceptor]
|
||||||
|
* @param retryBlock called at the end of the process, in this block generally retry executing the task, with
|
||||||
|
* provided authUpdate
|
||||||
|
* @return true if UIA is handled without error
|
||||||
|
*/
|
||||||
|
internal suspend fun handleUIA(failure: Throwable,
|
||||||
|
interceptor: UserInteractiveAuthInterceptor,
|
||||||
|
retryBlock: suspend (UIABaseAuth) -> Unit): Boolean {
|
||||||
Timber.d("## UIA: check error ${failure.message}")
|
Timber.d("## UIA: check error ${failure.message}")
|
||||||
val flowResponse = failure.toRegistrationFlowResponse()
|
val flowResponse = failure.toRegistrationFlowResponse()
|
||||||
?: return false.also {
|
?: return false.also {
|
||||||
|
@ -38,16 +49,16 @@ internal suspend fun handleUIA(failure: Throwable, interceptor: UserInteractiveA
|
||||||
suspendCoroutine<UIABaseAuth> { continuation ->
|
suspendCoroutine<UIABaseAuth> { continuation ->
|
||||||
interceptor.performStage(flowResponse, (failure as? Failure.ServerError)?.error?.code, continuation)
|
interceptor.performStage(flowResponse, (failure as? Failure.ServerError)?.error?.code, continuation)
|
||||||
}
|
}
|
||||||
} catch (failure: Throwable) {
|
} catch (failure2: Throwable) {
|
||||||
Timber.w(failure, "## UIA: failed to participate")
|
Timber.w(failure2, "## UIA: failed to participate")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
Timber.d("## UIA: updated auth $authUpdate")
|
Timber.d("## UIA: updated auth")
|
||||||
return try {
|
return try {
|
||||||
retryBlock(authUpdate)
|
retryBlock(authUpdate)
|
||||||
true
|
true
|
||||||
} catch (failure: Throwable) {
|
} catch (failure3: Throwable) {
|
||||||
handleUIA(failure, interceptor, retryBlock)
|
handleUIA(failure3, interceptor, retryBlock)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,6 @@ import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStoreModule
|
||||||
import org.matrix.android.sdk.internal.crypto.tasks.ClaimOneTimeKeysForUsersDeviceTask
|
import org.matrix.android.sdk.internal.crypto.tasks.ClaimOneTimeKeysForUsersDeviceTask
|
||||||
import org.matrix.android.sdk.internal.crypto.tasks.DefaultClaimOneTimeKeysForUsersDevice
|
import org.matrix.android.sdk.internal.crypto.tasks.DefaultClaimOneTimeKeysForUsersDevice
|
||||||
import org.matrix.android.sdk.internal.crypto.tasks.DefaultDeleteDeviceTask
|
import org.matrix.android.sdk.internal.crypto.tasks.DefaultDeleteDeviceTask
|
||||||
import org.matrix.android.sdk.internal.crypto.tasks.DefaultDeleteDeviceWithUserPasswordTask
|
|
||||||
import org.matrix.android.sdk.internal.crypto.tasks.DefaultDownloadKeysForUsers
|
import org.matrix.android.sdk.internal.crypto.tasks.DefaultDownloadKeysForUsers
|
||||||
import org.matrix.android.sdk.internal.crypto.tasks.DefaultEncryptEventTask
|
import org.matrix.android.sdk.internal.crypto.tasks.DefaultEncryptEventTask
|
||||||
import org.matrix.android.sdk.internal.crypto.tasks.DefaultGetDeviceInfoTask
|
import org.matrix.android.sdk.internal.crypto.tasks.DefaultGetDeviceInfoTask
|
||||||
|
@ -75,7 +74,6 @@ import org.matrix.android.sdk.internal.crypto.tasks.DefaultUploadKeysTask
|
||||||
import org.matrix.android.sdk.internal.crypto.tasks.DefaultUploadSignaturesTask
|
import org.matrix.android.sdk.internal.crypto.tasks.DefaultUploadSignaturesTask
|
||||||
import org.matrix.android.sdk.internal.crypto.tasks.DefaultUploadSigningKeysTask
|
import org.matrix.android.sdk.internal.crypto.tasks.DefaultUploadSigningKeysTask
|
||||||
import org.matrix.android.sdk.internal.crypto.tasks.DeleteDeviceTask
|
import org.matrix.android.sdk.internal.crypto.tasks.DeleteDeviceTask
|
||||||
import org.matrix.android.sdk.internal.crypto.tasks.DeleteDeviceWithUserPasswordTask
|
|
||||||
import org.matrix.android.sdk.internal.crypto.tasks.DownloadKeysForUsersTask
|
import org.matrix.android.sdk.internal.crypto.tasks.DownloadKeysForUsersTask
|
||||||
import org.matrix.android.sdk.internal.crypto.tasks.EncryptEventTask
|
import org.matrix.android.sdk.internal.crypto.tasks.EncryptEventTask
|
||||||
import org.matrix.android.sdk.internal.crypto.tasks.GetDeviceInfoTask
|
import org.matrix.android.sdk.internal.crypto.tasks.GetDeviceInfoTask
|
||||||
|
@ -240,9 +238,6 @@ internal abstract class CryptoModule {
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun bindClaimOneTimeKeysForUsersDeviceTask(task: DefaultClaimOneTimeKeysForUsersDevice): ClaimOneTimeKeysForUsersDeviceTask
|
abstract fun bindClaimOneTimeKeysForUsersDeviceTask(task: DefaultClaimOneTimeKeysForUsersDevice): ClaimOneTimeKeysForUsersDeviceTask
|
||||||
|
|
||||||
@Binds
|
|
||||||
abstract fun bindDeleteDeviceWithUserPasswordTask(task: DefaultDeleteDeviceWithUserPasswordTask): DeleteDeviceWithUserPasswordTask
|
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun bindCrossSigningService(service: DefaultCrossSigningService): CrossSigningService
|
abstract fun bindCrossSigningService(service: DefaultCrossSigningService): CrossSigningService
|
||||||
|
|
||||||
|
|
|
@ -75,7 +75,6 @@ import org.matrix.android.sdk.internal.crypto.model.toRest
|
||||||
import org.matrix.android.sdk.internal.crypto.repository.WarnOnUnknownDeviceRepository
|
import org.matrix.android.sdk.internal.crypto.repository.WarnOnUnknownDeviceRepository
|
||||||
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
||||||
import org.matrix.android.sdk.internal.crypto.tasks.DeleteDeviceTask
|
import org.matrix.android.sdk.internal.crypto.tasks.DeleteDeviceTask
|
||||||
import org.matrix.android.sdk.internal.crypto.tasks.DeleteDeviceWithUserPasswordTask
|
|
||||||
import org.matrix.android.sdk.internal.crypto.tasks.GetDeviceInfoTask
|
import org.matrix.android.sdk.internal.crypto.tasks.GetDeviceInfoTask
|
||||||
import org.matrix.android.sdk.internal.crypto.tasks.GetDevicesTask
|
import org.matrix.android.sdk.internal.crypto.tasks.GetDevicesTask
|
||||||
import org.matrix.android.sdk.internal.crypto.tasks.SetDeviceNameTask
|
import org.matrix.android.sdk.internal.crypto.tasks.SetDeviceNameTask
|
||||||
|
@ -153,9 +152,8 @@ internal class DefaultCryptoService @Inject constructor(
|
||||||
// Repository
|
// Repository
|
||||||
private val megolmEncryptionFactory: MXMegolmEncryptionFactory,
|
private val megolmEncryptionFactory: MXMegolmEncryptionFactory,
|
||||||
private val olmEncryptionFactory: MXOlmEncryptionFactory,
|
private val olmEncryptionFactory: MXOlmEncryptionFactory,
|
||||||
private val deleteDeviceTask: DeleteDeviceTask,
|
|
||||||
private val deleteDeviceWithUserPasswordTask: DeleteDeviceWithUserPasswordTask,
|
|
||||||
// Tasks
|
// Tasks
|
||||||
|
private val deleteDeviceTask: DeleteDeviceTask,
|
||||||
private val getDevicesTask: GetDevicesTask,
|
private val getDevicesTask: GetDevicesTask,
|
||||||
private val getDeviceInfoTask: GetDeviceInfoTask,
|
private val getDeviceInfoTask: GetDeviceInfoTask,
|
||||||
private val setDeviceNameTask: SetDeviceNameTask,
|
private val setDeviceNameTask: SetDeviceNameTask,
|
||||||
|
@ -217,15 +215,6 @@ internal class DefaultCryptoService @Inject constructor(
|
||||||
.executeBy(taskExecutor)
|
.executeBy(taskExecutor)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun deleteDeviceWithUserPassword(deviceId: String, authSession: String?, password: String, callback: MatrixCallback<Unit>) {
|
|
||||||
deleteDeviceWithUserPasswordTask
|
|
||||||
.configureWith(DeleteDeviceWithUserPasswordTask.Params(deviceId, authSession, password)) {
|
|
||||||
this.executionThread = TaskThread.CRYPTO
|
|
||||||
this.callback = callback
|
|
||||||
}
|
|
||||||
.executeBy(taskExecutor)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getCryptoVersion(context: Context, longFormat: Boolean): String {
|
override fun getCryptoVersion(context: Context, longFormat: Boolean): String {
|
||||||
return if (longFormat) olmManager.getDetailedVersion(context) else olmManager.version
|
return if (longFormat) olmManager.getDetailedVersion(context) else olmManager.version
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,12 +47,16 @@ internal class DefaultDeleteDeviceTask @Inject constructor(
|
||||||
}
|
}
|
||||||
} catch (throwable: Throwable) {
|
} catch (throwable: Throwable) {
|
||||||
if (params.userInteractiveAuthInterceptor == null
|
if (params.userInteractiveAuthInterceptor == null
|
||||||
|| !handleUIA(throwable, params.userInteractiveAuthInterceptor) { auth ->
|
|| !handleUIA(
|
||||||
execute(params.copy(userAuthParam = auth))
|
failure = throwable,
|
||||||
}
|
interceptor = params.userInteractiveAuthInterceptor,
|
||||||
|
retryBlock = { authUpdate ->
|
||||||
|
execute(params.copy(userAuthParam = authUpdate))
|
||||||
|
}
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
Timber.d("## UIA: propagate failure")
|
Timber.d("## UIA: propagate failure")
|
||||||
throw throwable
|
throw throwable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,57 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.crypto.tasks
|
|
||||||
|
|
||||||
import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
|
|
||||||
import org.matrix.android.sdk.internal.crypto.api.CryptoApi
|
|
||||||
import org.matrix.android.sdk.internal.crypto.model.rest.DeleteDeviceParams
|
|
||||||
import org.matrix.android.sdk.api.auth.UserPasswordAuth
|
|
||||||
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.task.Task
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
internal interface DeleteDeviceWithUserPasswordTask : Task<DeleteDeviceWithUserPasswordTask.Params, Unit> {
|
|
||||||
data class Params(
|
|
||||||
val deviceId: String,
|
|
||||||
val authSession: String?,
|
|
||||||
val password: String
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class DefaultDeleteDeviceWithUserPasswordTask @Inject constructor(
|
|
||||||
private val cryptoApi: CryptoApi,
|
|
||||||
@UserId private val userId: String,
|
|
||||||
private val globalErrorReceiver: GlobalErrorReceiver
|
|
||||||
) : DeleteDeviceWithUserPasswordTask {
|
|
||||||
|
|
||||||
override suspend fun execute(params: DeleteDeviceWithUserPasswordTask.Params) {
|
|
||||||
return executeRequest(globalErrorReceiver) {
|
|
||||||
apiCall = cryptoApi.deleteDevice(params.deviceId,
|
|
||||||
DeleteDeviceParams(
|
|
||||||
auth = UserPasswordAuth(
|
|
||||||
type = LoginFlowTypes.PASSWORD,
|
|
||||||
session = params.authSession,
|
|
||||||
user = userId,
|
|
||||||
password = params.password
|
|
||||||
).asMap()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -126,11 +126,16 @@ internal class DefaultInitializeCrossSigningTask @Inject constructor(
|
||||||
uploadSigningKeysTask.execute(uploadSigningKeysParams)
|
uploadSigningKeysTask.execute(uploadSigningKeysParams)
|
||||||
} catch (failure: Throwable) {
|
} catch (failure: Throwable) {
|
||||||
if (params.interactiveAuthInterceptor == null
|
if (params.interactiveAuthInterceptor == null
|
||||||
|| !handleUIA(failure, params.interactiveAuthInterceptor) { authUpdate ->
|
|| !handleUIA(
|
||||||
uploadSigningKeysTask.execute(uploadSigningKeysParams.copy(userAuthParam = authUpdate))
|
failure = failure,
|
||||||
}) {
|
interceptor = params.interactiveAuthInterceptor,
|
||||||
|
retryBlock = { authUpdate ->
|
||||||
|
uploadSigningKeysTask.execute(uploadSigningKeysParams.copy(userAuthParam = authUpdate))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
) {
|
||||||
Timber.d("## UIA: propagate failure")
|
Timber.d("## UIA: propagate failure")
|
||||||
throw failure
|
throw failure
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,10 +16,9 @@
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.session.account
|
package org.matrix.android.sdk.internal.session.account
|
||||||
|
|
||||||
|
import org.matrix.android.sdk.api.auth.UIABaseAuth
|
||||||
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
||||||
import org.matrix.android.sdk.internal.auth.registration.handleUIA
|
import org.matrix.android.sdk.internal.auth.registration.handleUIA
|
||||||
import org.matrix.android.sdk.api.auth.UIABaseAuth
|
|
||||||
import org.matrix.android.sdk.internal.di.UserId
|
|
||||||
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
|
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
|
||||||
import org.matrix.android.sdk.internal.network.executeRequest
|
import org.matrix.android.sdk.internal.network.executeRequest
|
||||||
import org.matrix.android.sdk.internal.session.cleanup.CleanupSession
|
import org.matrix.android.sdk.internal.session.cleanup.CleanupSession
|
||||||
|
@ -30,8 +29,8 @@ import javax.inject.Inject
|
||||||
|
|
||||||
internal interface DeactivateAccountTask : Task<DeactivateAccountTask.Params, Unit> {
|
internal interface DeactivateAccountTask : Task<DeactivateAccountTask.Params, Unit> {
|
||||||
data class Params(
|
data class Params(
|
||||||
val userInteractiveAuthInterceptor: UserInteractiveAuthInterceptor,
|
|
||||||
val eraseAllData: Boolean,
|
val eraseAllData: Boolean,
|
||||||
|
val userInteractiveAuthInterceptor: UserInteractiveAuthInterceptor,
|
||||||
val userAuthParam: UIABaseAuth? = null
|
val userAuthParam: UIABaseAuth? = null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -39,7 +38,6 @@ internal interface DeactivateAccountTask : Task<DeactivateAccountTask.Params, Un
|
||||||
internal class DefaultDeactivateAccountTask @Inject constructor(
|
internal class DefaultDeactivateAccountTask @Inject constructor(
|
||||||
private val accountAPI: AccountAPI,
|
private val accountAPI: AccountAPI,
|
||||||
private val globalErrorReceiver: GlobalErrorReceiver,
|
private val globalErrorReceiver: GlobalErrorReceiver,
|
||||||
@UserId private val userId: String,
|
|
||||||
private val identityDisconnectTask: IdentityDisconnectTask,
|
private val identityDisconnectTask: IdentityDisconnectTask,
|
||||||
private val cleanupSession: CleanupSession
|
private val cleanupSession: CleanupSession
|
||||||
) : DeactivateAccountTask {
|
) : DeactivateAccountTask {
|
||||||
|
@ -47,23 +45,33 @@ internal class DefaultDeactivateAccountTask @Inject constructor(
|
||||||
override suspend fun execute(params: DeactivateAccountTask.Params) {
|
override suspend fun execute(params: DeactivateAccountTask.Params) {
|
||||||
val deactivateAccountParams = DeactivateAccountParams.create(params.userAuthParam, params.eraseAllData)
|
val deactivateAccountParams = DeactivateAccountParams.create(params.userAuthParam, params.eraseAllData)
|
||||||
|
|
||||||
try {
|
val canCleanup = try {
|
||||||
executeRequest<Unit>(globalErrorReceiver) {
|
executeRequest<Unit>(globalErrorReceiver) {
|
||||||
apiCall = accountAPI.deactivate(deactivateAccountParams)
|
apiCall = accountAPI.deactivate(deactivateAccountParams)
|
||||||
}
|
}
|
||||||
|
true
|
||||||
} catch (throwable: Throwable) {
|
} catch (throwable: Throwable) {
|
||||||
if (!handleUIA(throwable, params.userInteractiveAuthInterceptor) { auth ->
|
if (!handleUIA(
|
||||||
execute(params.copy(userAuthParam = auth))
|
failure = throwable,
|
||||||
}
|
interceptor = params.userInteractiveAuthInterceptor,
|
||||||
|
retryBlock = { authUpdate ->
|
||||||
|
execute(params.copy(userAuthParam = authUpdate))
|
||||||
|
}
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
Timber.d("## UIA: propagate failure")
|
Timber.d("## UIA: propagate failure")
|
||||||
throw throwable
|
throw throwable
|
||||||
|
} else {
|
||||||
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Logout from identity server if any, ignoring errors
|
|
||||||
runCatching { identityDisconnectTask.execute(Unit) }
|
|
||||||
.onFailure { Timber.w(it, "Unable to disconnect identity server") }
|
|
||||||
|
|
||||||
cleanupSession.handle()
|
if (canCleanup) {
|
||||||
|
// Logout from identity server if any, ignoring errors
|
||||||
|
runCatching { identityDisconnectTask.execute(Unit) }
|
||||||
|
.onFailure { Timber.w(it, "Unable to disconnect identity server") }
|
||||||
|
|
||||||
|
cleanupSession.handle()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ internal class DefaultAccountService @Inject constructor(private val changePassw
|
||||||
changePasswordTask.execute(ChangePasswordTask.Params(password, newPassword))
|
changePasswordTask.execute(ChangePasswordTask.Params(password, newPassword))
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun deactivateAccount(userInteractiveAuthInterceptor: UserInteractiveAuthInterceptor, eraseAllData: Boolean) {
|
override suspend fun deactivateAccount(eraseAllData: Boolean, userInteractiveAuthInterceptor: UserInteractiveAuthInterceptor) {
|
||||||
deactivateAccountTask.execute(DeactivateAccountTask.Params(userInteractiveAuthInterceptor, eraseAllData))
|
deactivateAccountTask.execute(DeactivateAccountTask.Params(eraseAllData, userInteractiveAuthInterceptor))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,16 +16,12 @@
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.session.call
|
package org.matrix.android.sdk.internal.session.call
|
||||||
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
|
||||||
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.api.util.Cancelable
|
|
||||||
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.launchToCallback
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -34,14 +30,16 @@ 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 taskExecutor: TaskExecutor,
|
private val turnServerDataSource: TurnServerDataSource,
|
||||||
private val turnServerDataSource: TurnServerDataSource
|
private val pstnProtocolChecker: PSTNProtocolChecker
|
||||||
) : CallSignalingService {
|
) : CallSignalingService {
|
||||||
|
|
||||||
override fun getTurnServer(callback: MatrixCallback<TurnServerResponse>): Cancelable {
|
override suspend fun getTurnServer(): TurnServerResponse {
|
||||||
return taskExecutor.executorScope.launchToCallback(Dispatchers.Default, callback) {
|
return turnServerDataSource.getTurnServer()
|
||||||
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 {
|
||||||
|
|
|
@ -26,7 +26,6 @@ import org.matrix.android.sdk.api.auth.UIABaseAuth
|
||||||
import org.matrix.android.sdk.internal.database.model.PendingThreePidEntity
|
import org.matrix.android.sdk.internal.database.model.PendingThreePidEntity
|
||||||
import org.matrix.android.sdk.internal.database.model.PendingThreePidEntityFields
|
import org.matrix.android.sdk.internal.database.model.PendingThreePidEntityFields
|
||||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||||
import org.matrix.android.sdk.internal.di.UserId
|
|
||||||
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
|
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
|
||||||
import org.matrix.android.sdk.internal.network.executeRequest
|
import org.matrix.android.sdk.internal.network.executeRequest
|
||||||
import org.matrix.android.sdk.internal.task.Task
|
import org.matrix.android.sdk.internal.task.Task
|
||||||
|
@ -47,11 +46,12 @@ internal class DefaultFinalizeAddingThreePidTask @Inject constructor(
|
||||||
private val profileAPI: ProfileAPI,
|
private val profileAPI: ProfileAPI,
|
||||||
@SessionDatabase private val monarchy: Monarchy,
|
@SessionDatabase private val monarchy: Monarchy,
|
||||||
private val pendingThreePidMapper: PendingThreePidMapper,
|
private val pendingThreePidMapper: PendingThreePidMapper,
|
||||||
@UserId private val userId: String,
|
|
||||||
private val globalErrorReceiver: GlobalErrorReceiver) : FinalizeAddingThreePidTask() {
|
private val globalErrorReceiver: GlobalErrorReceiver) : FinalizeAddingThreePidTask() {
|
||||||
|
|
||||||
override suspend fun execute(params: Params) {
|
override suspend fun execute(params: Params) {
|
||||||
if (params.userWantsToCancel.not()) {
|
val canCleanup = if (params.userWantsToCancel) {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
// Get the required pending data
|
// Get the required pending data
|
||||||
val pendingThreePids = monarchy.fetchAllMappedSync(
|
val pendingThreePids = monarchy.fetchAllMappedSync(
|
||||||
{ it.where(PendingThreePidEntity::class.java) },
|
{ it.where(PendingThreePidEntity::class.java) },
|
||||||
|
@ -69,21 +69,30 @@ internal class DefaultFinalizeAddingThreePidTask @Inject constructor(
|
||||||
)
|
)
|
||||||
apiCall = profileAPI.finalizeAddThreePid(body)
|
apiCall = profileAPI.finalizeAddThreePid(body)
|
||||||
}
|
}
|
||||||
|
true
|
||||||
} catch (throwable: Throwable) {
|
} catch (throwable: Throwable) {
|
||||||
if (params.userInteractiveAuthInterceptor == null
|
if (params.userInteractiveAuthInterceptor == null
|
||||||
|| !handleUIA(throwable, params.userInteractiveAuthInterceptor) { auth ->
|
|| !handleUIA(
|
||||||
execute(params.copy(userAuthParam = auth))
|
failure = throwable,
|
||||||
}
|
interceptor = params.userInteractiveAuthInterceptor,
|
||||||
|
retryBlock = { authUpdate ->
|
||||||
|
execute(params.copy(userAuthParam = authUpdate))
|
||||||
|
}
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
Timber.d("## UIA: propagate failure")
|
Timber.d("## UIA: propagate failure")
|
||||||
throw throwable.toRegistrationFlowResponse()
|
throw throwable.toRegistrationFlowResponse()
|
||||||
?.let { Failure.RegistrationFlowError(it) }
|
?.let { Failure.RegistrationFlowError(it) }
|
||||||
?: throwable
|
?: throwable
|
||||||
|
} else {
|
||||||
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanupDatabase(params)
|
if (canCleanup) {
|
||||||
|
cleanupDatabase(params)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun cleanupDatabase(params: Params) {
|
private suspend fun cleanupDatabase(params: Params) {
|
||||||
|
|
|
@ -41,6 +41,9 @@ parser.add_argument('-b',
|
||||||
type=int,
|
type=int,
|
||||||
required=True,
|
required=True,
|
||||||
help='the buildkite build number.')
|
help='the buildkite build number.')
|
||||||
|
parser.add_argument('-f',
|
||||||
|
'--filename',
|
||||||
|
help='the filename, to download only one artifact.')
|
||||||
parser.add_argument('-e',
|
parser.add_argument('-e',
|
||||||
'--expecting',
|
'--expecting',
|
||||||
type=int,
|
type=int,
|
||||||
|
@ -148,6 +151,8 @@ for elt in data:
|
||||||
print(" %s: %s" % (key, str(value)))
|
print(" %s: %s" % (key, str(value)))
|
||||||
url = elt.get("download_url")
|
url = elt.get("download_url")
|
||||||
filename = elt.get("filename")
|
filename = elt.get("filename")
|
||||||
|
if args.filename is not None and args.filename != filename:
|
||||||
|
continue
|
||||||
target = targetDir + "/" + filename
|
target = targetDir + "/" + filename
|
||||||
print("Downloading %s to '%s'..." % (filename, targetDir))
|
print("Downloading %s to '%s'..." % (filename, targetDir))
|
||||||
if not args.simulate:
|
if not args.simulate:
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2019 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.core.epoxy
|
||||||
|
|
||||||
|
import com.airbnb.epoxy.EpoxyAttribute
|
||||||
|
import com.airbnb.epoxy.EpoxyModelClass
|
||||||
|
import im.vector.app.R
|
||||||
|
import im.vector.app.features.home.room.detail.timeline.item.ItemWithEvents
|
||||||
|
|
||||||
|
@EpoxyModelClass(layout = R.layout.item_timeline_empty)
|
||||||
|
abstract class TimelineEmptyItem : VectorEpoxyModel<TimelineEmptyItem.Holder>(), ItemWithEvents {
|
||||||
|
|
||||||
|
@EpoxyAttribute lateinit var eventId: String
|
||||||
|
|
||||||
|
override fun getEventIds(): List<String> {
|
||||||
|
return listOf(eventId)
|
||||||
|
}
|
||||||
|
|
||||||
|
class Holder : VectorEpoxyHolder()
|
||||||
|
}
|
|
@ -113,7 +113,7 @@ class ReAuthActivity : SimpleFragmentActivity(), ReAuthViewModel.Factory {
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
// It's the only way we have to know if sso falback flow was successful
|
// It's the only way we have to know if sso fallback flow was successful
|
||||||
withState(sharedViewModel) {
|
withState(sharedViewModel) {
|
||||||
if (it.ssoFallbackPageWasShown) {
|
if (it.ssoFallbackPageWasShown) {
|
||||||
Timber.d("## UIA ssoFallbackPageWasShown tentative success")
|
Timber.d("## UIA ssoFallbackPageWasShown tentative success")
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package im.vector.app.features.call
|
package im.vector.app.features.call
|
||||||
|
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.airbnb.mvrx.Fail
|
import com.airbnb.mvrx.Fail
|
||||||
import com.airbnb.mvrx.Loading
|
import com.airbnb.mvrx.Loading
|
||||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||||
|
@ -29,17 +30,16 @@ 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 org.matrix.android.sdk.api.MatrixCallback
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
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.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.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.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.MatrixItem
|
||||||
import org.matrix.android.sdk.api.util.toMatrixItem
|
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||||
import java.util.Timer
|
|
||||||
import java.util.TimerTask
|
|
||||||
|
|
||||||
class VectorCallViewModel @AssistedInject constructor(
|
class VectorCallViewModel @AssistedInject constructor(
|
||||||
@Assisted initialState: VectorCallViewState,
|
@Assisted initialState: VectorCallViewState,
|
||||||
|
@ -50,7 +50,7 @@ class VectorCallViewModel @AssistedInject constructor(
|
||||||
|
|
||||||
private var call: WebRtcCall? = null
|
private var call: WebRtcCall? = null
|
||||||
|
|
||||||
private var connectionTimeoutTimer: Timer? = null
|
private var connectionTimeoutJob: Job? = null
|
||||||
private var hasBeenConnectedOnce = false
|
private var hasBeenConnectedOnce = false
|
||||||
|
|
||||||
private val callListener = object : WebRtcCall.Listener {
|
private val callListener = object : WebRtcCall.Listener {
|
||||||
|
@ -92,26 +92,20 @@ class VectorCallViewModel @AssistedInject constructor(
|
||||||
val callState = call.state
|
val callState = call.state
|
||||||
if (callState is CallState.Connected && callState.iceConnectionState == MxPeerConnectionState.CONNECTED) {
|
if (callState is CallState.Connected && callState.iceConnectionState == MxPeerConnectionState.CONNECTED) {
|
||||||
hasBeenConnectedOnce = true
|
hasBeenConnectedOnce = true
|
||||||
connectionTimeoutTimer?.cancel()
|
connectionTimeoutJob?.cancel()
|
||||||
connectionTimeoutTimer = null
|
connectionTimeoutJob = null
|
||||||
} else {
|
} else {
|
||||||
// do we reset as long as it's moving?
|
// do we reset as long as it's moving?
|
||||||
connectionTimeoutTimer?.cancel()
|
connectionTimeoutJob?.cancel()
|
||||||
if (hasBeenConnectedOnce) {
|
if (hasBeenConnectedOnce) {
|
||||||
connectionTimeoutTimer = Timer().apply {
|
connectionTimeoutJob = viewModelScope.launch {
|
||||||
schedule(object : TimerTask() {
|
delay(30_000)
|
||||||
override fun run() {
|
try {
|
||||||
session.callSignalingService().getTurnServer(object : MatrixCallback<TurnServerResponse> {
|
val turn = session.callSignalingService().getTurnServer()
|
||||||
override fun onFailure(failure: Throwable) {
|
_viewEvents.post(VectorCallViewEvents.ConnectionTimeout(turn))
|
||||||
_viewEvents.post(VectorCallViewEvents.ConnectionTimeout(null))
|
} catch (failure: Throwable) {
|
||||||
}
|
_viewEvents.post(VectorCallViewEvents.ConnectionTimeout(null))
|
||||||
|
}
|
||||||
override fun onSuccess(data: TurnServerResponse) {
|
|
||||||
_viewEvents.post(VectorCallViewEvents.ConnectionTimeout(data))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}, 30_000)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,12 +83,12 @@ class CallAudioManager(private val context: Context, val configChange: (() -> Un
|
||||||
fun setAudioDevice(device: Device) {
|
fun setAudioDevice(device: Device) {
|
||||||
runInAudioThread(Runnable {
|
runInAudioThread(Runnable {
|
||||||
if (!_availableDevices.contains(device)) {
|
if (!_availableDevices.contains(device)) {
|
||||||
Timber.w(" Audio device not available: $device")
|
Timber.w("Audio device not available: $device")
|
||||||
userSelectedDevice = null
|
userSelectedDevice = null
|
||||||
return@Runnable
|
return@Runnable
|
||||||
}
|
}
|
||||||
if (mode != Mode.DEFAULT) {
|
if (mode != Mode.DEFAULT) {
|
||||||
Timber.i(" User selected device set to: $device")
|
Timber.i("User selected device set to: $device")
|
||||||
userSelectedDevice = device
|
userSelectedDevice = device
|
||||||
updateAudioRoute(mode, false)
|
updateAudioRoute(mode, false)
|
||||||
}
|
}
|
||||||
|
@ -108,7 +108,7 @@ class CallAudioManager(private val context: Context, val configChange: (() -> Un
|
||||||
success = updateAudioRoute(mode, false)
|
success = updateAudioRoute(mode, false)
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
success = false
|
success = false
|
||||||
Timber.e(e, " Failed to update audio route for mode: " + mode)
|
Timber.e(e, "Failed to update audio route for mode: $mode")
|
||||||
}
|
}
|
||||||
if (success) {
|
if (success) {
|
||||||
this@CallAudioManager.mode = mode
|
this@CallAudioManager.mode = mode
|
||||||
|
@ -124,7 +124,7 @@ class CallAudioManager(private val context: Context, val configChange: (() -> Un
|
||||||
* `false`, otherwise.
|
* `false`, otherwise.
|
||||||
*/
|
*/
|
||||||
private fun updateAudioRoute(mode: Mode, force: Boolean): Boolean {
|
private fun updateAudioRoute(mode: Mode, force: Boolean): Boolean {
|
||||||
Timber.i(" Update audio route for mode: " + mode)
|
Timber.i("Update audio route for mode: $mode")
|
||||||
if (!audioDeviceRouter?.setMode(mode).orFalse()) {
|
if (!audioDeviceRouter?.setMode(mode).orFalse()) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -158,7 +158,7 @@ class CallAudioManager(private val context: Context, val configChange: (() -> Un
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
selectedDevice = audioDevice
|
selectedDevice = audioDevice
|
||||||
Timber.i(" Selected audio device: " + audioDevice)
|
Timber.i("Selected audio device: $audioDevice")
|
||||||
audioDeviceRouter?.setAudioRoute(audioDevice)
|
audioDeviceRouter?.setAudioRoute(audioDevice)
|
||||||
configChange?.invoke()
|
configChange?.invoke()
|
||||||
return true
|
return true
|
||||||
|
|
|
@ -22,20 +22,22 @@ 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 javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class DialPadLookup @Inject constructor(val session: Session,
|
class DialPadLookup @Inject constructor(
|
||||||
val directRoomHelper: DirectRoomHelper,
|
private val session: Session,
|
||||||
val callManager: WebRtcCallManager
|
private val directRoomHelper: DirectRoomHelper,
|
||||||
|
private val callManager: WebRtcCallManager
|
||||||
) {
|
) {
|
||||||
|
|
||||||
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 supportedProtocolKey = callManager.supportedPSTNProtocol ?: throw Failure()
|
||||||
val thirdPartyUser = tryOrNull {
|
val thirdPartyUser = tryOrNull {
|
||||||
session.thirdPartyService().getThirdPartyUser(supportedProtocolKey, fields = mapOf(
|
session.thirdPartyService().getThirdPartyUser(
|
||||||
"m.id.phone" to phoneNumber
|
protocol = supportedProtocolKey,
|
||||||
)).firstOrNull()
|
fields = mapOf("m.id.phone" to phoneNumber)
|
||||||
|
).firstOrNull()
|
||||||
} ?: throw Failure()
|
} ?: throw Failure()
|
||||||
|
|
||||||
val roomId = directRoomHelper.ensureDMExists(thirdPartyUser.userId)
|
val roomId = directRoomHelper.ensureDMExists(thirdPartyUser.userId)
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 kotlinx.coroutines.delay
|
|
||||||
import org.matrix.android.sdk.api.session.Session
|
|
||||||
import org.matrix.android.sdk.api.session.room.model.thirdparty.ThirdPartyProtocol
|
|
||||||
|
|
||||||
private const val PSTN_VECTOR_KEY = "im.vector.protocol.pstn"
|
|
||||||
private const val PSTN_MATRIX_KEY = "m.protocol.pstn"
|
|
||||||
|
|
||||||
suspend fun Session.getSupportedPSTN(maxTries: Int): String? {
|
|
||||||
val thirdPartyProtocols: Map<String, ThirdPartyProtocol> = try {
|
|
||||||
thirdPartyService().getThirdPartyProtocols()
|
|
||||||
} catch (failure: Throwable) {
|
|
||||||
if (maxTries == 1) {
|
|
||||||
return null
|
|
||||||
} 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
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -53,7 +53,6 @@ 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.CallNegotiateContent
|
import org.matrix.android.sdk.api.session.room.model.call.CallNegotiateContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.call.SdpType
|
import org.matrix.android.sdk.api.session.room.model.call.SdpType
|
||||||
import org.matrix.android.sdk.internal.util.awaitCallback
|
|
||||||
import org.threeten.bp.Duration
|
import org.threeten.bp.Duration
|
||||||
import org.webrtc.AudioSource
|
import org.webrtc.AudioSource
|
||||||
import org.webrtc.AudioTrack
|
import org.webrtc.AudioTrack
|
||||||
|
@ -420,9 +419,7 @@ class WebRtcCall(val mxCall: MxCall,
|
||||||
|
|
||||||
private suspend fun getTurnServer(): TurnServerResponse? {
|
private suspend fun getTurnServer(): TurnServerResponse? {
|
||||||
return tryOrNull {
|
return tryOrNull {
|
||||||
awaitCallback {
|
sessionProvider.get()?.callSignalingService()?.getTurnServer()
|
||||||
sessionProvider.get()?.callSignalingService()?.getTurnServer(it)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,14 +26,13 @@ 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.utils.EglUtils
|
import im.vector.app.features.call.utils.EglUtils
|
||||||
import im.vector.app.push.fcm.FcmHelper
|
import im.vector.app.push.fcm.FcmHelper
|
||||||
import kotlinx.coroutines.GlobalScope
|
|
||||||
import kotlinx.coroutines.asCoroutineDispatcher
|
import kotlinx.coroutines.asCoroutineDispatcher
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
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
|
||||||
|
@ -65,22 +64,26 @@ class WebRtcCallManager @Inject constructor(
|
||||||
private val currentSession: Session?
|
private val currentSession: Session?
|
||||||
get() = activeSessionDataSource.currentValue?.orNull()
|
get() = activeSessionDataSource.currentValue?.orNull()
|
||||||
|
|
||||||
|
private val pstnProtocolChecker: PSTNProtocolChecker?
|
||||||
|
get() = currentSession?.callSignalingService()?.getPSTNProtocolChecker()
|
||||||
|
|
||||||
interface CurrentCallListener {
|
interface CurrentCallListener {
|
||||||
fun onCurrentCallChange(call: WebRtcCall?) {}
|
fun onCurrentCallChange(call: WebRtcCall?) {}
|
||||||
fun onAudioDevicesChange() {}
|
fun onAudioDevicesChange() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PSTNSupportListener {
|
val supportedPSTNProtocol: String?
|
||||||
fun onPSTNSupportUpdated()
|
get() = pstnProtocolChecker?.supportedPSTNProtocol
|
||||||
|
|
||||||
|
val supportsPSTNProtocol: Boolean
|
||||||
|
get() = supportedPSTNProtocol != null
|
||||||
|
|
||||||
|
fun addPstnSupportListener(listener: PSTNProtocolChecker.Listener) {
|
||||||
|
pstnProtocolChecker?.addListener(listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val pstnSupportListeners = emptyList<PSTNSupportListener>().toMutableList()
|
fun removePstnSupportListener(listener: PSTNProtocolChecker.Listener) {
|
||||||
fun addPstnSupportListener(listener: PSTNSupportListener) {
|
pstnProtocolChecker?.removeListener(listener)
|
||||||
pstnSupportListeners.add(listener)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun removePstnSupportListener(listener: PSTNSupportListener) {
|
|
||||||
pstnSupportListeners.remove(listener)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private val currentCallsListeners = CopyOnWriteArrayList<CurrentCallListener>()
|
private val currentCallsListeners = CopyOnWriteArrayList<CurrentCallListener>()
|
||||||
|
@ -104,27 +107,11 @@ class WebRtcCallManager @Inject constructor(
|
||||||
private var peerConnectionFactory: PeerConnectionFactory? = null
|
private var peerConnectionFactory: PeerConnectionFactory? = null
|
||||||
private val executor = Executors.newSingleThreadExecutor()
|
private val executor = Executors.newSingleThreadExecutor()
|
||||||
private val dispatcher = executor.asCoroutineDispatcher()
|
private val dispatcher = executor.asCoroutineDispatcher()
|
||||||
var supportedPSTNProtocol: String? = null
|
|
||||||
private set
|
|
||||||
|
|
||||||
val supportsPSTNProtocol: Boolean
|
|
||||||
get() = supportedPSTNProtocol != null
|
|
||||||
|
|
||||||
private val rootEglBase by lazy { EglUtils.rootEglBase }
|
private val rootEglBase by lazy { EglUtils.rootEglBase }
|
||||||
|
|
||||||
private var isInBackground: Boolean = true
|
private var isInBackground: Boolean = true
|
||||||
|
|
||||||
init {
|
|
||||||
GlobalScope.launch {
|
|
||||||
supportedPSTNProtocol = currentSession?.getSupportedPSTN(3)
|
|
||||||
if (supportedPSTNProtocol != null) {
|
|
||||||
pstnSupportListeners.forEach {
|
|
||||||
tryOrNull { it.onPSTNSupportUpdated() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
|
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
|
||||||
fun entersForeground() {
|
fun entersForeground() {
|
||||||
isInBackground = false
|
isInBackground = false
|
||||||
|
@ -167,6 +154,10 @@ class WebRtcCallManager @Inject constructor(
|
||||||
return callsByCallId.values.toList()
|
return callsByCallId.values.toList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun checkForPSTNSupportIfNeeded() {
|
||||||
|
pstnProtocolChecker?.checkForPSTNSupportIfNeeded()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return a set of all advertised call during the lifetime of the app.
|
* @return a set of all advertised call during the lifetime of the app.
|
||||||
*/
|
*/
|
||||||
|
@ -176,7 +167,6 @@ class WebRtcCallManager @Inject constructor(
|
||||||
Timber.v("## VOIP headSetButtonTapped")
|
Timber.v("## VOIP headSetButtonTapped")
|
||||||
val call = getCurrentCall() ?: return
|
val call = getCurrentCall() ?: return
|
||||||
if (call.mxCall.state is CallState.LocalRinging) {
|
if (call.mxCall.state is CallState.LocalRinging) {
|
||||||
// accept call
|
|
||||||
call.acceptIncomingCall()
|
call.acceptIncomingCall()
|
||||||
}
|
}
|
||||||
if (call.mxCall.state is CallState.Connected) {
|
if (call.mxCall.state is CallState.Connected) {
|
||||||
|
|
|
@ -55,6 +55,7 @@ import org.matrix.android.sdk.internal.util.awaitCallback
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
import kotlin.coroutines.Continuation
|
import kotlin.coroutines.Continuation
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
|
import kotlin.coroutines.resumeWithException
|
||||||
|
|
||||||
class BootstrapSharedViewModel @AssistedInject constructor(
|
class BootstrapSharedViewModel @AssistedInject constructor(
|
||||||
@Assisted initialState: BootstrapViewState,
|
@Assisted initialState: BootstrapViewState,
|
||||||
|
@ -421,7 +422,7 @@ class BootstrapSharedViewModel @AssistedInject constructor(
|
||||||
_viewEvents.post(BootstrapViewEvents.RequestReAuth(flowResponse, errCode))
|
_viewEvents.post(BootstrapViewEvents.RequestReAuth(flowResponse, errCode))
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
promise.resumeWith(Result.failure(UnsupportedOperationException()))
|
promise.resumeWithException(UnsupportedOperationException())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,7 @@ import org.matrix.android.sdk.rx.rx
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import kotlin.coroutines.Continuation
|
import kotlin.coroutines.Continuation
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
|
import kotlin.coroutines.resumeWithException
|
||||||
|
|
||||||
class HomeActivityViewModel @AssistedInject constructor(
|
class HomeActivityViewModel @AssistedInject constructor(
|
||||||
@Assisted initialState: HomeActivityViewState,
|
@Assisted initialState: HomeActivityViewState,
|
||||||
|
@ -228,7 +229,7 @@ class HomeActivityViewModel @AssistedInject constructor(
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
promise.resumeWith(Result.failure(Exception("Cannot silently initialize cross signing, UIA missing")))
|
promise.resumeWithException(Exception("Cannot silently initialize cross signing, UIA missing"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -26,8 +26,8 @@ import com.airbnb.mvrx.ViewModelContext
|
||||||
import com.jakewharton.rxrelay2.BehaviorRelay
|
import com.jakewharton.rxrelay2.BehaviorRelay
|
||||||
import com.jakewharton.rxrelay2.PublishRelay
|
import com.jakewharton.rxrelay2.PublishRelay
|
||||||
import dagger.assisted.Assisted
|
import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedInject
|
|
||||||
import dagger.assisted.AssistedFactory
|
import dagger.assisted.AssistedFactory
|
||||||
|
import dagger.assisted.AssistedInject
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.extensions.exhaustive
|
import im.vector.app.core.extensions.exhaustive
|
||||||
import im.vector.app.core.platform.VectorViewModel
|
import im.vector.app.core.platform.VectorViewModel
|
||||||
|
@ -64,6 +64,7 @@ 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.raw.RawService
|
import org.matrix.android.sdk.api.raw.RawService
|
||||||
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
|
||||||
|
@ -120,7 +121,7 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||||
private val directRoomHelper: DirectRoomHelper,
|
private val directRoomHelper: DirectRoomHelper,
|
||||||
timelineSettingsFactory: TimelineSettingsFactory
|
timelineSettingsFactory: TimelineSettingsFactory
|
||||||
) : VectorViewModel<RoomDetailViewState, RoomDetailAction, RoomDetailViewEvents>(initialState),
|
) : VectorViewModel<RoomDetailViewState, RoomDetailAction, RoomDetailViewEvents>(initialState),
|
||||||
Timeline.Listener, ChatEffectManager.Delegate, WebRtcCallManager.PSTNSupportListener {
|
Timeline.Listener, ChatEffectManager.Delegate, PSTNProtocolChecker.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
|
||||||
|
@ -176,6 +177,7 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||||
// Inform the SDK that the room is displayed
|
// Inform the SDK that the room is displayed
|
||||||
session.onRoomDisplayed(initialState.roomId)
|
session.onRoomDisplayed(initialState.roomId)
|
||||||
callManager.addPstnSupportListener(this)
|
callManager.addPstnSupportListener(this)
|
||||||
|
callManager.checkForPSTNSupportIfNeeded()
|
||||||
chatEffectManager.delegate = this
|
chatEffectManager.delegate = this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,65 +233,65 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||||
|
|
||||||
override fun handle(action: RoomDetailAction) {
|
override fun handle(action: RoomDetailAction) {
|
||||||
when (action) {
|
when (action) {
|
||||||
is RoomDetailAction.UserIsTyping -> handleUserIsTyping(action)
|
is RoomDetailAction.UserIsTyping -> handleUserIsTyping(action)
|
||||||
is RoomDetailAction.SaveDraft -> handleSaveDraft(action)
|
is RoomDetailAction.SaveDraft -> handleSaveDraft(action)
|
||||||
is RoomDetailAction.SendMessage -> handleSendMessage(action)
|
is RoomDetailAction.SendMessage -> handleSendMessage(action)
|
||||||
is RoomDetailAction.SendMedia -> handleSendMedia(action)
|
is RoomDetailAction.SendMedia -> handleSendMedia(action)
|
||||||
is RoomDetailAction.SendSticker -> handleSendSticker(action)
|
is RoomDetailAction.SendSticker -> handleSendSticker(action)
|
||||||
is RoomDetailAction.TimelineEventTurnsVisible -> handleEventVisible(action)
|
is RoomDetailAction.TimelineEventTurnsVisible -> handleEventVisible(action)
|
||||||
is RoomDetailAction.TimelineEventTurnsInvisible -> handleEventInvisible(action)
|
is RoomDetailAction.TimelineEventTurnsInvisible -> handleEventInvisible(action)
|
||||||
is RoomDetailAction.LoadMoreTimelineEvents -> handleLoadMore(action)
|
is RoomDetailAction.LoadMoreTimelineEvents -> handleLoadMore(action)
|
||||||
is RoomDetailAction.SendReaction -> handleSendReaction(action)
|
is RoomDetailAction.SendReaction -> handleSendReaction(action)
|
||||||
is RoomDetailAction.AcceptInvite -> handleAcceptInvite()
|
is RoomDetailAction.AcceptInvite -> handleAcceptInvite()
|
||||||
is RoomDetailAction.RejectInvite -> handleRejectInvite()
|
is RoomDetailAction.RejectInvite -> handleRejectInvite()
|
||||||
is RoomDetailAction.RedactAction -> handleRedactEvent(action)
|
is RoomDetailAction.RedactAction -> handleRedactEvent(action)
|
||||||
is RoomDetailAction.UndoReaction -> handleUndoReact(action)
|
is RoomDetailAction.UndoReaction -> handleUndoReact(action)
|
||||||
is RoomDetailAction.UpdateQuickReactAction -> handleUpdateQuickReaction(action)
|
is RoomDetailAction.UpdateQuickReactAction -> handleUpdateQuickReaction(action)
|
||||||
is RoomDetailAction.EnterRegularMode -> handleEnterRegularMode(action)
|
is RoomDetailAction.EnterRegularMode -> handleEnterRegularMode(action)
|
||||||
is RoomDetailAction.EnterEditMode -> handleEditAction(action)
|
is RoomDetailAction.EnterEditMode -> handleEditAction(action)
|
||||||
is RoomDetailAction.EnterQuoteMode -> handleQuoteAction(action)
|
is RoomDetailAction.EnterQuoteMode -> handleQuoteAction(action)
|
||||||
is RoomDetailAction.EnterReplyMode -> handleReplyAction(action)
|
is RoomDetailAction.EnterReplyMode -> handleReplyAction(action)
|
||||||
is RoomDetailAction.DownloadOrOpen -> handleOpenOrDownloadFile(action)
|
is RoomDetailAction.DownloadOrOpen -> handleOpenOrDownloadFile(action)
|
||||||
is RoomDetailAction.NavigateToEvent -> handleNavigateToEvent(action)
|
is RoomDetailAction.NavigateToEvent -> handleNavigateToEvent(action)
|
||||||
is RoomDetailAction.HandleTombstoneEvent -> handleTombstoneEvent(action)
|
is RoomDetailAction.HandleTombstoneEvent -> handleTombstoneEvent(action)
|
||||||
is RoomDetailAction.ResendMessage -> handleResendEvent(action)
|
is RoomDetailAction.ResendMessage -> handleResendEvent(action)
|
||||||
is RoomDetailAction.RemoveFailedEcho -> handleRemove(action)
|
is RoomDetailAction.RemoveFailedEcho -> handleRemove(action)
|
||||||
is RoomDetailAction.ResendAll -> handleResendAll()
|
is RoomDetailAction.ResendAll -> handleResendAll()
|
||||||
is RoomDetailAction.MarkAllAsRead -> handleMarkAllAsRead()
|
is RoomDetailAction.MarkAllAsRead -> handleMarkAllAsRead()
|
||||||
is RoomDetailAction.ReportContent -> handleReportContent(action)
|
is RoomDetailAction.ReportContent -> handleReportContent(action)
|
||||||
is RoomDetailAction.IgnoreUser -> handleIgnoreUser(action)
|
is RoomDetailAction.IgnoreUser -> handleIgnoreUser(action)
|
||||||
is RoomDetailAction.EnterTrackingUnreadMessagesState -> startTrackingUnreadMessages()
|
is RoomDetailAction.EnterTrackingUnreadMessagesState -> startTrackingUnreadMessages()
|
||||||
is RoomDetailAction.ExitTrackingUnreadMessagesState -> stopTrackingUnreadMessages()
|
is RoomDetailAction.ExitTrackingUnreadMessagesState -> stopTrackingUnreadMessages()
|
||||||
is RoomDetailAction.ReplyToOptions -> handleReplyToOptions(action)
|
is RoomDetailAction.ReplyToOptions -> handleReplyToOptions(action)
|
||||||
is RoomDetailAction.AcceptVerificationRequest -> handleAcceptVerification(action)
|
is RoomDetailAction.AcceptVerificationRequest -> handleAcceptVerification(action)
|
||||||
is RoomDetailAction.DeclineVerificationRequest -> handleDeclineVerification(action)
|
is RoomDetailAction.DeclineVerificationRequest -> handleDeclineVerification(action)
|
||||||
is RoomDetailAction.RequestVerification -> handleRequestVerification(action)
|
is RoomDetailAction.RequestVerification -> handleRequestVerification(action)
|
||||||
is RoomDetailAction.ResumeVerification -> handleResumeRequestVerification(action)
|
is RoomDetailAction.ResumeVerification -> handleResumeRequestVerification(action)
|
||||||
is RoomDetailAction.ReRequestKeys -> handleReRequestKeys(action)
|
is RoomDetailAction.ReRequestKeys -> handleReRequestKeys(action)
|
||||||
is RoomDetailAction.TapOnFailedToDecrypt -> handleTapOnFailedToDecrypt(action)
|
is RoomDetailAction.TapOnFailedToDecrypt -> handleTapOnFailedToDecrypt(action)
|
||||||
is RoomDetailAction.SelectStickerAttachment -> handleSelectStickerAttachment()
|
is RoomDetailAction.SelectStickerAttachment -> handleSelectStickerAttachment()
|
||||||
is RoomDetailAction.OpenIntegrationManager -> handleOpenIntegrationManager()
|
is RoomDetailAction.OpenIntegrationManager -> handleOpenIntegrationManager()
|
||||||
is RoomDetailAction.StartCallWithPhoneNumber -> handleStartCallWithPhoneNumber(action)
|
is RoomDetailAction.StartCallWithPhoneNumber -> handleStartCallWithPhoneNumber(action)
|
||||||
is RoomDetailAction.StartCall -> handleStartCall(action)
|
is RoomDetailAction.StartCall -> handleStartCall(action)
|
||||||
is RoomDetailAction.AcceptCall -> handleAcceptCall(action)
|
is RoomDetailAction.AcceptCall -> handleAcceptCall(action)
|
||||||
is RoomDetailAction.EndCall -> handleEndCall()
|
is RoomDetailAction.EndCall -> handleEndCall()
|
||||||
is RoomDetailAction.ManageIntegrations -> handleManageIntegrations()
|
is RoomDetailAction.ManageIntegrations -> handleManageIntegrations()
|
||||||
is RoomDetailAction.AddJitsiWidget -> handleAddJitsiConference(action)
|
is RoomDetailAction.AddJitsiWidget -> handleAddJitsiConference(action)
|
||||||
is RoomDetailAction.RemoveWidget -> handleDeleteWidget(action.widgetId)
|
is RoomDetailAction.RemoveWidget -> handleDeleteWidget(action.widgetId)
|
||||||
is RoomDetailAction.EnsureNativeWidgetAllowed -> handleCheckWidgetAllowed(action)
|
is RoomDetailAction.EnsureNativeWidgetAllowed -> handleCheckWidgetAllowed(action)
|
||||||
is RoomDetailAction.CancelSend -> handleCancel(action)
|
is RoomDetailAction.CancelSend -> handleCancel(action)
|
||||||
is RoomDetailAction.OpenOrCreateDm -> handleOpenOrCreateDm(action)
|
is RoomDetailAction.OpenOrCreateDm -> handleOpenOrCreateDm(action)
|
||||||
is RoomDetailAction.JumpToReadReceipt -> handleJumpToReadReceipt(action)
|
is RoomDetailAction.JumpToReadReceipt -> handleJumpToReadReceipt(action)
|
||||||
RoomDetailAction.QuickActionInvitePeople -> handleInvitePeople()
|
RoomDetailAction.QuickActionInvitePeople -> handleInvitePeople()
|
||||||
RoomDetailAction.QuickActionSetAvatar -> handleQuickSetAvatar()
|
RoomDetailAction.QuickActionSetAvatar -> handleQuickSetAvatar()
|
||||||
is RoomDetailAction.SetAvatarAction -> handleSetNewAvatar(action)
|
is RoomDetailAction.SetAvatarAction -> handleSetNewAvatar(action)
|
||||||
RoomDetailAction.QuickActionSetTopic -> _viewEvents.post(RoomDetailViewEvents.OpenRoomSettings)
|
RoomDetailAction.QuickActionSetTopic -> _viewEvents.post(RoomDetailViewEvents.OpenRoomSettings)
|
||||||
is RoomDetailAction.ShowRoomAvatarFullScreen -> {
|
is RoomDetailAction.ShowRoomAvatarFullScreen -> {
|
||||||
_viewEvents.post(
|
_viewEvents.post(
|
||||||
RoomDetailViewEvents.ShowRoomAvatarFullScreen(action.matrixItem, action.transitionView)
|
RoomDetailViewEvents.ShowRoomAvatarFullScreen(action.matrixItem, action.transitionView)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is RoomDetailAction.DoNotShowPreviewUrlFor -> handleDoNotShowPreviewUrlFor(action)
|
is RoomDetailAction.DoNotShowPreviewUrlFor -> handleDoNotShowPreviewUrlFor(action)
|
||||||
}.exhaustive
|
}.exhaustive
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -618,10 +620,10 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||||
return@withState false
|
return@withState false
|
||||||
}
|
}
|
||||||
when (itemId) {
|
when (itemId) {
|
||||||
R.id.resend_all -> state.asyncRoomSummary()?.hasFailedSending == true
|
R.id.resend_all -> state.asyncRoomSummary()?.hasFailedSending == true
|
||||||
R.id.timeline_setting -> true
|
R.id.timeline_setting -> true
|
||||||
R.id.invite -> state.canInvite
|
R.id.invite -> state.canInvite
|
||||||
R.id.clear_all -> state.asyncRoomSummary()?.hasFailedSending == true
|
R.id.clear_all -> state.asyncRoomSummary()?.hasFailedSending == true
|
||||||
R.id.open_matrix_apps -> true
|
R.id.open_matrix_apps -> true
|
||||||
R.id.voice_call,
|
R.id.voice_call,
|
||||||
R.id.video_call -> callManager.getCallsByRoomId(state.roomId).isEmpty()
|
R.id.video_call -> callManager.getCallsByRoomId(state.roomId).isEmpty()
|
||||||
|
@ -741,7 +743,7 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||||
_viewEvents.post(RoomDetailViewEvents.SlashCommandHandled())
|
_viewEvents.post(RoomDetailViewEvents.SlashCommandHandled())
|
||||||
popDraft()
|
popDraft()
|
||||||
}
|
}
|
||||||
is ParsedCommand.SendChatEffect -> {
|
is ParsedCommand.SendChatEffect -> {
|
||||||
sendChatEffect(slashCommandResult)
|
sendChatEffect(slashCommandResult)
|
||||||
_viewEvents.post(RoomDetailViewEvents.SlashCommandHandled())
|
_viewEvents.post(RoomDetailViewEvents.SlashCommandHandled())
|
||||||
popDraft()
|
popDraft()
|
||||||
|
@ -774,7 +776,7 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
}.exhaustive
|
}.exhaustive
|
||||||
}
|
}
|
||||||
is SendMode.EDIT -> {
|
is SendMode.EDIT -> {
|
||||||
// is original event a reply?
|
// is original event a reply?
|
||||||
val inReplyTo = state.sendMode.timelineEvent.getRelationContent()?.inReplyTo?.eventId
|
val inReplyTo = state.sendMode.timelineEvent.getRelationContent()?.inReplyTo?.eventId
|
||||||
if (inReplyTo != null) {
|
if (inReplyTo != null) {
|
||||||
|
@ -799,7 +801,7 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||||
_viewEvents.post(RoomDetailViewEvents.MessageSent)
|
_viewEvents.post(RoomDetailViewEvents.MessageSent)
|
||||||
popDraft()
|
popDraft()
|
||||||
}
|
}
|
||||||
is SendMode.QUOTE -> {
|
is SendMode.QUOTE -> {
|
||||||
val messageContent: MessageContent? =
|
val messageContent: MessageContent? =
|
||||||
state.sendMode.timelineEvent.annotations?.editSummary?.aggregatedContent.toModel()
|
state.sendMode.timelineEvent.annotations?.editSummary?.aggregatedContent.toModel()
|
||||||
?: state.sendMode.timelineEvent.root.getClearContent().toModel()
|
?: state.sendMode.timelineEvent.root.getClearContent().toModel()
|
||||||
|
@ -822,7 +824,7 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||||
_viewEvents.post(RoomDetailViewEvents.MessageSent)
|
_viewEvents.post(RoomDetailViewEvents.MessageSent)
|
||||||
popDraft()
|
popDraft()
|
||||||
}
|
}
|
||||||
is SendMode.REPLY -> {
|
is SendMode.REPLY -> {
|
||||||
state.sendMode.timelineEvent.let {
|
state.sendMode.timelineEvent.let {
|
||||||
room.replyToMessage(it, action.text.toString(), action.autoMarkdown)
|
room.replyToMessage(it, action.text.toString(), action.autoMarkdown)
|
||||||
_viewEvents.post(RoomDetailViewEvents.MessageSent)
|
_viewEvents.post(RoomDetailViewEvents.MessageSent)
|
||||||
|
@ -1441,7 +1443,7 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPSTNSupportUpdated() {
|
override fun onPSTNSupportUpdated() {
|
||||||
updateShowDialerOptionState()
|
updateShowDialerOptionState()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateShowDialerOptionState() {
|
private fun updateShowDialerOptionState() {
|
||||||
|
|
|
@ -19,7 +19,7 @@ package im.vector.app.features.home.room.detail
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import im.vector.app.core.platform.DefaultListUpdateCallback
|
import im.vector.app.core.platform.DefaultListUpdateCallback
|
||||||
import im.vector.app.features.home.room.detail.timeline.TimelineEventController
|
import im.vector.app.features.home.room.detail.timeline.TimelineEventController
|
||||||
import im.vector.app.features.home.room.detail.timeline.item.BaseEventItem
|
import im.vector.app.features.home.room.detail.timeline.item.ItemWithEvents
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.concurrent.CopyOnWriteArrayList
|
import java.util.concurrent.CopyOnWriteArrayList
|
||||||
|
|
||||||
|
@ -47,8 +47,8 @@ class ScrollOnNewMessageCallback(private val layoutManager: LinearLayoutManager,
|
||||||
if (layoutManager.findFirstVisibleItemPosition() != position) {
|
if (layoutManager.findFirstVisibleItemPosition() != position) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val firstNewItem = timelineEventController.adapter.getModelAtPosition(position) as? BaseEventItem ?: return
|
val firstNewItem = timelineEventController.adapter.getModelAtPosition(position) as? ItemWithEvents ?: return
|
||||||
val firstNewItemIds = firstNewItem.getEventIds().firstOrNull()
|
val firstNewItemIds = firstNewItem.getEventIds().firstOrNull() ?: return
|
||||||
val indexOfFirstNewItem = newTimelineEventIds.indexOf(firstNewItemIds)
|
val indexOfFirstNewItem = newTimelineEventIds.indexOf(firstNewItemIds)
|
||||||
if (indexOfFirstNewItem != -1) {
|
if (indexOfFirstNewItem != -1) {
|
||||||
Timber.v("Should scroll to position: $position")
|
Timber.v("Should scroll to position: $position")
|
||||||
|
|
|
@ -38,18 +38,15 @@ import im.vector.app.features.home.room.detail.timeline.factory.MergedHeaderItem
|
||||||
import im.vector.app.features.home.room.detail.timeline.factory.TimelineItemFactory
|
import im.vector.app.features.home.room.detail.timeline.factory.TimelineItemFactory
|
||||||
import im.vector.app.features.home.room.detail.timeline.helper.ContentDownloadStateTrackerBinder
|
import im.vector.app.features.home.room.detail.timeline.helper.ContentDownloadStateTrackerBinder
|
||||||
import im.vector.app.features.home.room.detail.timeline.helper.ContentUploadStateTrackerBinder
|
import im.vector.app.features.home.room.detail.timeline.helper.ContentUploadStateTrackerBinder
|
||||||
import im.vector.app.features.home.room.detail.timeline.helper.ReadMarkerVisibilityStateChangedListener
|
import im.vector.app.features.home.room.detail.timeline.helper.TimelineControllerInterceptorHelper
|
||||||
import im.vector.app.features.home.room.detail.timeline.helper.TimelineEventDiffUtilCallback
|
import im.vector.app.features.home.room.detail.timeline.helper.TimelineEventDiffUtilCallback
|
||||||
import im.vector.app.features.home.room.detail.timeline.helper.TimelineEventVisibilityStateChangedListener
|
import im.vector.app.features.home.room.detail.timeline.helper.TimelineEventVisibilityStateChangedListener
|
||||||
import im.vector.app.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider
|
import im.vector.app.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider
|
||||||
import im.vector.app.features.home.room.detail.timeline.item.BaseEventItem
|
|
||||||
import im.vector.app.features.home.room.detail.timeline.item.BasedMergedItem
|
import im.vector.app.features.home.room.detail.timeline.item.BasedMergedItem
|
||||||
import im.vector.app.features.home.room.detail.timeline.item.CallTileTimelineItem
|
|
||||||
import im.vector.app.features.home.room.detail.timeline.item.DaySeparatorItem
|
import im.vector.app.features.home.room.detail.timeline.item.DaySeparatorItem
|
||||||
import im.vector.app.features.home.room.detail.timeline.item.DaySeparatorItem_
|
import im.vector.app.features.home.room.detail.timeline.item.DaySeparatorItem_
|
||||||
import im.vector.app.features.home.room.detail.timeline.item.MessageInformationData
|
import im.vector.app.features.home.room.detail.timeline.item.MessageInformationData
|
||||||
import im.vector.app.features.home.room.detail.timeline.item.ReadReceiptData
|
import im.vector.app.features.home.room.detail.timeline.item.ReadReceiptData
|
||||||
import im.vector.app.features.home.room.detail.timeline.item.TimelineReadMarkerItem_
|
|
||||||
import im.vector.app.features.home.room.detail.timeline.url.PreviewUrlRetriever
|
import im.vector.app.features.home.room.detail.timeline.url.PreviewUrlRetriever
|
||||||
import im.vector.app.features.media.ImageContentRenderer
|
import im.vector.app.features.media.ImageContentRenderer
|
||||||
import im.vector.app.features.media.VideoContentRenderer
|
import im.vector.app.features.media.VideoContentRenderer
|
||||||
|
@ -194,75 +191,20 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val interceptorHelper = TimelineControllerInterceptorHelper(
|
||||||
|
::positionOfReadMarker,
|
||||||
|
adapterPositionMapping,
|
||||||
|
vectorPreferences,
|
||||||
|
callManager
|
||||||
|
)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
addInterceptor(this)
|
addInterceptor(this)
|
||||||
requestModelBuild()
|
requestModelBuild()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update position when we are building new items
|
|
||||||
override fun intercept(models: MutableList<EpoxyModel<*>>) = synchronized(modelCache) {
|
override fun intercept(models: MutableList<EpoxyModel<*>>) = synchronized(modelCache) {
|
||||||
positionOfReadMarker = null
|
interceptorHelper.intercept(models, unreadState, timeline, callback)
|
||||||
adapterPositionMapping.clear()
|
|
||||||
val callIds = mutableSetOf<String>()
|
|
||||||
val modelsIterator = models.listIterator()
|
|
||||||
val showHiddenEvents = vectorPreferences.shouldShowHiddenEvents()
|
|
||||||
modelsIterator.withIndex().forEach {
|
|
||||||
val index = it.index
|
|
||||||
val epoxyModel = it.value
|
|
||||||
if (epoxyModel is CallTileTimelineItem) {
|
|
||||||
val callId = epoxyModel.attributes.callId
|
|
||||||
// We should remove the call tile if we already have one for this call or
|
|
||||||
// if this is an active call tile without an actual call (which can happen with permalink)
|
|
||||||
val shouldRemoveCallItem = callIds.contains(callId)
|
|
||||||
|| (!callManager.getAdvertisedCalls().contains(callId) && epoxyModel.attributes.callStatus.isActive())
|
|
||||||
if (shouldRemoveCallItem && !showHiddenEvents) {
|
|
||||||
modelsIterator.remove()
|
|
||||||
return@forEach
|
|
||||||
}
|
|
||||||
callIds.add(callId)
|
|
||||||
}
|
|
||||||
if (epoxyModel is BaseEventItem) {
|
|
||||||
epoxyModel.getEventIds().forEach { eventId ->
|
|
||||||
adapterPositionMapping[eventId] = index
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val currentUnreadState = this.unreadState
|
|
||||||
if (currentUnreadState is UnreadState.HasUnread) {
|
|
||||||
val position = adapterPositionMapping[currentUnreadState.firstUnreadEventId]?.plus(1)
|
|
||||||
positionOfReadMarker = position
|
|
||||||
if (position != null) {
|
|
||||||
val readMarker = TimelineReadMarkerItem_()
|
|
||||||
.also {
|
|
||||||
it.id("read_marker")
|
|
||||||
it.setOnVisibilityStateChanged(ReadMarkerVisibilityStateChangedListener(callback))
|
|
||||||
}
|
|
||||||
models.add(position, readMarker)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val shouldAddBackwardPrefetch = timeline?.hasMoreToLoad(Timeline.Direction.BACKWARDS) ?: false
|
|
||||||
if (shouldAddBackwardPrefetch) {
|
|
||||||
val indexOfPrefetchBackward = (previousModelsSize - 1)
|
|
||||||
.coerceAtMost(models.size - DEFAULT_PREFETCH_THRESHOLD)
|
|
||||||
.coerceAtLeast(0)
|
|
||||||
|
|
||||||
val loadingItem = LoadingItem_()
|
|
||||||
.id("prefetch_backward_loading${System.currentTimeMillis()}")
|
|
||||||
.showLoader(false)
|
|
||||||
.setVisibilityStateChangedListener(Timeline.Direction.BACKWARDS)
|
|
||||||
|
|
||||||
models.add(indexOfPrefetchBackward, loadingItem)
|
|
||||||
}
|
|
||||||
val shouldAddForwardPrefetch = timeline?.hasMoreToLoad(Timeline.Direction.FORWARDS) ?: false
|
|
||||||
if (shouldAddForwardPrefetch) {
|
|
||||||
val indexOfPrefetchForward = DEFAULT_PREFETCH_THRESHOLD.coerceAtMost(models.size - 1)
|
|
||||||
val loadingItem = LoadingItem_()
|
|
||||||
.id("prefetch_forward_loading${System.currentTimeMillis()}")
|
|
||||||
.showLoader(false)
|
|
||||||
.setVisibilityStateChangedListener(Timeline.Direction.FORWARDS)
|
|
||||||
models.add(indexOfPrefetchForward, loadingItem)
|
|
||||||
}
|
|
||||||
previousModelsSize = models.size
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun update(viewState: RoomDetailViewState) {
|
fun update(viewState: RoomDetailViewState) {
|
||||||
|
@ -431,6 +373,14 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun LoadingItem_.setVisibilityStateChangedListener(direction: Timeline.Direction): LoadingItem_ {
|
||||||
|
return onVisibilityStateChanged { _, _, visibilityState ->
|
||||||
|
if (visibilityState == VisibilityState.VISIBLE) {
|
||||||
|
callback?.onLoadMore(direction)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun updateUTDStates(event: TimelineEvent, nextEvent: TimelineEvent?) {
|
private fun updateUTDStates(event: TimelineEvent, nextEvent: TimelineEvent?) {
|
||||||
if (vectorPreferences.labShowCompleteHistoryInEncryptedRoom()) {
|
if (vectorPreferences.labShowCompleteHistoryInEncryptedRoom()) {
|
||||||
return
|
return
|
||||||
|
@ -461,14 +411,6 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
|
||||||
return shouldAdd
|
return shouldAdd
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun LoadingItem_.setVisibilityStateChangedListener(direction: Timeline.Direction): LoadingItem_ {
|
|
||||||
return onVisibilityStateChanged { _, _, visibilityState ->
|
|
||||||
if (visibilityState == VisibilityState.VISIBLE) {
|
|
||||||
callback?.onLoadMore(direction)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun searchPositionOfEvent(eventId: String?): Int? = synchronized(modelCache) {
|
fun searchPositionOfEvent(eventId: String?): Int? = synchronized(modelCache) {
|
||||||
return adapterPositionMapping[eventId]
|
return adapterPositionMapping[eventId]
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,8 @@
|
||||||
|
|
||||||
package im.vector.app.features.home.room.detail.timeline.factory
|
package im.vector.app.features.home.room.detail.timeline.factory
|
||||||
|
|
||||||
import im.vector.app.core.epoxy.EmptyItem_
|
import im.vector.app.core.epoxy.TimelineEmptyItem
|
||||||
|
import im.vector.app.core.epoxy.TimelineEmptyItem_
|
||||||
import im.vector.app.core.epoxy.VectorEpoxyModel
|
import im.vector.app.core.epoxy.VectorEpoxyModel
|
||||||
import im.vector.app.core.resources.UserPreferencesProvider
|
import im.vector.app.core.resources.UserPreferencesProvider
|
||||||
import im.vector.app.features.home.room.detail.timeline.TimelineEventController
|
import im.vector.app.features.home.room.detail.timeline.TimelineEventController
|
||||||
|
@ -114,6 +115,12 @@ class TimelineItemFactory @Inject constructor(private val messageItemFactory: Me
|
||||||
Timber.e(throwable, "failed to create message item")
|
Timber.e(throwable, "failed to create message item")
|
||||||
defaultItemFactory.create(event, highlight, callback, throwable)
|
defaultItemFactory.create(event, highlight, callback, throwable)
|
||||||
}
|
}
|
||||||
return (computedModel ?: EmptyItem_())
|
return computedModel ?: buildEmptyItem(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildEmptyItem(timelineEvent: TimelineEvent): TimelineEmptyItem {
|
||||||
|
return TimelineEmptyItem_()
|
||||||
|
.id(timelineEvent.localId)
|
||||||
|
.eventId(timelineEvent.eventId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,151 @@
|
||||||
|
/*
|
||||||
|
* 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.home.room.detail.timeline.helper
|
||||||
|
|
||||||
|
import com.airbnb.epoxy.EpoxyModel
|
||||||
|
import com.airbnb.epoxy.VisibilityState
|
||||||
|
import im.vector.app.core.epoxy.LoadingItem_
|
||||||
|
import im.vector.app.core.epoxy.TimelineEmptyItem_
|
||||||
|
import im.vector.app.features.call.webrtc.WebRtcCallManager
|
||||||
|
import im.vector.app.features.home.room.detail.UnreadState
|
||||||
|
import im.vector.app.features.home.room.detail.timeline.TimelineEventController
|
||||||
|
import im.vector.app.features.home.room.detail.timeline.item.CallTileTimelineItem
|
||||||
|
import im.vector.app.features.home.room.detail.timeline.item.ItemWithEvents
|
||||||
|
import im.vector.app.features.home.room.detail.timeline.item.TimelineReadMarkerItem_
|
||||||
|
import im.vector.app.features.settings.VectorPreferences
|
||||||
|
import org.matrix.android.sdk.api.session.room.timeline.Timeline
|
||||||
|
import kotlin.reflect.KMutableProperty0
|
||||||
|
|
||||||
|
private const val DEFAULT_PREFETCH_THRESHOLD = 30
|
||||||
|
|
||||||
|
class TimelineControllerInterceptorHelper(private val positionOfReadMarker: KMutableProperty0<Int?>,
|
||||||
|
private val adapterPositionMapping: MutableMap<String, Int>,
|
||||||
|
private val vectorPreferences: VectorPreferences,
|
||||||
|
private val callManager: WebRtcCallManager
|
||||||
|
) {
|
||||||
|
|
||||||
|
private var previousModelsSize = 0
|
||||||
|
|
||||||
|
// Update position when we are building new items
|
||||||
|
fun intercept(
|
||||||
|
models: MutableList<EpoxyModel<*>>,
|
||||||
|
unreadState: UnreadState,
|
||||||
|
timeline: Timeline?,
|
||||||
|
callback: TimelineEventController.Callback?
|
||||||
|
) {
|
||||||
|
positionOfReadMarker.set(null)
|
||||||
|
adapterPositionMapping.clear()
|
||||||
|
val callIds = mutableSetOf<String>()
|
||||||
|
|
||||||
|
// Add some prefetch loader if needed
|
||||||
|
models.addBackwardPrefetchIfNeeded(timeline, callback)
|
||||||
|
models.addForwardPrefetchIfNeeded(timeline, callback)
|
||||||
|
|
||||||
|
val modelsIterator = models.listIterator()
|
||||||
|
val showHiddenEvents = vectorPreferences.shouldShowHiddenEvents()
|
||||||
|
var index = 0
|
||||||
|
val firstUnreadEventId = (unreadState as? UnreadState.HasUnread)?.firstUnreadEventId
|
||||||
|
// Then iterate on models so we have the exact positions in the adapter
|
||||||
|
modelsIterator.forEach { epoxyModel ->
|
||||||
|
if (epoxyModel is ItemWithEvents) {
|
||||||
|
epoxyModel.getEventIds().forEach { eventId ->
|
||||||
|
adapterPositionMapping[eventId] = index
|
||||||
|
if (eventId == firstUnreadEventId) {
|
||||||
|
modelsIterator.addReadMarkerItem(callback)
|
||||||
|
index++
|
||||||
|
positionOfReadMarker.set(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (epoxyModel is CallTileTimelineItem) {
|
||||||
|
modelsIterator.removeCallItemIfNeeded(epoxyModel, callIds, showHiddenEvents)
|
||||||
|
}
|
||||||
|
index++
|
||||||
|
}
|
||||||
|
previousModelsSize = models.size
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun MutableListIterator<EpoxyModel<*>>.addReadMarkerItem(callback: TimelineEventController.Callback?) {
|
||||||
|
val readMarker = TimelineReadMarkerItem_()
|
||||||
|
.also {
|
||||||
|
it.id("read_marker")
|
||||||
|
it.setOnVisibilityStateChanged(ReadMarkerVisibilityStateChangedListener(callback))
|
||||||
|
}
|
||||||
|
add(readMarker)
|
||||||
|
// Use next as we still have some process to do before the next iterator loop
|
||||||
|
next()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun MutableListIterator<EpoxyModel<*>>.removeCallItemIfNeeded(
|
||||||
|
epoxyModel: CallTileTimelineItem,
|
||||||
|
callIds: MutableSet<String>,
|
||||||
|
showHiddenEvents: Boolean
|
||||||
|
) {
|
||||||
|
val callId = epoxyModel.attributes.callId
|
||||||
|
// We should remove the call tile if we already have one for this call or
|
||||||
|
// if this is an active call tile without an actual call (which can happen with permalink)
|
||||||
|
val shouldRemoveCallItem = callIds.contains(callId)
|
||||||
|
|| (!callManager.getAdvertisedCalls().contains(callId) && epoxyModel.attributes.callStatus.isActive())
|
||||||
|
if (shouldRemoveCallItem && !showHiddenEvents) {
|
||||||
|
remove()
|
||||||
|
val emptyItem = TimelineEmptyItem_()
|
||||||
|
.id(epoxyModel.id())
|
||||||
|
.eventId(epoxyModel.attributes.informationData.eventId)
|
||||||
|
add(emptyItem)
|
||||||
|
}
|
||||||
|
callIds.add(callId)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun MutableList<EpoxyModel<*>>.addBackwardPrefetchIfNeeded(timeline: Timeline?, callback: TimelineEventController.Callback?) {
|
||||||
|
val shouldAddBackwardPrefetch = timeline?.hasMoreToLoad(Timeline.Direction.BACKWARDS) ?: false
|
||||||
|
if (shouldAddBackwardPrefetch) {
|
||||||
|
val indexOfPrefetchBackward = (previousModelsSize - 1)
|
||||||
|
.coerceAtMost(size - DEFAULT_PREFETCH_THRESHOLD)
|
||||||
|
.coerceAtLeast(0)
|
||||||
|
|
||||||
|
val loadingItem = LoadingItem_()
|
||||||
|
.id("prefetch_backward_loading${System.currentTimeMillis()}")
|
||||||
|
.showLoader(false)
|
||||||
|
.setVisibilityStateChangedListener(Timeline.Direction.BACKWARDS, callback)
|
||||||
|
|
||||||
|
add(indexOfPrefetchBackward, loadingItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun MutableList<EpoxyModel<*>>.addForwardPrefetchIfNeeded(timeline: Timeline?, callback: TimelineEventController.Callback?) {
|
||||||
|
val shouldAddForwardPrefetch = timeline?.hasMoreToLoad(Timeline.Direction.FORWARDS) ?: false
|
||||||
|
if (shouldAddForwardPrefetch) {
|
||||||
|
val indexOfPrefetchForward = DEFAULT_PREFETCH_THRESHOLD.coerceAtMost(size - 1)
|
||||||
|
val loadingItem = LoadingItem_()
|
||||||
|
.id("prefetch_forward_loading${System.currentTimeMillis()}")
|
||||||
|
.showLoader(false)
|
||||||
|
.setVisibilityStateChangedListener(Timeline.Direction.FORWARDS, callback)
|
||||||
|
add(indexOfPrefetchForward, loadingItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun LoadingItem_.setVisibilityStateChangedListener(
|
||||||
|
direction: Timeline.Direction,
|
||||||
|
callback: TimelineEventController.Callback?
|
||||||
|
): LoadingItem_ {
|
||||||
|
return onVisibilityStateChanged { _, _, visibilityState ->
|
||||||
|
if (visibilityState == VisibilityState.VISIBLE) {
|
||||||
|
callback?.onLoadMore(direction)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,7 +32,7 @@ import im.vector.app.core.utils.DimensionConverter
|
||||||
/**
|
/**
|
||||||
* Children must override getViewType()
|
* Children must override getViewType()
|
||||||
*/
|
*/
|
||||||
abstract class BaseEventItem<H : BaseEventItem.BaseHolder> : VectorEpoxyModel<H>() {
|
abstract class BaseEventItem<H : BaseEventItem.BaseHolder> : VectorEpoxyModel<H>(), ItemWithEvents {
|
||||||
|
|
||||||
// To use for instance when opening a permalink with an eventId
|
// To use for instance when opening a permalink with an eventId
|
||||||
@EpoxyAttribute
|
@EpoxyAttribute
|
||||||
|
@ -53,12 +53,6 @@ abstract class BaseEventItem<H : BaseEventItem.BaseHolder> : VectorEpoxyModel<H>
|
||||||
holder.checkableBackground.isChecked = highlighted
|
holder.checkableBackground.isChecked = highlighted
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the eventIds associated with the EventItem.
|
|
||||||
* Will generally get only one, but it handles the merging items.
|
|
||||||
*/
|
|
||||||
abstract fun getEventIds(): List<String>
|
|
||||||
|
|
||||||
abstract class BaseHolder(@IdRes val stubId: Int) : VectorEpoxyHolder() {
|
abstract class BaseHolder(@IdRes val stubId: Int) : VectorEpoxyHolder() {
|
||||||
val leftGuideline by bind<View>(R.id.messageStartGuideline)
|
val leftGuideline by bind<View>(R.id.messageStartGuideline)
|
||||||
val checkableBackground by bind<CheckableView>(R.id.messageSelectedBackground)
|
val checkableBackground by bind<CheckableView>(R.id.messageSelectedBackground)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2019 New Vector Ltd
|
* Copyright (c) 2021 New Vector Ltd
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -14,12 +14,12 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package im.vector.app.core.epoxy
|
package im.vector.app.features.home.room.detail.timeline.item
|
||||||
|
|
||||||
import com.airbnb.epoxy.EpoxyModelClass
|
interface ItemWithEvents {
|
||||||
import im.vector.app.R
|
/**
|
||||||
|
* Returns the eventIds associated with the EventItem.
|
||||||
@EpoxyModelClass(layout = R.layout.item_empty)
|
* Will generally get only one, but it handles the merged items.
|
||||||
abstract class EmptyItem : VectorEpoxyModel<EmptyItem.Holder>() {
|
*/
|
||||||
class Holder : VectorEpoxyHolder()
|
fun getEventIds(): List<String>
|
||||||
}
|
}
|
|
@ -38,6 +38,7 @@ import org.matrix.android.sdk.api.auth.UserPasswordAuth
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import kotlin.coroutines.Continuation
|
import kotlin.coroutines.Continuation
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
|
import kotlin.coroutines.resumeWithException
|
||||||
|
|
||||||
data class DeactivateAccountViewState(
|
data class DeactivateAccountViewState(
|
||||||
val passwordShown: Boolean = false
|
val passwordShown: Boolean = false
|
||||||
|
@ -64,7 +65,7 @@ class DeactivateAccountViewModel @AssistedInject constructor(@Assisted private v
|
||||||
if (pendingAuth != null) {
|
if (pendingAuth != null) {
|
||||||
uiaContinuation?.resume(pendingAuth!!)
|
uiaContinuation?.resume(pendingAuth!!)
|
||||||
} else {
|
} else {
|
||||||
uiaContinuation?.resumeWith(Result.failure((IllegalArgumentException())))
|
uiaContinuation?.resumeWithException(IllegalArgumentException())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is DeactivateAccountAction.PasswordAuthDone -> {
|
is DeactivateAccountAction.PasswordAuthDone -> {
|
||||||
|
@ -79,7 +80,7 @@ class DeactivateAccountViewModel @AssistedInject constructor(@Assisted private v
|
||||||
}
|
}
|
||||||
DeactivateAccountAction.ReAuthCancelled -> {
|
DeactivateAccountAction.ReAuthCancelled -> {
|
||||||
Timber.d("## UIA - Reauth cancelled")
|
Timber.d("## UIA - Reauth cancelled")
|
||||||
uiaContinuation?.resumeWith(Result.failure((Exception())))
|
uiaContinuation?.resumeWithException(Exception())
|
||||||
uiaContinuation = null
|
uiaContinuation = null
|
||||||
pendingAuth = null
|
pendingAuth = null
|
||||||
}
|
}
|
||||||
|
@ -98,13 +99,15 @@ class DeactivateAccountViewModel @AssistedInject constructor(@Assisted private v
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val event = try {
|
val event = try {
|
||||||
session.deactivateAccount(
|
session.deactivateAccount(
|
||||||
|
action.eraseAllData,
|
||||||
object : UserInteractiveAuthInterceptor {
|
object : UserInteractiveAuthInterceptor {
|
||||||
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
||||||
_viewEvents.post(DeactivateAccountViewEvents.RequestReAuth(flowResponse, errCode))
|
_viewEvents.post(DeactivateAccountViewEvents.RequestReAuth(flowResponse, errCode))
|
||||||
pendingAuth = DefaultBaseAuth(session = flowResponse.session)
|
pendingAuth = DefaultBaseAuth(session = flowResponse.session)
|
||||||
uiaContinuation = promise
|
uiaContinuation = promise
|
||||||
}
|
}
|
||||||
}, action.eraseAllData)
|
}
|
||||||
|
)
|
||||||
DeactivateAccountViewEvents.Done
|
DeactivateAccountViewEvents.Done
|
||||||
} catch (failure: Exception) {
|
} catch (failure: Exception) {
|
||||||
if (failure.isInvalidUIAAuth()) {
|
if (failure.isInvalidUIAAuth()) {
|
||||||
|
|
|
@ -49,6 +49,7 @@ import org.matrix.android.sdk.rx.rx
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import kotlin.coroutines.Continuation
|
import kotlin.coroutines.Continuation
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
|
import kotlin.coroutines.resumeWithException
|
||||||
|
|
||||||
class CrossSigningSettingsViewModel @AssistedInject constructor(
|
class CrossSigningSettingsViewModel @AssistedInject constructor(
|
||||||
@Assisted private val initialState: CrossSigningSettingsViewState,
|
@Assisted private val initialState: CrossSigningSettingsViewState,
|
||||||
|
@ -130,7 +131,7 @@ class CrossSigningSettingsViewModel @AssistedInject constructor(
|
||||||
if (pendingAuth != null) {
|
if (pendingAuth != null) {
|
||||||
uiaContinuation?.resume(pendingAuth!!)
|
uiaContinuation?.resume(pendingAuth!!)
|
||||||
} else {
|
} else {
|
||||||
uiaContinuation?.resumeWith(Result.failure((IllegalArgumentException())))
|
uiaContinuation?.resumeWithException(IllegalArgumentException())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is CrossSigningSettingsAction.PasswordAuthDone -> {
|
is CrossSigningSettingsAction.PasswordAuthDone -> {
|
||||||
|
@ -146,7 +147,7 @@ class CrossSigningSettingsViewModel @AssistedInject constructor(
|
||||||
CrossSigningSettingsAction.ReAuthCancelled -> {
|
CrossSigningSettingsAction.ReAuthCancelled -> {
|
||||||
Timber.d("## UIA - Reauth cancelled")
|
Timber.d("## UIA - Reauth cancelled")
|
||||||
_viewEvents.post(CrossSigningSettingsViewEvents.HideModalWaitingView)
|
_viewEvents.post(CrossSigningSettingsViewEvents.HideModalWaitingView)
|
||||||
uiaContinuation?.resumeWith(Result.failure((Exception())))
|
uiaContinuation?.resumeWithException(Exception())
|
||||||
uiaContinuation = null
|
uiaContinuation = null
|
||||||
pendingAuth = null
|
pendingAuth = null
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,6 +64,7 @@ import java.util.concurrent.TimeUnit
|
||||||
import javax.net.ssl.HttpsURLConnection
|
import javax.net.ssl.HttpsURLConnection
|
||||||
import kotlin.coroutines.Continuation
|
import kotlin.coroutines.Continuation
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
|
import kotlin.coroutines.resumeWithException
|
||||||
|
|
||||||
data class DevicesViewState(
|
data class DevicesViewState(
|
||||||
val myDeviceId: String = "",
|
val myDeviceId: String = "",
|
||||||
|
@ -217,7 +218,7 @@ class DevicesViewModel @AssistedInject constructor(
|
||||||
if (pendingAuth != null) {
|
if (pendingAuth != null) {
|
||||||
uiaContinuation?.resume(pendingAuth!!)
|
uiaContinuation?.resume(pendingAuth!!)
|
||||||
} else {
|
} else {
|
||||||
uiaContinuation?.resumeWith(Result.failure((IllegalArgumentException())))
|
uiaContinuation?.resumeWithException(IllegalArgumentException())
|
||||||
}
|
}
|
||||||
Unit
|
Unit
|
||||||
}
|
}
|
||||||
|
@ -235,7 +236,7 @@ class DevicesViewModel @AssistedInject constructor(
|
||||||
DevicesAction.ReAuthCancelled -> {
|
DevicesAction.ReAuthCancelled -> {
|
||||||
Timber.d("## UIA - Reauth cancelled")
|
Timber.d("## UIA - Reauth cancelled")
|
||||||
// _viewEvents.post(DevicesViewEvents.Loading)
|
// _viewEvents.post(DevicesViewEvents.Loading)
|
||||||
uiaContinuation?.resumeWith(Result.failure((Exception())))
|
uiaContinuation?.resumeWithException(Exception())
|
||||||
uiaContinuation = null
|
uiaContinuation = null
|
||||||
pendingAuth = null
|
pendingAuth = null
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,7 @@ import org.matrix.android.sdk.rx.rx
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import kotlin.coroutines.Continuation
|
import kotlin.coroutines.Continuation
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
|
import kotlin.coroutines.resumeWithException
|
||||||
|
|
||||||
class ThreePidsSettingsViewModel @AssistedInject constructor(
|
class ThreePidsSettingsViewModel @AssistedInject constructor(
|
||||||
@Assisted initialState: ThreePidsSettingsViewState,
|
@Assisted initialState: ThreePidsSettingsViewState,
|
||||||
|
@ -140,7 +141,7 @@ class ThreePidsSettingsViewModel @AssistedInject constructor(
|
||||||
if (pendingAuth != null) {
|
if (pendingAuth != null) {
|
||||||
uiaContinuation?.resume(pendingAuth!!)
|
uiaContinuation?.resume(pendingAuth!!)
|
||||||
} else {
|
} else {
|
||||||
uiaContinuation?.resumeWith(Result.failure((IllegalArgumentException())))
|
uiaContinuation?.resumeWithException(IllegalArgumentException())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is ThreePidsSettingsAction.PasswordAuthDone -> {
|
is ThreePidsSettingsAction.PasswordAuthDone -> {
|
||||||
|
@ -155,7 +156,7 @@ class ThreePidsSettingsViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
ThreePidsSettingsAction.ReAuthCancelled -> {
|
ThreePidsSettingsAction.ReAuthCancelled -> {
|
||||||
Timber.d("## UIA - Reauth cancelled")
|
Timber.d("## UIA - Reauth cancelled")
|
||||||
uiaContinuation?.resumeWith(Result.failure((Exception())))
|
uiaContinuation?.resumeWithException(Exception())
|
||||||
uiaContinuation = null
|
uiaContinuation = null
|
||||||
pendingAuth = null
|
pendingAuth = null
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,55 +1,52 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
|
|
||||||
<declare-styleable name="VectorStyles">
|
<!-- application bar text color -->
|
||||||
|
<attr name="vctr_toolbar_primary_text_color" format="color" />
|
||||||
|
<attr name="vctr_toolbar_secondary_text_color" format="color" />
|
||||||
|
<attr name="vctr_toolbar_link_text_color" format="color" />
|
||||||
|
|
||||||
<!-- application bar text color -->
|
<!-- default text colors -->
|
||||||
<attr name="vctr_toolbar_primary_text_color" format="color" />
|
<attr name="vctr_default_text_hint_color" format="color" />
|
||||||
<attr name="vctr_toolbar_secondary_text_color" format="color" />
|
|
||||||
<attr name="vctr_toolbar_link_text_color" format="color" />
|
|
||||||
|
|
||||||
<!-- default text colors -->
|
<!-- room message colors -->
|
||||||
<attr name="vctr_default_text_hint_color" format="color" />
|
<attr name="vctr_unsent_message_text_color" format="color" />
|
||||||
|
<attr name="vctr_message_text_color" format="color" />
|
||||||
|
<attr name="vctr_notice_text_color" format="color" />
|
||||||
|
<attr name="vctr_notice_secondary" format="color" />
|
||||||
|
<attr name="vctr_encrypting_message_text_color" format="color" />
|
||||||
|
<attr name="vctr_sending_message_text_color" format="color" />
|
||||||
|
<attr name="vctr_markdown_block_background_color" format="color" />
|
||||||
|
<attr name="vctr_spoiler_background_color" format="color" />
|
||||||
|
|
||||||
<!-- room message colors -->
|
<!-- tab bar colors -->
|
||||||
<attr name="vctr_unsent_message_text_color" format="color" />
|
<attr name="vctr_tab_bar_inverted_background_color" format="color" />
|
||||||
<attr name="vctr_message_text_color" format="color" />
|
|
||||||
<attr name="vctr_notice_text_color" format="color" />
|
|
||||||
<attr name="vctr_notice_secondary" format="color" />
|
|
||||||
<attr name="vctr_encrypting_message_text_color" format="color" />
|
|
||||||
<attr name="vctr_sending_message_text_color" format="color" />
|
|
||||||
<attr name="vctr_markdown_block_background_color" format="color" />
|
|
||||||
<attr name="vctr_spoiler_background_color" format="color" />
|
|
||||||
|
|
||||||
<!-- tab bar colors -->
|
<!-- list colors -->
|
||||||
<attr name="vctr_tab_bar_inverted_background_color" format="color" />
|
<attr name="vctr_list_header_background_color" format="color" />
|
||||||
|
<attr name="vctr_list_header_primary_text_color" format="color" />
|
||||||
|
<attr name="vctr_list_header_secondary_text_color" format="color" />
|
||||||
|
|
||||||
<!-- list colors -->
|
<attr name="vctr_list_divider_color" format="color" />
|
||||||
<attr name="vctr_list_header_background_color" format="color" />
|
|
||||||
<attr name="vctr_list_header_primary_text_color" format="color" />
|
|
||||||
<attr name="vctr_list_header_secondary_text_color" format="color" />
|
|
||||||
|
|
||||||
<attr name="vctr_list_divider_color" format="color" />
|
<!-- outgoing call background color -->
|
||||||
|
<attr name="vctr_pending_outgoing_view_background_color" format="color" />
|
||||||
|
|
||||||
<!-- outgoing call background color -->
|
<!-- room notification text color (typing, unsent...) -->
|
||||||
<attr name="vctr_pending_outgoing_view_background_color" format="color" />
|
<attr name="vctr_room_notification_text_color" format="color" />
|
||||||
|
|
||||||
<!-- room notification text color (typing, unsent...) -->
|
<!-- icon colors -->
|
||||||
<attr name="vctr_room_notification_text_color" format="color" />
|
<attr name="vctr_icon_tint_on_light_action_bar_color" format="color" />
|
||||||
|
<attr name="vctr_settings_icon_tint_color" format="color" />
|
||||||
|
|
||||||
<!-- icon colors -->
|
<attr name="vctr_social_login_button_google_style" format="reference" />
|
||||||
<attr name="vctr_icon_tint_on_light_action_bar_color" format="color" />
|
<attr name="vctr_social_login_button_github_style" format="reference" />
|
||||||
<attr name="vctr_settings_icon_tint_color" format="color" />
|
<attr name="vctr_social_login_button_facebook_style" format="reference" />
|
||||||
|
<attr name="vctr_social_login_button_twitter_style" format="reference" />
|
||||||
|
<attr name="vctr_social_login_button_apple_style" format="reference" />
|
||||||
|
<attr name="vctr_social_login_button_gitlab_style" format="reference" />
|
||||||
|
|
||||||
<attr name="vctr_social_login_button_google_style" format="reference" />
|
<attr name="vctr_chat_effect_snow_background" format="color" />
|
||||||
<attr name="vctr_social_login_button_github_style" format="reference" />
|
|
||||||
<attr name="vctr_social_login_button_facebook_style" format="reference" />
|
|
||||||
<attr name="vctr_social_login_button_twitter_style" format="reference" />
|
|
||||||
<attr name="vctr_social_login_button_apple_style" format="reference" />
|
|
||||||
<attr name="vctr_social_login_button_gitlab_style" format="reference" />
|
|
||||||
|
|
||||||
<attr name="vctr_chat_effect_snow_background" format="color" />
|
|
||||||
</declare-styleable>
|
|
||||||
|
|
||||||
<declare-styleable name="PollResultLineView">
|
<declare-styleable name="PollResultLineView">
|
||||||
<attr name="optionName" format="string" localization="suggested" />
|
<attr name="optionName" format="string" localization="suggested" />
|
||||||
|
@ -70,16 +67,16 @@
|
||||||
|
|
||||||
<declare-styleable name="SignOutBottomSheetActionButton">
|
<declare-styleable name="SignOutBottomSheetActionButton">
|
||||||
<attr name="iconTint" format="color" />
|
<attr name="iconTint" format="color" />
|
||||||
<attr name="actionTitle"/>
|
<attr name="actionTitle" />
|
||||||
<attr name="leftIcon" />
|
<attr name="leftIcon" />
|
||||||
<attr name="textColor" format="color" />
|
<attr name="textColor" format="color" />
|
||||||
</declare-styleable>
|
</declare-styleable>
|
||||||
|
|
||||||
<declare-styleable name="SocialLoginButtonsView">
|
<declare-styleable name="SocialLoginButtonsView">
|
||||||
<attr name="signMode" format="enum">
|
<attr name="signMode" format="enum">
|
||||||
<enum name="signin" value="0"/>
|
<enum name="signin" value="0" />
|
||||||
<enum name="signup" value="1"/>
|
<enum name="signup" value="1" />
|
||||||
<enum name="continue_with" value="2"/>
|
<enum name="continue_with" value="2" />
|
||||||
</attr>
|
</attr>
|
||||||
</declare-styleable>
|
</declare-styleable>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -1,7 +1,115 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
|
|
||||||
<!-- Source: https://zpl.io/aBKw9Mk -->
|
<!-- Error colors -->
|
||||||
|
<color name="vector_success_color">#70BF56</color>
|
||||||
|
<color name="vector_warning_color">#ff4b55</color>
|
||||||
|
<color name="vector_error_color">#ff4b55</color>
|
||||||
|
<color name="vector_info_color">#2f9edb</color>
|
||||||
|
|
||||||
|
<!-- main app colors -->
|
||||||
|
<color name="vector_fuchsia_color">#ff4b55</color>
|
||||||
|
<color name="vector_silver_color">#FFC7C7C7</color>
|
||||||
|
<color name="vector_dark_grey_color">#FF999999</color>
|
||||||
|
|
||||||
|
<!-- theses colours are requested a background cannot be set by an ?attr on android < 5 -->
|
||||||
|
<!-- dedicated drawables are created for each theme -->
|
||||||
|
<!-- Default/Background-->
|
||||||
|
<color name="riot_primary_background_color_light">#FFFFFFFF</color>
|
||||||
|
<!-- Dark/Background-->
|
||||||
|
<color name="riot_primary_background_color_dark">#FF181B21</color>
|
||||||
|
<!-- Black/Background-->
|
||||||
|
<color name="riot_primary_background_color_black">#F000</color>
|
||||||
|
|
||||||
|
<!--Default/Android Status Bar-->
|
||||||
|
<color name="primary_color_dark_light">#FF1A2027</color>
|
||||||
|
<!--Default/Base-->
|
||||||
|
<color name="primary_color_light">#03b381</color>
|
||||||
|
<!--Default/Accent-->
|
||||||
|
<color name="accent_color_light">#03b381</color>
|
||||||
|
|
||||||
|
<!--Dark/Android Status Bar-->
|
||||||
|
<color name="primary_color_dark_dark">#FF0D0E10</color>
|
||||||
|
<!--Dark/Base-->
|
||||||
|
<color name="primary_color_dark">#FF15171B</color>
|
||||||
|
<!--Dark/Accent-->
|
||||||
|
<color name="accent_color_dark">#03b381</color>
|
||||||
|
|
||||||
|
<!--Black/Android Status Bar-->
|
||||||
|
<color name="primary_color_dark_black">#000</color>
|
||||||
|
<!--Black/Base-->
|
||||||
|
<color name="primary_color_black">#FF060708</color>
|
||||||
|
|
||||||
|
<!--Default/Line break mobile-->
|
||||||
|
<attr name="list_divider_color" format="color" />
|
||||||
|
<color name="list_divider_color_light">#EEEFEF</color>
|
||||||
|
<!--Dark/Line break mobile-->
|
||||||
|
<color name="list_divider_color_dark">#FF61708B</color>
|
||||||
|
<!--Black/Line break mobile-->
|
||||||
|
<color name="list_divider_color_black">#FF22262E</color>
|
||||||
|
|
||||||
|
<attr name="tab_bar_selected_background_color" format="color" />
|
||||||
|
<color name="tab_bar_selected_background_color_light">@color/riotx_android_secondary_light</color>
|
||||||
|
<color name="tab_bar_selected_background_color_dark">@color/riotx_android_secondary_dark</color>
|
||||||
|
|
||||||
|
<attr name="tab_bar_unselected_background_color" format="color" />
|
||||||
|
<color name="tab_bar_unselected_background_color_light">@color/riotx_background_light</color>
|
||||||
|
<color name="tab_bar_unselected_background_color_dark">@color/riotx_background_dark</color>
|
||||||
|
|
||||||
|
<!-- Hint Colors -->
|
||||||
|
<color name="primary_hint_text_color_light">#FFFFFF</color>
|
||||||
|
<color name="primary_hint_text_color_dark">#FFFFFF</color>
|
||||||
|
|
||||||
|
<color name="default_text_hint_color_light">#903C3C3C</color>
|
||||||
|
<color name="default_text_hint_color_dark">#CCDDDDDD</color>
|
||||||
|
|
||||||
|
<!-- Text Colors -->
|
||||||
|
<attr name="riot_primary_text_color" format="color" />
|
||||||
|
<attr name="riot_primary_text_color_disabled" format="color" />
|
||||||
|
|
||||||
|
<!--Default/Text Primary-->
|
||||||
|
<color name="riot_primary_text_color_light">#FF2E2F32</color>
|
||||||
|
<color name="riot_primary_text_color_disabled_light">#FF9E9E9E</color>
|
||||||
|
<!--Default/Text Secondary-->
|
||||||
|
<color name="riot_secondary_text_color_light">#FF9E9E9E</color>
|
||||||
|
<color name="riot_tertiary_text_color_light">@color/riot_primary_text_color_light</color>
|
||||||
|
|
||||||
|
<!--Dark /Text Primary-->
|
||||||
|
<color name="riot_primary_text_color_dark">#FFEDF3FF</color>
|
||||||
|
<color name="riot_primary_text_color_disabled_dark">#FFA1B2D1</color>
|
||||||
|
<!--Dark /Text Secondary-->
|
||||||
|
<color name="riot_secondary_text_color_dark">#FFA1B2D1</color>
|
||||||
|
<color name="riot_tertiary_text_color_dark">@color/riot_primary_text_color_dark</color>
|
||||||
|
|
||||||
|
<!-- Notification view colors -->
|
||||||
|
<color name="soft_resource_limit_exceeded">#2f9edb</color>
|
||||||
|
<color name="hard_resource_limit_exceeded">@color/vector_fuchsia_color</color>
|
||||||
|
|
||||||
|
<!-- Password Strength bar colors -->
|
||||||
|
<color name="password_strength_bar_weak">#FFF56679</color>
|
||||||
|
<color name="password_strength_bar_low">#FFFFC666</color>
|
||||||
|
<color name="password_strength_bar_ok">#FFF8E71C</color>
|
||||||
|
<color name="password_strength_bar_strong">#FF7AC9A1</color>
|
||||||
|
<color name="password_strength_bar_undefined">#FF9E9E9E</color>
|
||||||
|
|
||||||
|
<!-- Button color -->
|
||||||
|
<color name="button_enabled_text_color">#FFFFFFFF</color>
|
||||||
|
<color name="button_disabled_text_color">#FFFFFFFF</color>
|
||||||
|
<color name="button_destructive_enabled_text_color">#FF4B55</color>
|
||||||
|
<color name="button_destructive_disabled_text_color">#FF4B55</color>
|
||||||
|
<color name="button_bot_enabled_text_color">#FF368BD6</color>
|
||||||
|
<color name="button_bot_disabled_text_color">#61708B</color>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Link color -->
|
||||||
|
<color name="link_color_light">#368BD6</color>
|
||||||
|
<color name="link_color_dark">#368BD6</color>
|
||||||
|
|
||||||
|
<!-- Notification (do not depends on theme) -->
|
||||||
|
<color name="notification_accent_color">#368BD6</color>
|
||||||
|
<color name="key_share_req_accent_color">#ff812d</color>
|
||||||
|
|
||||||
|
<!-- Source: https://zpl.io/aBKw9Mk -->
|
||||||
|
|
||||||
<!-- Accents -->
|
<!-- Accents -->
|
||||||
<color name="riotx_accent">#FF0DBD8B</color>
|
<color name="riotx_accent">#FF0DBD8B</color>
|
||||||
|
@ -38,7 +146,7 @@
|
||||||
<color name="riotx_username_7">#5c56f5</color>
|
<color name="riotx_username_7">#5c56f5</color>
|
||||||
<color name="riotx_username_8">#74d12c</color>
|
<color name="riotx_username_8">#74d12c</color>
|
||||||
|
|
||||||
<!-- Other usefull color -->
|
<!-- Other useful color -->
|
||||||
<color name="black">#FF000000</color>
|
<color name="black">#FF000000</color>
|
||||||
<color name="white">#FFFFFFFF</color>
|
<color name="white">#FFFFFFFF</color>
|
||||||
<color name="black_alpha">#55000000</color>
|
<color name="black_alpha">#55000000</color>
|
||||||
|
@ -256,6 +364,4 @@
|
||||||
<color name="riotx_keys_backup_banner_accent_color_light">#FFF8E3</color>
|
<color name="riotx_keys_backup_banner_accent_color_light">#FFF8E3</color>
|
||||||
<color name="riotx_keys_backup_banner_accent_color_dark">#22262E</color>
|
<color name="riotx_keys_backup_banner_accent_color_dark">#22262E</color>
|
||||||
|
|
||||||
|
</resources>
|
||||||
|
|
||||||
</resources>
|
|
|
@ -1,112 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
|
|
||||||
<!-- Error colors -->
|
|
||||||
<color name="vector_success_color">#70BF56</color>
|
|
||||||
<color name="vector_warning_color">#ff4b55</color>
|
|
||||||
<color name="vector_error_color">#ff4b55</color>
|
|
||||||
<color name="vector_info_color">#2f9edb</color>
|
|
||||||
|
|
||||||
<!-- main app colors -->
|
|
||||||
<color name="vector_fuchsia_color">#ff4b55</color>
|
|
||||||
<color name="vector_silver_color">#FFC7C7C7</color>
|
|
||||||
<color name="vector_dark_grey_color">#FF999999</color>
|
|
||||||
|
|
||||||
<!-- theses colours are requested a background cannot be set by an ?attr on android < 5 -->
|
|
||||||
<!-- dedicated drawables are created for each theme -->
|
|
||||||
<!-- Default/Background-->
|
|
||||||
<color name="riot_primary_background_color_light">#FFFFFFFF</color>
|
|
||||||
<!-- Dark/Background-->
|
|
||||||
<color name="riot_primary_background_color_dark">#FF181B21</color>
|
|
||||||
<!-- Black/Background-->
|
|
||||||
<color name="riot_primary_background_color_black">#F000</color>
|
|
||||||
|
|
||||||
<!--Default/Android Status Bar-->
|
|
||||||
<color name="primary_color_dark_light">#FF1A2027</color>
|
|
||||||
<!--Default/Base-->
|
|
||||||
<color name="primary_color_light">#03b381</color>
|
|
||||||
<!--Default/Accent-->
|
|
||||||
<color name="accent_color_light">#03b381</color>
|
|
||||||
|
|
||||||
<!--Dark/Android Status Bar-->
|
|
||||||
<color name="primary_color_dark_dark">#FF0D0E10</color>
|
|
||||||
<!--Dark/Base-->
|
|
||||||
<color name="primary_color_dark">#FF15171B</color>
|
|
||||||
<!--Dark/Accent-->
|
|
||||||
<color name="accent_color_dark">#03b381</color>
|
|
||||||
|
|
||||||
<!--Black/Android Status Bar-->
|
|
||||||
<color name="primary_color_dark_black">#000</color>
|
|
||||||
<!--Black/Base-->
|
|
||||||
<color name="primary_color_black">#FF060708</color>
|
|
||||||
|
|
||||||
<!--Default/Line break mobile-->
|
|
||||||
<attr name="list_divider_color" format="color" />
|
|
||||||
<color name="list_divider_color_light">#EEEFEF</color>
|
|
||||||
<!--Dark/Line break mobile-->
|
|
||||||
<color name="list_divider_color_dark">#FF61708B</color>
|
|
||||||
<!--Black/Line break mobile-->
|
|
||||||
<color name="list_divider_color_black">#FF22262E</color>
|
|
||||||
|
|
||||||
<attr name="tab_bar_selected_background_color" format="color" />
|
|
||||||
<color name="tab_bar_selected_background_color_light">@color/riotx_android_secondary_light</color>
|
|
||||||
<color name="tab_bar_selected_background_color_dark">@color/riotx_android_secondary_dark</color>
|
|
||||||
|
|
||||||
<attr name="tab_bar_unselected_background_color" format="color" />
|
|
||||||
<color name="tab_bar_unselected_background_color_light">@color/riotx_background_light</color>
|
|
||||||
<color name="tab_bar_unselected_background_color_dark">@color/riotx_background_dark</color>
|
|
||||||
|
|
||||||
<!-- Hint Colors -->
|
|
||||||
<color name="primary_hint_text_color_light">#FFFFFF</color>
|
|
||||||
<color name="primary_hint_text_color_dark">#FFFFFF</color>
|
|
||||||
|
|
||||||
<color name="default_text_hint_color_light">#903C3C3C</color>
|
|
||||||
<color name="default_text_hint_color_dark">#CCDDDDDD</color>
|
|
||||||
|
|
||||||
<!-- Text Colors -->
|
|
||||||
<attr name="riot_primary_text_color" format="color" />
|
|
||||||
<attr name="riot_primary_text_color_disabled" format="color" />
|
|
||||||
|
|
||||||
<!--Default/Text Primary-->
|
|
||||||
<color name="riot_primary_text_color_light">#FF2E2F32</color>
|
|
||||||
<color name="riot_primary_text_color_disabled_light">#FF9E9E9E</color>
|
|
||||||
<!--Default/Text Secondary-->
|
|
||||||
<color name="riot_secondary_text_color_light">#FF9E9E9E</color>
|
|
||||||
<color name="riot_tertiary_text_color_light">@color/riot_primary_text_color_light</color>
|
|
||||||
|
|
||||||
<!--Dark /Text Primary-->
|
|
||||||
<color name="riot_primary_text_color_dark">#FFEDF3FF</color>
|
|
||||||
<color name="riot_primary_text_color_disabled_dark">#FFA1B2D1</color>
|
|
||||||
<!--Dark /Text Secondary-->
|
|
||||||
<color name="riot_secondary_text_color_dark">#FFA1B2D1</color>
|
|
||||||
<color name="riot_tertiary_text_color_dark">@color/riot_primary_text_color_dark</color>
|
|
||||||
|
|
||||||
<!-- Notification view colors -->
|
|
||||||
<color name="soft_resource_limit_exceeded">#2f9edb</color>
|
|
||||||
<color name="hard_resource_limit_exceeded">@color/vector_fuchsia_color</color>
|
|
||||||
|
|
||||||
<!-- Password Strength bar colors -->
|
|
||||||
<color name="password_strength_bar_weak">#FFF56679</color>
|
|
||||||
<color name="password_strength_bar_low">#FFFFC666</color>
|
|
||||||
<color name="password_strength_bar_ok">#FFF8E71C</color>
|
|
||||||
<color name="password_strength_bar_strong">#FF7AC9A1</color>
|
|
||||||
<color name="password_strength_bar_undefined">#FF9E9E9E</color>
|
|
||||||
|
|
||||||
<!-- Button color -->
|
|
||||||
<color name="button_enabled_text_color">#FFFFFFFF</color>
|
|
||||||
<color name="button_disabled_text_color">#FFFFFFFF</color>
|
|
||||||
<color name="button_destructive_enabled_text_color">#FF4B55</color>
|
|
||||||
<color name="button_destructive_disabled_text_color">#FF4B55</color>
|
|
||||||
<color name="button_bot_enabled_text_color">#FF368BD6</color>
|
|
||||||
<color name="button_bot_disabled_text_color">#61708B</color>
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Link color -->
|
|
||||||
<color name="link_color_light">#368BD6</color>
|
|
||||||
<color name="link_color_dark">#368BD6</color>
|
|
||||||
|
|
||||||
<!-- Notification (do not depends on theme) -->
|
|
||||||
<color name="notification_accent_color">#368BD6</color>
|
|
||||||
<color name="key_share_req_accent_color">#ff812d</color>
|
|
||||||
|
|
||||||
</resources>
|
|
Loading…
Add table
Reference in a new issue