Merge branch 'develop' into feature/fix_user_vs_room_member
This commit is contained in:
commit
bf9d80c14c
36
CHANGES.md
36
CHANGES.md
@ -1,9 +1,31 @@
|
|||||||
Changes in Element 1.0.10 (2020-XX-XX)
|
Changes in Element 1.0.11 (2020-XX-XX)
|
||||||
===================================================
|
===================================================
|
||||||
|
|
||||||
Features ✨:
|
Features ✨:
|
||||||
-
|
-
|
||||||
|
|
||||||
|
Improvements 🙌:
|
||||||
|
- Open an existing DM instead of creating a new one (#2319)
|
||||||
|
|
||||||
|
Bugfix 🐛:
|
||||||
|
- Fix issue when updating the avatar of a room (new avatar vanishing)
|
||||||
|
- Discard change dialog displayed by mistake when avatar has been updated
|
||||||
|
|
||||||
|
Translations 🗣:
|
||||||
|
-
|
||||||
|
|
||||||
|
SDK API changes ⚠️:
|
||||||
|
- AccountService now exposes suspendable function instead of using MatrixCallback (#2354). Note: We will incrementally migrate all the SDK API in a near future.
|
||||||
|
|
||||||
|
Build 🧱:
|
||||||
|
-
|
||||||
|
|
||||||
|
Other changes:
|
||||||
|
- Upgrade Realm dependency to 10.0.0
|
||||||
|
|
||||||
|
Changes in Element 1.0.10 (2020-11-04)
|
||||||
|
===================================================
|
||||||
|
|
||||||
Improvements 🙌:
|
Improvements 🙌:
|
||||||
- Rework sending Event management (#154)
|
- Rework sending Event management (#154)
|
||||||
- New room creation screen: set topic and avatar in the room creation form (#2078)
|
- New room creation screen: set topic and avatar in the room creation form (#2078)
|
||||||
@ -28,18 +50,6 @@ Bugfix 🐛:
|
|||||||
- KeysBackup: Avoid using `!!` (#2262)
|
- KeysBackup: Avoid using `!!` (#2262)
|
||||||
- Two elements in the task switcher (#2299)
|
- Two elements in the task switcher (#2299)
|
||||||
|
|
||||||
Translations 🗣:
|
|
||||||
-
|
|
||||||
|
|
||||||
SDK API changes ⚠️:
|
|
||||||
-
|
|
||||||
|
|
||||||
Build 🧱:
|
|
||||||
-
|
|
||||||
|
|
||||||
Other changes:
|
|
||||||
-
|
|
||||||
|
|
||||||
Changes in Element 1.0.9 (2020-10-16)
|
Changes in Element 1.0.9 (2020-10-16)
|
||||||
===================================================
|
===================================================
|
||||||
|
|
||||||
|
@ -1 +1,2 @@
|
|||||||
// TODO
|
This new version mainly contains bug fixes and improvements. Sending a message is now much faster.
|
||||||
|
Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.0.10
|
@ -9,7 +9,7 @@ buildscript {
|
|||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath "io.realm:realm-gradle-plugin:6.1.0"
|
classpath "io.realm:realm-gradle-plugin:10.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,7 +149,7 @@ dependencies {
|
|||||||
implementation 'androidx.exifinterface:exifinterface:1.3.0'
|
implementation 'androidx.exifinterface:exifinterface:1.3.0'
|
||||||
|
|
||||||
// Database
|
// Database
|
||||||
implementation 'com.github.Zhuinden:realm-monarchy:0.5.1'
|
implementation 'com.github.Zhuinden:realm-monarchy:0.7.1'
|
||||||
kapt 'dk.ilios:realmfieldnameshelper:1.1.1'
|
kapt 'dk.ilios:realmfieldnameshelper:1.1.1'
|
||||||
|
|
||||||
// Work
|
// Work
|
||||||
|
@ -43,8 +43,8 @@ class ChangePasswordTest : InstrumentedTest {
|
|||||||
val session = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(withInitialSync = false))
|
val session = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(withInitialSync = false))
|
||||||
|
|
||||||
// Change password
|
// Change password
|
||||||
commonTestHelper.doSync<Unit> {
|
commonTestHelper.runBlockingTest {
|
||||||
session.changePassword(TestConstants.PASSWORD, NEW_PASSWORD, it)
|
session.changePassword(TestConstants.PASSWORD, NEW_PASSWORD)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to login with the previous password, it will fail
|
// Try to login with the previous password, it will fail
|
||||||
|
@ -43,8 +43,8 @@ class DeactivateAccountTest : InstrumentedTest {
|
|||||||
val session = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(withInitialSync = false))
|
val session = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(withInitialSync = false))
|
||||||
|
|
||||||
// Deactivate the account
|
// Deactivate the account
|
||||||
commonTestHelper.doSync<Unit> {
|
commonTestHelper.runBlockingTest {
|
||||||
session.deactivateAccount(TestConstants.PASSWORD, false, it)
|
session.deactivateAccount(TestConstants.PASSWORD, 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)
|
||||||
|
@ -40,6 +40,7 @@ import kotlinx.coroutines.GlobalScope
|
|||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import kotlinx.coroutines.withTimeout
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Assert.assertNotNull
|
import org.junit.Assert.assertNotNull
|
||||||
import org.junit.Assert.assertTrue
|
import org.junit.Assert.assertTrue
|
||||||
@ -343,6 +344,14 @@ class CommonTestHelper(context: Context) {
|
|||||||
await(latch, timeout)
|
await(latch, timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun <T> runBlockingTest(timeout: Long = TestConstants.timeOutMillis, block: suspend () -> T): T {
|
||||||
|
return runBlocking {
|
||||||
|
withTimeout(timeout) {
|
||||||
|
block()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Transform a method with a MatrixCallback to a synchronous method
|
// Transform a method with a MatrixCallback to a synchronous method
|
||||||
inline fun <reified T> doSync(timeout: Long? = TestConstants.timeOutMillis, block: (MatrixCallback<T>) -> Unit): T {
|
inline fun <reified T> doSync(timeout: Long? = TestConstants.timeOutMillis, block: (MatrixCallback<T>) -> Unit): T {
|
||||||
val lock = CountDownLatch(1)
|
val lock = CountDownLatch(1)
|
||||||
|
@ -555,7 +555,7 @@ class SASTest : InstrumentedTest {
|
|||||||
|
|
||||||
mTestHelper.waitWithLatch {
|
mTestHelper.waitWithLatch {
|
||||||
mTestHelper.retryPeriodicallyWithLatch(it) {
|
mTestHelper.retryPeriodicallyWithLatch(it) {
|
||||||
val prAlicePOV = aliceVerificationService.getExistingVerificationRequest(bobSession.myUserId)?.firstOrNull()
|
val prAlicePOV = aliceVerificationService.getExistingVerificationRequests(bobSession.myUserId).firstOrNull()
|
||||||
requestID = prAlicePOV?.transactionId
|
requestID = prAlicePOV?.transactionId
|
||||||
Log.v("TEST", "== alicePOV is $prAlicePOV")
|
Log.v("TEST", "== alicePOV is $prAlicePOV")
|
||||||
prAlicePOV?.transactionId != null && prAlicePOV.localId == req.localId
|
prAlicePOV?.transactionId != null && prAlicePOV.localId == req.localId
|
||||||
@ -566,7 +566,7 @@ class SASTest : InstrumentedTest {
|
|||||||
|
|
||||||
mTestHelper.waitWithLatch {
|
mTestHelper.waitWithLatch {
|
||||||
mTestHelper.retryPeriodicallyWithLatch(it) {
|
mTestHelper.retryPeriodicallyWithLatch(it) {
|
||||||
val prBobPOV = bobVerificationService.getExistingVerificationRequest(aliceSession.myUserId)?.firstOrNull()
|
val prBobPOV = bobVerificationService.getExistingVerificationRequests(aliceSession.myUserId).firstOrNull()
|
||||||
Log.v("TEST", "== prBobPOV is $prBobPOV")
|
Log.v("TEST", "== prBobPOV is $prBobPOV")
|
||||||
prBobPOV?.transactionId == requestID
|
prBobPOV?.transactionId == requestID
|
||||||
}
|
}
|
||||||
@ -581,7 +581,7 @@ class SASTest : InstrumentedTest {
|
|||||||
// wait for alice to get the ready
|
// wait for alice to get the ready
|
||||||
mTestHelper.waitWithLatch {
|
mTestHelper.waitWithLatch {
|
||||||
mTestHelper.retryPeriodicallyWithLatch(it) {
|
mTestHelper.retryPeriodicallyWithLatch(it) {
|
||||||
val prAlicePOV = aliceVerificationService.getExistingVerificationRequest(bobSession.myUserId)?.firstOrNull()
|
val prAlicePOV = aliceVerificationService.getExistingVerificationRequests(bobSession.myUserId).firstOrNull()
|
||||||
Log.v("TEST", "== prAlicePOV is $prAlicePOV")
|
Log.v("TEST", "== prAlicePOV is $prAlicePOV")
|
||||||
prAlicePOV?.transactionId == requestID && prAlicePOV?.isReady != null
|
prAlicePOV?.transactionId == requestID && prAlicePOV?.isReady != null
|
||||||
}
|
}
|
||||||
|
@ -16,9 +16,6 @@
|
|||||||
|
|
||||||
package org.matrix.android.sdk.api.session.account
|
package org.matrix.android.sdk.api.session.account
|
||||||
|
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
|
||||||
import org.matrix.android.sdk.api.util.Cancelable
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This interface defines methods to manage the account. It's implemented at the session level.
|
* This interface defines methods to manage the account. It's implemented at the session level.
|
||||||
*/
|
*/
|
||||||
@ -28,7 +25,7 @@ interface AccountService {
|
|||||||
* @param password Current password.
|
* @param password Current password.
|
||||||
* @param newPassword New password
|
* @param newPassword New password
|
||||||
*/
|
*/
|
||||||
fun changePassword(password: String, newPassword: String, callback: MatrixCallback<Unit>): Cancelable
|
suspend fun changePassword(password: String, newPassword: String)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deactivate the account.
|
* Deactivate the account.
|
||||||
@ -46,5 +43,5 @@ interface AccountService {
|
|||||||
* @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
|
||||||
*/
|
*/
|
||||||
fun deactivateAccount(password: String, eraseAllData: Boolean, callback: MatrixCallback<Unit>): Cancelable
|
suspend fun deactivateAccount(password: String, eraseAllData: Boolean)
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ interface VerificationService {
|
|||||||
|
|
||||||
fun getExistingTransaction(otherUserId: String, tid: String): VerificationTransaction?
|
fun getExistingTransaction(otherUserId: String, tid: String): VerificationTransaction?
|
||||||
|
|
||||||
fun getExistingVerificationRequest(otherUserId: String): List<PendingVerificationRequest>?
|
fun getExistingVerificationRequests(otherUserId: String): List<PendingVerificationRequest>
|
||||||
|
|
||||||
fun getExistingVerificationRequest(otherUserId: String, tid: String?): PendingVerificationRequest?
|
fun getExistingVerificationRequest(otherUserId: String, tid: String?): PendingVerificationRequest?
|
||||||
|
|
||||||
|
@ -123,6 +123,7 @@ internal abstract class CryptoModule {
|
|||||||
}
|
}
|
||||||
.name("crypto_store.realm")
|
.name("crypto_store.realm")
|
||||||
.modules(RealmCryptoStoreModule())
|
.modules(RealmCryptoStoreModule())
|
||||||
|
.allowWritesOnUiThread(true)
|
||||||
.schemaVersion(RealmCryptoStoreMigration.CRYPTO_STORE_SCHEMA_VERSION)
|
.schemaVersion(RealmCryptoStoreMigration.CRYPTO_STORE_SCHEMA_VERSION)
|
||||||
.migration(realmCryptoStoreMigration)
|
.migration(realmCryptoStoreMigration)
|
||||||
.build()
|
.build()
|
||||||
|
@ -537,11 +537,10 @@ internal class DefaultVerificationService @Inject constructor(
|
|||||||
// If there is a corresponding request, we can auto accept
|
// If there is a corresponding request, we can auto accept
|
||||||
// as we are the one requesting in first place (or we accepted the request)
|
// as we are the one requesting in first place (or we accepted the request)
|
||||||
// I need to check if the pending request was related to this device also
|
// I need to check if the pending request was related to this device also
|
||||||
val autoAccept = getExistingVerificationRequest(otherUserId)?.any {
|
val autoAccept = getExistingVerificationRequests(otherUserId).any {
|
||||||
it.transactionId == startReq.transactionId
|
it.transactionId == startReq.transactionId
|
||||||
&& (it.requestInfo?.fromDevice == this.deviceId || it.readyInfo?.fromDevice == this.deviceId)
|
&& (it.requestInfo?.fromDevice == this.deviceId || it.readyInfo?.fromDevice == this.deviceId)
|
||||||
}
|
}
|
||||||
?: false
|
|
||||||
val tx = DefaultIncomingSASDefaultVerificationTransaction(
|
val tx = DefaultIncomingSASDefaultVerificationTransaction(
|
||||||
// this,
|
// this,
|
||||||
setDeviceVerificationAction,
|
setDeviceVerificationAction,
|
||||||
@ -837,8 +836,8 @@ internal class DefaultVerificationService @Inject constructor(
|
|||||||
// SAS do not care for now?
|
// SAS do not care for now?
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now transactions are udated, let's also update Requests
|
// Now transactions are updated, let's also update Requests
|
||||||
val existingRequest = getExistingVerificationRequest(senderId)?.find { it.transactionId == doneReq.transactionId }
|
val existingRequest = getExistingVerificationRequests(senderId).find { it.transactionId == doneReq.transactionId }
|
||||||
if (existingRequest == null) {
|
if (existingRequest == null) {
|
||||||
Timber.e("## SAS Received Done for unknown request txId:${doneReq.transactionId}")
|
Timber.e("## SAS Received Done for unknown request txId:${doneReq.transactionId}")
|
||||||
return
|
return
|
||||||
@ -892,7 +891,7 @@ internal class DefaultVerificationService @Inject constructor(
|
|||||||
private fun handleReadyReceived(senderId: String,
|
private fun handleReadyReceived(senderId: String,
|
||||||
readyReq: ValidVerificationInfoReady,
|
readyReq: ValidVerificationInfoReady,
|
||||||
transportCreator: (DefaultVerificationTransaction) -> VerificationTransport) {
|
transportCreator: (DefaultVerificationTransaction) -> VerificationTransport) {
|
||||||
val existingRequest = getExistingVerificationRequest(senderId)?.find { it.transactionId == readyReq.transactionId }
|
val existingRequest = getExistingVerificationRequests(senderId).find { it.transactionId == readyReq.transactionId }
|
||||||
if (existingRequest == null) {
|
if (existingRequest == null) {
|
||||||
Timber.e("## SAS Received Ready for unknown request txId:${readyReq.transactionId} fromDevice ${readyReq.fromDevice}")
|
Timber.e("## SAS Received Ready for unknown request txId:${readyReq.transactionId} fromDevice ${readyReq.fromDevice}")
|
||||||
return
|
return
|
||||||
@ -1041,9 +1040,9 @@ internal class DefaultVerificationService @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getExistingVerificationRequest(otherUserId: String): List<PendingVerificationRequest>? {
|
override fun getExistingVerificationRequests(otherUserId: String): List<PendingVerificationRequest> {
|
||||||
synchronized(lock = pendingRequests) {
|
synchronized(lock = pendingRequests) {
|
||||||
return pendingRequests[otherUserId]
|
return pendingRequests[otherUserId].orEmpty()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.database
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.Base64
|
import android.util.Base64
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
|
import io.realm.Realm
|
||||||
import org.matrix.android.sdk.BuildConfig
|
import org.matrix.android.sdk.BuildConfig
|
||||||
import org.matrix.android.sdk.internal.session.securestorage.SecretStoringUtils
|
import org.matrix.android.sdk.internal.session.securestorage.SecretStoringUtils
|
||||||
import io.realm.RealmConfiguration
|
import io.realm.RealmConfiguration
|
||||||
@ -46,7 +47,7 @@ internal class RealmKeysUtils @Inject constructor(context: Context,
|
|||||||
private val sharedPreferences = context.getSharedPreferences("im.vector.matrix.android.keys", Context.MODE_PRIVATE)
|
private val sharedPreferences = context.getSharedPreferences("im.vector.matrix.android.keys", Context.MODE_PRIVATE)
|
||||||
|
|
||||||
private fun generateKeyForRealm(): ByteArray {
|
private fun generateKeyForRealm(): ByteArray {
|
||||||
val keyForRealm = ByteArray(RealmConfiguration.KEY_LENGTH)
|
val keyForRealm = ByteArray(Realm.ENCRYPTION_KEY_LENGTH)
|
||||||
rng.nextBytes(keyForRealm)
|
rng.nextBytes(keyForRealm)
|
||||||
return keyForRealm
|
return keyForRealm
|
||||||
}
|
}
|
||||||
|
@ -69,6 +69,7 @@ internal class SessionRealmConfigurationFactory @Inject constructor(
|
|||||||
.apply {
|
.apply {
|
||||||
realmKeysUtils.configureEncryption(this, SessionModule.getKeyAlias(userMd5))
|
realmKeysUtils.configureEncryption(this, SessionModule.getKeyAlias(userMd5))
|
||||||
}
|
}
|
||||||
|
.allowWritesOnUiThread(true)
|
||||||
.modules(SessionRealmModule())
|
.modules(SessionRealmModule())
|
||||||
.schemaVersion(RealmSessionStoreMigration.SESSION_STORE_SCHEMA_VERSION)
|
.schemaVersion(RealmSessionStoreMigration.SESSION_STORE_SCHEMA_VERSION)
|
||||||
.migration(migration)
|
.migration(migration)
|
||||||
|
@ -16,30 +16,17 @@
|
|||||||
|
|
||||||
package org.matrix.android.sdk.internal.session.account
|
package org.matrix.android.sdk.internal.session.account
|
||||||
|
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
|
||||||
import org.matrix.android.sdk.api.session.account.AccountService
|
import org.matrix.android.sdk.api.session.account.AccountService
|
||||||
import org.matrix.android.sdk.api.util.Cancelable
|
|
||||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
|
||||||
import org.matrix.android.sdk.internal.task.configureWith
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class DefaultAccountService @Inject constructor(private val changePasswordTask: ChangePasswordTask,
|
internal class DefaultAccountService @Inject constructor(private val changePasswordTask: ChangePasswordTask,
|
||||||
private val deactivateAccountTask: DeactivateAccountTask,
|
private val deactivateAccountTask: DeactivateAccountTask) : AccountService {
|
||||||
private val taskExecutor: TaskExecutor) : AccountService {
|
|
||||||
|
|
||||||
override fun changePassword(password: String, newPassword: String, callback: MatrixCallback<Unit>): Cancelable {
|
override suspend fun changePassword(password: String, newPassword: String) {
|
||||||
return changePasswordTask
|
changePasswordTask.execute(ChangePasswordTask.Params(password, newPassword))
|
||||||
.configureWith(ChangePasswordTask.Params(password, newPassword)) {
|
|
||||||
this.callback = callback
|
|
||||||
}
|
|
||||||
.executeBy(taskExecutor)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun deactivateAccount(password: String, eraseAllData: Boolean, callback: MatrixCallback<Unit>): Cancelable {
|
override suspend fun deactivateAccount(password: String, eraseAllData: Boolean) {
|
||||||
return deactivateAccountTask
|
deactivateAccountTask.execute(DeactivateAccountTask.Params(password, eraseAllData))
|
||||||
.configureWith(DeactivateAccountTask.Params(password, eraseAllData)) {
|
|
||||||
this.callback = callback
|
|
||||||
}
|
|
||||||
.executeBy(taskExecutor)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,6 +66,7 @@ internal abstract class IdentityModule {
|
|||||||
.apply {
|
.apply {
|
||||||
realmKeysUtils.configureEncryption(this, SessionModule.getKeyAlias(userMd5))
|
realmKeysUtils.configureEncryption(this, SessionModule.getKeyAlias(userMd5))
|
||||||
}
|
}
|
||||||
|
.allowWritesOnUiThread(true)
|
||||||
.modules(IdentityRealmModule())
|
.modules(IdentityRealmModule())
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,7 @@ import org.matrix.android.sdk.internal.task.TaskExecutor
|
|||||||
import org.matrix.android.sdk.internal.task.configureWith
|
import org.matrix.android.sdk.internal.task.configureWith
|
||||||
import org.matrix.android.sdk.internal.task.launchToCallback
|
import org.matrix.android.sdk.internal.task.launchToCallback
|
||||||
import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
|
import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
|
||||||
|
import org.matrix.android.sdk.internal.util.awaitCallback
|
||||||
|
|
||||||
internal class DefaultStateService @AssistedInject constructor(@Assisted private val roomId: String,
|
internal class DefaultStateService @AssistedInject constructor(@Assisted private val roomId: String,
|
||||||
private val stateEventDataSource: StateEventDataSource,
|
private val stateEventDataSource: StateEventDataSource,
|
||||||
@ -132,23 +133,23 @@ internal class DefaultStateService @AssistedInject constructor(@Assisted private
|
|||||||
override fun updateAvatar(avatarUri: Uri, fileName: String, callback: MatrixCallback<Unit>): Cancelable {
|
override fun updateAvatar(avatarUri: Uri, fileName: String, callback: MatrixCallback<Unit>): Cancelable {
|
||||||
return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, callback) {
|
return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, callback) {
|
||||||
val response = fileUploader.uploadFromUri(avatarUri, fileName, "image/jpeg")
|
val response = fileUploader.uploadFromUri(avatarUri, fileName, "image/jpeg")
|
||||||
sendStateEvent(
|
awaitCallback<Unit> {
|
||||||
eventType = EventType.STATE_ROOM_AVATAR,
|
sendStateEvent(
|
||||||
body = mapOf("url" to response.contentUri),
|
eventType = EventType.STATE_ROOM_AVATAR,
|
||||||
callback = callback,
|
body = mapOf("url" to response.contentUri),
|
||||||
stateKey = null
|
callback = it,
|
||||||
)
|
stateKey = null
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun deleteAvatar(callback: MatrixCallback<Unit>): Cancelable {
|
override fun deleteAvatar(callback: MatrixCallback<Unit>): Cancelable {
|
||||||
return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, callback) {
|
return sendStateEvent(
|
||||||
sendStateEvent(
|
eventType = EventType.STATE_ROOM_AVATAR,
|
||||||
eventType = EventType.STATE_ROOM_AVATAR,
|
body = emptyMap(),
|
||||||
body = emptyMap(),
|
callback = callback,
|
||||||
callback = callback,
|
stateKey = null
|
||||||
stateKey = null
|
)
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
68
matrix-sdk-android/src/main/res/values-it/strings_sas.xml
Normal file
68
matrix-sdk-android/src/main/res/values-it/strings_sas.xml
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<!-- Generated file, do not edit -->
|
||||||
|
<string name="verification_emoji_dog">Cane</string>
|
||||||
|
<string name="verification_emoji_cat">Gatto</string>
|
||||||
|
<string name="verification_emoji_lion">Leone</string>
|
||||||
|
<string name="verification_emoji_horse">Cavallo</string>
|
||||||
|
<string name="verification_emoji_unicorn">Unicorno</string>
|
||||||
|
<string name="verification_emoji_pig">Maiale</string>
|
||||||
|
<string name="verification_emoji_elephant">Elefante</string>
|
||||||
|
<string name="verification_emoji_rabbit">Coniglio</string>
|
||||||
|
<string name="verification_emoji_panda">Panda</string>
|
||||||
|
<string name="verification_emoji_rooster">Gallo</string>
|
||||||
|
<string name="verification_emoji_penguin">Pinguino</string>
|
||||||
|
<string name="verification_emoji_turtle">Tartaruga</string>
|
||||||
|
<string name="verification_emoji_fish">Pesce</string>
|
||||||
|
<string name="verification_emoji_octopus">Polpo</string>
|
||||||
|
<string name="verification_emoji_butterfly">Farfalla</string>
|
||||||
|
<string name="verification_emoji_flower">Fiore</string>
|
||||||
|
<string name="verification_emoji_tree">Albero</string>
|
||||||
|
<string name="verification_emoji_cactus">Cactus</string>
|
||||||
|
<string name="verification_emoji_mushroom">Fungo</string>
|
||||||
|
<string name="verification_emoji_globe">Globo</string>
|
||||||
|
<string name="verification_emoji_moon">Luna</string>
|
||||||
|
<string name="verification_emoji_cloud">Nuvola</string>
|
||||||
|
<string name="verification_emoji_fire">Fuoco</string>
|
||||||
|
<string name="verification_emoji_banana">Banana</string>
|
||||||
|
<string name="verification_emoji_apple">Mela</string>
|
||||||
|
<string name="verification_emoji_strawberry">Fragola</string>
|
||||||
|
<string name="verification_emoji_corn">Mais</string>
|
||||||
|
<string name="verification_emoji_pizza">Pizza</string>
|
||||||
|
<string name="verification_emoji_cake">Torta</string>
|
||||||
|
<string name="verification_emoji_heart">Cuore</string>
|
||||||
|
<string name="verification_emoji_smiley">Faccina sorridente</string>
|
||||||
|
<string name="verification_emoji_robot">Robot</string>
|
||||||
|
<string name="verification_emoji_hat">Cappello</string>
|
||||||
|
<string name="verification_emoji_glasses">Occhiali</string>
|
||||||
|
<string name="verification_emoji_spanner">Chiave inglese</string>
|
||||||
|
<string name="verification_emoji_santa">Babbo Natale</string>
|
||||||
|
<string name="verification_emoji_thumbs_up">Pollice alzato</string>
|
||||||
|
<string name="verification_emoji_umbrella">Ombrello</string>
|
||||||
|
<string name="verification_emoji_hourglass">Clessidra</string>
|
||||||
|
<string name="verification_emoji_clock">Orologio</string>
|
||||||
|
<string name="verification_emoji_gift">Regalo</string>
|
||||||
|
<string name="verification_emoji_light_bulb">Lampadina</string>
|
||||||
|
<string name="verification_emoji_book">Libro</string>
|
||||||
|
<string name="verification_emoji_pencil">Matita</string>
|
||||||
|
<string name="verification_emoji_paperclip">Graffetta</string>
|
||||||
|
<string name="verification_emoji_scissors">Forbici</string>
|
||||||
|
<string name="verification_emoji_lock">Lucchetto</string>
|
||||||
|
<string name="verification_emoji_key">Chiave</string>
|
||||||
|
<string name="verification_emoji_hammer">Martello</string>
|
||||||
|
<string name="verification_emoji_telephone">Telefono</string>
|
||||||
|
<string name="verification_emoji_flag">Bandiera</string>
|
||||||
|
<string name="verification_emoji_train">Treno</string>
|
||||||
|
<string name="verification_emoji_bicycle">Bicicletta</string>
|
||||||
|
<string name="verification_emoji_aeroplane">Aeroplano</string>
|
||||||
|
<string name="verification_emoji_rocket">Razzo</string>
|
||||||
|
<string name="verification_emoji_trophy">Trofeo</string>
|
||||||
|
<string name="verification_emoji_ball">Palla</string>
|
||||||
|
<string name="verification_emoji_guitar">Chitarra</string>
|
||||||
|
<string name="verification_emoji_trumpet">Trombetta</string>
|
||||||
|
<string name="verification_emoji_bell">Campana</string>
|
||||||
|
<string name="verification_emoji_anchor">Ancora</string>
|
||||||
|
<string name="verification_emoji_headphones">Cuffie</string>
|
||||||
|
<string name="verification_emoji_folder">Cartella</string>
|
||||||
|
<string name="verification_emoji_pin">Puntina</string>
|
||||||
|
</resources>
|
@ -94,19 +94,21 @@ class CameraPicker {
|
|||||||
return Intent(MediaStore.ACTION_IMAGE_CAPTURE)
|
return Intent(MediaStore.ACTION_IMAGE_CAPTURE)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createPhotoUri(context: Context): Uri {
|
companion object {
|
||||||
val file = createImageFile(context)
|
fun createPhotoUri(context: Context): Uri {
|
||||||
val authority = context.packageName + ".multipicker.fileprovider"
|
val file = createImageFile(context)
|
||||||
return FileProvider.getUriForFile(context, authority, file)
|
val authority = context.packageName + ".multipicker.fileprovider"
|
||||||
}
|
return FileProvider.getUriForFile(context, authority, file)
|
||||||
|
}
|
||||||
|
|
||||||
private fun createImageFile(context: Context): File {
|
private fun createImageFile(context: Context): File {
|
||||||
val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date())
|
val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date())
|
||||||
val storageDir: File = context.filesDir
|
val storageDir: File = context.filesDir
|
||||||
return File.createTempFile(
|
return File.createTempFile(
|
||||||
"${timeStamp}_", /* prefix */
|
"${timeStamp}_", /* prefix */
|
||||||
".jpg", /* suffix */
|
".jpg", /* suffix */
|
||||||
storageDir /* directory */
|
storageDir /* directory */
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ androidExtensions {
|
|||||||
// Note: 2 digits max for each value
|
// Note: 2 digits max for each value
|
||||||
ext.versionMajor = 1
|
ext.versionMajor = 1
|
||||||
ext.versionMinor = 0
|
ext.versionMinor = 0
|
||||||
ext.versionPatch = 10
|
ext.versionPatch = 11
|
||||||
|
|
||||||
static def getGitTimestamp() {
|
static def getGitTimestamp() {
|
||||||
def cmd = 'git show -s --format=%ct'
|
def cmd = 'git show -s --format=%ct'
|
||||||
|
@ -23,6 +23,7 @@ import androidx.core.net.toUri
|
|||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import com.yalantis.ucrop.UCrop
|
import com.yalantis.ucrop.UCrop
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
|
import im.vector.app.core.extensions.insertBeforeLast
|
||||||
import im.vector.app.core.extensions.registerStartForActivityResult
|
import im.vector.app.core.extensions.registerStartForActivityResult
|
||||||
import im.vector.app.core.resources.ColorProvider
|
import im.vector.app.core.resources.ColorProvider
|
||||||
import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO
|
import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO
|
||||||
@ -86,7 +87,7 @@ class GalleryOrCameraDialogHelper(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun startUCrop(image: MultiPickerImageType) {
|
private fun startUCrop(image: MultiPickerImageType) {
|
||||||
val destinationFile = File(activity.cacheDir, "${image.displayName}_e_${System.currentTimeMillis()}")
|
val destinationFile = File(activity.cacheDir, image.displayName.insertBeforeLast("_e_${System.currentTimeMillis()}"))
|
||||||
val uri = image.contentUri
|
val uri = image.contentUri
|
||||||
createUCropWithDefaultSettings(colorProvider, uri, destinationFile.toUri(), fragment.getString(R.string.rotate_and_crop_screen_title))
|
createUCropWithDefaultSettings(colorProvider, uri, destinationFile.toUri(), fragment.getString(R.string.rotate_and_crop_screen_title))
|
||||||
.withAspectRatio(1f, 1f)
|
.withAspectRatio(1f, 1f)
|
||||||
@ -116,7 +117,7 @@ class GalleryOrCameraDialogHelper(
|
|||||||
when (type) {
|
when (type) {
|
||||||
Type.Camera ->
|
Type.Camera ->
|
||||||
if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, activity, takePhotoPermissionActivityResultLauncher)) {
|
if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, activity, takePhotoPermissionActivityResultLauncher)) {
|
||||||
avatarCameraUri = MultiPicker.get(MultiPicker.CAMERA).startWithExpectingFile(activity, takePhotoActivityResultLauncher)
|
doOpenCamera()
|
||||||
}
|
}
|
||||||
Type.Gallery ->
|
Type.Gallery ->
|
||||||
MultiPicker.get(MultiPicker.IMAGE).single().startWith(pickImageActivityResultLauncher)
|
MultiPicker.get(MultiPicker.IMAGE).single().startWith(pickImageActivityResultLauncher)
|
||||||
|
@ -48,3 +48,21 @@ fun CharSequence.isMsisdn(): Boolean {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Useful to append a String at the end of a filename but before the extension if any
|
||||||
|
* Ex:
|
||||||
|
* - "file.txt".insertBeforeLast("_foo") will return "file_foo.txt"
|
||||||
|
* - "file".insertBeforeLast("_foo") will return "file_foo"
|
||||||
|
* - "fi.le.txt".insertBeforeLast("_foo") will return "fi.le_foo.txt"
|
||||||
|
* - null.insertBeforeLast("_foo") will return "_foo"
|
||||||
|
*/
|
||||||
|
fun String?.insertBeforeLast(insert: String, delimiter: String = ".") : String {
|
||||||
|
if (this == null) return insert
|
||||||
|
val idx = lastIndexOf(delimiter)
|
||||||
|
return if (idx == -1) {
|
||||||
|
this + insert
|
||||||
|
} else {
|
||||||
|
replaceRange(idx, idx, insert)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -38,6 +38,7 @@ import com.airbnb.mvrx.withState
|
|||||||
import com.yalantis.ucrop.UCrop
|
import com.yalantis.ucrop.UCrop
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.extensions.cleanup
|
import im.vector.app.core.extensions.cleanup
|
||||||
|
import im.vector.app.core.extensions.insertBeforeLast
|
||||||
import im.vector.app.core.extensions.registerStartForActivityResult
|
import im.vector.app.core.extensions.registerStartForActivityResult
|
||||||
import im.vector.app.core.platform.VectorBaseFragment
|
import im.vector.app.core.platform.VectorBaseFragment
|
||||||
import im.vector.app.core.resources.ColorProvider
|
import im.vector.app.core.resources.ColorProvider
|
||||||
@ -170,7 +171,7 @@ class AttachmentsPreviewFragment @Inject constructor(
|
|||||||
|
|
||||||
private fun handleEditAction() = withState(viewModel) {
|
private fun handleEditAction() = withState(viewModel) {
|
||||||
val currentAttachment = it.attachments.getOrNull(it.currentAttachmentIndex) ?: return@withState
|
val currentAttachment = it.attachments.getOrNull(it.currentAttachmentIndex) ?: return@withState
|
||||||
val destinationFile = File(requireContext().cacheDir, "${currentAttachment.name}_edited_image_${System.currentTimeMillis()}")
|
val destinationFile = File(requireContext().cacheDir, currentAttachment.name.insertBeforeLast("_edited_image_${System.currentTimeMillis()}"))
|
||||||
val uri = currentAttachment.queryUri
|
val uri = currentAttachment.queryUri
|
||||||
createUCropWithDefaultSettings(colorProvider, uri, destinationFile.toUri(), currentAttachment.name)
|
createUCropWithDefaultSettings(colorProvider, uri, destinationFile.toUri(), currentAttachment.name)
|
||||||
.getIntent(requireContext())
|
.getIntent(requireContext())
|
||||||
|
@ -20,5 +20,8 @@ import im.vector.app.core.platform.VectorViewModelAction
|
|||||||
import im.vector.app.features.userdirectory.PendingInvitee
|
import im.vector.app.features.userdirectory.PendingInvitee
|
||||||
|
|
||||||
sealed class CreateDirectRoomAction : VectorViewModelAction {
|
sealed class CreateDirectRoomAction : VectorViewModelAction {
|
||||||
data class CreateRoomAndInviteSelectedUsers(val invitees: Set<PendingInvitee>) : CreateDirectRoomAction()
|
data class CreateRoomAndInviteSelectedUsers(
|
||||||
|
val invitees: Set<PendingInvitee>,
|
||||||
|
val existingDmRoomId: String?
|
||||||
|
) : CreateDirectRoomAction()
|
||||||
}
|
}
|
||||||
|
@ -91,7 +91,8 @@ class CreateDirectRoomActivity : SimpleFragmentActivity() {
|
|||||||
KnownUsersFragment::class.java,
|
KnownUsersFragment::class.java,
|
||||||
KnownUsersFragmentArgs(
|
KnownUsersFragmentArgs(
|
||||||
title = getString(R.string.fab_menu_create_chat),
|
title = getString(R.string.fab_menu_create_chat),
|
||||||
menuResId = R.menu.vector_create_direct_room
|
menuResId = R.menu.vector_create_direct_room,
|
||||||
|
isCreatingRoom = true
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -121,7 +122,10 @@ class CreateDirectRoomActivity : SimpleFragmentActivity() {
|
|||||||
|
|
||||||
private fun onMenuItemSelected(action: UserDirectorySharedAction.OnMenuItemSelected) {
|
private fun onMenuItemSelected(action: UserDirectorySharedAction.OnMenuItemSelected) {
|
||||||
if (action.itemId == R.id.action_create_direct_room) {
|
if (action.itemId == R.id.action_create_direct_room) {
|
||||||
viewModel.handle(CreateDirectRoomAction.CreateRoomAndInviteSelectedUsers(action.invitees))
|
viewModel.handle(CreateDirectRoomAction.CreateRoomAndInviteSelectedUsers(
|
||||||
|
action.invitees,
|
||||||
|
action.existingDmRoomId
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ package im.vector.app.features.createdirect
|
|||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.airbnb.mvrx.ActivityViewModelContext
|
import com.airbnb.mvrx.ActivityViewModelContext
|
||||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||||
|
import com.airbnb.mvrx.Success
|
||||||
import com.airbnb.mvrx.ViewModelContext
|
import com.airbnb.mvrx.ViewModelContext
|
||||||
import com.squareup.inject.assisted.Assisted
|
import com.squareup.inject.assisted.Assisted
|
||||||
import com.squareup.inject.assisted.AssistedInject
|
import com.squareup.inject.assisted.AssistedInject
|
||||||
@ -56,7 +57,22 @@ class CreateDirectRoomViewModel @AssistedInject constructor(@Assisted
|
|||||||
|
|
||||||
override fun handle(action: CreateDirectRoomAction) {
|
override fun handle(action: CreateDirectRoomAction) {
|
||||||
when (action) {
|
when (action) {
|
||||||
is CreateDirectRoomAction.CreateRoomAndInviteSelectedUsers -> createRoomAndInviteSelectedUsers(action.invitees)
|
is CreateDirectRoomAction.CreateRoomAndInviteSelectedUsers -> onSubmitInvitees(action)
|
||||||
|
}.exhaustive
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If users already have a DM room then navigate to it instead of creating a new room.
|
||||||
|
*/
|
||||||
|
private fun onSubmitInvitees(action: CreateDirectRoomAction.CreateRoomAndInviteSelectedUsers) {
|
||||||
|
if (action.existingDmRoomId != null) {
|
||||||
|
// Do not create a new DM, just tell that the creation is successful by passing the existing roomId
|
||||||
|
setState {
|
||||||
|
copy(createAndInviteState = Success(action.existingDmRoomId))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Create the DM
|
||||||
|
createRoomAndInviteSelectedUsers(action.invitees)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,6 +140,12 @@ class SharedSecureStorageViewModel @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun handleDoResetAll() {
|
private fun handleDoResetAll() {
|
||||||
|
// as we are going to reset, we'd better cancel all outgoing requests
|
||||||
|
// if not they could be accepted in the middle of the reset process
|
||||||
|
// and cause strange use cases
|
||||||
|
session.cryptoService().verificationService().getExistingVerificationRequests(session.myUserId).forEach {
|
||||||
|
session.cryptoService().verificationService().cancelVerificationRequest(it)
|
||||||
|
}
|
||||||
_viewEvents.post(SharedSecureStorageViewEvent.ShowResetBottomSheet)
|
_viewEvents.post(SharedSecureStorageViewEvent.ShowResetBottomSheet)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,7 +237,7 @@ class BootstrapCrossSigningTask @Inject constructor(
|
|||||||
Timber.d("## BootstrapCrossSigningTask: Creating 4S - Checking megolm backup")
|
Timber.d("## BootstrapCrossSigningTask: Creating 4S - Checking megolm backup")
|
||||||
|
|
||||||
// First ensure that in sync
|
// First ensure that in sync
|
||||||
val serverVersion = awaitCallback<KeysVersionResult?> {
|
var serverVersion = awaitCallback<KeysVersionResult?> {
|
||||||
session.cryptoService().keysBackupService().getCurrentVersion(it)
|
session.cryptoService().keysBackupService().getCurrentVersion(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -247,6 +247,16 @@ class BootstrapCrossSigningTask @Inject constructor(
|
|||||||
|| (params.setupMode == SetupMode.PASSPHRASE_AND_NEEDED_SECRETS_RESET && !isMegolmBackupSecretKnown)
|
|| (params.setupMode == SetupMode.PASSPHRASE_AND_NEEDED_SECRETS_RESET && !isMegolmBackupSecretKnown)
|
||||||
|| (params.setupMode == SetupMode.HARD_RESET)
|
|| (params.setupMode == SetupMode.HARD_RESET)
|
||||||
if (shouldCreateKeyBackup) {
|
if (shouldCreateKeyBackup) {
|
||||||
|
// clear all existing backups
|
||||||
|
while (serverVersion != null) {
|
||||||
|
awaitCallback<Unit> {
|
||||||
|
session.cryptoService().keysBackupService().deleteBackup(serverVersion!!.version, it)
|
||||||
|
}
|
||||||
|
serverVersion = awaitCallback {
|
||||||
|
session.cryptoService().keysBackupService().getCurrentVersion(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Timber.d("## BootstrapCrossSigningTask: Creating 4S - Create megolm backup")
|
Timber.d("## BootstrapCrossSigningTask: Creating 4S - Create megolm backup")
|
||||||
val creationInfo = awaitCallback<MegolmBackupCreationInfo> {
|
val creationInfo = awaitCallback<MegolmBackupCreationInfo> {
|
||||||
session.cryptoService().keysBackupService().prepareKeysBackupVersion(null, null, it)
|
session.cryptoService().keysBackupService().prepareKeysBackupVersion(null, null, it)
|
||||||
|
@ -164,7 +164,7 @@ class IncomingVerificationRequestHandler @Inject constructor(
|
|||||||
|
|
||||||
override fun verificationRequestUpdated(pr: PendingVerificationRequest) {
|
override fun verificationRequestUpdated(pr: PendingVerificationRequest) {
|
||||||
// If an incoming request is readied (by another device?) we should discard the alert
|
// If an incoming request is readied (by another device?) we should discard the alert
|
||||||
if (pr.isIncoming && (pr.isReady || pr.handledByOtherSession)) {
|
if (pr.isIncoming && (pr.isReady || pr.handledByOtherSession || pr.cancelConclusion != null)) {
|
||||||
popupAlertManager.cancelAlert(uniqueIdForVerificationRequest(pr))
|
popupAlertManager.cancelAlert(uniqueIdForVerificationRequest(pr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,8 +99,8 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(
|
|||||||
val pr = if (selfVerificationMode) {
|
val pr = if (selfVerificationMode) {
|
||||||
// See if active tx for this user and take it
|
// See if active tx for this user and take it
|
||||||
|
|
||||||
session.cryptoService().verificationService().getExistingVerificationRequest(args.otherUserId)
|
session.cryptoService().verificationService().getExistingVerificationRequests(args.otherUserId)
|
||||||
?.lastOrNull { !it.isFinished }
|
.lastOrNull { !it.isFinished }
|
||||||
?.also { verificationRequest ->
|
?.also { verificationRequest ->
|
||||||
if (verificationRequest.isIncoming && !verificationRequest.isReady) {
|
if (verificationRequest.isIncoming && !verificationRequest.isReady) {
|
||||||
// auto ready in this case, as we are waiting
|
// auto ready in this case, as we are waiting
|
||||||
|
@ -153,20 +153,20 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun handleSetAvatarAction(action: RoomSettingsAction.SetAvatarAction) {
|
private fun handleSetAvatarAction(action: RoomSettingsAction.SetAvatarAction) {
|
||||||
deletePendingAvatar()
|
setState {
|
||||||
setState { copy(avatarAction = action.avatarAction) }
|
deletePendingAvatar(this)
|
||||||
}
|
copy(avatarAction = action.avatarAction)
|
||||||
|
|
||||||
private fun deletePendingAvatar() {
|
|
||||||
// Maybe delete the pending avatar
|
|
||||||
withState {
|
|
||||||
(it.avatarAction as? RoomSettingsViewState.AvatarAction.UpdateAvatar)
|
|
||||||
?.let { tryOrNull { it.newAvatarUri.toFile().delete() } }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun deletePendingAvatar(state: RoomSettingsViewState) {
|
||||||
|
// Maybe delete the pending avatar
|
||||||
|
(state.avatarAction as? RoomSettingsViewState.AvatarAction.UpdateAvatar)
|
||||||
|
?.let { tryOrNull { it.newAvatarUri.toFile().delete() } }
|
||||||
|
}
|
||||||
|
|
||||||
private fun cancel() {
|
private fun cancel() {
|
||||||
deletePendingAvatar()
|
withState { deletePendingAvatar(it) }
|
||||||
|
|
||||||
_viewEvents.post(RoomSettingsViewEvents.GoBack)
|
_viewEvents.post(RoomSettingsViewEvents.GoBack)
|
||||||
}
|
}
|
||||||
@ -180,7 +180,7 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState:
|
|||||||
|
|
||||||
when (val avatarAction = state.avatarAction) {
|
when (val avatarAction = state.avatarAction) {
|
||||||
RoomSettingsViewState.AvatarAction.None -> Unit
|
RoomSettingsViewState.AvatarAction.None -> Unit
|
||||||
RoomSettingsViewState.AvatarAction.DeleteAvatar -> {
|
RoomSettingsViewState.AvatarAction.DeleteAvatar -> {
|
||||||
operationList.add(room.rx().deleteAvatar())
|
operationList.add(room.rx().deleteAvatar())
|
||||||
}
|
}
|
||||||
is RoomSettingsViewState.AvatarAction.UpdateAvatar -> {
|
is RoomSettingsViewState.AvatarAction.UpdateAvatar -> {
|
||||||
@ -209,8 +209,13 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState:
|
|||||||
.subscribe(
|
.subscribe(
|
||||||
{
|
{
|
||||||
postLoading(false)
|
postLoading(false)
|
||||||
setState { copy(newHistoryVisibility = null) }
|
setState {
|
||||||
deletePendingAvatar()
|
deletePendingAvatar(this)
|
||||||
|
copy(
|
||||||
|
avatarAction = RoomSettingsViewState.AvatarAction.None,
|
||||||
|
newHistoryVisibility = null
|
||||||
|
)
|
||||||
|
}
|
||||||
_viewEvents.post(RoomSettingsViewEvents.Success)
|
_viewEvents.post(RoomSettingsViewEvents.Success)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -27,6 +27,7 @@ import android.widget.ImageView
|
|||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.preference.EditTextPreference
|
import androidx.preference.EditTextPreference
|
||||||
import androidx.preference.Preference
|
import androidx.preference.Preference
|
||||||
import androidx.preference.PreferenceCategory
|
import androidx.preference.PreferenceCategory
|
||||||
@ -451,28 +452,25 @@ class VectorSettingsGeneralFragment @Inject constructor(
|
|||||||
val newPwd = newPasswordText.text.toString()
|
val newPwd = newPasswordText.text.toString()
|
||||||
|
|
||||||
showPasswordLoadingView(true)
|
showPasswordLoadingView(true)
|
||||||
session.changePassword(oldPwd, newPwd, object : MatrixCallback<Unit> {
|
lifecycleScope.launch {
|
||||||
override fun onSuccess(data: Unit) {
|
val result = runCatching {
|
||||||
if (!isAdded) {
|
session.changePassword(oldPwd, newPwd)
|
||||||
return
|
}
|
||||||
}
|
if (!isAdded) {
|
||||||
showPasswordLoadingView(false)
|
return@launch
|
||||||
|
}
|
||||||
|
showPasswordLoadingView(false)
|
||||||
|
result.fold({
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
activity.toast(R.string.settings_password_updated)
|
activity.toast(R.string.settings_password_updated)
|
||||||
}
|
}, { failure ->
|
||||||
|
|
||||||
override fun onFailure(failure: Throwable) {
|
|
||||||
if (!isAdded) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
showPasswordLoadingView(false)
|
|
||||||
if (failure.isInvalidPassword()) {
|
if (failure.isInvalidPassword()) {
|
||||||
oldPasswordTil.error = getString(R.string.settings_fail_to_update_password_invalid_current_password)
|
oldPasswordTil.error = getString(R.string.settings_fail_to_update_password_invalid_current_password)
|
||||||
} else {
|
} else {
|
||||||
oldPasswordTil.error = getString(R.string.settings_fail_to_update_password)
|
oldPasswordTil.error = getString(R.string.settings_fail_to_update_password)
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dialog.show()
|
dialog.show()
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package im.vector.app.features.settings.account.deactivation
|
package im.vector.app.features.settings.account.deactivation
|
||||||
|
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.airbnb.mvrx.FragmentViewModelContext
|
import com.airbnb.mvrx.FragmentViewModelContext
|
||||||
import com.airbnb.mvrx.MvRxState
|
import com.airbnb.mvrx.MvRxState
|
||||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||||
@ -24,9 +25,10 @@ import com.squareup.inject.assisted.AssistedInject
|
|||||||
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
|
||||||
import im.vector.app.core.platform.VectorViewModelAction
|
import im.vector.app.core.platform.VectorViewModelAction
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
import kotlinx.coroutines.launch
|
||||||
import org.matrix.android.sdk.api.failure.isInvalidPassword
|
import org.matrix.android.sdk.api.failure.isInvalidPassword
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
|
import java.lang.Exception
|
||||||
|
|
||||||
data class DeactivateAccountViewState(
|
data class DeactivateAccountViewState(
|
||||||
val passwordShown: Boolean = false
|
val passwordShown: Boolean = false
|
||||||
@ -67,19 +69,20 @@ class DeactivateAccountViewModel @AssistedInject constructor(@Assisted private v
|
|||||||
|
|
||||||
_viewEvents.post(DeactivateAccountViewEvents.Loading())
|
_viewEvents.post(DeactivateAccountViewEvents.Loading())
|
||||||
|
|
||||||
session.deactivateAccount(action.password, action.eraseAllData, object : MatrixCallback<Unit> {
|
viewModelScope.launch {
|
||||||
override fun onSuccess(data: Unit) {
|
val event = try {
|
||||||
_viewEvents.post(DeactivateAccountViewEvents.Done)
|
session.deactivateAccount(action.password, action.eraseAllData)
|
||||||
}
|
DeactivateAccountViewEvents.Done
|
||||||
|
} catch (failure: Exception) {
|
||||||
override fun onFailure(failure: Throwable) {
|
|
||||||
if (failure.isInvalidPassword()) {
|
if (failure.isInvalidPassword()) {
|
||||||
_viewEvents.post(DeactivateAccountViewEvents.InvalidPassword)
|
DeactivateAccountViewEvents.InvalidPassword
|
||||||
} else {
|
} else {
|
||||||
_viewEvents.post(DeactivateAccountViewEvents.OtherFailure(failure))
|
DeactivateAccountViewEvents.OtherFailure(failure)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
_viewEvents.post(event)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object : MvRxViewModelFactory<DeactivateAccountViewModel, DeactivateAccountViewState> {
|
companion object : MvRxViewModelFactory<DeactivateAccountViewModel, DeactivateAccountViewState> {
|
||||||
|
@ -91,13 +91,20 @@ class KnownUsersFragment @Inject constructor(
|
|||||||
val showMenuItem = it.pendingInvitees.isNotEmpty()
|
val showMenuItem = it.pendingInvitees.isNotEmpty()
|
||||||
menu.forEach { menuItem ->
|
menu.forEach { menuItem ->
|
||||||
menuItem.isVisible = showMenuItem
|
menuItem.isVisible = showMenuItem
|
||||||
|
if (args.isCreatingRoom) {
|
||||||
|
menuItem.setTitle(if (it.existingDmRoomId != null) R.string.action_open else R.string.create_room_action_create)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
super.onPrepareOptionsMenu(menu)
|
super.onPrepareOptionsMenu(menu)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean = withState(viewModel) {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean = withState(viewModel) {
|
||||||
sharedActionViewModel.post(UserDirectorySharedAction.OnMenuItemSelected(item.itemId, it.pendingInvitees))
|
sharedActionViewModel.post(UserDirectorySharedAction.OnMenuItemSelected(
|
||||||
|
item.itemId,
|
||||||
|
it.pendingInvitees,
|
||||||
|
it.existingDmRoomId
|
||||||
|
))
|
||||||
return@withState true
|
return@withState true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,5 +23,6 @@ import kotlinx.android.parcel.Parcelize
|
|||||||
data class KnownUsersFragmentArgs(
|
data class KnownUsersFragmentArgs(
|
||||||
val title: String,
|
val title: String,
|
||||||
val menuResId: Int,
|
val menuResId: Int,
|
||||||
val excludedUserIds: Set<String>? = null
|
val excludedUserIds: Set<String>? = null,
|
||||||
|
val isCreatingRoom: Boolean = false
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
@ -23,5 +23,7 @@ sealed class UserDirectorySharedAction : VectorSharedAction {
|
|||||||
object OpenPhoneBook : UserDirectorySharedAction()
|
object OpenPhoneBook : UserDirectorySharedAction()
|
||||||
object Close : UserDirectorySharedAction()
|
object Close : UserDirectorySharedAction()
|
||||||
object GoBack : UserDirectorySharedAction()
|
object GoBack : UserDirectorySharedAction()
|
||||||
data class OnMenuItemSelected(val itemId: Int, val invitees: Set<PendingInvitee>) : UserDirectorySharedAction()
|
data class OnMenuItemSelected(val itemId: Int,
|
||||||
|
val invitees: Set<PendingInvitee>,
|
||||||
|
val existingDmRoomId: String?) : UserDirectorySharedAction()
|
||||||
}
|
}
|
||||||
|
@ -87,14 +87,32 @@ class UserDirectoryViewModel @AssistedInject constructor(@Assisted
|
|||||||
|
|
||||||
private fun handleRemoveSelectedUser(action: UserDirectoryAction.RemovePendingInvitee) = withState { state ->
|
private fun handleRemoveSelectedUser(action: UserDirectoryAction.RemovePendingInvitee) = withState { state ->
|
||||||
val selectedUsers = state.pendingInvitees.minus(action.pendingInvitee)
|
val selectedUsers = state.pendingInvitees.minus(action.pendingInvitee)
|
||||||
setState { copy(pendingInvitees = selectedUsers) }
|
setState {
|
||||||
|
copy(
|
||||||
|
pendingInvitees = selectedUsers,
|
||||||
|
existingDmRoomId = getExistingDmRoomId(selectedUsers)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleSelectUser(action: UserDirectoryAction.SelectPendingInvitee) = withState { state ->
|
private fun handleSelectUser(action: UserDirectoryAction.SelectPendingInvitee) = withState { state ->
|
||||||
// Reset the filter asap
|
// Reset the filter asap
|
||||||
directoryUsersSearch.accept("")
|
directoryUsersSearch.accept("")
|
||||||
val selectedUsers = state.pendingInvitees.toggle(action.pendingInvitee)
|
val selectedUsers = state.pendingInvitees.toggle(action.pendingInvitee)
|
||||||
setState { copy(pendingInvitees = selectedUsers) }
|
setState {
|
||||||
|
copy(
|
||||||
|
pendingInvitees = selectedUsers,
|
||||||
|
existingDmRoomId = getExistingDmRoomId(selectedUsers)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getExistingDmRoomId(selectedUsers: Set<PendingInvitee>): String? {
|
||||||
|
return selectedUsers
|
||||||
|
.takeIf { it.size == 1 }
|
||||||
|
?.filterIsInstance(PendingInvitee.UserPendingInvitee::class.java)
|
||||||
|
?.firstOrNull()
|
||||||
|
?.let { invitee -> session.getExistingDirectRoomWithUser(invitee.user.userId) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun observeDirectoryUsers() = withState { state ->
|
private fun observeDirectoryUsers() = withState { state ->
|
||||||
|
@ -30,7 +30,8 @@ data class UserDirectoryViewState(
|
|||||||
val pendingInvitees: Set<PendingInvitee> = emptySet(),
|
val pendingInvitees: Set<PendingInvitee> = emptySet(),
|
||||||
val createAndInviteState: Async<String> = Uninitialized,
|
val createAndInviteState: Async<String> = Uninitialized,
|
||||||
val directorySearchTerm: String = "",
|
val directorySearchTerm: String = "",
|
||||||
val filterKnownUsersValue: Option<String> = Option.empty()
|
val filterKnownUsersValue: Option<String> = Option.empty(),
|
||||||
|
val existingDmRoomId: String? = null
|
||||||
) : MvRxState {
|
) : MvRxState {
|
||||||
|
|
||||||
constructor(args: KnownUsersFragmentArgs) : this(excludedUserIds = args.excludedUserIds)
|
constructor(args: KnownUsersFragmentArgs) : this(excludedUserIds = args.excludedUserIds)
|
||||||
|
@ -115,7 +115,9 @@
|
|||||||
|
|
||||||
</im.vector.app.core.preference.VectorPreferenceCategory>
|
</im.vector.app.core.preference.VectorPreferenceCategory>
|
||||||
|
|
||||||
<im.vector.app.core.preference.VectorPreferenceCategory android:title="@string/settings_other">
|
<im.vector.app.core.preference.VectorPreferenceCategory
|
||||||
|
android:key="SETTINGS_SECURITY_OTHER_CATEGORY"
|
||||||
|
android:title="@string/settings_other">
|
||||||
|
|
||||||
<im.vector.app.core.preference.VectorPreference
|
<im.vector.app.core.preference.VectorPreference
|
||||||
android:key="SETTINGS_SECURITY_PIN"
|
android:key="SETTINGS_SECURITY_PIN"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user