diff --git a/CHANGES.md b/CHANGES.md
index 135b881501..4d7eedc38c 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,4 +1,34 @@
-Changes in Element 1.0.14 (2020-XX-XX)
+Changes in Element 1.0.15 (2020-02-03)
+===================================================
+
+Features ✨:
+ - Social Login support
+
+Improvements 🙌:
+ - SSO support for cross signing (#1062)
+ - Deactivate account when logged in with SSO (#1264)
+ - SSO UIA doesn't work (#2754)
+
+Bugfix 🐛:
+ - Fix clear cache issue: sometimes, after a clear cache, there is still a token, so the init sync service is not started.
+ - Sidebar too large in horizontal orientation or tablets (#475)
+ - UrlPreview should be updated when the url is edited and changed (#2678)
+ - When receiving a new pepper from identity server, use it on the next hash lookup (#2708)
+ - Crashes reported by PlayStore (new in 1.0.14) (#2707)
+ - Widgets: Support $matrix_widget_id parameter (#2748)
+ - Data for Worker overload (#2721)
+ - Fix multiple tasks
+
+SDK API changes ⚠️:
+ - Increase targetSdkVersion to 30 (#2600)
+
+Build 🧱:
+ - Compile with Android SDK 30 (Android 11)
+
+Other changes:
+ - Update Dagger to 2.31 version so we can use the embedded AssistedInject feature
+
+Changes in Element 1.0.14 (2020-01-15)
===================================================
Features ✨:
diff --git a/attachment-viewer/build.gradle b/attachment-viewer/build.gradle
index d8cd7d0c98..5ce9f1eff6 100644
--- a/attachment-viewer/build.gradle
+++ b/attachment-viewer/build.gradle
@@ -32,11 +32,11 @@ buildscript {
}
android {
- compileSdkVersion 29
+ compileSdkVersion 30
defaultConfig {
minSdkVersion 21
- targetSdkVersion 29
+ targetSdkVersion 30
versionCode 1
versionName "1.0"
}
diff --git a/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/AttachmentViewerActivity.kt b/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/AttachmentViewerActivity.kt
index 9b1345cd39..418b5b5cbb 100644
--- a/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/AttachmentViewerActivity.kt
+++ b/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/AttachmentViewerActivity.kt
@@ -18,15 +18,19 @@
package im.vector.lib.attachmentviewer
import android.graphics.Color
+import android.os.Build
import android.os.Bundle
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.ScaleGestureDetector
import android.view.View
import android.view.ViewGroup
+import android.view.WindowInsets
+import android.view.WindowInsetsController
import android.view.WindowManager
import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity
+import androidx.core.content.ContextCompat
import androidx.core.view.GestureDetectorCompat
import androidx.core.view.ViewCompat
import androidx.core.view.isVisible
@@ -94,14 +98,7 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- // This is important for the dispatchTouchEvent, if not we must correct
- // the touch coordinates
- window.decorView.systemUiVisibility = (
- View.SYSTEM_UI_FLAG_LAYOUT_STABLE
- or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
- or View.SYSTEM_UI_FLAG_IMMERSIVE)
- window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
- window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION, WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
+ setDecorViewFullScreen()
views = ActivityAttachmentViewerBinding.inflate(layoutInflater)
setContentView(views.root)
@@ -134,6 +131,29 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
}
}
+ @Suppress("DEPRECATION")
+ private fun setDecorViewFullScreen() {
+ // This is important for the dispatchTouchEvent, if not we must correct
+ // the touch coordinates
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ // New API instead of SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+ window.setDecorFitsSystemWindows(false)
+ // New API instead of SYSTEM_UI_FLAG_IMMERSIVE
+ window.decorView.windowInsetsController?.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE
+ // New API instead of FLAG_TRANSLUCENT_STATUS
+ window.statusBarColor = ContextCompat.getColor(this, R.color.half_transparent_status_bar)
+ // new API instead of FLAG_TRANSLUCENT_NAVIGATION
+ window.navigationBarColor = ContextCompat.getColor(this, R.color.half_transparent_status_bar)
+ } else {
+ window.decorView.systemUiVisibility = (
+ View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ or View.SYSTEM_UI_FLAG_IMMERSIVE)
+ window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
+ window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION, WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
+ }
+ }
+
fun onSelectedPositionChanged(position: Int) {
attachmentsAdapter.recyclerView?.findViewHolderForAdapterPosition(currentPosition)?.let {
(it as? BaseViewHolder)?.onSelected(false)
@@ -313,28 +333,48 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
?.handleCommand(commands)
}
+ @Suppress("DEPRECATION")
private fun hideSystemUI() {
systemUiVisibility = false
// Enables regular immersive mode.
// For "lean back" mode, remove SYSTEM_UI_FLAG_IMMERSIVE.
// Or for "sticky immersive," replace it with SYSTEM_UI_FLAG_IMMERSIVE_STICKY
- window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE
- // Set the content to appear under the system bars so that the
- // content doesn't resize when the system bars hide and show.
- or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
- or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
- or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
- // Hide the nav bar and status bar
- or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
- or View.SYSTEM_UI_FLAG_FULLSCREEN)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ // New API instead of SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+ window.setDecorFitsSystemWindows(false)
+ // new API instead of SYSTEM_UI_FLAG_HIDE_NAVIGATION
+ window.decorView.windowInsetsController?.hide(WindowInsets.Type.navigationBars())
+ // New API instead of SYSTEM_UI_FLAG_IMMERSIVE
+ window.decorView.windowInsetsController?.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE
+ // New API instead of FLAG_TRANSLUCENT_STATUS
+ window.statusBarColor = ContextCompat.getColor(this, R.color.half_transparent_status_bar)
+ // New API instead of FLAG_TRANSLUCENT_NAVIGATION
+ window.navigationBarColor = ContextCompat.getColor(this, R.color.half_transparent_status_bar)
+ } else {
+ window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE
+ // Set the content to appear under the system bars so that the
+ // content doesn't resize when the system bars hide and show.
+ or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+ or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ // Hide the nav bar and status bar
+ or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+ or View.SYSTEM_UI_FLAG_FULLSCREEN)
+ }
}
// Shows the system bars by removing all the flags
// except for the ones that make the content appear under the system bars.
+ @Suppress("DEPRECATION")
private fun showSystemUI() {
systemUiVisibility = true
- window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
- or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
- or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ // New API instead of SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+ window.setDecorFitsSystemWindows(false)
+ } else {
+ window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+ or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)
+ }
}
}
diff --git a/attachment-viewer/src/main/res/values/colors.xml b/attachment-viewer/src/main/res/values/colors.xml
new file mode 100644
index 0000000000..7ceef40881
--- /dev/null
+++ b/attachment-viewer/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+
+ #80000000
+
+
\ No newline at end of file
diff --git a/fastlane/metadata/android/en-US/changelogs/40100150.txt b/fastlane/metadata/android/en-US/changelogs/40100150.txt
new file mode 100644
index 0000000000..c3e10cefbd
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/40100150.txt
@@ -0,0 +1,2 @@
+Main changes in this version: Social Login support.
+Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.0.15
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index f1577bddaa..517ae0d4ce 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionSha256Sum=a7ca23b3ccf265680f2bfd35f1f00b1424f4466292c7337c85d46c9641b3f053
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-all.zip
+distributionSha256Sum=3db89524a3981819ff28c3f979236c1274a726e146ced0c8a2020417f9bc0782
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.1-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/matrix-sdk-android-rx/build.gradle b/matrix-sdk-android-rx/build.gradle
index a99b5856ba..0e899e21ff 100644
--- a/matrix-sdk-android-rx/build.gradle
+++ b/matrix-sdk-android-rx/build.gradle
@@ -3,11 +3,11 @@ apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
android {
- compileSdkVersion 29
+ compileSdkVersion 30
defaultConfig {
minSdkVersion 21
- targetSdkVersion 29
+ targetSdkVersion 30
versionCode 1
versionName "1.0"
diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle
index 6a2f7575e5..836b49b3f2 100644
--- a/matrix-sdk-android/build.gradle
+++ b/matrix-sdk-android/build.gradle
@@ -14,12 +14,12 @@ buildscript {
}
android {
- compileSdkVersion 29
+ compileSdkVersion 30
testOptions.unitTests.includeAndroidResources = true
defaultConfig {
minSdkVersion 21
- targetSdkVersion 29
+ targetSdkVersion 30
versionCode 1
versionName "0.0.1"
// Multidex is useful for tests
@@ -112,7 +112,7 @@ dependencies {
def lifecycle_version = '2.2.0'
def arch_version = '2.1.0'
def markwon_version = '3.1.0'
- def daggerVersion = '2.29.1'
+ def daggerVersion = '2.31'
def work_version = '2.4.0'
def retrofit_version = '2.6.2'
@@ -160,8 +160,6 @@ dependencies {
// DI
implementation "com.google.dagger:dagger:$daggerVersion"
kapt "com.google.dagger:dagger-compiler:$daggerVersion"
- compileOnly 'com.squareup.inject:assisted-inject-annotations-dagger2:0.5.0'
- kapt 'com.squareup.inject:assisted-inject-processor-dagger2:0.5.0'
// Logging
implementation 'com.jakewharton.timber:timber:4.7.1'
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/account/DeactivateAccountTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/account/DeactivateAccountTest.kt
index 9996eef0a8..b0df6fcb44 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/account/DeactivateAccountTest.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/account/DeactivateAccountTest.kt
@@ -16,8 +16,18 @@
package org.matrix.android.sdk.account
+import org.junit.Assert.assertTrue
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.junit.runners.MethodSorters
import org.matrix.android.sdk.InstrumentedTest
+import org.matrix.android.sdk.api.auth.UIABaseAuth
+import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
+import org.matrix.android.sdk.api.auth.UserPasswordAuth
import org.matrix.android.sdk.api.auth.data.LoginFlowResult
+import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
import org.matrix.android.sdk.api.auth.registration.RegistrationResult
import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.failure.MatrixError
@@ -25,12 +35,8 @@ import org.matrix.android.sdk.common.CommonTestHelper
import org.matrix.android.sdk.common.SessionTestParams
import org.matrix.android.sdk.common.TestConstants
import org.matrix.android.sdk.common.TestMatrixCallback
-import org.junit.Assert.assertTrue
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-import org.junit.runners.MethodSorters
+import kotlin.coroutines.Continuation
+import kotlin.coroutines.resume
@RunWith(JUnit4::class)
@FixMethodOrder(MethodSorters.JVM)
@@ -44,7 +50,18 @@ class DeactivateAccountTest : InstrumentedTest {
// Deactivate the account
commonTestHelper.runBlockingTest {
- session.deactivateAccount(TestConstants.PASSWORD, false)
+ session.deactivateAccount(
+ object : UserInteractiveAuthInterceptor {
+ override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) {
+ promise.resume(
+ UserPasswordAuth(
+ user = session.myUserId,
+ password = TestConstants.PASSWORD,
+ session = flowResponse.session
+ )
+ )
+ }
+ }, false)
}
// Try to login on the previous account, it will fail (M_USER_DEACTIVATED)
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt
index cb49ee8818..a4dbd70b11 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt
@@ -378,7 +378,9 @@ class CommonTestHelper(context: Context) {
fun Iterable.signOutAndClose() = forEach { signOutAndClose(it) }
fun signOutAndClose(session: Session) {
- doSync(60_000) { session.signOut(true, it) }
+ runBlockingTest(timeout = 60_000) {
+ session.signOut(true)
+ }
// no need signout will close
// session.close()
}
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt
index 3d5856fc64..eb7e4a9fbe 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt
@@ -19,6 +19,18 @@ package org.matrix.android.sdk.common
import android.os.SystemClock
import android.util.Log
import androidx.lifecycle.Observer
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotNull
+import org.junit.Assert.assertNull
+import org.junit.Assert.assertTrue
+import org.matrix.android.sdk.api.auth.UIABaseAuth
+import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
+import org.matrix.android.sdk.api.auth.UserPasswordAuth
+import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.crypto.verification.IncomingSasVerificationTransaction
import org.matrix.android.sdk.api.session.crypto.verification.OutgoingSasVerificationTransaction
@@ -36,17 +48,10 @@ import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM_BACKUP
import org.matrix.android.sdk.internal.crypto.keysbackup.model.MegolmBackupAuthData
import org.matrix.android.sdk.internal.crypto.keysbackup.model.MegolmBackupCreationInfo
-import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.runBlocking
-import org.junit.Assert.assertEquals
-import org.junit.Assert.assertNotNull
-import org.junit.Assert.assertNull
-import org.junit.Assert.assertTrue
import java.util.UUID
import java.util.concurrent.CountDownLatch
+import kotlin.coroutines.Continuation
+import kotlin.coroutines.resume
class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
@@ -304,10 +309,18 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
fun initializeCrossSigning(session: Session) {
mTestHelper.doSync {
session.cryptoService().crossSigningService()
- .initializeCrossSigning(UserPasswordAuth(
- user = session.myUserId,
- password = TestConstants.PASSWORD
- ), it)
+ .initializeCrossSigning(
+ object : UserInteractiveAuthInterceptor {
+ override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) {
+ promise.resume(
+ UserPasswordAuth(
+ user = session.myUserId,
+ password = TestConstants.PASSWORD,
+ session = flowResponse.session
+ )
+ )
+ }
+ }, it)
}
}
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/UnwedgingTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/UnwedgingTest.kt
index 0e3b29118c..cf31294e2f 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/UnwedgingTest.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/UnwedgingTest.kt
@@ -17,7 +17,18 @@
package org.matrix.android.sdk.internal.crypto
import androidx.test.ext.junit.runners.AndroidJUnit4
+import org.amshove.kluent.shouldBe
+import org.junit.Assert
+import org.junit.Before
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
import org.matrix.android.sdk.InstrumentedTest
+import org.matrix.android.sdk.api.auth.UIABaseAuth
+import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
+import org.matrix.android.sdk.api.auth.UserPasswordAuth
+import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
import org.matrix.android.sdk.api.session.events.model.EventType
@@ -30,19 +41,13 @@ import org.matrix.android.sdk.common.CryptoTestHelper
import org.matrix.android.sdk.common.TestConstants
import org.matrix.android.sdk.internal.crypto.model.OlmSessionWrapper
import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent
-import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth
import org.matrix.android.sdk.internal.crypto.store.db.deserializeFromRealm
import org.matrix.android.sdk.internal.crypto.store.db.serializeForRealm
-import org.amshove.kluent.shouldBe
-import org.junit.Assert
-import org.junit.Before
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
import org.matrix.olm.OlmSession
import timber.log.Timber
import java.util.concurrent.CountDownLatch
+import kotlin.coroutines.Continuation
+import kotlin.coroutines.resume
/**
* Ref:
@@ -202,10 +207,18 @@ class UnwedgingTest : InstrumentedTest {
// It's a trick to force key request on fail to decrypt
mTestHelper.doSync {
bobSession.cryptoService().crossSigningService()
- .initializeCrossSigning(UserPasswordAuth(
- user = bobSession.myUserId,
- password = TestConstants.PASSWORD
- ), it)
+ .initializeCrossSigning(
+ object : UserInteractiveAuthInterceptor {
+ override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) {
+ promise.resume(
+ UserPasswordAuth(
+ user = bobSession.myUserId,
+ password = TestConstants.PASSWORD,
+ session = flowResponse.session
+ )
+ )
+ }
+ }, it)
}
// Wait until we received back the key
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/crosssigning/XSigningTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/crosssigning/XSigningTest.kt
index 38c57bd22a..44af87bcbe 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/crosssigning/XSigningTest.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/crosssigning/XSigningTest.kt
@@ -17,14 +17,6 @@
package org.matrix.android.sdk.internal.crypto.crosssigning
import androidx.test.ext.junit.runners.AndroidJUnit4
-import org.matrix.android.sdk.InstrumentedTest
-import org.matrix.android.sdk.common.CommonTestHelper
-import org.matrix.android.sdk.common.CryptoTestHelper
-import org.matrix.android.sdk.common.SessionTestParams
-import org.matrix.android.sdk.common.TestConstants
-import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
-import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
-import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotNull
@@ -35,6 +27,19 @@ import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
+import org.matrix.android.sdk.InstrumentedTest
+import org.matrix.android.sdk.api.auth.UIABaseAuth
+import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
+import org.matrix.android.sdk.api.auth.UserPasswordAuth
+import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
+import org.matrix.android.sdk.common.CommonTestHelper
+import org.matrix.android.sdk.common.CryptoTestHelper
+import org.matrix.android.sdk.common.SessionTestParams
+import org.matrix.android.sdk.common.TestConstants
+import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
+import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
+import kotlin.coroutines.Continuation
+import kotlin.coroutines.resume
@RunWith(AndroidJUnit4::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -49,10 +54,17 @@ class XSigningTest : InstrumentedTest {
mTestHelper.doSync {
aliceSession.cryptoService().crossSigningService()
- .initializeCrossSigning(UserPasswordAuth(
- user = aliceSession.myUserId,
- password = TestConstants.PASSWORD
- ), it)
+ .initializeCrossSigning(object : UserInteractiveAuthInterceptor {
+ override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) {
+ promise.resume(
+ UserPasswordAuth(
+ user = aliceSession.myUserId,
+ password = TestConstants.PASSWORD,
+ session = flowResponse.session
+ )
+ )
+ }
+ }, it)
}
val myCrossSigningKeys = aliceSession.cryptoService().crossSigningService().getMyCrossSigningKeys()
@@ -86,8 +98,18 @@ class XSigningTest : InstrumentedTest {
password = TestConstants.PASSWORD
)
- mTestHelper.doSync { aliceSession.cryptoService().crossSigningService().initializeCrossSigning(aliceAuthParams, it) }
- mTestHelper.doSync { bobSession.cryptoService().crossSigningService().initializeCrossSigning(bobAuthParams, it) }
+ mTestHelper.doSync {
+ aliceSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
+ override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) {
+ promise.resume(aliceAuthParams)
+ }
+ }, it)
+ }
+ mTestHelper.doSync { bobSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
+ override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) {
+ promise.resume(bobAuthParams)
+ }
+ }, it) }
// Check that alice can see bob keys
mTestHelper.doSync> { aliceSession.cryptoService().downloadKeys(listOf(bobSession.myUserId), true, it) }
@@ -122,8 +144,16 @@ class XSigningTest : InstrumentedTest {
password = TestConstants.PASSWORD
)
- mTestHelper.doSync { aliceSession.cryptoService().crossSigningService().initializeCrossSigning(aliceAuthParams, it) }
- mTestHelper.doSync { bobSession.cryptoService().crossSigningService().initializeCrossSigning(bobAuthParams, it) }
+ mTestHelper.doSync { aliceSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
+ override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) {
+ promise.resume(aliceAuthParams)
+ }
+ }, it) }
+ mTestHelper.doSync { bobSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
+ override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) {
+ promise.resume(bobAuthParams)
+ }
+ }, it) }
// Check that alice can see bob keys
val bobUserId = bobSession.myUserId
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/KeyShareTests.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/KeyShareTests.kt
index 2c4d89b070..8c3917adc1 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/KeyShareTests.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/KeyShareTests.kt
@@ -18,7 +18,21 @@ package org.matrix.android.sdk.internal.crypto.gossiping
import android.util.Log
import androidx.test.ext.junit.runners.AndroidJUnit4
+import junit.framework.TestCase.assertEquals
+import junit.framework.TestCase.assertNotNull
+import junit.framework.TestCase.assertTrue
+import junit.framework.TestCase.fail
+import org.junit.Assert
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
import org.matrix.android.sdk.InstrumentedTest
+import org.matrix.android.sdk.api.auth.UIABaseAuth
+import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
+import org.matrix.android.sdk.api.auth.UserPasswordAuth
+import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
+import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.session.crypto.verification.IncomingSasVerificationTransaction
import org.matrix.android.sdk.api.session.crypto.verification.SasVerificationTransaction
import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod
@@ -28,6 +42,7 @@ import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxStat
import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
+import org.matrix.android.sdk.api.session.room.model.message.MessageContent
import org.matrix.android.sdk.common.CommonTestHelper
import org.matrix.android.sdk.common.CryptoTestHelper
import org.matrix.android.sdk.common.SessionTestParams
@@ -40,19 +55,9 @@ import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersion
import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent
-import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth
-import junit.framework.TestCase.assertEquals
-import junit.framework.TestCase.assertNotNull
-import junit.framework.TestCase.assertTrue
-import junit.framework.TestCase.fail
-import org.junit.Assert
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.matrix.android.sdk.api.extensions.tryOrNull
-import org.matrix.android.sdk.api.session.room.model.message.MessageContent
import java.util.concurrent.CountDownLatch
+import kotlin.coroutines.Continuation
+import kotlin.coroutines.resume
@RunWith(AndroidJUnit4::class)
@FixMethodOrder(MethodSorters.JVM)
@@ -200,10 +205,17 @@ class KeyShareTests : InstrumentedTest {
mTestHelper.doSync {
aliceSession1.cryptoService().crossSigningService()
- .initializeCrossSigning(UserPasswordAuth(
- user = aliceSession1.myUserId,
- password = TestConstants.PASSWORD
- ), it)
+ .initializeCrossSigning(
+ object : UserInteractiveAuthInterceptor {
+ override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) {
+ promise.resume(
+ UserPasswordAuth(
+ user = aliceSession1.myUserId,
+ password = TestConstants.PASSWORD
+ )
+ )
+ }
+ }, it)
}
// Also bootstrap keybackup on first session
@@ -305,10 +317,18 @@ class KeyShareTests : InstrumentedTest {
mTestHelper.doSync {
aliceSession.cryptoService().crossSigningService()
- .initializeCrossSigning(UserPasswordAuth(
- user = aliceSession.myUserId,
- password = TestConstants.PASSWORD
- ), it)
+ .initializeCrossSigning(
+ object : UserInteractiveAuthInterceptor {
+ override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) {
+ promise.resume(
+ UserPasswordAuth(
+ user = aliceSession.myUserId,
+ password = TestConstants.PASSWORD,
+ session = flowResponse.session
+ )
+ )
+ }
+ }, it)
}
// Create an encrypted room and send a couple of messages
@@ -332,10 +352,18 @@ class KeyShareTests : InstrumentedTest {
val bobSession = mTestHelper.createAccount(TestConstants.USER_BOB, SessionTestParams(true))
mTestHelper.doSync {
bobSession.cryptoService().crossSigningService()
- .initializeCrossSigning(UserPasswordAuth(
- user = bobSession.myUserId,
- password = TestConstants.PASSWORD
- ), it)
+ .initializeCrossSigning(
+ object : UserInteractiveAuthInterceptor {
+ override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) {
+ promise.resume(
+ UserPasswordAuth(
+ user = bobSession.myUserId,
+ password = TestConstants.PASSWORD,
+ session = flowResponse.session
+ )
+ )
+ }
+ }, it)
}
// Let alice invite bob
@@ -356,7 +384,7 @@ class KeyShareTests : InstrumentedTest {
val roomRoomBobPov = aliceSession.getRoom(roomId)
val beforeJoin = roomRoomBobPov!!.getTimeLineEvent(secondEventId)
- var dRes = tryOrNull { bobSession.cryptoService().decryptEvent(beforeJoin!!.root, "") }
+ var dRes = tryOrNull { bobSession.cryptoService().decryptEvent(beforeJoin!!.root, "") }
assert(dRes == null)
@@ -367,7 +395,7 @@ class KeyShareTests : InstrumentedTest {
Thread.sleep(3_000)
// With the bug the first session would have improperly reshare that key :/
- dRes = tryOrNull { bobSession.cryptoService().decryptEvent(beforeJoin.root, "") }
+ dRes = tryOrNull { bobSession.cryptoService().decryptEvent(beforeJoin.root, "") }
Log.d("#TEST", "KS: sgould not decrypt that ${beforeJoin.root.getClearContent().toModel()?.body}")
assert(dRes?.clearEvent == null)
}
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/VerificationTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/VerificationTest.kt
index 1385dac1ec..397f7f9441 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/VerificationTest.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/VerificationTest.kt
@@ -17,20 +17,25 @@
package org.matrix.android.sdk.internal.crypto.verification.qrcode
import androidx.test.ext.junit.runners.AndroidJUnit4
-import org.matrix.android.sdk.InstrumentedTest
-import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod
-import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
-import org.matrix.android.sdk.common.CommonTestHelper
-import org.matrix.android.sdk.common.CryptoTestHelper
-import org.matrix.android.sdk.common.TestConstants
-import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth
-import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest
import org.amshove.kluent.shouldBe
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
+import org.matrix.android.sdk.InstrumentedTest
+import org.matrix.android.sdk.api.auth.UIABaseAuth
+import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
+import org.matrix.android.sdk.api.auth.UserPasswordAuth
+import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
+import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest
+import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod
+import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
+import org.matrix.android.sdk.common.CommonTestHelper
+import org.matrix.android.sdk.common.CryptoTestHelper
+import org.matrix.android.sdk.common.TestConstants
import java.util.concurrent.CountDownLatch
+import kotlin.coroutines.Continuation
+import kotlin.coroutines.resume
@RunWith(AndroidJUnit4::class)
@FixMethodOrder(MethodSorters.JVM)
@@ -157,18 +162,34 @@ class VerificationTest : InstrumentedTest {
mTestHelper.doSync { callback ->
aliceSession.cryptoService().crossSigningService()
- .initializeCrossSigning(UserPasswordAuth(
- user = aliceSession.myUserId,
- password = TestConstants.PASSWORD
- ), callback)
+ .initializeCrossSigning(
+ object : UserInteractiveAuthInterceptor {
+ override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) {
+ promise.resume(
+ UserPasswordAuth(
+ user = aliceSession.myUserId,
+ password = TestConstants.PASSWORD,
+ session = flowResponse.session
+ )
+ )
+ }
+ }, callback)
}
mTestHelper.doSync { callback ->
bobSession.cryptoService().crossSigningService()
- .initializeCrossSigning(UserPasswordAuth(
- user = bobSession.myUserId,
- password = TestConstants.PASSWORD
- ), callback)
+ .initializeCrossSigning(
+ object : UserInteractiveAuthInterceptor {
+ override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) {
+ promise.resume(
+ UserPasswordAuth(
+ user = bobSession.myUserId,
+ password = TestConstants.PASSWORD,
+ session = flowResponse.session
+ )
+ )
+ }
+ }, callback)
}
val aliceVerificationService = aliceSession.cryptoService().verificationService()
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/session/media/UrlsExtractorTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/session/media/UrlsExtractorTest.kt
index 9ee84fdfc6..473b18b31b 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/session/media/UrlsExtractorTest.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/session/media/UrlsExtractorTest.kt
@@ -26,6 +26,8 @@ import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.toContent
import org.matrix.android.sdk.api.session.room.model.message.MessageTextContent
import org.matrix.android.sdk.api.session.room.model.message.MessageType
+import org.matrix.android.sdk.api.session.room.sender.SenderInfo
+import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
@RunWith(AndroidJUnit4::class)
internal class UrlsExtractorTest : InstrumentedTest {
@@ -36,6 +38,7 @@ internal class UrlsExtractorTest : InstrumentedTest {
fun wrongEventTypeTest() {
createEvent(body = "https://matrix.org")
.copy(type = EventType.STATE_ROOM_GUEST_ACCESS)
+ .toFakeTimelineEvent()
.let { urlsExtractor.extract(it) }
.size shouldBeEqualTo 0
}
@@ -43,6 +46,7 @@ internal class UrlsExtractorTest : InstrumentedTest {
@Test
fun oneUrlTest() {
createEvent(body = "https://matrix.org")
+ .toFakeTimelineEvent()
.let { urlsExtractor.extract(it) }
.let { result ->
result.size shouldBeEqualTo 1
@@ -53,6 +57,7 @@ internal class UrlsExtractorTest : InstrumentedTest {
@Test
fun withoutProtocolTest() {
createEvent(body = "www.matrix.org")
+ .toFakeTimelineEvent()
.let { urlsExtractor.extract(it) }
.size shouldBeEqualTo 0
}
@@ -60,6 +65,7 @@ internal class UrlsExtractorTest : InstrumentedTest {
@Test
fun oneUrlWithParamTest() {
createEvent(body = "https://matrix.org?foo=bar")
+ .toFakeTimelineEvent()
.let { urlsExtractor.extract(it) }
.let { result ->
result.size shouldBeEqualTo 1
@@ -70,6 +76,7 @@ internal class UrlsExtractorTest : InstrumentedTest {
@Test
fun oneUrlWithParamsTest() {
createEvent(body = "https://matrix.org?foo=bar&bar=foo")
+ .toFakeTimelineEvent()
.let { urlsExtractor.extract(it) }
.let { result ->
result.size shouldBeEqualTo 1
@@ -80,16 +87,18 @@ internal class UrlsExtractorTest : InstrumentedTest {
@Test
fun oneUrlInlinedTest() {
createEvent(body = "Hello https://matrix.org, how are you?")
+ .toFakeTimelineEvent()
.let { urlsExtractor.extract(it) }
.let { result ->
result.size shouldBeEqualTo 1
- result[0] shouldBeEqualTo "https://matrix.org"
+ result[0] shouldBeEqualTo "https://matrix.org"
}
}
@Test
fun twoUrlsTest() {
createEvent(body = "https://matrix.org https://example.org")
+ .toFakeTimelineEvent()
.let { urlsExtractor.extract(it) }
.let { result ->
result.size shouldBeEqualTo 2
@@ -99,10 +108,26 @@ internal class UrlsExtractorTest : InstrumentedTest {
}
private fun createEvent(body: String): Event = Event(
+ eventId = "!fake",
type = EventType.MESSAGE,
content = MessageTextContent(
msgType = MessageType.MSGTYPE_TEXT,
body = body
).toContent()
)
+
+ private fun Event.toFakeTimelineEvent(): TimelineEvent {
+ return TimelineEvent(
+ root = this,
+ localId = 0L,
+ eventId = eventId!!,
+ displayIndex = 0,
+ senderInfo = SenderInfo(
+ userId = "",
+ displayName = null,
+ isUniqueDisplayName = true,
+ avatarUrl = null
+ )
+ )
+ }
}
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineForwardPaginationTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineForwardPaginationTest.kt
index 34edf37733..f156a5eb64 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineForwardPaginationTest.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineForwardPaginationTest.kt
@@ -66,8 +66,8 @@ class TimelineForwardPaginationTest : InstrumentedTest {
numberOfMessagesToSend)
// Alice clear the cache
- commonTestHelper.doSync {
- aliceSession.clearCache(it)
+ commonTestHelper.runBlockingTest {
+ aliceSession.clearCache()
}
// And restarts the sync
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/TokenBasedAuth.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/TokenBasedAuth.kt
new file mode 100644
index 0000000000..e522352c38
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/TokenBasedAuth.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2020 The Matrix.org Foundation C.I.C.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.matrix.android.sdk.api.auth
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
+
+/**
+ * This class provides the authentication data by using user and password
+ */
+@JsonClass(generateAdapter = true)
+data class TokenBasedAuth(
+
+ /**
+ * This is a session identifier that the client must pass back to the homeserver,
+ * if one is provided, in subsequent attempts to authenticate in the same API call.
+ */
+ @Json(name = "session")
+ override val session: String? = null,
+
+ /**
+ * A client may receive a login token via some external service, such as email or SMS.
+ * Note that a login token is separate from an access token, the latter providing general authentication to various API endpoints.
+ */
+ @Json(name = "token")
+ val token: String? = null,
+
+ /**
+ * The txn_id should be a random string generated by the client for the request.
+ * The same txn_id should be used if retrying the request.
+ * The txn_id may be used by the server to disallow other devices from using the token,
+ * thus providing "single use" tokens while still allowing the device to retry the request.
+ * This would be done by tying the token to the txn_id server side, as well as potentially invalidating
+ * the token completely once the device has successfully logged in
+ * (e.g. when we receive a request from the newly provisioned access_token).
+ */
+ @Json(name = "txn_id")
+ val transactionId: String? = null,
+
+ // registration information
+ @Json(name = "type")
+ val type: String? = LoginFlowTypes.TOKEN
+
+) : UIABaseAuth {
+ override fun hasAuthInfo() = token != null
+
+ override fun copyWithSession(session: String) = this.copy(session = session)
+
+ override fun asMap(): Map = mapOf(
+ "session" to session,
+ "token" to token,
+ "transactionId" to transactionId,
+ "type" to type
+ )
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/SessionAssistedInjectModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/UIABaseAuth.kt
similarity index 55%
rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/SessionAssistedInjectModule.kt
rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/UIABaseAuth.kt
index da3ecfb907..d5e323e457 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/SessionAssistedInjectModule.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/UIABaseAuth.kt
@@ -5,7 +5,7 @@
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,11 +14,18 @@
* limitations under the License.
*/
-package org.matrix.android.sdk.internal.di
+package org.matrix.android.sdk.api.auth
-import com.squareup.inject.assisted.dagger2.AssistedModule
-import dagger.Module
+interface UIABaseAuth {
+ /**
+ * This is a session identifier that the client must pass back to the homeserver,
+ * if one is provided, in subsequent attempts to authenticate in the same API call.
+ */
+ val session: String?
-@AssistedModule
-@Module(includes = [AssistedInject_SessionAssistedInjectModule::class])
-interface SessionAssistedInjectModule
+ fun hasAuthInfo(): Boolean
+
+ fun copyWithSession(session: String): UIABaseAuth
+
+ fun asMap() : Map
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/UserInteractiveAuthInterceptor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/UserInteractiveAuthInterceptor.kt
new file mode 100644
index 0000000000..16a5c8073d
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/UserInteractiveAuthInterceptor.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2020 The Matrix.org Foundation C.I.C.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.matrix.android.sdk.api.auth
+
+import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
+import kotlin.coroutines.Continuation
+
+/**
+ * Some API endpoints require authentication that interacts with the user.
+ * The homeserver may provide many different ways of authenticating, such as user/password auth, login via a social network (OAuth2),
+ * login by confirming a token sent to their email address, etc.
+ *
+ * The process takes the form of one or more 'stages'.
+ * At each stage the client submits a set of data for a given authentication type and awaits a response from the server,
+ * which will either be a final success or a request to perform an additional stage.
+ * This exchange continues until the final success.
+ *
+ * For each endpoint, a server offers one or more 'flows' that the client can use to authenticate itself.
+ * Each flow comprises a series of stages, as described above.
+ * The client is free to choose which flow it follows, however the flow's stages must be completed in order.
+ * Failing to follow the flows in order must result in an HTTP 401 response.
+ * When all stages in a flow are complete, authentication is complete and the API call succeeds.
+ */
+interface UserInteractiveAuthInterceptor {
+
+ /**
+ * When the API needs additional auth, this will be called.
+ * Implementation should check the flows from flow response and act accordingly.
+ * Updated auth should be provided using promise.resume, this allow implementation to perform
+ * an async operation (prompt for user password, open sso fallback) and then resume initial API call when done.
+ */
+ fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation)
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/rest/UserPasswordAuth.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/UserPasswordAuth.kt
similarity index 74%
rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/rest/UserPasswordAuth.kt
rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/UserPasswordAuth.kt
index ba8b34096c..e985c5f08a 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/rest/UserPasswordAuth.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/UserPasswordAuth.kt
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.matrix.android.sdk.internal.crypto.model.rest
+package org.matrix.android.sdk.api.auth
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@@ -27,7 +27,7 @@ data class UserPasswordAuth(
// device device session id
@Json(name = "session")
- val session: String? = null,
+ override val session: String? = null,
// registration information
@Json(name = "type")
@@ -38,4 +38,16 @@ data class UserPasswordAuth(
@Json(name = "password")
val password: String? = null
-)
+) : UIABaseAuth {
+
+ override fun hasAuthInfo() = password != null
+
+ override fun copyWithSession(session: String) = this.copy(session = session)
+
+ override fun asMap(): Map = mapOf(
+ "session" to session,
+ "user" to user,
+ "password" to password,
+ "type" to type
+ )
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/data/SsoIdentityProvider.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/data/SsoIdentityProvider.kt
index 6759c59237..cfaf74ce24 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/data/SsoIdentityProvider.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/data/SsoIdentityProvider.kt
@@ -38,15 +38,24 @@ data class SsoIdentityProvider(
* If present then it must be an HTTPS URL to an image resource.
* This should be hosted by the homeserver service provider to not leak the client's IP address unnecessarily.
*/
- @Json(name = "icon") val iconUrl: String?
+ @Json(name = "icon") val iconUrl: String?,
+
+ /**
+ * The `brand` field is **optional**. It allows the client to style the login
+ * button to suit a particular brand. It should be a string matching the
+ * "Common namespaced identifier grammar" as defined in
+ * [MSC2758](https://github.com/matrix-org/matrix-doc/pull/2758).
+ */
+ @Json(name = "brand") val brand: String?
+
) : Parcelable {
companion object {
- // Not really defined by the spec, but we may define some ids here
- const val ID_GOOGLE = "google"
- const val ID_GITHUB = "github"
- const val ID_APPLE = "apple"
- const val ID_FACEBOOK = "facebook"
- const val ID_TWITTER = "twitter"
+ const val BRAND_GOOGLE = "org.matrix.google"
+ const val BRAND_GITHUB = "org.matrix.github"
+ const val BRAND_APPLE = "org.matrix.apple"
+ const val BRAND_FACEBOOK = "org.matrix.facebook"
+ const val BRAND_TWITTER = "org.matrix.twitter"
+ const val BRAND_GITLAB = "org.matrix.gitlab"
}
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/RegistrationFlowResponse.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/registration/RegistrationFlowResponse.kt
similarity index 93%
rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/RegistrationFlowResponse.kt
rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/registration/RegistrationFlowResponse.kt
index 3461a4d738..2b1c1c09b3 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/RegistrationFlowResponse.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/registration/RegistrationFlowResponse.kt
@@ -14,14 +14,11 @@
* limitations under the License.
*/
-package org.matrix.android.sdk.internal.auth.registration
+package org.matrix.android.sdk.api.auth.registration
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
-import org.matrix.android.sdk.api.auth.registration.FlowResult
-import org.matrix.android.sdk.api.auth.registration.Stage
-import org.matrix.android.sdk.api.auth.registration.TermPolicies
import org.matrix.android.sdk.api.util.JsonDict
import org.matrix.android.sdk.internal.auth.data.InteractiveAuthenticationFlow
@@ -109,3 +106,8 @@ fun RegistrationFlowResponse.toFlowResult(): FlowResult {
return FlowResult(missingStage, completedStage)
}
+
+fun RegistrationFlowResponse.nextUncompletedStage(flowIndex: Int = 0): String? {
+ val completed = completedStages ?: emptyList()
+ return flows?.getOrNull(flowIndex)?.stages?.firstOrNull { completed.contains(it).not() }
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/failure/Extensions.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/failure/Extensions.kt
index 4711f7957d..c06cdd9e23 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/failure/Extensions.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/failure/Extensions.kt
@@ -16,8 +16,8 @@
package org.matrix.android.sdk.api.failure
+import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
import org.matrix.android.sdk.api.extensions.tryOrNull
-import org.matrix.android.sdk.internal.auth.registration.RegistrationFlowResponse
import org.matrix.android.sdk.internal.di.MoshiProvider
import java.io.IOException
import javax.net.ssl.HttpsURLConnection
@@ -43,6 +43,12 @@ fun Throwable.isInvalidPassword(): Boolean {
&& error.message == "Invalid password"
}
+fun Throwable.isInvalidUIAAuth(): Boolean {
+ return this is Failure.ServerError
+ && error.code == MatrixError.M_FORBIDDEN
+ && error.flows != null
+}
+
/**
* Try to convert to a RegistrationFlowResponse. Return null in the cases it's not possible
*/
@@ -53,6 +59,16 @@ fun Throwable.toRegistrationFlowResponse(): RegistrationFlowResponse? {
.adapter(RegistrationFlowResponse::class.java)
.fromJson(this.errorBody)
}
+ } else if (this is Failure.ServerError && this.httpCode == 401 && this.error.code == MatrixError.M_FORBIDDEN) {
+ // This happens when the submission for this stage was bad (like bad password)
+ if (this.error.session != null && this.error.flows != null) {
+ RegistrationFlowResponse(
+ flows = this.error.flows,
+ session = this.error.session,
+ completedStages = this.error.completedStages,
+ params = this.error.params
+ )
+ } else null
} else {
null
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/failure/Failure.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/failure/Failure.kt
index de881b9e02..b241903364 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/failure/Failure.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/failure/Failure.kt
@@ -16,8 +16,8 @@
package org.matrix.android.sdk.api.failure
+import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
-import org.matrix.android.sdk.internal.auth.registration.RegistrationFlowResponse
import org.matrix.android.sdk.internal.network.ssl.Fingerprint
import java.io.IOException
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/failure/MatrixError.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/failure/MatrixError.kt
index 895be0031a..3820a442aa 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/failure/MatrixError.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/failure/MatrixError.kt
@@ -18,6 +18,8 @@ package org.matrix.android.sdk.api.failure
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
+import org.matrix.android.sdk.api.util.JsonDict
+import org.matrix.android.sdk.internal.auth.data.InteractiveAuthenticationFlow
/**
* This data class holds the error defined by the matrix specifications.
@@ -42,7 +44,17 @@ data class MatrixError(
@Json(name = "soft_logout") val isSoftLogout: Boolean = false,
// For M_INVALID_PEPPER
// {"error": "pepper does not match 'erZvr'", "lookup_pepper": "pQgMS", "algorithm": "sha256", "errcode": "M_INVALID_PEPPER"}
- @Json(name = "lookup_pepper") val newLookupPepper: String? = null
+ @Json(name = "lookup_pepper") val newLookupPepper: String? = null,
+
+ // For M_FORBIDDEN UIA
+ @Json(name = "session")
+ val session: String? = null,
+ @Json(name = "completed")
+ val completedStages: List? = null,
+ @Json(name = "flows")
+ val flows: List? = null,
+ @Json(name = "params")
+ val params: JsonDict? = null
) {
companion object {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/Session.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/Session.kt
index 8a95baf3cb..ff7c9f0521 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/Session.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/Session.kt
@@ -245,6 +245,8 @@ interface Session :
val sharedSecretStorageService: SharedSecretStorageService
+ fun getUiaSsoFallbackUrl(authenticationSessionId: String): String
+
/**
* Maintenance API, allows to print outs info on DB size to logcat
*/
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/account/AccountService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/account/AccountService.kt
index 8915202f35..eb327dfd56 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/account/AccountService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/account/AccountService.kt
@@ -16,6 +16,8 @@
package org.matrix.android.sdk.api.session.account
+import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
+
/**
* This interface defines methods to manage the account. It's implemented at the session level.
*/
@@ -43,5 +45,5 @@ interface AccountService {
* @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
*/
- suspend fun deactivateAccount(password: String, eraseAllData: Boolean)
+ suspend fun deactivateAccount(userInteractiveAuthInterceptor: UserInteractiveAuthInterceptor, eraseAllData: Boolean)
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/cache/CacheService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/cache/CacheService.kt
index c1c5663227..2945cc45d6 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/cache/CacheService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/cache/CacheService.kt
@@ -16,8 +16,6 @@
package org.matrix.android.sdk.api.session.cache
-import org.matrix.android.sdk.api.MatrixCallback
-
/**
* This interface defines a method to clear the cache. It's implemented at the session level.
*/
@@ -26,5 +24,5 @@ interface CacheService {
/**
* Clear the whole cached data, except credentials. Once done, the sync has to be restarted by the sdk user.
*/
- fun clearCache(callback: MatrixCallback)
+ suspend fun clearCache()
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt
index 0eefca1b4c..fa5ea359e8 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt
@@ -20,6 +20,7 @@ import android.content.Context
import androidx.lifecycle.LiveData
import androidx.paging.PagedList
import org.matrix.android.sdk.api.MatrixCallback
+import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
import org.matrix.android.sdk.api.listeners.ProgressListener
import org.matrix.android.sdk.api.session.crypto.crosssigning.CrossSigningService
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupService
@@ -53,7 +54,7 @@ interface CryptoService {
fun setDeviceName(deviceId: String, deviceName: String, callback: MatrixCallback)
- fun deleteDevice(deviceId: String, callback: MatrixCallback)
+ fun deleteDevice(deviceId: String, userInteractiveAuthInterceptor: UserInteractiveAuthInterceptor, callback: MatrixCallback)
fun deleteDeviceWithUserPassword(deviceId: String, authSession: String?, password: String, callback: MatrixCallback)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/crosssigning/CrossSigningService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/crosssigning/CrossSigningService.kt
index 6a646cd4c7..359e33cc2c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/crosssigning/CrossSigningService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/crosssigning/CrossSigningService.kt
@@ -18,10 +18,10 @@ package org.matrix.android.sdk.api.session.crypto.crosssigning
import androidx.lifecycle.LiveData
import org.matrix.android.sdk.api.MatrixCallback
+import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
import org.matrix.android.sdk.api.util.Optional
import org.matrix.android.sdk.internal.crypto.crosssigning.DeviceTrustResult
import org.matrix.android.sdk.internal.crypto.crosssigning.UserTrustResult
-import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth
import org.matrix.android.sdk.internal.crypto.store.PrivateKeysInfo
interface CrossSigningService {
@@ -40,7 +40,7 @@ interface CrossSigningService {
* Initialize cross signing for this user.
* Users needs to enter credentials
*/
- fun initializeCrossSigning(authParams: UserPasswordAuth?,
+ fun initializeCrossSigning(uiaInterceptor: UserInteractiveAuthInterceptor?,
callback: MatrixCallback)
fun isCrossSigningInitialized(): Boolean = getMyCrossSigningKeys() != null
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/media/MediaService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/media/MediaService.kt
index 9040ec7d5c..3b3ef57d73 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/media/MediaService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/media/MediaService.kt
@@ -17,15 +17,16 @@
package org.matrix.android.sdk.api.session.media
import org.matrix.android.sdk.api.cache.CacheStrategy
-import org.matrix.android.sdk.api.session.events.model.Event
+import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
import org.matrix.android.sdk.api.util.JsonDict
interface MediaService {
/**
- * Extract URLs from an Event.
- * @return the list of URLs contains in the body of the Event. It does not mean that URLs in this list have UrlPreview data
+ * Extract URLs from a TimelineEvent.
+ * @param event TimelineEvent to extract the URL from.
+ * @return the list of URLs contains in the body of the TimelineEvent. It does not mean that URLs in this list have UrlPreview data
*/
- fun extractUrls(event: Event): List
+ fun extractUrls(event: TimelineEvent): List
/**
* Get Raw Url Preview data from the homeserver. There is no cache management for this request
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/profile/ProfileService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/profile/ProfileService.kt
index 1fd8360253..a4d5b665c6 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/profile/ProfileService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/profile/ProfileService.kt
@@ -20,6 +20,7 @@ package org.matrix.android.sdk.api.session.profile
import android.net.Uri
import androidx.lifecycle.LiveData
import org.matrix.android.sdk.api.MatrixCallback
+import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
import org.matrix.android.sdk.api.session.identity.ThreePid
import org.matrix.android.sdk.api.util.Cancelable
import org.matrix.android.sdk.api.util.JsonDict
@@ -107,8 +108,7 @@ interface ProfileService {
* Finalize adding a 3Pids. Call this method once the user has validated that he owns the ThreePid
*/
fun finalizeAddingThreePid(threePid: ThreePid,
- uiaSession: String?,
- accountPassword: String?,
+ userInteractiveAuthInterceptor: UserInteractiveAuthInterceptor,
matrixCallback: MatrixCallback): Cancelable
/**
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineEvent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineEvent.kt
index 73cb94b417..b10fb540e1 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineEvent.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineEvent.kt
@@ -89,6 +89,17 @@ data class TimelineEvent(
*/
fun TimelineEvent.hasBeenEdited() = annotations?.editSummary != null
+/**
+ * Get the latest known eventId for an edited event, or the eventId for an Event which has not been edited
+ */
+fun TimelineEvent.getLatestEventId(): String {
+ return annotations
+ ?.editSummary
+ ?.sourceEvents
+ ?.lastOrNull()
+ ?: eventId
+}
+
/**
* Get the relation content if any
*/
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/signout/SignOutService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/signout/SignOutService.kt
index ebbbac527a..4e4eba274e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/signout/SignOutService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/signout/SignOutService.kt
@@ -16,9 +16,7 @@
package org.matrix.android.sdk.api.session.signout
-import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.auth.data.Credentials
-import org.matrix.android.sdk.api.util.Cancelable
/**
* This interface defines a method to sign out, or to renew the token. It's implemented at the session level.
@@ -29,19 +27,16 @@ interface SignOutService {
* Ask the homeserver for a new access token.
* The same deviceId will be used
*/
- fun signInAgain(password: String,
- callback: MatrixCallback): Cancelable
+ suspend fun signInAgain(password: String)
/**
* Update the session with credentials received after SSO
*/
- fun updateCredentials(credentials: Credentials,
- callback: MatrixCallback): Cancelable
+ suspend fun updateCredentials(credentials: Credentials)
/**
* Sign out, and release the session, clear all the session data, including crypto data
* @param signOutFromHomeserver true if the sign out request has to be done
*/
- fun signOut(signOutFromHomeserver: Boolean,
- callback: MatrixCallback): Cancelable
+ suspend fun signOut(signOutFromHomeserver: Boolean)
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/Constants.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/Constants.kt
index 642279cc27..e0c52cf9ca 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/Constants.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/Constants.kt
@@ -36,3 +36,6 @@ internal const val SSO_REDIRECT_PATH = "/_matrix/client/r0/login/sso/redirect"
internal const val MSC2858_SSO_REDIRECT_PATH = "/_matrix/client/unstable/org.matrix.msc2858/login/sso/redirect"
internal const val SSO_REDIRECT_URL_PARAM = "redirectUrl"
+
+// Ref: https://matrix.org/docs/spec/client_server/r0.6.1#single-sign-on
+internal const val SSO_UIA_FALLBACK_PATH = "/_matrix/client/r0/auth/m.login.sso/fallback/web"
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/LoginFlowResponse.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/LoginFlowResponse.kt
index 2b26115f30..d0d17e2cd5 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/LoginFlowResponse.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/LoginFlowResponse.kt
@@ -43,5 +43,6 @@ internal data class LoginFlow(
* See MSC #2858
*/
@Json(name = "org.matrix.msc2858.identity_providers")
- val ssoIdentityProvider: List?
+ val ssoIdentityProvider: List? = null
+
)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/DefaultRegistrationWizard.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/DefaultRegistrationWizard.kt
index 9c6b942a4f..163009d918 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/DefaultRegistrationWizard.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/DefaultRegistrationWizard.kt
@@ -24,6 +24,7 @@ import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
import org.matrix.android.sdk.api.auth.registration.RegisterThreePid
import org.matrix.android.sdk.api.auth.registration.RegistrationResult
import org.matrix.android.sdk.api.auth.registration.RegistrationWizard
+import org.matrix.android.sdk.api.auth.registration.toFlowResult
import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.failure.Failure.RegistrationFlowError
import org.matrix.android.sdk.api.util.Cancelable
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/UIAExt.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/UIAExt.kt
new file mode 100644
index 0000000000..1a0383cb22
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/UIAExt.kt
@@ -0,0 +1,53 @@
+/*
+ * 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.auth.registration
+
+import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
+import org.matrix.android.sdk.api.failure.Failure
+import org.matrix.android.sdk.api.failure.toRegistrationFlowResponse
+import org.matrix.android.sdk.api.auth.UIABaseAuth
+import timber.log.Timber
+import kotlin.coroutines.suspendCoroutine
+
+internal suspend fun handleUIA(failure: Throwable, interceptor: UserInteractiveAuthInterceptor, retryBlock: suspend (UIABaseAuth) -> Unit): Boolean {
+ Timber.d("## UIA: check error ${failure.message}")
+ val flowResponse = failure.toRegistrationFlowResponse()
+ ?: return false.also {
+ Timber.d("## UIA: not a UIA error")
+ }
+
+ Timber.d("## UIA: error can be passed to interceptor")
+ Timber.d("## UIA: type = ${flowResponse.flows}")
+
+ Timber.d("## UIA: delegate to interceptor...")
+ val authUpdate = try {
+ suspendCoroutine { continuation ->
+ interceptor.performStage(flowResponse, (failure as? Failure.ServerError)?.error?.code, continuation)
+ }
+ } catch (failure: Throwable) {
+ Timber.w(failure, "## UIA: failed to participate")
+ return false
+ }
+
+ Timber.d("## UIA: updated auth $authUpdate")
+ return try {
+ retryBlock(authUpdate)
+ true
+ } catch (failure: Throwable) {
+ handleUIA(failure, interceptor, retryBlock)
+ }
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt
index ebd809f777..678bc9819f 100755
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt
@@ -30,6 +30,7 @@ import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.NoOpMatrixCallback
+import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
import org.matrix.android.sdk.api.crypto.MXCryptoConfig
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.failure.Failure
@@ -207,9 +208,9 @@ internal class DefaultCryptoService @Inject constructor(
.executeBy(taskExecutor)
}
- override fun deleteDevice(deviceId: String, callback: MatrixCallback) {
+ override fun deleteDevice(deviceId: String, userInteractiveAuthInterceptor: UserInteractiveAuthInterceptor, callback: MatrixCallback) {
deleteDeviceTask
- .configureWith(DeleteDeviceTask.Params(deviceId)) {
+ .configureWith(DeleteDeviceTask.Params(deviceId, userInteractiveAuthInterceptor, null)) {
this.executionThread = TaskThread.CRYPTO
this.callback = callback
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/DefaultCrossSigningService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/DefaultCrossSigningService.kt
index bcad448eb6..9b282f0a84 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/DefaultCrossSigningService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/DefaultCrossSigningService.kt
@@ -19,30 +19,30 @@ package org.matrix.android.sdk.internal.crypto.crosssigning
import androidx.lifecycle.LiveData
import androidx.work.BackoffPolicy
import androidx.work.ExistingWorkPolicy
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.MatrixCallback
+import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.crypto.crosssigning.CrossSigningService
import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo
import org.matrix.android.sdk.api.util.Optional
import org.matrix.android.sdk.internal.crypto.DeviceListManager
+import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
import org.matrix.android.sdk.internal.crypto.model.rest.UploadSignatureQueryBuilder
-import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
import org.matrix.android.sdk.internal.crypto.store.PrivateKeysInfo
import org.matrix.android.sdk.internal.crypto.tasks.InitializeCrossSigningTask
import org.matrix.android.sdk.internal.crypto.tasks.UploadSignaturesTask
+import org.matrix.android.sdk.internal.di.SessionId
import org.matrix.android.sdk.internal.di.UserId
+import org.matrix.android.sdk.internal.di.WorkManagerProvider
import org.matrix.android.sdk.internal.session.SessionScope
import org.matrix.android.sdk.internal.task.TaskExecutor
import org.matrix.android.sdk.internal.task.TaskThread
import org.matrix.android.sdk.internal.task.configureWith
import org.matrix.android.sdk.internal.util.JsonCanonicalizer
import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.launch
-import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
-import org.matrix.android.sdk.internal.di.SessionId
-import org.matrix.android.sdk.internal.di.WorkManagerProvider
import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
import org.matrix.olm.OlmPkSigning
import org.matrix.olm.OlmUtility
@@ -61,7 +61,10 @@ internal class DefaultCrossSigningService @Inject constructor(
private val taskExecutor: TaskExecutor,
private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val cryptoCoroutineScope: CoroutineScope,
- private val workManagerProvider: WorkManagerProvider) : CrossSigningService, DeviceListManager.UserDevicesUpdateListener {
+ private val workManagerProvider: WorkManagerProvider,
+ private val updateTrustWorkerDataRepository: UpdateTrustWorkerDataRepository
+) : CrossSigningService,
+ DeviceListManager.UserDevicesUpdateListener {
private var olmUtility: OlmUtility? = null
@@ -147,11 +150,11 @@ internal class DefaultCrossSigningService @Inject constructor(
* - Sign the keys and upload them
* - Sign the current device with SSK and sign MSK with device key (migration) and upload signatures
*/
- override fun initializeCrossSigning(authParams: UserPasswordAuth?, callback: MatrixCallback) {
+ override fun initializeCrossSigning(uiaInterceptor: UserInteractiveAuthInterceptor?, callback: MatrixCallback) {
Timber.d("## CrossSigning initializeCrossSigning")
val params = InitializeCrossSigningTask.Params(
- authParams = authParams
+ interactiveAuthInterceptor = uiaInterceptor
)
initializeCrossSigningTask.configureWith(params) {
this.callbackThread = TaskThread.CRYPTO
@@ -689,7 +692,7 @@ internal class DefaultCrossSigningService @Inject constructor(
return DeviceTrustResult.Success(DeviceTrustLevel(crossSigningVerified = true, locallyVerified = locallyTrusted))
}
- fun checkDeviceTrust(myKeys: MXCrossSigningInfo?, otherKeys: MXCrossSigningInfo?, otherDevice: CryptoDeviceInfo) : DeviceTrustResult {
+ fun checkDeviceTrust(myKeys: MXCrossSigningInfo?, otherKeys: MXCrossSigningInfo?, otherDevice: CryptoDeviceInfo): DeviceTrustResult {
val locallyTrusted = otherDevice.trustLevel?.isLocallyVerified()
myKeys ?: return legacyFallbackTrust(locallyTrusted, DeviceTrustResult.CrossSigningNotConfigured(userId))
@@ -747,8 +750,11 @@ internal class DefaultCrossSigningService @Inject constructor(
}
override fun onUsersDeviceUpdate(userIds: List) {
- Timber.d("## CrossSigning - onUsersDeviceUpdate for $userIds")
- val workerParams = UpdateTrustWorker.Params(sessionId = sessionId, updatedUserIds = userIds)
+ Timber.d("## CrossSigning - onUsersDeviceUpdate for ${userIds.size} users: $userIds")
+ val workerParams = UpdateTrustWorker.Params(
+ sessionId = sessionId,
+ filename = updateTrustWorkerDataRepository.createParam(userIds)
+ )
val workerData = WorkerParamsFactory.toData(workerParams)
val workRequest = workManagerProvider.matrixOneTimeWorkRequestBuilder()
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/UpdateTrustWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/UpdateTrustWorker.kt
index 665d770e7f..1660bae0b7 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/UpdateTrustWorker.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/UpdateTrustWorker.kt
@@ -55,7 +55,11 @@ internal class UpdateTrustWorker(context: Context,
internal data class Params(
override val sessionId: String,
override val lastFailureMessage: String? = null,
- val updatedUserIds: List
+ // Kept for compatibility, but not used anymore (can be used for pending Worker)
+ val updatedUserIds: List? = null,
+ // Passing a long list of userId can break the Work Manager due to data size limitation.
+ // so now we use a temporary file to store the data
+ val filename: String? = null
) : SessionWorkerParams
@Inject lateinit var crossSigningService: DefaultCrossSigningService
@@ -64,6 +68,7 @@ internal class UpdateTrustWorker(context: Context,
@CryptoDatabase @Inject lateinit var realmConfiguration: RealmConfiguration
@UserId @Inject lateinit var myUserId: String
@Inject lateinit var crossSigningKeysMapper: CrossSigningKeysMapper
+ @Inject lateinit var updateTrustWorkerDataRepository: UpdateTrustWorkerDataRepository
@SessionDatabase @Inject lateinit var sessionRealmConfiguration: RealmConfiguration
// @Inject lateinit var roomSummaryUpdater: RoomSummaryUpdater
@@ -74,7 +79,17 @@ internal class UpdateTrustWorker(context: Context,
}
override suspend fun doSafeWork(params: Params): Result {
- var userList = params.updatedUserIds
+ var userList = params.filename
+ ?.let { updateTrustWorkerDataRepository.getParam(it) }
+ ?.userIds
+ ?: params.updatedUserIds.orEmpty()
+
+ if (userList.isEmpty()) {
+ // This should not happen, but let's avoid go further in case of empty list
+ cleanup(params)
+ return Result.success()
+ }
+
// Unfortunately we don't have much info on what did exactly changed (is it the cross signing keys of that user,
// or a new device?) So we check all again :/
@@ -213,9 +228,15 @@ internal class UpdateTrustWorker(context: Context,
}
}
+ cleanup(params)
return Result.success()
}
+ private fun cleanup(params: Params) {
+ params.filename
+ ?.let { updateTrustWorkerDataRepository.delete(it) }
+ }
+
private fun updateCrossSigningKeysTrust(realm: Realm, userId: String, verified: Boolean) {
val xInfoEntity = realm.where(CrossSigningInfoEntity::class.java)
.equalTo(CrossSigningInfoEntityFields.USER_ID, userId)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/UpdateTrustWorkerDataRepository.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/UpdateTrustWorkerDataRepository.kt
new file mode 100644
index 0000000000..0878a9f765
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/UpdateTrustWorkerDataRepository.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2021 The Matrix.org Foundation C.I.C.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.matrix.android.sdk.internal.crypto.crosssigning
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+import org.matrix.android.sdk.api.extensions.tryOrNull
+import org.matrix.android.sdk.internal.di.MoshiProvider
+import org.matrix.android.sdk.internal.di.SessionFilesDirectory
+import java.io.File
+import java.util.UUID
+import javax.inject.Inject
+
+@JsonClass(generateAdapter = true)
+internal data class UpdateTrustWorkerData(
+ @Json(name = "userIds")
+ val userIds: List
+)
+
+internal class UpdateTrustWorkerDataRepository @Inject constructor(
+ @SessionFilesDirectory parentDir: File
+) {
+ private val workingDirectory = File(parentDir, "tw")
+ private val jsonAdapter = MoshiProvider.providesMoshi().adapter(UpdateTrustWorkerData::class.java)
+
+ // Return the path of the created file
+ fun createParam(userIds: List): String {
+ val filename = "${UUID.randomUUID()}.json"
+ workingDirectory.mkdirs()
+ val file = File(workingDirectory, filename)
+
+ UpdateTrustWorkerData(userIds = userIds)
+ .let { jsonAdapter.toJson(it) }
+ .let { file.writeText(it) }
+
+ return filename
+ }
+
+ fun getParam(filename: String): UpdateTrustWorkerData? {
+ return File(workingDirectory, filename)
+ .takeIf { it.exists() }
+ ?.readText()
+ ?.let { jsonAdapter.fromJson(it) }
+ }
+
+ fun delete(filename: String) {
+ tryOrNull("Unable to delete $filename") {
+ File(workingDirectory, filename).delete()
+ }
+ }
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/rest/DefaultBaseAuth.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/rest/DefaultBaseAuth.kt
new file mode 100644
index 0000000000..bbb4a3a654
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/rest/DefaultBaseAuth.kt
@@ -0,0 +1,34 @@
+/*
+ * 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.model.rest
+
+import org.matrix.android.sdk.api.auth.UIABaseAuth
+
+data class DefaultBaseAuth(
+ /**
+ * This is a session identifier that the client must pass back to the homeserver,
+ * if one is provided, in subsequent attempts to authenticate in the same API call.
+ */
+ override val session: String? = null
+
+) : UIABaseAuth {
+ override fun hasAuthInfo() = true
+
+ override fun copyWithSession(session: String) = this.copy(session = session)
+
+ override fun asMap(): Map = mapOf("session" to session)
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/rest/DeleteDeviceParams.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/rest/DeleteDeviceParams.kt
index 0ce6f1f41c..f636ab890d 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/rest/DeleteDeviceParams.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/rest/DeleteDeviceParams.kt
@@ -24,5 +24,5 @@ import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
internal data class DeleteDeviceParams(
@Json(name = "auth")
- val userPasswordAuth: UserPasswordAuth? = null
+ val auth: Map? = null
)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/rest/UploadSigningKeysBody.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/rest/UploadSigningKeysBody.kt
index 3418bb327d..d24b7ae5f0 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/rest/UploadSigningKeysBody.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/rest/UploadSigningKeysBody.kt
@@ -30,5 +30,5 @@ internal data class UploadSigningKeysBody(
val userSigningKey: RestKeyInfo? = null,
@Json(name = "auth")
- val auth: UserPasswordAuth? = null
+ val auth: Map? = null
)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/DeleteDeviceTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/DeleteDeviceTask.kt
index 8f1569a037..ff25ac0f66 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/DeleteDeviceTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/DeleteDeviceTask.kt
@@ -16,18 +16,22 @@
package org.matrix.android.sdk.internal.crypto.tasks
-import org.matrix.android.sdk.api.failure.Failure
-import org.matrix.android.sdk.api.failure.toRegistrationFlowResponse
+import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
+import org.matrix.android.sdk.internal.auth.registration.handleUIA
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.UIABaseAuth
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 timber.log.Timber
import javax.inject.Inject
internal interface DeleteDeviceTask : Task {
data class Params(
- val deviceId: String
+ val deviceId: String,
+ val userInteractiveAuthInterceptor: UserInteractiveAuthInterceptor?,
+ val userAuthParam: UIABaseAuth?
)
}
@@ -39,12 +43,17 @@ internal class DefaultDeleteDeviceTask @Inject constructor(
override suspend fun execute(params: DeleteDeviceTask.Params) {
try {
executeRequest(globalErrorReceiver) {
- apiCall = cryptoApi.deleteDevice(params.deviceId, DeleteDeviceParams())
+ apiCall = cryptoApi.deleteDevice(params.deviceId, DeleteDeviceParams(params.userAuthParam?.asMap()))
}
} catch (throwable: Throwable) {
- throw throwable.toRegistrationFlowResponse()
- ?.let { Failure.RegistrationFlowError(it) }
- ?: throwable
+ if (params.userInteractiveAuthInterceptor == null
+ || !handleUIA(throwable, params.userInteractiveAuthInterceptor) { auth ->
+ execute(params.copy(userAuthParam = auth))
+ }
+ ) {
+ Timber.d("## UIA: propagate failure")
+ throw throwable
+ }
}
}
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/DeleteDeviceWithUserPasswordTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/DeleteDeviceWithUserPasswordTask.kt
index b4c1e6d27c..dc0077425e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/DeleteDeviceWithUserPasswordTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/DeleteDeviceWithUserPasswordTask.kt
@@ -19,7 +19,7 @@ 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.internal.crypto.model.rest.UserPasswordAuth
+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
@@ -44,12 +44,12 @@ internal class DefaultDeleteDeviceWithUserPasswordTask @Inject constructor(
return executeRequest(globalErrorReceiver) {
apiCall = cryptoApi.deleteDevice(params.deviceId,
DeleteDeviceParams(
- userPasswordAuth = UserPasswordAuth(
+ auth = UserPasswordAuth(
type = LoginFlowTypes.PASSWORD,
session = params.authSession,
user = userId,
password = params.password
- )
+ ).asMap()
)
)
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/InitializeCrossSigningTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/InitializeCrossSigningTask.kt
index 6c0a76fa7d..ef31130f55 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/InitializeCrossSigningTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/InitializeCrossSigningTask.kt
@@ -17,6 +17,8 @@
package org.matrix.android.sdk.internal.crypto.tasks
import dagger.Lazy
+import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
+import org.matrix.android.sdk.internal.auth.registration.handleUIA
import org.matrix.android.sdk.internal.crypto.MXOlmDevice
import org.matrix.android.sdk.internal.crypto.MyDeviceInfoHolder
import org.matrix.android.sdk.internal.crypto.crosssigning.canonicalSignable
@@ -24,7 +26,6 @@ import org.matrix.android.sdk.internal.crypto.crosssigning.toBase64NoPadding
import org.matrix.android.sdk.internal.crypto.model.CryptoCrossSigningKey
import org.matrix.android.sdk.internal.crypto.model.KeyUsage
import org.matrix.android.sdk.internal.crypto.model.rest.UploadSignatureQueryBuilder
-import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth
import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.task.Task
import org.matrix.android.sdk.internal.util.JsonCanonicalizer
@@ -34,7 +35,7 @@ import javax.inject.Inject
internal interface InitializeCrossSigningTask : Task {
data class Params(
- val authParams: UserPasswordAuth?
+ val interactiveAuthInterceptor: UserInteractiveAuthInterceptor?
)
data class Result(
@@ -117,10 +118,21 @@ internal class DefaultInitializeCrossSigningTask @Inject constructor(
.key(sskPublicKey)
.signature(userId, masterPublicKey, signedSSK)
.build(),
- userPasswordAuth = params.authParams
+ userAuthParam = null
+// userAuthParam = params.authParams
)
- uploadSigningKeysTask.execute(uploadSigningKeysParams)
+ try {
+ uploadSigningKeysTask.execute(uploadSigningKeysParams)
+ } catch (failure: Throwable) {
+ if (params.interactiveAuthInterceptor == null
+ || !handleUIA(failure, params.interactiveAuthInterceptor) { authUpdate ->
+ uploadSigningKeysTask.execute(uploadSigningKeysParams.copy(userAuthParam = authUpdate))
+ }) {
+ Timber.d("## UIA: propagate failure")
+ throw failure
+ }
+ }
// Sign the current device with SSK
val uploadSignatureQueryBuilder = UploadSignatureQueryBuilder()
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/UploadSigningKeysTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/UploadSigningKeysTask.kt
index cceff355bb..14fad2ea38 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/UploadSigningKeysTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/UploadSigningKeysTask.kt
@@ -16,14 +16,12 @@
package org.matrix.android.sdk.internal.crypto.tasks
-import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
import org.matrix.android.sdk.api.failure.Failure
-import org.matrix.android.sdk.api.failure.toRegistrationFlowResponse
import org.matrix.android.sdk.internal.crypto.api.CryptoApi
import org.matrix.android.sdk.internal.crypto.model.CryptoCrossSigningKey
import org.matrix.android.sdk.internal.crypto.model.rest.KeysQueryResponse
+import org.matrix.android.sdk.api.auth.UIABaseAuth
import org.matrix.android.sdk.internal.crypto.model.rest.UploadSigningKeysBody
-import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth
import org.matrix.android.sdk.internal.crypto.model.toRest
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
import org.matrix.android.sdk.internal.network.executeRequest
@@ -39,15 +37,9 @@ internal interface UploadSigningKeysTask : Task) {
+ override suspend fun clearCache() {
stopSync()
stopAnyBackgroundSync()
uiHandler.post {
lifecycleObservers.forEach { it.onClearCache() }
}
- cacheService.get().clearCache(callback)
+ cacheService.get().clearCache()
workManagerProvider.cancelAllWorks()
}
@@ -274,6 +275,18 @@ internal class DefaultSession @Inject constructor(
return "$myUserId - ${sessionParams.deviceId}"
}
+ override fun getUiaSsoFallbackUrl(authenticationSessionId: String): String {
+ val hsBas = sessionParams.homeServerConnectionConfig
+ .homeServerUri
+ .toString()
+ .trim { it == '/' }
+ return buildString {
+ append(hsBas)
+ append(SSO_UIA_FALLBACK_PATH)
+ appendParamToUrl("session", authenticationSessionId)
+ }
+ }
+
override fun logDbUsageInfo() {
RealmDebugTools(realmConfiguration).logInfo("Session")
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionComponent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionComponent.kt
index 659fcc8f5c..f5eade1704 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionComponent.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionComponent.kt
@@ -27,7 +27,6 @@ import org.matrix.android.sdk.internal.crypto.SendGossipWorker
import org.matrix.android.sdk.internal.crypto.crosssigning.UpdateTrustWorker
import org.matrix.android.sdk.internal.crypto.verification.SendVerificationMessageWorker
import org.matrix.android.sdk.internal.di.MatrixComponent
-import org.matrix.android.sdk.internal.di.SessionAssistedInjectModule
import org.matrix.android.sdk.internal.network.NetworkConnectivityChecker
import org.matrix.android.sdk.internal.session.account.AccountModule
import org.matrix.android.sdk.internal.session.cache.CacheModule
@@ -86,7 +85,6 @@ import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
TermsModule::class,
AccountDataModule::class,
ProfileModule::class,
- SessionAssistedInjectModule::class,
AccountModule::class,
CallModule::class,
SearchModule::class
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/account/ChangePasswordParams.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/account/ChangePasswordParams.kt
index 70cdbda399..1b95820918 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/account/ChangePasswordParams.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/account/ChangePasswordParams.kt
@@ -18,7 +18,7 @@ package org.matrix.android.sdk.internal.session.account
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
-import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth
+import org.matrix.android.sdk.api.auth.UserPasswordAuth
/**
* Class to pass request parameters to update the password.
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/account/DeactivateAccountParams.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/account/DeactivateAccountParams.kt
index 6c2e8b4a4e..d9b4c748b9 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/account/DeactivateAccountParams.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/account/DeactivateAccountParams.kt
@@ -18,21 +18,21 @@ package org.matrix.android.sdk.internal.session.account
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
-import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth
+import org.matrix.android.sdk.api.auth.UIABaseAuth
@JsonClass(generateAdapter = true)
internal data class DeactivateAccountParams(
- @Json(name = "auth")
- val auth: UserPasswordAuth? = null,
-
// Set to true to erase all data of the account
@Json(name = "erase")
- val erase: Boolean
+ val erase: Boolean,
+
+ @Json(name = "auth")
+ val auth: Map? = null
) {
companion object {
- fun create(userId: String, password: String, erase: Boolean): DeactivateAccountParams {
+ fun create(auth: UIABaseAuth?, erase: Boolean): DeactivateAccountParams {
return DeactivateAccountParams(
- auth = UserPasswordAuth(user = userId, password = password),
+ auth = auth?.asMap(),
erase = erase
)
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/account/DeactivateAccountTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/account/DeactivateAccountTask.kt
index 9fb1cbb7d7..d67b21567e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/account/DeactivateAccountTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/account/DeactivateAccountTask.kt
@@ -16,6 +16,9 @@
package org.matrix.android.sdk.internal.session.account
+import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
+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.executeRequest
@@ -27,8 +30,9 @@ import javax.inject.Inject
internal interface DeactivateAccountTask : Task {
data class Params(
- val password: String,
- val eraseAllData: Boolean
+ val userInteractiveAuthInterceptor: UserInteractiveAuthInterceptor,
+ val eraseAllData: Boolean,
+ val userAuthParam: UIABaseAuth? = null
)
}
@@ -41,12 +45,21 @@ internal class DefaultDeactivateAccountTask @Inject constructor(
) : DeactivateAccountTask {
override suspend fun execute(params: DeactivateAccountTask.Params) {
- val deactivateAccountParams = DeactivateAccountParams.create(userId, params.password, params.eraseAllData)
+ val deactivateAccountParams = DeactivateAccountParams.create(params.userAuthParam, params.eraseAllData)
- executeRequest(globalErrorReceiver) {
- apiCall = accountAPI.deactivate(deactivateAccountParams)
+ try {
+ executeRequest(globalErrorReceiver) {
+ apiCall = accountAPI.deactivate(deactivateAccountParams)
+ }
+ } catch (throwable: Throwable) {
+ if (!handleUIA(throwable, params.userInteractiveAuthInterceptor) { auth ->
+ execute(params.copy(userAuthParam = auth))
+ }
+ ) {
+ Timber.d("## UIA: propagate failure")
+ throw throwable
+ }
}
-
// Logout from identity server if any, ignoring errors
runCatching { identityDisconnectTask.execute(Unit) }
.onFailure { Timber.w(it, "Unable to disconnect identity server") }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/account/DefaultAccountService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/account/DefaultAccountService.kt
index 1165d2116b..25b67159a9 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/account/DefaultAccountService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/account/DefaultAccountService.kt
@@ -16,6 +16,7 @@
package org.matrix.android.sdk.internal.session.account
+import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
import org.matrix.android.sdk.api.session.account.AccountService
import javax.inject.Inject
@@ -26,7 +27,7 @@ internal class DefaultAccountService @Inject constructor(private val changePassw
changePasswordTask.execute(ChangePasswordTask.Params(password, newPassword))
}
- override suspend fun deactivateAccount(password: String, eraseAllData: Boolean) {
- deactivateAccountTask.execute(DeactivateAccountTask.Params(password, eraseAllData))
+ override suspend fun deactivateAccount(userInteractiveAuthInterceptor: UserInteractiveAuthInterceptor, eraseAllData: Boolean) {
+ deactivateAccountTask.execute(DeactivateAccountTask.Params(userInteractiveAuthInterceptor, eraseAllData))
}
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/cache/DefaultCacheService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/cache/DefaultCacheService.kt
index 19365fce0a..6d0cd37e1f 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/cache/DefaultCacheService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/cache/DefaultCacheService.kt
@@ -16,23 +16,18 @@
package org.matrix.android.sdk.internal.session.cache
-import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.session.cache.CacheService
import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.task.TaskExecutor
-import org.matrix.android.sdk.internal.task.configureWith
import javax.inject.Inject
internal class DefaultCacheService @Inject constructor(@SessionDatabase
private val clearCacheTask: ClearCacheTask,
- private val taskExecutor: TaskExecutor) : CacheService {
+ private val taskExecutor: TaskExecutor
+) : CacheService {
- override fun clearCache(callback: MatrixCallback) {
+ override suspend fun clearCache() {
taskExecutor.cancelAll()
- clearCacheTask
- .configureWith {
- this.callback = callback
- }
- .executeBy(taskExecutor)
+ clearCacheTask.execute(Unit)
}
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/ImageCompressor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/ImageCompressor.kt
index 6cf65b867c..1d6cd61060 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/ImageCompressor.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/ImageCompressor.kt
@@ -28,9 +28,8 @@ import java.io.File
import java.util.UUID
import javax.inject.Inject
-internal class ImageCompressor @Inject constructor() {
+internal class ImageCompressor @Inject constructor(private val context: Context) {
suspend fun compress(
- context: Context,
imageFile: File,
desiredWidth: Int,
desiredHeight: Int,
@@ -46,7 +45,7 @@ internal class ImageCompressor @Inject constructor() {
}
} ?: return@withContext imageFile
- val destinationFile = createDestinationFile(context)
+ val destinationFile = createDestinationFile()
runCatching {
destinationFile.outputStream().use {
@@ -118,7 +117,7 @@ internal class ImageCompressor @Inject constructor() {
}
}
- private fun createDestinationFile(context: Context): File {
+ private fun createDestinationFile(): File {
return File.createTempFile(UUID.randomUUID().toString(), null, context.cacheDir)
}
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/ThumbnailExtractor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/ThumbnailExtractor.kt
index 4b31db59b1..c28668a53e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/ThumbnailExtractor.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/ThumbnailExtractor.kt
@@ -47,22 +47,24 @@ internal object ThumbnailExtractor {
val mediaMetadataRetriever = MediaMetadataRetriever()
try {
mediaMetadataRetriever.setDataSource(context, attachment.queryUri)
- val thumbnail = mediaMetadataRetriever.frameAtTime
-
- val outputStream = ByteArrayOutputStream()
- thumbnail.compress(Bitmap.CompressFormat.JPEG, 100, outputStream)
- val thumbnailWidth = thumbnail.width
- val thumbnailHeight = thumbnail.height
- val thumbnailSize = outputStream.size()
- thumbnailData = ThumbnailData(
- width = thumbnailWidth,
- height = thumbnailHeight,
- size = thumbnailSize.toLong(),
- bytes = outputStream.toByteArray(),
- mimeType = MimeTypes.Jpeg
- )
- thumbnail.recycle()
- outputStream.reset()
+ mediaMetadataRetriever.frameAtTime?.let { thumbnail ->
+ val outputStream = ByteArrayOutputStream()
+ thumbnail.compress(Bitmap.CompressFormat.JPEG, 100, outputStream)
+ val thumbnailWidth = thumbnail.width
+ val thumbnailHeight = thumbnail.height
+ val thumbnailSize = outputStream.size()
+ thumbnailData = ThumbnailData(
+ width = thumbnailWidth,
+ height = thumbnailHeight,
+ size = thumbnailSize.toLong(),
+ bytes = outputStream.toByteArray(),
+ mimeType = MimeTypes.Jpeg
+ )
+ thumbnail.recycle()
+ outputStream.reset()
+ } ?: run {
+ Timber.e("Cannot extract video thumbnail at %s", attachment.queryUri.toString())
+ }
} catch (e: Exception) {
Timber.e(e, "Cannot extract video thumbnail")
} finally {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt
index 672d407d25..3b727690bf 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt
@@ -156,7 +156,7 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
// Do not compress gif
&& attachment.mimeType != MimeTypes.Gif
&& params.compressBeforeSending) {
- fileToUpload = imageCompressor.compress(context, workingFile, MAX_IMAGE_SIZE, MAX_IMAGE_SIZE)
+ fileToUpload = imageCompressor.compress(workingFile, MAX_IMAGE_SIZE, MAX_IMAGE_SIZE)
.also { compressedFile ->
// Get new Bitmap size
compressedFile.inputStream().use {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityBulkLookupTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityBulkLookupTask.kt
index a03bef9501..67f3b2aa56 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityBulkLookupTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityBulkLookupTask.kt
@@ -52,65 +52,60 @@ internal class DefaultIdentityBulkLookupTask @Inject constructor(
val pepper = identityData.hashLookupPepper
val hashDetailResponse = if (pepper == null) {
// We need to fetch the hash details first
- fetchAndStoreHashDetails(identityAPI)
+ fetchHashDetails(identityAPI)
+ .also { identityStore.setHashDetails(it) }
} else {
IdentityHashDetailResponse(pepper, identityData.hashLookupAlgorithm)
}
- if (hashDetailResponse.algorithms.contains("sha256").not()) {
+ if (hashDetailResponse.algorithms.contains(IdentityHashDetailResponse.ALGORITHM_SHA256).not()) {
// TODO We should ask the user if he is ok to send their 3Pid in clear, but for the moment we do not do it
// Also, what we have in cache could be outdated, the identity server maybe now supports sha256
throw IdentityServiceError.BulkLookupSha256NotSupported
}
- val hashedAddresses = withOlmUtility { olmUtility ->
- params.threePids.map { threePid ->
- base64ToBase64Url(
- olmUtility.sha256(threePid.value.toLowerCase(Locale.ROOT)
- + " " + threePid.toMedium() + " " + hashDetailResponse.pepper)
- )
- }
- }
-
- val identityLookUpV2Response = lookUpInternal(identityAPI, hashedAddresses, hashDetailResponse, true)
+ val lookUpData = lookUpInternal(identityAPI, params.threePids, hashDetailResponse, true)
// Convert back to List
- return handleSuccess(params.threePids, hashedAddresses, identityLookUpV2Response)
+ return handleSuccess(params.threePids, lookUpData)
}
+ data class LookUpData(
+ val hashedAddresses: List,
+ val identityLookUpResponse: IdentityLookUpResponse
+ )
+
private suspend fun lookUpInternal(identityAPI: IdentityAPI,
- hashedAddresses: List,
+ threePids: List,
hashDetailResponse: IdentityHashDetailResponse,
- canRetry: Boolean): IdentityLookUpResponse {
+ canRetry: Boolean): LookUpData {
+ val hashedAddresses = getHashedAddresses(threePids, hashDetailResponse.pepper)
return try {
- executeRequest(null) {
- apiCall = identityAPI.lookup(IdentityLookUpParams(
- hashedAddresses,
- IdentityHashDetailResponse.ALGORITHM_SHA256,
- hashDetailResponse.pepper
- ))
- }
+ LookUpData(hashedAddresses,
+ executeRequest(null) {
+ apiCall = identityAPI.lookup(IdentityLookUpParams(
+ hashedAddresses,
+ IdentityHashDetailResponse.ALGORITHM_SHA256,
+ hashDetailResponse.pepper
+ ))
+ })
} catch (failure: Throwable) {
// Catch invalid hash pepper and retry
if (canRetry && failure is Failure.ServerError && failure.error.code == MatrixError.M_INVALID_PEPPER) {
// This is not documented, but the error can contain the new pepper!
- if (!failure.error.newLookupPepper.isNullOrEmpty()) {
+ val newHashDetailResponse = if (!failure.error.newLookupPepper.isNullOrEmpty()) {
// Store it and use it right now
hashDetailResponse.copy(pepper = failure.error.newLookupPepper)
- .also { identityStore.setHashDetails(it) }
- .let { lookUpInternal(identityAPI, hashedAddresses, it, false /* Avoid infinite loop */) }
} else {
// Retrieve the new hash details
- val newHashDetailResponse = fetchAndStoreHashDetails(identityAPI)
-
- if (hashDetailResponse.algorithms.contains(IdentityHashDetailResponse.ALGORITHM_SHA256).not()) {
- // TODO We should ask the user if he is ok to send their 3Pid in clear, but for the moment we do not do it
- // Also, what we have in cache is maybe outdated, the identity server maybe now support sha256
- throw IdentityServiceError.BulkLookupSha256NotSupported
- }
-
- lookUpInternal(identityAPI, hashedAddresses, newHashDetailResponse, false /* Avoid infinite loop */)
+ fetchHashDetails(identityAPI)
}
+ .also { identityStore.setHashDetails(it) }
+ if (newHashDetailResponse.algorithms.contains(IdentityHashDetailResponse.ALGORITHM_SHA256).not()) {
+ // TODO We should ask the user if he is ok to send their 3Pid in clear, but for the moment we do not do it
+ throw IdentityServiceError.BulkLookupSha256NotSupported
+ }
+ lookUpInternal(identityAPI, threePids, newHashDetailResponse, false /* Avoid infinite loop */)
} else {
// Other error
throw failure
@@ -118,16 +113,29 @@ internal class DefaultIdentityBulkLookupTask @Inject constructor(
}
}
- private suspend fun fetchAndStoreHashDetails(identityAPI: IdentityAPI): IdentityHashDetailResponse {
- return executeRequest(null) {
- apiCall = identityAPI.hashDetails()
+ private fun getHashedAddresses(threePids: List, pepper: String): List {
+ return withOlmUtility { olmUtility ->
+ threePids.map { threePid ->
+ base64ToBase64Url(
+ olmUtility.sha256(threePid.value.toLowerCase(Locale.ROOT)
+ + " " + threePid.toMedium() + " " + pepper)
+ )
+ }
}
- .also { identityStore.setHashDetails(it) }
}
- private fun handleSuccess(threePids: List, hashedAddresses: List, identityLookUpResponse: IdentityLookUpResponse): List {
- return identityLookUpResponse.mappings.keys.map { hashedAddress ->
- FoundThreePid(threePids[hashedAddresses.indexOf(hashedAddress)], identityLookUpResponse.mappings[hashedAddress] ?: error(""))
+ private suspend fun fetchHashDetails(identityAPI: IdentityAPI): IdentityHashDetailResponse {
+ return executeRequest(null) {
+ apiCall = identityAPI.hashDetails()
+ }
+ }
+
+ private fun handleSuccess(threePids: List, lookupData: LookUpData): List {
+ return lookupData.identityLookUpResponse.mappings.keys.map { hashedAddress ->
+ FoundThreePid(
+ threePids[lookupData.hashedAddresses.indexOf(hashedAddress)],
+ lookupData.identityLookUpResponse.mappings[hashedAddress] ?: error("")
+ )
}
}
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/DefaultMediaService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/DefaultMediaService.kt
index 1a400ccfcf..9b807d03de 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/DefaultMediaService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/DefaultMediaService.kt
@@ -18,9 +18,10 @@ package org.matrix.android.sdk.internal.session.media
import androidx.collection.LruCache
import org.matrix.android.sdk.api.cache.CacheStrategy
-import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.media.MediaService
import org.matrix.android.sdk.api.session.media.PreviewUrlData
+import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
+import org.matrix.android.sdk.api.session.room.timeline.getLatestEventId
import org.matrix.android.sdk.api.util.JsonDict
import org.matrix.android.sdk.internal.util.getOrPut
import javax.inject.Inject
@@ -34,11 +35,12 @@ internal class DefaultMediaService @Inject constructor(
// Cache of extracted URLs
private val extractedUrlsCache = LruCache>(1_000)
- override fun extractUrls(event: Event): List {
+ override fun extractUrls(event: TimelineEvent): List {
return extractedUrlsCache.getOrPut(event.cacheKey()) { urlsExtractor.extract(event) }
}
- private fun Event.cacheKey() = "${eventId ?: ""}-${roomId ?: ""}"
+ // Use the id of the latest Event edition
+ private fun TimelineEvent.cacheKey() = "${getLatestEventId()}-${root.roomId ?: ""}"
override suspend fun getRawPreviewUrl(url: String, timestamp: Long?): JsonDict {
return getRawPreviewUrlTask.execute(GetRawPreviewUrlTask.Params(url, timestamp))
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/UrlsExtractor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/UrlsExtractor.kt
index e531d6af9f..6137b4152c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/UrlsExtractor.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/UrlsExtractor.kt
@@ -17,21 +17,19 @@
package org.matrix.android.sdk.internal.session.media
import android.util.Patterns
-import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.events.model.EventType
-import org.matrix.android.sdk.api.session.events.model.toModel
-import org.matrix.android.sdk.api.session.room.model.message.MessageContent
import org.matrix.android.sdk.api.session.room.model.message.MessageType
+import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
+import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent
import javax.inject.Inject
internal class UrlsExtractor @Inject constructor() {
// Sadly Patterns.WEB_URL_WITH_PROTOCOL is not public so filter the protocol later
private val urlRegex = Patterns.WEB_URL.toRegex()
- fun extract(event: Event): List {
- return event.takeIf { it.getClearType() == EventType.MESSAGE }
- ?.getClearContent()
- ?.toModel()
+ fun extract(event: TimelineEvent): List {
+ return event.takeIf { it.root.getClearType() == EventType.MESSAGE }
+ ?.getLastMessageContent()
?.takeIf {
it.msgType == MessageType.MSGTYPE_TEXT
|| it.msgType == MessageType.MSGTYPE_NOTICE
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/DefaultProfileService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/DefaultProfileService.kt
index 500d43408e..b3216d744d 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/DefaultProfileService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/DefaultProfileService.kt
@@ -22,6 +22,7 @@ import androidx.lifecycle.LiveData
import com.zhuinden.monarchy.Monarchy
import io.realm.kotlin.where
import org.matrix.android.sdk.api.MatrixCallback
+import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
import org.matrix.android.sdk.api.session.identity.ThreePid
import org.matrix.android.sdk.api.session.profile.ProfileService
import org.matrix.android.sdk.api.util.Cancelable
@@ -170,14 +171,12 @@ internal class DefaultProfileService @Inject constructor(private val taskExecuto
}
override fun finalizeAddingThreePid(threePid: ThreePid,
- uiaSession: String?,
- accountPassword: String?,
+ userInteractiveAuthInterceptor: UserInteractiveAuthInterceptor,
matrixCallback: MatrixCallback): Cancelable {
return finalizeAddingThreePidTask
.configureWith(FinalizeAddingThreePidTask.Params(
threePid = threePid,
- session = uiaSession,
- accountPassword = accountPassword,
+ userInteractiveAuthInterceptor = userInteractiveAuthInterceptor,
userWantsToCancel = false
)) {
callback = alsoRefresh(matrixCallback)
@@ -189,8 +188,7 @@ internal class DefaultProfileService @Inject constructor(private val taskExecuto
return finalizeAddingThreePidTask
.configureWith(FinalizeAddingThreePidTask.Params(
threePid = threePid,
- session = null,
- accountPassword = null,
+ userInteractiveAuthInterceptor = null,
userWantsToCancel = true
)) {
callback = alsoRefresh(matrixCallback)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/FinalizeAddThreePidBody.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/FinalizeAddThreePidBody.kt
index 4e46dd096d..6301929545 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/FinalizeAddThreePidBody.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/FinalizeAddThreePidBody.kt
@@ -17,7 +17,6 @@ package org.matrix.android.sdk.internal.session.profile
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
-import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth
@JsonClass(generateAdapter = true)
internal data class FinalizeAddThreePidBody(
@@ -37,5 +36,5 @@ internal data class FinalizeAddThreePidBody(
* Additional authentication information for the user-interactive authentication API.
*/
@Json(name = "auth")
- val auth: UserPasswordAuth?
+ val auth: Map? = null
)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/FinalizeAddingThreePidTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/FinalizeAddingThreePidTask.kt
index 1e3a2cb501..916a602936 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/FinalizeAddingThreePidTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/FinalizeAddingThreePidTask.kt
@@ -17,10 +17,12 @@
package org.matrix.android.sdk.internal.session.profile
import com.zhuinden.monarchy.Monarchy
+import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.failure.toRegistrationFlowResponse
import org.matrix.android.sdk.api.session.identity.ThreePid
-import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth
+import org.matrix.android.sdk.internal.auth.registration.handleUIA
+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.PendingThreePidEntityFields
import org.matrix.android.sdk.internal.di.SessionDatabase
@@ -29,13 +31,14 @@ 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 org.matrix.android.sdk.internal.util.awaitTransaction
+import timber.log.Timber
import javax.inject.Inject
internal abstract class FinalizeAddingThreePidTask : Task {
data class Params(
val threePid: ThreePid,
- val session: String?,
- val accountPassword: String?,
+ val userInteractiveAuthInterceptor: UserInteractiveAuthInterceptor?,
+ val userAuthParam: UIABaseAuth? = null,
val userWantsToCancel: Boolean
)
}
@@ -62,20 +65,21 @@ internal class DefaultFinalizeAddingThreePidTask @Inject constructor(
val body = FinalizeAddThreePidBody(
clientSecret = pendingThreePids.clientSecret,
sid = pendingThreePids.sid,
- auth = if (params.session != null && params.accountPassword != null) {
- UserPasswordAuth(
- session = params.session,
- user = userId,
- password = params.accountPassword
- )
- } else null
+ auth = params.userAuthParam?.asMap()
)
apiCall = profileAPI.finalizeAddThreePid(body)
}
} catch (throwable: Throwable) {
- throw throwable.toRegistrationFlowResponse()
- ?.let { Failure.RegistrationFlowError(it) }
- ?: throwable
+ if (params.userInteractiveAuthInterceptor == null
+ || !handleUIA(throwable, params.userInteractiveAuthInterceptor) { auth ->
+ execute(params.copy(userAuthParam = auth))
+ }
+ ) {
+ Timber.d("## UIA: propagate failure")
+ throw throwable.toRegistrationFlowResponse()
+ ?.let { Failure.RegistrationFlowError(it) }
+ ?: throwable
+ }
}
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/DefaultAliasService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/DefaultAliasService.kt
index b6c69224e6..8f58094a2a 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/DefaultAliasService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/DefaultAliasService.kt
@@ -16,8 +16,9 @@
package org.matrix.android.sdk.internal.session.room.alias
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import org.matrix.android.sdk.api.session.room.alias.AliasService
internal class DefaultAliasService @AssistedInject constructor(
@@ -26,9 +27,9 @@ internal class DefaultAliasService @AssistedInject constructor(
private val addRoomAliasTask: AddRoomAliasTask
) : AliasService {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
- fun create(roomId: String): AliasService
+ fun create(roomId: String): DefaultAliasService
}
override suspend fun getRoomAliases(): List {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/call/DefaultRoomCallService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/call/DefaultRoomCallService.kt
index 205a085df6..9bde5054f6 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/call/DefaultRoomCallService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/call/DefaultRoomCallService.kt
@@ -16,8 +16,9 @@
package org.matrix.android.sdk.internal.session.room.call
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.room.call.RoomCallService
import org.matrix.android.sdk.internal.session.room.RoomGetter
@@ -27,9 +28,9 @@ internal class DefaultRoomCallService @AssistedInject constructor(
private val roomGetter: RoomGetter
) : RoomCallService {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
- fun create(roomId: String): RoomCallService
+ fun create(roomId: String): DefaultRoomCallService
}
override fun canStartCall(): Boolean {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomBodyBuilder.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomBodyBuilder.kt
index fb840b4eb3..5e823fc87f 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomBodyBuilder.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomBodyBuilder.kt
@@ -143,9 +143,11 @@ internal class CreateRoomBodyBuilder @Inject constructor(
}
private suspend fun canEnableEncryption(params: CreateRoomParams): Boolean {
- return (params.enableEncryptionIfInvitedUsersSupportIt
- && crossSigningService.isCrossSigningVerified()
- && params.invite3pids.isEmpty())
+ return params.enableEncryptionIfInvitedUsersSupportIt
+ // Parity with web, enable if users have encryption ready devices
+ // for now remove checks on cross signing and 3pid invites
+ // && crossSigningService.isCrossSigningVerified()
+ && params.invite3pids.isEmpty()
&& params.invitedUserIds.isNotEmpty()
&& params.invitedUserIds.let { userIds ->
val keys = deviceListManager.downloadKeys(userIds, forceDownload = false)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/draft/DefaultDraftService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/draft/DefaultDraftService.kt
index 93fbfb4df0..1d4ab6d516 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/draft/DefaultDraftService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/draft/DefaultDraftService.kt
@@ -17,8 +17,9 @@
package org.matrix.android.sdk.internal.session.room.draft
import androidx.lifecycle.LiveData
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import kotlinx.coroutines.withContext
import org.matrix.android.sdk.api.session.room.send.DraftService
import org.matrix.android.sdk.api.session.room.send.UserDraft
@@ -30,9 +31,9 @@ internal class DefaultDraftService @AssistedInject constructor(@Assisted private
private val coroutineDispatchers: MatrixCoroutineDispatchers
) : DraftService {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
- fun create(roomId: String): DraftService
+ fun create(roomId: String): DefaultDraftService
}
/**
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt
index 4fc865304b..cd1c9bbbdd 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt
@@ -17,8 +17,9 @@
package org.matrix.android.sdk.internal.session.room.membership
import androidx.lifecycle.LiveData
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import com.zhuinden.monarchy.Monarchy
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.session.identity.ThreePid
@@ -58,9 +59,9 @@ internal class DefaultMembershipService @AssistedInject constructor(
private val userId: String
) : MembershipService {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
- fun create(roomId: String): MembershipService
+ fun create(roomId: String): DefaultMembershipService
}
override fun loadRoomMembersIfNeeded(matrixCallback: MatrixCallback): Cancelable {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/notification/DefaultRoomPushRuleService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/notification/DefaultRoomPushRuleService.kt
index 67ae55c066..5486d96e28 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/notification/DefaultRoomPushRuleService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/notification/DefaultRoomPushRuleService.kt
@@ -18,8 +18,9 @@ package org.matrix.android.sdk.internal.session.room.notification
import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import com.zhuinden.monarchy.Monarchy
import org.matrix.android.sdk.api.pushrules.RuleScope
import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState
@@ -33,9 +34,9 @@ internal class DefaultRoomPushRuleService @AssistedInject constructor(@Assisted
@SessionDatabase private val monarchy: Monarchy)
: RoomPushRuleService {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
- fun create(roomId: String): RoomPushRuleService
+ fun create(roomId: String): DefaultRoomPushRuleService
}
override fun getLiveRoomNotificationState(): LiveData {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/read/DefaultReadService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/read/DefaultReadService.kt
index 025bea09f4..3cf8cfe5ee 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/read/DefaultReadService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/read/DefaultReadService.kt
@@ -18,8 +18,9 @@ package org.matrix.android.sdk.internal.session.room.read
import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import com.zhuinden.monarchy.Monarchy
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.session.room.model.ReadReceipt
@@ -46,9 +47,9 @@ internal class DefaultReadService @AssistedInject constructor(
@UserId private val userId: String
) : ReadService {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
- fun create(roomId: String): ReadService
+ fun create(roomId: String): DefaultReadService
}
override fun markAsRead(params: ReadService.MarkAsReadParams, callback: MatrixCallback) {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/DefaultRelationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/DefaultRelationService.kt
index a7f3f83980..b7caf62865 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/DefaultRelationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/DefaultRelationService.kt
@@ -17,8 +17,9 @@ package org.matrix.android.sdk.internal.session.room.relation
import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import com.zhuinden.monarchy.Monarchy
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.session.events.model.Event
@@ -56,9 +57,9 @@ internal class DefaultRelationService @AssistedInject constructor(
private val taskExecutor: TaskExecutor)
: RelationService {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
- fun create(roomId: String): RelationService
+ fun create(roomId: String): DefaultRelationService
}
override fun sendReaction(targetEventId: String, reaction: String): Cancelable {
@@ -140,7 +141,7 @@ internal class DefaultRelationService @AssistedInject constructor(
}
override fun fetchEditHistory(eventId: String, callback: MatrixCallback>) {
- val params = FetchEditHistoryTask.Params(roomId, cryptoSessionInfoProvider.isRoomEncrypted(roomId), eventId)
+ val params = FetchEditHistoryTask.Params(roomId, eventId)
fetchEditHistoryTask
.configureWith(params) {
this.callback = callback
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/FetchEditHistoryTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/FetchEditHistoryTask.kt
index 99d02b50da..854585ca29 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/FetchEditHistoryTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/FetchEditHistoryTask.kt
@@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.session.room.relation
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.RelationType
+import org.matrix.android.sdk.internal.crypto.CryptoSessionInfoProvider
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.session.room.RoomAPI
@@ -25,25 +26,27 @@ import org.matrix.android.sdk.internal.task.Task
import javax.inject.Inject
internal interface FetchEditHistoryTask : Task> {
-
data class Params(
val roomId: String,
- val isRoomEncrypted: Boolean,
val eventId: String
)
}
internal class DefaultFetchEditHistoryTask @Inject constructor(
private val roomAPI: RoomAPI,
- private val globalErrorReceiver: GlobalErrorReceiver
+ private val globalErrorReceiver: GlobalErrorReceiver,
+ private val cryptoSessionInfoProvider: CryptoSessionInfoProvider
) : FetchEditHistoryTask {
override suspend fun execute(params: FetchEditHistoryTask.Params): List {
+ val isRoomEncrypted = cryptoSessionInfoProvider.isRoomEncrypted(params.roomId)
val response = executeRequest(globalErrorReceiver) {
- apiCall = roomAPI.getRelations(params.roomId,
- params.eventId,
- RelationType.REPLACE,
- if (params.isRoomEncrypted) EventType.ENCRYPTED else EventType.MESSAGE)
+ apiCall = roomAPI.getRelations(
+ roomId = params.roomId,
+ eventId = params.eventId,
+ relationType = RelationType.REPLACE,
+ eventType = if (isRoomEncrypted) EventType.ENCRYPTED else EventType.MESSAGE
+ )
}
val events = response.chunks.toMutableList()
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/reporting/DefaultReportingService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/reporting/DefaultReportingService.kt
index cac87a9d30..add17a9fa5 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/reporting/DefaultReportingService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/reporting/DefaultReportingService.kt
@@ -16,17 +16,18 @@
package org.matrix.android.sdk.internal.session.room.reporting
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import org.matrix.android.sdk.api.session.room.reporting.ReportingService
internal class DefaultReportingService @AssistedInject constructor(@Assisted private val roomId: String,
private val reportContentTask: ReportContentTask
) : ReportingService {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
- fun create(roomId: String): ReportingService
+ fun create(roomId: String): DefaultReportingService
}
override suspend fun reportContent(eventId: String, score: Int, reason: String) {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt
index 8828f3dfed..a12962b51f 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt
@@ -21,8 +21,9 @@ import androidx.work.BackoffPolicy
import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequest
import androidx.work.Operation
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.session.content.ContentAttachmentData
import org.matrix.android.sdk.api.session.events.model.Event
@@ -71,9 +72,9 @@ internal class DefaultSendService @AssistedInject constructor(
private val cancelSendTracker: CancelSendTracker
) : SendService {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
- fun create(roomId: String): SendService
+ fun create(roomId: String): DefaultSendService
}
private val workerFutureListenerExecutor = Executors.newSingleThreadExecutor()
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt
index 804968bac0..f2640fd1e7 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt
@@ -18,8 +18,9 @@ package org.matrix.android.sdk.internal.session.room.state
import android.net.Uri
import androidx.lifecycle.LiveData
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import org.matrix.android.sdk.api.query.QueryStringValue
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.events.model.EventType
@@ -42,9 +43,9 @@ internal class DefaultStateService @AssistedInject constructor(@Assisted private
private val fileUploader: FileUploader
) : StateService {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
- fun create(roomId: String): StateService
+ fun create(roomId: String): DefaultStateService
}
override fun getStateEvent(eventType: String, stateKey: QueryStringValue): Event? {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt
index 8c71604183..fff780fb0c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt
@@ -140,14 +140,13 @@ internal class RoomSummaryUpdater @Inject constructor(
.queryActiveRoomMembersEvent()
.notEqualTo(RoomMemberSummaryEntityFields.USER_ID, userId)
.findAll()
- .asSequence()
.map { it.userId }
roomSummaryEntity.otherMemberIds.clear()
roomSummaryEntity.otherMemberIds.addAll(otherRoomMembers)
if (roomSummaryEntity.isEncrypted) {
// mmm maybe we could only refresh shield instead of checking trust also?
- crossSigningService.onUsersDeviceUpdate(roomSummaryEntity.otherMemberIds.toList())
+ crossSigningService.onUsersDeviceUpdate(otherRoomMembers)
}
}
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/tags/DefaultTagsService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/tags/DefaultTagsService.kt
index d6c02f0a49..02acaa0570 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/tags/DefaultTagsService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/tags/DefaultTagsService.kt
@@ -16,8 +16,9 @@
package org.matrix.android.sdk.internal.session.room.tags
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import org.matrix.android.sdk.api.session.room.tags.TagsService
internal class DefaultTagsService @AssistedInject constructor(
@@ -26,9 +27,9 @@ internal class DefaultTagsService @AssistedInject constructor(
private val deleteTagFromRoomTask: DeleteTagFromRoomTask
) : TagsService {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
- fun create(roomId: String): TagsService
+ fun create(roomId: String): DefaultTagsService
}
override suspend fun addTag(tag: String, order: Double?) {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt
index fce09cc97c..ef890db79e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt
@@ -18,8 +18,9 @@ package org.matrix.android.sdk.internal.session.room.timeline
import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import com.zhuinden.monarchy.Monarchy
import io.realm.Sort
import io.realm.kotlin.where
@@ -55,9 +56,9 @@ internal class DefaultTimelineService @AssistedInject constructor(@Assisted priv
private val loadRoomMembersTask: LoadRoomMembersTask
) : TimelineService {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
- fun create(roomId: String): TimelineService
+ fun create(roomId: String): DefaultTimelineService
}
override fun createTimeline(eventId: String?, settings: TimelineSettings): Timeline {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/EventContextResponse.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/EventContextResponse.kt
index bce03354d7..654cf0fb74 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/EventContextResponse.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/EventContextResponse.kt
@@ -21,16 +21,34 @@ import com.squareup.moshi.JsonClass
import org.matrix.android.sdk.api.session.events.model.Event
@JsonClass(generateAdapter = true)
-data class EventContextResponse(
+internal data class EventContextResponse(
+ /**
+ * Details of the requested event.
+ */
@Json(name = "event") val event: Event,
+ /**
+ * A token that can be used to paginate backwards with.
+ */
@Json(name = "start") override val start: String? = null,
- @Json(name = "events_before") val eventsBefore: List = emptyList(),
- @Json(name = "events_after") val eventsAfter: List = emptyList(),
+ /**
+ * A list of room events that happened just before the requested event, in reverse-chronological order.
+ */
+ @Json(name = "events_before") val eventsBefore: List? = null,
+ /**
+ * A list of room events that happened just after the requested event, in chronological order.
+ */
+ @Json(name = "events_after") val eventsAfter: List? = null,
+ /**
+ * A token that can be used to paginate forwards with.
+ */
@Json(name = "end") override val end: String? = null,
- @Json(name = "state") override val stateEvents: List = emptyList()
+ /**
+ * The state of the room at the last event returned.
+ */
+ @Json(name = "state") override val stateEvents: List? = null
) : TokenChunkEvent {
override val events: List by lazy {
- eventsAfter.reversed() + listOf(event) + eventsBefore
+ eventsAfter.orEmpty().reversed() + event + eventsBefore.orEmpty()
}
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/PaginationResponse.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/PaginationResponse.kt
index ed384d3b3c..2f61b1cce8 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/PaginationResponse.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/PaginationResponse.kt
@@ -22,8 +22,28 @@ import org.matrix.android.sdk.api.session.events.model.Event
@JsonClass(generateAdapter = true)
internal data class PaginationResponse(
+ /**
+ * The token the pagination starts from. If dir=b this will be the token supplied in from.
+ */
@Json(name = "start") override val start: String? = null,
+ /**
+ * The token the pagination ends at. If dir=b this token should be used again to request even earlier events.
+ */
@Json(name = "end") override val end: String? = null,
- @Json(name = "chunk") override val events: List = emptyList(),
- @Json(name = "state") override val stateEvents: List = emptyList()
-) : TokenChunkEvent
+ /**
+ * A list of room events. The order depends on the dir parameter. For dir=b events will be in
+ * reverse-chronological order, for dir=f in chronological order, so that events start at the from point.
+ */
+ @Json(name = "chunk") val chunk: List? = null,
+ /**
+ * A list of state events relevant to showing the chunk. For example, if lazy_load_members is enabled
+ * in the filter then this may contain the membership events for the senders of events in the chunk.
+ *
+ * Unless include_redundant_members is true, the server may remove membership events which would have
+ * already been sent to the client in prior calls to this endpoint, assuming the membership of those members has not changed.
+ */
+ @Json(name = "state") override val stateEvents: List? = null
+) : TokenChunkEvent {
+ override val events: List
+ get() = chunk.orEmpty()
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEvent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEvent.kt
index 08b20f1701..465b0faac8 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEvent.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEvent.kt
@@ -22,7 +22,7 @@ internal interface TokenChunkEvent {
val start: String?
val end: String?
val events: List
- val stateEvents: List
+ val stateEvents: List?
fun hasMore() = start != end
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt
index 2a532c6bf5..1a497b8835 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt
@@ -156,7 +156,7 @@ internal class TokenChunkEventPersistor @Inject constructor(@SessionDatabase pri
}
}
return if (receivedChunk.events.isEmpty()) {
- if (receivedChunk.start != receivedChunk.end) {
+ if (receivedChunk.hasMore()) {
Result.SHOULD_FETCH_MORE
} else {
Result.REACHED_END
@@ -196,7 +196,7 @@ internal class TokenChunkEventPersistor @Inject constructor(@SessionDatabase pri
val now = System.currentTimeMillis()
- for (stateEvent in stateEvents) {
+ stateEvents?.forEach { stateEvent ->
val ageLocalTs = stateEvent.unsignedData?.age?.let { now - it }
val stateEventEntity = stateEvent.toEntity(roomId, SendState.SYNCED, ageLocalTs).copyToRealmOrIgnore(realm, EventInsertType.PAGINATION)
currentChunk.addStateEvent(roomId, stateEventEntity, direction)
@@ -205,9 +205,9 @@ internal class TokenChunkEventPersistor @Inject constructor(@SessionDatabase pri
}
}
val eventIds = ArrayList(eventList.size)
- for (event in eventList) {
+ eventList.forEach { event ->
if (event.eventId == null || event.senderId == null) {
- continue
+ return@forEach
}
val ageLocalTs = event.unsignedData?.age?.let { now - it }
eventIds.add(event.eventId)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/typing/DefaultTypingService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/typing/DefaultTypingService.kt
index 5dcf3fcdd6..39b7967bc1 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/typing/DefaultTypingService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/typing/DefaultTypingService.kt
@@ -17,8 +17,9 @@
package org.matrix.android.sdk.internal.session.room.typing
import android.os.SystemClock
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.session.room.typing.TypingService
import org.matrix.android.sdk.api.util.Cancelable
@@ -38,9 +39,9 @@ internal class DefaultTypingService @AssistedInject constructor(
private val sendTypingTask: SendTypingTask
) : TypingService {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
- fun create(roomId: String): TypingService
+ fun create(roomId: String): DefaultTypingService
}
private var currentTask: Cancelable? = null
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/uploads/DefaultUploadsService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/uploads/DefaultUploadsService.kt
index 895f1cf50d..6d841644dc 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/uploads/DefaultUploadsService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/uploads/DefaultUploadsService.kt
@@ -16,8 +16,9 @@
package org.matrix.android.sdk.internal.session.room.uploads
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import org.matrix.android.sdk.api.session.crypto.CryptoService
import org.matrix.android.sdk.api.session.room.uploads.GetUploadsResult
import org.matrix.android.sdk.api.session.room.uploads.UploadsService
@@ -28,9 +29,9 @@ internal class DefaultUploadsService @AssistedInject constructor(
private val cryptoService: CryptoService
) : UploadsService {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
- fun create(roomId: String): UploadsService
+ fun create(roomId: String): DefaultUploadsService
}
override suspend fun getUploads(numberOfEvents: Int, since: String?): GetUploadsResult {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/uploads/GetUploadsTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/uploads/GetUploadsTask.kt
index 0c0e6a8ed0..b3e4a5aa05 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/uploads/GetUploadsTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/uploads/GetUploadsTask.kt
@@ -56,8 +56,8 @@ internal class DefaultGetUploadsTask @Inject constructor(
private val roomAPI: RoomAPI,
private val tokenStore: SyncTokenStore,
@SessionDatabase private val monarchy: Monarchy,
- private val globalErrorReceiver: GlobalErrorReceiver)
- : GetUploadsTask {
+ private val globalErrorReceiver: GlobalErrorReceiver
+) : GetUploadsTask {
override suspend fun execute(params: GetUploadsTask.Params): GetUploadsResult {
val result: GetUploadsResult
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/signout/DefaultSignOutService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/signout/DefaultSignOutService.kt
index ea3730b195..e7b20f905b 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/signout/DefaultSignOutService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/signout/DefaultSignOutService.kt
@@ -16,45 +16,25 @@
package org.matrix.android.sdk.internal.session.signout
-import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.auth.data.Credentials
import org.matrix.android.sdk.api.session.signout.SignOutService
-import org.matrix.android.sdk.api.util.Cancelable
import org.matrix.android.sdk.internal.auth.SessionParamsStore
-import org.matrix.android.sdk.internal.task.TaskExecutor
-import org.matrix.android.sdk.internal.task.configureWith
-import org.matrix.android.sdk.internal.task.launchToCallback
-import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
import javax.inject.Inject
internal class DefaultSignOutService @Inject constructor(private val signOutTask: SignOutTask,
private val signInAgainTask: SignInAgainTask,
- private val sessionParamsStore: SessionParamsStore,
- private val coroutineDispatchers: MatrixCoroutineDispatchers,
- private val taskExecutor: TaskExecutor) : SignOutService {
+ private val sessionParamsStore: SessionParamsStore
+) : SignOutService {
- override fun signInAgain(password: String,
- callback: MatrixCallback): Cancelable {
- return signInAgainTask
- .configureWith(SignInAgainTask.Params(password)) {
- this.callback = callback
- }
- .executeBy(taskExecutor)
+ override suspend fun signInAgain(password: String) {
+ signInAgainTask.execute(SignInAgainTask.Params(password))
}
- override fun updateCredentials(credentials: Credentials,
- callback: MatrixCallback): Cancelable {
- return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, callback) {
- sessionParamsStore.updateCredentials(credentials)
- }
+ override suspend fun updateCredentials(credentials: Credentials) {
+ sessionParamsStore.updateCredentials(credentials)
}
- override fun signOut(signOutFromHomeserver: Boolean,
- callback: MatrixCallback): Cancelable {
- return signOutTask
- .configureWith(SignOutTask.Params(signOutFromHomeserver)) {
- this.callback = callback
- }
- .executeBy(taskExecutor)
+ override suspend fun signOut(signOutFromHomeserver: Boolean) {
+ return signOutTask.execute(SignOutTask.Params(signOutFromHomeserver))
}
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/helper/WidgetFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/helper/WidgetFactory.kt
index c41f1df0de..000b9e38b9 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/helper/WidgetFactory.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/helper/WidgetFactory.kt
@@ -53,7 +53,7 @@ internal class WidgetFactory @Inject constructor(private val userDataSource: Use
}
}
val isAddedByMe = widgetEvent.senderId == userId
- val computedUrl = widgetContent.computeURL(widgetEvent.roomId)
+ val computedUrl = widgetContent.computeURL(widgetEvent.roomId, widgetId)
return Widget(
widgetContent = widgetContent,
event = widgetEvent,
@@ -65,13 +65,14 @@ internal class WidgetFactory @Inject constructor(private val userDataSource: Use
)
}
- private fun WidgetContent.computeURL(roomId: String?): String? {
+ private fun WidgetContent.computeURL(roomId: String?, widgetId: String): String? {
var computedUrl = url ?: return null
val myUser = userDataSource.getUser(userId)
computedUrl = computedUrl
.replace("\$matrix_user_id", userId)
.replace("\$matrix_display_name", myUser?.displayName ?: userId)
.replace("\$matrix_avatar_url", myUser?.avatarUrl ?: "")
+ .replace("\$matrix_widget_id", widgetId)
if (roomId != null) {
computedUrl = computedUrl.replace("\$matrix_room_id", roomId)
diff --git a/multipicker/build.gradle b/multipicker/build.gradle
index c58c4586b2..10dc18e488 100644
--- a/multipicker/build.gradle
+++ b/multipicker/build.gradle
@@ -19,11 +19,11 @@ apply plugin: 'kotlin-android'
apply plugin: 'kotlin-parcelize'
android {
- compileSdkVersion 29
+ compileSdkVersion 30
defaultConfig {
minSdkVersion 19
- targetSdkVersion 29
+ targetSdkVersion 30
versionCode 1
versionName "1.0"
diff --git a/multipicker/src/main/java/im/vector/lib/multipicker/AudioPicker.kt b/multipicker/src/main/java/im/vector/lib/multipicker/AudioPicker.kt
index 516022100d..e8970d72ef 100644
--- a/multipicker/src/main/java/im/vector/lib/multipicker/AudioPicker.kt
+++ b/multipicker/src/main/java/im/vector/lib/multipicker/AudioPicker.kt
@@ -58,7 +58,7 @@ class AudioPicker : Picker() {
context.contentResolver.openFileDescriptor(selectedUri, "r")?.use { pfd ->
val mediaMetadataRetriever = MediaMetadataRetriever()
mediaMetadataRetriever.setDataSource(pfd.fileDescriptor)
- duration = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION).toLong()
+ duration = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)?.toLong() ?: 0L
}
audioList.add(
diff --git a/multipicker/src/main/java/im/vector/lib/multipicker/VideoPicker.kt b/multipicker/src/main/java/im/vector/lib/multipicker/VideoPicker.kt
index c7c06f795f..dada9ac5bd 100644
--- a/multipicker/src/main/java/im/vector/lib/multipicker/VideoPicker.kt
+++ b/multipicker/src/main/java/im/vector/lib/multipicker/VideoPicker.kt
@@ -61,10 +61,10 @@ class VideoPicker : Picker() {
context.contentResolver.openFileDescriptor(selectedUri, "r")?.use { pfd ->
val mediaMetadataRetriever = MediaMetadataRetriever()
mediaMetadataRetriever.setDataSource(pfd.fileDescriptor)
- duration = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION).toLong()
- width = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH).toInt()
- height = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT).toInt()
- orientation = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION).toInt()
+ duration = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)?.toLong() ?: 0L
+ width = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH)?.toInt() ?: 0
+ height = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)?.toInt() ?: 0
+ orientation = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION)?.toInt() ?: 0
}
videoList.add(
diff --git a/tools/templates/ElementFeature/root/src/app_package/ViewModel.kt.ftl b/tools/templates/ElementFeature/root/src/app_package/ViewModel.kt.ftl
index d3c50c961c..8c25f9f9a8 100644
--- a/tools/templates/ElementFeature/root/src/app_package/ViewModel.kt.ftl
+++ b/tools/templates/ElementFeature/root/src/app_package/ViewModel.kt.ftl
@@ -4,8 +4,9 @@ import com.airbnb.mvrx.ActivityViewModelContext
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
@@ -21,7 +22,7 @@ class ${viewModelClass} @AssistedInject constructor(@Assisted initialState: ${vi
: VectorViewModel<${viewStateClass}, ${actionClass}, EmptyViewEvents>(initialState) {
#if>
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: ${viewStateClass}): ${viewModelClass}
}
diff --git a/vector/build.gradle b/vector/build.gradle
index f6ba5d6e27..ced0899cf9 100644
--- a/vector/build.gradle
+++ b/vector/build.gradle
@@ -13,7 +13,7 @@ kapt {
// Note: 2 digits max for each value
ext.versionMajor = 1
ext.versionMinor = 0
-ext.versionPatch = 14
+ext.versionPatch = 15
static def getGitTimestamp() {
def cmd = 'git show -s --format=%ct'
@@ -101,7 +101,7 @@ ext.abiVersionCodes = ["armeabi-v7a": 1, "arm64-v8a": 2, "x86": 3, "x86_64": 4].
def buildNumber = System.env.BUILDKITE_BUILD_NUMBER as Integer ?: 0
android {
- compileSdkVersion 29
+ compileSdkVersion 30
// Due to a bug introduced in Android gradle plugin 3.6.0, we have to specify the ndk version to use
// Ref: https://issuetracker.google.com/issues/144111441
@@ -111,7 +111,7 @@ android {
applicationId "im.vector.app"
// Set to API 21: see #405
minSdkVersion 21
- targetSdkVersion 29
+ targetSdkVersion 30
multiDexEnabled true
// `develop` branch will have version code from timestamp, to ensure each build from CI has a incremented versionCode.
@@ -291,7 +291,7 @@ dependencies {
def big_image_viewer_version = '1.6.2'
def glide_version = '4.11.0'
def moshi_version = '1.11.0'
- def daggerVersion = '2.29.1'
+ def daggerVersion = '2.31'
def autofill_version = "1.0.0"
def work_version = '2.4.0'
def arch_version = '2.1.0'
@@ -412,8 +412,6 @@ dependencies {
// DI
implementation "com.google.dagger:dagger:$daggerVersion"
kapt "com.google.dagger:dagger-compiler:$daggerVersion"
- compileOnly 'com.squareup.inject:assisted-inject-annotations-dagger2:0.5.0'
- kapt 'com.squareup.inject:assisted-inject-processor-dagger2:0.5.0'
// gplay flavor only
gplayImplementation('com.google.firebase:firebase-messaging:21.0.0') {
diff --git a/vector/src/androidTest/java/im/vector/app/VerifySessionInteractiveTest.kt b/vector/src/androidTest/java/im/vector/app/VerifySessionInteractiveTest.kt
index d9005e4a63..571bcf474c 100644
--- a/vector/src/androidTest/java/im/vector/app/VerifySessionInteractiveTest.kt
+++ b/vector/src/androidTest/java/im/vector/app/VerifySessionInteractiveTest.kt
@@ -42,13 +42,18 @@ import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.matrix.android.sdk.api.Matrix
+import org.matrix.android.sdk.api.auth.UIABaseAuth
+import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.crypto.verification.SasVerificationTransaction
import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod
import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState
-import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth
+import org.matrix.android.sdk.api.auth.UserPasswordAuth
+import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
+import kotlin.coroutines.Continuation
+import kotlin.coroutines.resume
@RunWith(AndroidJUnit4::class)
@LargeTest
@@ -67,10 +72,18 @@ class VerifySessionInteractiveTest : VerificationTestBase() {
existingSession = createAccountAndSync(matrix, userName, password, true)
doSync {
existingSession!!.cryptoService().crossSigningService()
- .initializeCrossSigning(UserPasswordAuth(
- user = existingSession!!.myUserId,
- password = "password"
- ), it)
+ .initializeCrossSigning(
+ object : UserInteractiveAuthInterceptor {
+ override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) {
+ promise.resume(
+ UserPasswordAuth(
+ user = existingSession!!.myUserId,
+ password = "password",
+ session = flowResponse.session
+ )
+ )
+ }
+ }, it)
}
}
diff --git a/vector/src/androidTest/java/im/vector/app/VerifySessionPassphraseTest.kt b/vector/src/androidTest/java/im/vector/app/VerifySessionPassphraseTest.kt
index 8a21260ac7..c51ff29669 100644
--- a/vector/src/androidTest/java/im/vector/app/VerifySessionPassphraseTest.kt
+++ b/vector/src/androidTest/java/im/vector/app/VerifySessionPassphraseTest.kt
@@ -46,8 +46,13 @@ import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.matrix.android.sdk.api.Matrix
+import org.matrix.android.sdk.api.auth.UIABaseAuth
+import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
+import org.matrix.android.sdk.api.auth.UserPasswordAuth
+import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
import org.matrix.android.sdk.api.session.Session
-import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth
+import kotlin.coroutines.Continuation
+import kotlin.coroutines.resume
@RunWith(AndroidJUnit4::class)
@LargeTest
@@ -67,17 +72,35 @@ class VerifySessionPassphraseTest : VerificationTestBase() {
existingSession = createAccountAndSync(matrix, userName, password, true)
doSync {
existingSession!!.cryptoService().crossSigningService()
- .initializeCrossSigning(UserPasswordAuth(
- user = existingSession!!.myUserId,
- password = "password"
- ), it)
+ .initializeCrossSigning(
+ object : UserInteractiveAuthInterceptor {
+ override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) {
+ promise.resume(
+ UserPasswordAuth(
+ user = existingSession!!.myUserId,
+ password = "password",
+ session = flowResponse.session
+ )
+ )
+ }
+ }, it)
}
val task = BootstrapCrossSigningTask(existingSession!!, StringProvider(context.resources))
runBlocking {
task.execute(Params(
- userPasswordAuth = UserPasswordAuth(password = password),
+ userInteractiveAuthInterceptor = object : UserInteractiveAuthInterceptor {
+ override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) {
+ promise.resume(
+ UserPasswordAuth(
+ user = existingSession!!.myUserId,
+ password = password,
+ session = flowResponse.session
+ )
+ )
+ }
+ },
passphrase = passphrase,
setupMode = SetupMode.NORMAL
))
diff --git a/vector/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt b/vector/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt
index 58b596b05f..2d0077fc55 100644
--- a/vector/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt
+++ b/vector/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt
@@ -196,6 +196,8 @@ class UiAllScreensSanityTest {
pressBack()
clickMenu(R.id.video_call)
pressBack()
+ clickMenu(R.id.search)
+ pressBack()
pressBack()
}
diff --git a/vector/src/main/AndroidManifest.xml b/vector/src/main/AndroidManifest.xml
index 0341059674..bfaea39cc6 100644
--- a/vector/src/main/AndroidManifest.xml
+++ b/vector/src/main/AndroidManifest.xml
@@ -63,7 +63,6 @@
@@ -242,6 +241,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
// homeserver not found
stringProvider.getString(R.string.login_error_no_homeserver_found)
+ HttpURLConnection.HTTP_UNAUTHORIZED ->
+ // uia errors?
+ stringProvider.getString(R.string.error_unauthorized)
else ->
throwable.localizedMessage
}
}
- is SsoFlowNotSupportedYet -> stringProvider.getString(R.string.error_sso_flow_not_supported_yet)
else -> throwable.localizedMessage
}
?: stringProvider.getString(R.string.unknown_error)
diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt b/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt
index a585e8ea77..d8b61f3cba 100644
--- a/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt
+++ b/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt
@@ -19,10 +19,12 @@ package im.vector.app.core.platform
import android.app.Activity
import android.content.Context
import android.content.res.Configuration
+import android.os.Build
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.View
+import android.view.WindowInsetsController
import android.view.WindowManager
import android.widget.TextView
import androidx.annotation.AttrRes
@@ -33,6 +35,7 @@ import androidx.annotation.StringRes
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import androidx.coordinatorlayout.widget.CoordinatorLayout
+import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentFactory
@@ -410,13 +413,25 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScr
/**
* Force to render the activity in fullscreen
*/
+ @Suppress("DEPRECATION")
private fun setFullScreen() {
- window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
- or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
- or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
- or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
- or View.SYSTEM_UI_FLAG_FULLSCREEN
- or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ // New API instead of SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+ window.setDecorFitsSystemWindows(false)
+ // New API instead of SYSTEM_UI_FLAG_IMMERSIVE
+ window.decorView.windowInsetsController?.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE
+ // New API instead of FLAG_TRANSLUCENT_STATUS
+ window.statusBarColor = ContextCompat.getColor(this, im.vector.lib.attachmentviewer.R.color.half_transparent_status_bar)
+ // New API instead of FLAG_TRANSLUCENT_NAVIGATION
+ window.navigationBarColor = ContextCompat.getColor(this, im.vector.lib.attachmentviewer.R.color.half_transparent_status_bar)
+ } else {
+ window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+ or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+ or View.SYSTEM_UI_FLAG_FULLSCREEN
+ or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)
+ }
}
/* ==========================================================================================
diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt b/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt
index 6f41a6a846..0b951fb5a2 100644
--- a/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt
+++ b/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt
@@ -200,6 +200,7 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), HasScre
}
protected fun showLoadingDialog(message: CharSequence? = null, cancelable: Boolean = false) {
+ progress?.dismiss()
progress = ProgressDialog(requireContext()).apply {
setCancelable(cancelable)
setMessage(message ?: getString(R.string.please_wait))
diff --git a/vector/src/main/java/im/vector/app/core/ui/list/GenericPositiveButtonItem.kt b/vector/src/main/java/im/vector/app/core/ui/list/GenericPositiveButtonItem.kt
new file mode 100644
index 0000000000..d18adde4ba
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/core/ui/list/GenericPositiveButtonItem.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2020 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package im.vector.app.core.ui.list
+
+import android.view.View
+import androidx.annotation.ColorInt
+import androidx.annotation.DrawableRes
+import com.airbnb.epoxy.EpoxyAttribute
+import com.airbnb.epoxy.EpoxyModelClass
+import com.google.android.material.button.MaterialButton
+import im.vector.app.R
+import im.vector.app.core.epoxy.VectorEpoxyHolder
+import im.vector.app.core.epoxy.VectorEpoxyModel
+
+/**
+ * A generic button list item.
+ */
+@EpoxyModelClass(layout = R.layout.item_positive_button)
+abstract class GenericPositiveButtonItem : VectorEpoxyModel() {
+
+ @EpoxyAttribute
+ var text: String? = null
+
+ @EpoxyAttribute
+ var buttonClickAction: View.OnClickListener? = null
+
+ @EpoxyAttribute
+ @ColorInt
+ var textColor: Int? = null
+
+ @EpoxyAttribute
+ @DrawableRes
+ var iconRes: Int? = null
+
+ override fun bind(holder: Holder) {
+ super.bind(holder)
+ holder.button.text = text
+ if (iconRes != null) {
+ holder.button.setIconResource(iconRes!!)
+ } else {
+ holder.button.icon = null
+ }
+
+ buttonClickAction?.let { holder.button.setOnClickListener(it) }
+ }
+
+ class Holder : VectorEpoxyHolder() {
+ val button by bind(R.id.itemGenericItemButton)
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/MainActivity.kt b/vector/src/main/java/im/vector/app/features/MainActivity.kt
index cb7be9ee3b..351163b026 100644
--- a/vector/src/main/java/im/vector/app/features/MainActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/MainActivity.kt
@@ -22,6 +22,7 @@ import android.os.Bundle
import android.os.Parcelable
import androidx.appcompat.app.AlertDialog
import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.lifecycleScope
import com.bumptech.glide.Glide
import im.vector.app.R
import im.vector.app.core.di.ActiveSessionHolder
@@ -45,10 +46,8 @@ import im.vector.app.features.signout.soft.SoftLogoutActivity
import im.vector.app.features.ui.UiStateRepository
import kotlinx.parcelize.Parcelize
import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
-import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.failure.GlobalError
import timber.log.Timber
import javax.inject.Inject
@@ -147,38 +146,39 @@ class MainActivity : VectorBaseActivity(), UnlockedActiv
}
when {
args.isAccountDeactivated -> {
- // Just do the local cleanup
- Timber.w("Account deactivated, start app")
- sessionHolder.clearActiveSession()
- doLocalCleanup(clearPreferences = true)
- startNextActivityAndFinish()
+ lifecycleScope.launch {
+ // Just do the local cleanup
+ Timber.w("Account deactivated, start app")
+ sessionHolder.clearActiveSession()
+ doLocalCleanup(clearPreferences = true)
+ startNextActivityAndFinish()
+ }
+ }
+ args.clearCredentials -> {
+ lifecycleScope.launch {
+ try {
+ session.signOut(!args.isUserLoggedOut)
+ Timber.w("SIGN_OUT: success, start app")
+ sessionHolder.clearActiveSession()
+ doLocalCleanup(clearPreferences = true)
+ startNextActivityAndFinish()
+ } catch (failure: Throwable) {
+ displayError(failure)
+ }
+ }
+ }
+ args.clearCache -> {
+ lifecycleScope.launch {
+ try {
+ session.clearCache()
+ doLocalCleanup(clearPreferences = false)
+ session.startSyncing(applicationContext)
+ startNextActivityAndFinish()
+ } catch (failure: Throwable) {
+ displayError(failure)
+ }
+ }
}
- args.clearCredentials -> session.signOut(
- !args.isUserLoggedOut,
- object : MatrixCallback {
- override fun onSuccess(data: Unit) {
- Timber.w("SIGN_OUT: success, start app")
- sessionHolder.clearActiveSession()
- doLocalCleanup(clearPreferences = true)
- startNextActivityAndFinish()
- }
-
- override fun onFailure(failure: Throwable) {
- displayError(failure)
- }
- })
- args.clearCache -> session.clearCache(
- object : MatrixCallback {
- override fun onSuccess(data: Unit) {
- doLocalCleanup(clearPreferences = false)
- session.startSyncing(applicationContext)
- startNextActivityAndFinish()
- }
-
- override fun onFailure(failure: Throwable) {
- displayError(failure)
- }
- })
}
}
@@ -187,24 +187,22 @@ class MainActivity : VectorBaseActivity(), UnlockedActiv
Timber.w("Ignoring invalid token global error")
}
- private fun doLocalCleanup(clearPreferences: Boolean) {
- GlobalScope.launch(Dispatchers.Main) {
- // On UI Thread
- Glide.get(this@MainActivity).clearMemory()
+ private suspend fun doLocalCleanup(clearPreferences: Boolean) {
+ // On UI Thread
+ Glide.get(this@MainActivity).clearMemory()
- if (clearPreferences) {
- vectorPreferences.clearPreferences()
- uiStateRepository.reset()
- pinLocker.unlock()
- pinCodeStore.deleteEncodedPin()
- }
- withContext(Dispatchers.IO) {
- // On BG thread
- Glide.get(this@MainActivity).clearDiskCache()
+ if (clearPreferences) {
+ vectorPreferences.clearPreferences()
+ uiStateRepository.reset()
+ pinLocker.unlock()
+ pinCodeStore.deleteEncodedPin()
+ }
+ withContext(Dispatchers.IO) {
+ // On BG thread
+ Glide.get(this@MainActivity).clearDiskCache()
- // Also clear cache (Logs, etc...)
- deleteAllFiles(this@MainActivity.cacheDir)
- }
+ // Also clear cache (Logs, etc...)
+ deleteAllFiles(this@MainActivity.cacheDir)
}
}
diff --git a/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewFragment.kt b/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewFragment.kt
index 407b51666b..cdb015e4da 100644
--- a/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewFragment.kt
@@ -19,6 +19,7 @@ package im.vector.app.features.attachments.preview
import android.app.Activity.RESULT_CANCELED
import android.app.Activity.RESULT_OK
+import android.os.Build
import android.os.Bundle
import android.os.Parcelable
import android.view.LayoutInflater
@@ -153,8 +154,13 @@ class AttachmentsPreviewFragment @Inject constructor(
)
}
+ @Suppress("DEPRECATION")
private fun applyInsets() {
- view?.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ activity?.window?.setDecorFitsSystemWindows(false)
+ } else {
+ view?.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+ }
ViewCompat.setOnApplyWindowInsetsListener(views.attachmentPreviewerBottomContainer) { v, insets ->
v.updatePadding(bottom = insets.systemWindowInsetBottom)
insets
diff --git a/vector/src/main/java/im/vector/app/features/auth/PromptFragment.kt b/vector/src/main/java/im/vector/app/features/auth/PromptFragment.kt
new file mode 100644
index 0000000000..917f60dacb
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/auth/PromptFragment.kt
@@ -0,0 +1,117 @@
+/*
+ * 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.auth
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.core.view.isVisible
+import com.airbnb.mvrx.activityViewModel
+import com.airbnb.mvrx.withState
+import im.vector.app.R
+import im.vector.app.core.extensions.showPassword
+import im.vector.app.core.platform.VectorBaseFragment
+import im.vector.app.databinding.FragmentReauthConfirmBinding
+import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
+
+class PromptFragment : VectorBaseFragment() {
+
+ private val viewModel: ReAuthViewModel by activityViewModel()
+
+ override fun getBinding(inflater: LayoutInflater, container: ViewGroup?) =
+ FragmentReauthConfirmBinding.inflate(layoutInflater, container, false)
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ views.reAuthConfirmButton.debouncedClicks {
+ onButtonClicked()
+ }
+ views.passwordReveal.debouncedClicks {
+ viewModel.handle(ReAuthActions.StartSSOFallback)
+ }
+
+ views.passwordReveal.debouncedClicks {
+ viewModel.handle(ReAuthActions.TogglePassVisibility)
+ }
+ }
+
+ private fun onButtonClicked() = withState(viewModel) { state ->
+ when (state.flowType) {
+ LoginFlowTypes.SSO -> {
+ viewModel.handle(ReAuthActions.StartSSOFallback)
+ }
+ LoginFlowTypes.PASSWORD -> {
+ val password = views.passwordField.text.toString()
+ if (password.isBlank()) {
+ // Prompt to enter something
+ views.passwordFieldTil.error = getString(R.string.error_empty_field_your_password)
+ } else {
+ views.passwordFieldTil.error = null
+ viewModel.handle(ReAuthActions.ReAuthWithPass(password))
+ }
+ }
+ else -> {
+ // not supported
+ }
+ }
+ }
+
+ override fun invalidate() = withState(viewModel) {
+ when (it.flowType) {
+ LoginFlowTypes.SSO -> {
+ views.passwordContainer.isVisible = false
+ views.reAuthConfirmButton.text = getString(R.string.auth_login_sso)
+ }
+ LoginFlowTypes.PASSWORD -> {
+ views.passwordContainer.isVisible = true
+ views.reAuthConfirmButton.text = getString(R.string._continue)
+ }
+ else -> {
+ // This login flow is not supported, you should use web?
+ }
+ }
+
+ views.passwordField.showPassword(it.passwordVisible)
+
+ if (it.passwordVisible) {
+ views.passwordReveal.setImageResource(R.drawable.ic_eye_closed)
+ views.passwordReveal.contentDescription = getString(R.string.a11y_hide_password)
+ } else {
+ views.passwordReveal.setImageResource(R.drawable.ic_eye)
+ views.passwordReveal.contentDescription = getString(R.string.a11y_show_password)
+ }
+
+ if (it.lastErrorCode != null) {
+ when (it.flowType) {
+ LoginFlowTypes.SSO -> {
+ views.genericErrorText.isVisible = true
+ views.genericErrorText.text = getString(R.string.authentication_error)
+ }
+ LoginFlowTypes.PASSWORD -> {
+ views.passwordFieldTil.error = getString(R.string.authentication_error)
+ }
+ else -> {
+ // nop
+ }
+ }
+ } else {
+ views.passwordFieldTil.error = null
+ views.genericErrorText.isVisible = false
+ }
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/core/di/AssistedInjectModule.kt b/vector/src/main/java/im/vector/app/features/auth/ReAuthActions.kt
similarity index 51%
rename from vector/src/main/java/im/vector/app/core/di/AssistedInjectModule.kt
rename to vector/src/main/java/im/vector/app/features/auth/ReAuthActions.kt
index 0ba4bedb92..036afda405 100644
--- a/vector/src/main/java/im/vector/app/core/di/AssistedInjectModule.kt
+++ b/vector/src/main/java/im/vector/app/features/auth/ReAuthActions.kt
@@ -1,11 +1,11 @@
/*
- * Copyright 2019 New Vector Ltd
+ * 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
+ * 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,
@@ -14,11 +14,14 @@
* limitations under the License.
*/
-package im.vector.app.core.di
+package im.vector.app.features.auth
-import com.squareup.inject.assisted.dagger2.AssistedModule
-import dagger.Module
+import im.vector.app.core.platform.VectorViewModelAction
-@AssistedModule
-@Module(includes = [AssistedInject_AssistedInjectModule::class])
-interface AssistedInjectModule
+sealed class ReAuthActions : VectorViewModelAction {
+ object StartSSOFallback : ReAuthActions()
+ object FallBackPageLoaded : ReAuthActions()
+ object FallBackPageClosed : ReAuthActions()
+ object TogglePassVisibility : ReAuthActions()
+ data class ReAuthWithPass(val password: String) : ReAuthActions()
+}
diff --git a/vector/src/main/java/im/vector/app/features/auth/ReAuthActivity.kt b/vector/src/main/java/im/vector/app/features/auth/ReAuthActivity.kt
new file mode 100644
index 0000000000..0385973386
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/auth/ReAuthActivity.kt
@@ -0,0 +1,228 @@
+/*
+ * 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.auth
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+import android.os.Bundle
+import android.os.Parcelable
+import androidx.browser.customtabs.CustomTabsCallback
+import androidx.browser.customtabs.CustomTabsClient
+import androidx.browser.customtabs.CustomTabsServiceConnection
+import androidx.browser.customtabs.CustomTabsSession
+import com.airbnb.mvrx.MvRx
+import com.airbnb.mvrx.viewModel
+import com.airbnb.mvrx.withState
+import im.vector.app.R
+import im.vector.app.core.di.ScreenComponent
+import im.vector.app.core.extensions.addFragment
+import im.vector.app.core.platform.SimpleFragmentActivity
+import im.vector.app.core.utils.openUrlInChromeCustomTab
+import kotlinx.parcelize.Parcelize
+import org.matrix.android.sdk.api.auth.AuthenticationService
+import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
+import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
+import org.matrix.android.sdk.api.auth.registration.nextUncompletedStage
+import timber.log.Timber
+import javax.inject.Inject
+
+class ReAuthActivity : SimpleFragmentActivity(), ReAuthViewModel.Factory {
+
+ @Parcelize
+ data class Args(
+ val flowType: String?,
+ val title: String?,
+ val session: String?,
+ val lastErrorCode: String?,
+ val resultKeyStoreAlias: String
+ ) : Parcelable
+
+ // For sso
+ private var customTabsServiceConnection: CustomTabsServiceConnection? = null
+ private var customTabsClient: CustomTabsClient? = null
+ private var customTabsSession: CustomTabsSession? = null
+
+ @Inject lateinit var authenticationService: AuthenticationService
+ @Inject lateinit var reAuthViewModelFactory: ReAuthViewModel.Factory
+
+ override fun create(initialState: ReAuthState) = reAuthViewModelFactory.create(initialState)
+
+ override fun injectWith(injector: ScreenComponent) {
+ super.injectWith(injector)
+ injector.inject(this)
+ }
+
+ private val sharedViewModel: ReAuthViewModel by viewModel()
+
+ // override fun getTitleRes() = R.string.re_authentication_activity_title
+
+ override fun initUiAndData() {
+ super.initUiAndData()
+
+ val title = intent.extras?.getString(EXTRA_REASON_TITLE) ?: getString(R.string.re_authentication_activity_title)
+ supportActionBar?.setTitle(title) ?: run { setTitle(title) }
+
+// val authArgs = intent.getParcelableExtra(MvRx.KEY_ARG)
+
+ // For the sso flow we can for now only rely on the fallback flow, that handles all
+ // the UI, due to the sandbox nature of CCT (chrome custom tab) we cannot get much information
+ // on how the process did go :/
+ // so we assume that after the user close the tab we return success and let caller retry the UIA flow :/
+ if (isFirstCreation()) {
+ addFragment(
+ R.id.container,
+ PromptFragment::class.java
+ )
+ }
+
+ sharedViewModel.observeViewEvents {
+ when (it) {
+ is ReAuthEvents.OpenSsoURl -> {
+ openInCustomTab(it.url)
+ }
+ ReAuthEvents.Dismiss -> {
+ setResult(RESULT_CANCELED)
+ finish()
+ }
+ is ReAuthEvents.PasswordFinishSuccess -> {
+ setResult(RESULT_OK, Intent().apply {
+ putExtra(RESULT_FLOW_TYPE, LoginFlowTypes.PASSWORD)
+ putExtra(RESULT_VALUE, it.passwordSafeForIntent)
+ })
+ finish()
+ }
+ }
+ }
+ }
+
+ override fun onResume() {
+ super.onResume()
+ // It's the only way we have to know if sso falback flow was successful
+ withState(sharedViewModel) {
+ if (it.ssoFallbackPageWasShown) {
+ Timber.d("## UIA ssoFallbackPageWasShown tentative success")
+ setResult(RESULT_OK, Intent().apply {
+ putExtra(RESULT_FLOW_TYPE, LoginFlowTypes.SSO)
+ })
+ finish()
+ }
+ }
+ }
+
+ override fun onStart() {
+ super.onStart()
+
+ withState(sharedViewModel) { state ->
+ if (state.ssoFallbackPageWasShown) {
+ sharedViewModel.handle(ReAuthActions.FallBackPageClosed)
+ return@withState
+ }
+ }
+
+ val packageName = CustomTabsClient.getPackageName(this, null)
+
+ // packageName can be null if there are 0 or several CustomTabs compatible browsers installed on the device
+ if (packageName != null) {
+ customTabsServiceConnection = object : CustomTabsServiceConnection() {
+ override fun onCustomTabsServiceConnected(name: ComponentName, client: CustomTabsClient) {
+ Timber.d("## CustomTab onCustomTabsServiceConnected($name)")
+ customTabsClient = client
+ .also { it.warmup(0L) }
+ customTabsSession = customTabsClient?.newSession(object : CustomTabsCallback() {
+// override fun onPostMessage(message: String, extras: Bundle?) {
+// Timber.v("## CustomTab onPostMessage($message)")
+// }
+//
+// override fun onMessageChannelReady(extras: Bundle?) {
+// Timber.v("## CustomTab onMessageChannelReady()")
+// }
+
+ override fun onNavigationEvent(navigationEvent: Int, extras: Bundle?) {
+ Timber.v("## CustomTab onNavigationEvent($navigationEvent), $extras")
+ super.onNavigationEvent(navigationEvent, extras)
+ if (navigationEvent == NAVIGATION_FINISHED) {
+// sharedViewModel.handle(ReAuthActions.FallBackPageLoaded)
+ }
+ }
+
+ override fun onRelationshipValidationResult(relation: Int, requestedOrigin: Uri, result: Boolean, extras: Bundle?) {
+ Timber.v("## CustomTab onRelationshipValidationResult($relation), $requestedOrigin")
+ super.onRelationshipValidationResult(relation, requestedOrigin, result, extras)
+ }
+ })
+ }
+
+ override fun onServiceDisconnected(name: ComponentName?) {
+ Timber.d("## CustomTab onServiceDisconnected($name)")
+ }
+ }.also {
+ CustomTabsClient.bindCustomTabsService(
+ this,
+ // Despite the API, packageName cannot be null
+ packageName,
+ it
+ )
+ }
+ }
+ }
+
+ override fun onStop() {
+ super.onStop()
+ customTabsServiceConnection?.let { this.unbindService(it) }
+ customTabsServiceConnection = null
+ customTabsSession = null
+ }
+
+ private fun openInCustomTab(ssoUrl: String) {
+ openUrlInChromeCustomTab(this, customTabsSession, ssoUrl)
+ val channelOpened = customTabsSession?.requestPostMessageChannel(Uri.parse("https://element.io"))
+ Timber.d("## CustomTab channelOpened: $channelOpened")
+ }
+
+ companion object {
+
+ const val EXTRA_AUTH_TYPE = "EXTRA_AUTH_TYPE"
+ const val EXTRA_REASON_TITLE = "EXTRA_REASON_TITLE"
+ const val RESULT_FLOW_TYPE = "RESULT_FLOW_TYPE"
+ const val RESULT_VALUE = "RESULT_VALUE"
+ const val DEFAULT_RESULT_KEYSTORE_ALIAS = "ReAuthActivity"
+
+ fun newIntent(context: Context,
+ fromError: RegistrationFlowResponse,
+ lastErrorCode: String?,
+ reasonTitle: String?,
+ resultKeyStoreAlias: String = DEFAULT_RESULT_KEYSTORE_ALIAS): Intent {
+ val authType = when (fromError.nextUncompletedStage()) {
+ LoginFlowTypes.PASSWORD -> {
+ LoginFlowTypes.PASSWORD
+ }
+ LoginFlowTypes.SSO -> {
+ LoginFlowTypes.SSO
+ }
+ else -> {
+ // TODO, support more auth type?
+ null
+ }
+ }
+ return Intent(context, ReAuthActivity::class.java).apply {
+ putExtra(MvRx.KEY_ARG, Args(authType, reasonTitle, fromError.session, lastErrorCode, resultKeyStoreAlias))
+ }
+ }
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/core/error/SsoFlowNotSupportedYet.kt b/vector/src/main/java/im/vector/app/features/auth/ReAuthEvents.kt
similarity index 61%
rename from vector/src/main/java/im/vector/app/core/error/SsoFlowNotSupportedYet.kt
rename to vector/src/main/java/im/vector/app/features/auth/ReAuthEvents.kt
index 7b22072c34..8cf9be6fb1 100644
--- a/vector/src/main/java/im/vector/app/core/error/SsoFlowNotSupportedYet.kt
+++ b/vector/src/main/java/im/vector/app/features/auth/ReAuthEvents.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 New Vector Ltd
+ * 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.
@@ -14,6 +14,12 @@
* limitations under the License.
*/
-package im.vector.app.core.error
+package im.vector.app.features.auth
-class SsoFlowNotSupportedYet : Throwable()
+import im.vector.app.core.platform.VectorViewEvents
+
+sealed class ReAuthEvents : VectorViewEvents {
+ data class OpenSsoURl(val url: String) : ReAuthEvents()
+ object Dismiss : ReAuthEvents()
+ data class PasswordFinishSuccess(val passwordSafeForIntent: String) : ReAuthEvents()
+}
diff --git a/vector/src/main/java/im/vector/app/features/auth/ReAuthState.kt b/vector/src/main/java/im/vector/app/features/auth/ReAuthState.kt
new file mode 100644
index 0000000000..540a08405c
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/auth/ReAuthState.kt
@@ -0,0 +1,39 @@
+/*
+ * 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.auth
+
+import com.airbnb.mvrx.MvRxState
+
+data class ReAuthState(
+ val title: String? = null,
+ val session: String? = null,
+ val flowType: String? = null,
+ val ssoFallbackPageWasShown: Boolean = false,
+ val passwordVisible: Boolean = false,
+ val lastErrorCode: String? = null,
+ val resultKeyStoreAlias: String = ""
+) : MvRxState {
+ constructor(args: ReAuthActivity.Args) : this(
+ args.title,
+ args.session,
+ args.flowType,
+ lastErrorCode = args.lastErrorCode,
+ resultKeyStoreAlias = args.resultKeyStoreAlias
+ )
+
+ constructor() : this(null, null)
+}
diff --git a/vector/src/main/java/im/vector/app/features/auth/ReAuthViewModel.kt b/vector/src/main/java/im/vector/app/features/auth/ReAuthViewModel.kt
new file mode 100644
index 0000000000..4204da0d24
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/auth/ReAuthViewModel.kt
@@ -0,0 +1,85 @@
+/*
+ * 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.auth
+
+import com.airbnb.mvrx.ActivityViewModelContext
+import com.airbnb.mvrx.FragmentViewModelContext
+import com.airbnb.mvrx.MvRxViewModelFactory
+import com.airbnb.mvrx.ViewModelContext
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import im.vector.app.core.platform.VectorViewModel
+import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
+import org.matrix.android.sdk.api.session.Session
+import org.matrix.android.sdk.internal.crypto.crosssigning.toBase64NoPadding
+import java.io.ByteArrayOutputStream
+
+class ReAuthViewModel @AssistedInject constructor(
+ @Assisted val initialState: ReAuthState,
+ private val session: Session
+) : VectorViewModel(initialState) {
+
+ @AssistedFactory
+ interface Factory {
+ fun create(initialState: ReAuthState): ReAuthViewModel
+ }
+
+ companion object : MvRxViewModelFactory {
+
+ override fun create(viewModelContext: ViewModelContext, state: ReAuthState): ReAuthViewModel? {
+ val factory = when (viewModelContext) {
+ is FragmentViewModelContext -> viewModelContext.fragment as? Factory
+ is ActivityViewModelContext -> viewModelContext.activity as? Factory
+ }
+ return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface")
+ }
+ }
+
+ override fun handle(action: ReAuthActions) = withState { state ->
+ when (action) {
+ ReAuthActions.StartSSOFallback -> {
+ if (state.flowType == LoginFlowTypes.SSO) {
+ setState { copy(ssoFallbackPageWasShown = true) }
+ val ssoURL = session.getUiaSsoFallbackUrl(initialState.session ?: "")
+ _viewEvents.post(ReAuthEvents.OpenSsoURl(ssoURL))
+ }
+ }
+ ReAuthActions.FallBackPageLoaded -> {
+ setState { copy(ssoFallbackPageWasShown = true) }
+ }
+ ReAuthActions.FallBackPageClosed -> {
+ // Should we do something here?
+ }
+ ReAuthActions.TogglePassVisibility -> {
+ setState {
+ copy(
+ passwordVisible = !state.passwordVisible
+ )
+ }
+ }
+ is ReAuthActions.ReAuthWithPass -> {
+ val safeForIntentCypher = ByteArrayOutputStream().also {
+ it.use {
+ session.securelyStoreObject(action.password, initialState.resultKeyStoreAlias, it)
+ }
+ }.toByteArray().toBase64NoPadding()
+ _viewEvents.post(ReAuthEvents.PasswordFinishSuccess(safeForIntentCypher))
+ }
+ }
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/autocomplete/member/AutocompleteMemberPresenter.kt b/vector/src/main/java/im/vector/app/features/autocomplete/member/AutocompleteMemberPresenter.kt
index de8003b28b..b2ad0a7688 100644
--- a/vector/src/main/java/im/vector/app/features/autocomplete/member/AutocompleteMemberPresenter.kt
+++ b/vector/src/main/java/im/vector/app/features/autocomplete/member/AutocompleteMemberPresenter.kt
@@ -18,8 +18,9 @@ package im.vector.app.features.autocomplete.member
import android.content.Context
import androidx.recyclerview.widget.RecyclerView
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.features.autocomplete.AutocompleteClickListener
import im.vector.app.features.autocomplete.RecyclerViewPresenter
import org.matrix.android.sdk.api.query.QueryStringValue
@@ -44,7 +45,7 @@ class AutocompleteMemberPresenter @AssistedInject constructor(context: Context,
controller.listener = null
}
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(roomId: String): AutocompleteMemberPresenter
}
diff --git a/vector/src/main/java/im/vector/app/features/call/CallAudioManager.kt b/vector/src/main/java/im/vector/app/features/call/CallAudioManager.kt
index 3a24cf6d48..82bbaf1d54 100644
--- a/vector/src/main/java/im/vector/app/features/call/CallAudioManager.kt
+++ b/vector/src/main/java/im/vector/app/features/call/CallAudioManager.kt
@@ -48,7 +48,7 @@ class CallAudioManager(
private var savedIsSpeakerPhoneOn = false
private var savedIsMicrophoneMute = false
- private var savedAudioMode = AudioManager.MODE_INVALID
+ private var savedAudioMode = AudioManager.MODE_NORMAL
private var connectedBlueToothHeadset: BluetoothProfile? = null
private var wantsBluetoothConnection = false
diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt
index 41bf7bbeaf..6c49d4d3e2 100644
--- a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt
@@ -25,8 +25,11 @@ import android.os.Bundle
import android.os.Parcelable
import android.view.View
import android.view.Window
+import android.view.WindowInsets
+import android.view.WindowInsetsController
import android.view.WindowManager
import androidx.appcompat.app.AlertDialog
+import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService
import androidx.core.view.ViewCompat
import androidx.core.view.isInvisible
@@ -102,29 +105,49 @@ class VectorCallActivity : VectorBaseActivity(), CallContro
setContentView(R.layout.activity_call)
}
+ @Suppress("DEPRECATION")
private fun hideSystemUI() {
systemUiVisibility = false
// Enables regular immersive mode.
// For "lean back" mode, remove SYSTEM_UI_FLAG_IMMERSIVE.
// Or for "sticky immersive," replace it with SYSTEM_UI_FLAG_IMMERSIVE_STICKY
- window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE
- // Set the content to appear under the system bars so that the
- // content doesn't resize when the system bars hide and show.
- or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
- or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
- or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
- // Hide the nav bar and status bar
- or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
- or View.SYSTEM_UI_FLAG_FULLSCREEN)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ // New API instead of SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+ window.setDecorFitsSystemWindows(false)
+ // New API instead of SYSTEM_UI_FLAG_HIDE_NAVIGATION
+ window.decorView.windowInsetsController?.hide(WindowInsets.Type.navigationBars())
+ // New API instead of SYSTEM_UI_FLAG_IMMERSIVE
+ window.decorView.windowInsetsController?.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE
+ // New API instead of FLAG_TRANSLUCENT_STATUS
+ window.statusBarColor = ContextCompat.getColor(this, R.color.half_transparent_status_bar)
+ // New API instead of FLAG_TRANSLUCENT_NAVIGATION
+ window.navigationBarColor = ContextCompat.getColor(this, R.color.half_transparent_status_bar)
+ } else {
+ window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE
+ // Set the content to appear under the system bars so that the
+ // content doesn't resize when the system bars hide and show.
+ or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+ or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ // Hide the nav bar and status bar
+ or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+ or View.SYSTEM_UI_FLAG_FULLSCREEN)
+ }
}
// Shows the system bars by removing all the flags
// except for the ones that make the content appear under the system bars.
+ @Suppress("DEPRECATION")
private fun showSystemUI() {
systemUiVisibility = true
- window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
- or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
- or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ // New API instead of SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+ window.setDecorFitsSystemWindows(false)
+ } else {
+ window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+ or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)
+ }
}
private fun toggleUiSystemVisibility() {
diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt
index bd16adf3e7..fd735de085 100644
--- a/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt
@@ -22,8 +22,9 @@ import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
import org.matrix.android.sdk.api.MatrixCallback
@@ -242,7 +243,7 @@ class VectorCallViewModel @AssistedInject constructor(
}.exhaustive
}
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: VectorCallViewState, args: CallArgs): VectorCallViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/call/conference/JitsiCallViewModel.kt b/vector/src/main/java/im/vector/app/features/call/conference/JitsiCallViewModel.kt
index 783b519706..97ee41154a 100644
--- a/vector/src/main/java/im/vector/app/features/call/conference/JitsiCallViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/call/conference/JitsiCallViewModel.kt
@@ -21,8 +21,9 @@ import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.StringProvider
import org.jitsi.meet.sdk.JitsiMeetUserInfo
@@ -40,7 +41,7 @@ class JitsiCallViewModel @AssistedInject constructor(
private val stringProvider: StringProvider
) : VectorViewModel(initialState) {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: JitsiCallViewState, args: VectorJitsiActivity.Args): JitsiCallViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/call/telecom/CallConnection.kt b/vector/src/main/java/im/vector/app/features/call/telecom/CallConnection.kt
index 6f69b4b0d0..0a9a164993 100644
--- a/vector/src/main/java/im/vector/app/features/call/telecom/CallConnection.kt
+++ b/vector/src/main/java/im/vector/app/features/call/telecom/CallConnection.kt
@@ -21,7 +21,6 @@ import android.os.Build
import android.telecom.Connection
import android.telecom.DisconnectCause
import androidx.annotation.RequiresApi
-import im.vector.app.features.call.VectorCallViewModel
import im.vector.app.features.call.WebRtcPeerConnectionManager
import timber.log.Timber
import javax.inject.Inject
@@ -33,7 +32,6 @@ import javax.inject.Inject
) : Connection() {
@Inject lateinit var peerConnectionManager: WebRtcPeerConnectionManager
- @Inject lateinit var callViewModel: VectorCallViewModel
init {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
diff --git a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewModel.kt b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewModel.kt
index 2c4c5d0596..d73794f8d8 100644
--- a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewModel.kt
@@ -24,8 +24,9 @@ import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.contacts.ContactsDataSource
import im.vector.app.core.contacts.MappedContact
import im.vector.app.core.extensions.exhaustive
@@ -48,7 +49,7 @@ class ContactsBookViewModel @AssistedInject constructor(@Assisted
private val session: Session)
: VectorViewModel(initialState) {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: ContactsBookViewState): ContactsBookViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomViewModel.kt b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomViewModel.kt
index d074c93587..30bbedf7ec 100644
--- a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomViewModel.kt
@@ -21,8 +21,9 @@ import com.airbnb.mvrx.ActivityViewModelContext
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.raw.wellknown.getElementWellknown
@@ -41,7 +42,7 @@ class CreateDirectRoomViewModel @AssistedInject constructor(@Assisted
val session: Session)
: VectorViewModel(initialState) {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: CreateDirectRoomViewState): CreateDirectRoomViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/settings/KeysBackupSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/settings/KeysBackupSettingsViewModel.kt
index 3666dbce8c..cb8a6ce4e9 100644
--- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/settings/KeysBackupSettingsViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/settings/KeysBackupSettingsViewModel.kt
@@ -22,8 +22,9 @@ import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.platform.EmptyViewEvents
import im.vector.app.core.platform.VectorViewModel
import org.matrix.android.sdk.api.MatrixCallback
@@ -39,7 +40,7 @@ class KeysBackupSettingsViewModel @AssistedInject constructor(@Assisted initialS
) : VectorViewModel(initialState),
KeysBackupStateListener {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: KeysBackupSettingViewState): KeysBackupSettingsViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt
index c7533cd3df..e95f250dd3 100644
--- a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt
@@ -26,8 +26,9 @@ import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.R
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
@@ -71,7 +72,7 @@ class SharedSecureStorageViewModel @AssistedInject constructor(
private val session: Session)
: VectorViewModel(initialState) {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: SharedSecureStorageViewState, args: SharedSecureStorageActivity.Args): SharedSecureStorageViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapAccountPasswordFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapAccountPasswordFragment.kt
deleted file mode 100644
index feea484f06..0000000000
--- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapAccountPasswordFragment.kt
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (c) 2020 New Vector Ltd
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package im.vector.app.features.crypto.recover
-
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.view.inputmethod.EditorInfo
-import androidx.core.text.toSpannable
-import com.airbnb.mvrx.parentFragmentViewModel
-import com.airbnb.mvrx.withState
-import com.jakewharton.rxbinding3.widget.editorActionEvents
-import com.jakewharton.rxbinding3.widget.textChanges
-import im.vector.app.R
-import im.vector.app.core.extensions.hideKeyboard
-import im.vector.app.core.extensions.showPassword
-import im.vector.app.core.platform.VectorBaseFragment
-import im.vector.app.core.resources.ColorProvider
-import im.vector.app.core.utils.colorizeMatchingText
-import im.vector.app.databinding.FragmentBootstrapEnterAccountPasswordBinding
-import io.reactivex.android.schedulers.AndroidSchedulers
-
-import java.util.concurrent.TimeUnit
-import javax.inject.Inject
-
-class BootstrapAccountPasswordFragment @Inject constructor(
- private val colorProvider: ColorProvider
-) : VectorBaseFragment() {
-
- override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentBootstrapEnterAccountPasswordBinding {
- return FragmentBootstrapEnterAccountPasswordBinding.inflate(inflater, container, false)
- }
-
- val sharedViewModel: BootstrapSharedViewModel by parentFragmentViewModel()
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
-
- val recPassPhrase = getString(R.string.account_password)
- views.bootstrapDescriptionText.text = getString(R.string.enter_account_password, recPassPhrase)
- .toSpannable()
- .colorizeMatchingText(recPassPhrase, colorProvider.getColorFromAttribute(android.R.attr.textColorLink))
-
- views.bootstrapAccountPasswordEditText.hint = getString(R.string.account_password)
-
- views.bootstrapAccountPasswordEditText.editorActionEvents()
- .throttleFirst(300, TimeUnit.MILLISECONDS)
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe {
- if (it.actionId == EditorInfo.IME_ACTION_DONE) {
- submit()
- }
- }
- .disposeOnDestroyView()
-
- views.bootstrapAccountPasswordEditText.textChanges()
- .distinctUntilChanged()
- .subscribe {
- if (!it.isNullOrBlank()) {
- views.bootstrapAccountPasswordTil.error = null
- }
- }
- .disposeOnDestroyView()
-
- views.ssssViewShowPassword.debouncedClicks { sharedViewModel.handle(BootstrapActions.TogglePasswordVisibility) }
- views.bootstrapPasswordButton.debouncedClicks { submit() }
-
- withState(sharedViewModel) { state ->
- (state.step as? BootstrapStep.AccountPassword)?.failure?.let {
- views.bootstrapAccountPasswordTil.error = it
- }
- }
- }
-
- private fun submit() = withState(sharedViewModel) { state ->
- if (state.step !is BootstrapStep.AccountPassword) {
- return@withState
- }
- val accountPassword = views.bootstrapAccountPasswordEditText.text?.toString()
- if (accountPassword.isNullOrBlank()) {
- views.bootstrapAccountPasswordTil.error = getString(R.string.error_empty_field_your_password)
- } else {
- view?.hideKeyboard()
- sharedViewModel.handle(BootstrapActions.ReAuth(accountPassword))
- }
- }
-
- override fun invalidate() = withState(sharedViewModel) { state ->
- if (state.step is BootstrapStep.AccountPassword) {
- val isPasswordVisible = state.step.isPasswordVisible
- views.bootstrapAccountPasswordEditText.showPassword(isPasswordVisible, updateCursor = false)
- views.ssssViewShowPassword.setImageResource(if (isPasswordVisible) R.drawable.ic_eye_closed else R.drawable.ic_eye)
- }
- }
-}
diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapActions.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapActions.kt
index 0785290d2a..ce06fe726f 100644
--- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapActions.kt
+++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapActions.kt
@@ -37,7 +37,7 @@ sealed class BootstrapActions : VectorViewModelAction {
object TogglePasswordVisibility : BootstrapActions()
data class UpdateCandidatePassphrase(val pass: String) : BootstrapActions()
data class UpdateConfirmCandidatePassphrase(val pass: String) : BootstrapActions()
- data class ReAuth(val pass: String) : BootstrapActions()
+// data class ReAuth(val pass: String) : BootstrapActions()
object RecoveryKeySaved : BootstrapActions()
object Completed : BootstrapActions()
object SaveReqQueryStarted : BootstrapActions()
@@ -47,4 +47,8 @@ sealed class BootstrapActions : VectorViewModelAction {
object HandleForgotBackupPassphrase : BootstrapActions()
data class DoMigrateWithPassphrase(val passphrase: String) : BootstrapActions()
data class DoMigrateWithRecoveryKey(val recoveryKey: String) : BootstrapActions()
+
+ object SsoAuthDone: BootstrapActions()
+ data class PasswordAuthDone(val password: String): BootstrapActions()
+ object ReAuthCancelled: BootstrapActions()
}
diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapBottomSheet.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapBottomSheet.kt
index f1ea50c9bf..5cc86fdf15 100644
--- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapBottomSheet.kt
+++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapBottomSheet.kt
@@ -16,7 +16,9 @@
package im.vector.app.features.crypto.recover
+import android.app.Activity
import android.app.Dialog
+import android.os.Build
import android.os.Bundle
import android.os.Parcelable
import android.view.KeyEvent
@@ -35,9 +37,12 @@ import im.vector.app.R
import im.vector.app.core.di.ScreenComponent
import im.vector.app.core.extensions.commitTransaction
import im.vector.app.core.extensions.exhaustive
+import im.vector.app.core.extensions.registerStartForActivityResult
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
import im.vector.app.databinding.BottomSheetBootstrapBinding
+import im.vector.app.features.auth.ReAuthActivity
import kotlinx.parcelize.Parcelize
+import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
import javax.inject.Inject
import kotlin.reflect.KClass
@@ -63,6 +68,25 @@ class BootstrapBottomSheet : VectorBaseBottomSheetDialogFragment
+ if (activityResult.resultCode == Activity.RESULT_OK) {
+ when (activityResult.data?.extras?.getString(ReAuthActivity.RESULT_FLOW_TYPE)) {
+ LoginFlowTypes.SSO -> {
+ viewModel.handle(BootstrapActions.SsoAuthDone)
+ }
+ LoginFlowTypes.PASSWORD -> {
+ val password = activityResult.data?.extras?.getString(ReAuthActivity.RESULT_VALUE) ?: ""
+ viewModel.handle(BootstrapActions.PasswordAuthDone(password))
+ }
+ else -> {
+ viewModel.handle(BootstrapActions.ReAuthCancelled)
+ }
+ }
+ } else {
+ viewModel.handle(BootstrapActions.ReAuthCancelled)
+ }
+ }
+
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel.observeViewEvents { event ->
@@ -84,6 +108,14 @@ class BootstrapBottomSheet : VectorBaseBottomSheetDialogFragment {
promptSkip()
}
+ is BootstrapViewEvents.RequestReAuth -> {
+ ReAuthActivity.newIntent(requireContext(),
+ event.flowResponse,
+ event.lastErrorCode,
+ getString(R.string.initialize_cross_signing)).let { intent ->
+ reAuthActivityResultLauncher.launch(intent)
+ }
+ }
}
}
}
@@ -102,7 +134,12 @@ class BootstrapBottomSheet : VectorBaseBottomSheetDialogFragment= Build.VERSION_CODES.R) {
+ dialog?.window?.setDecorFitsSystemWindows(false)
+ } else {
+ @Suppress("DEPRECATION")
+ dialog?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
+ }
return rootView
}
@@ -143,11 +180,11 @@ class BootstrapBottomSheet : VectorBaseBottomSheetDialogFragment {
+ is BootstrapStep.AccountReAuth -> {
views.bootstrapIcon.isVisible = true
views.bootstrapIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_user))
- views.bootstrapTitleText.text = getString(R.string.account_password)
- showFragment(BootstrapAccountPasswordFragment::class, Bundle())
+ views.bootstrapTitleText.text = getString(R.string.re_authentication_activity_title)
+ showFragment(BootstrapReAuthFragment::class, Bundle())
}
is BootstrapStep.Initializing -> {
views.bootstrapIcon.isVisible = true
diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapCrossSigningTask.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapCrossSigningTask.kt
index 47e373ed0a..d1a1237463 100644
--- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapCrossSigningTask.kt
+++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapCrossSigningTask.kt
@@ -20,10 +20,9 @@ import im.vector.app.R
import im.vector.app.core.platform.ViewModelTask
import im.vector.app.core.platform.WaitingViewData
import im.vector.app.core.resources.StringProvider
-import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
+import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.failure.MatrixError
-import org.matrix.android.sdk.api.failure.toRegistrationFlowResponse
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME
import org.matrix.android.sdk.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME
@@ -38,7 +37,6 @@ import org.matrix.android.sdk.internal.crypto.keysbackup.model.MegolmBackupCreat
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersion
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersionResult
import org.matrix.android.sdk.internal.crypto.keysbackup.util.extractCurveKeyFromRecoveryKey
-import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth
import org.matrix.android.sdk.internal.util.awaitCallback
import timber.log.Timber
import java.util.UUID
@@ -51,16 +49,12 @@ sealed class BootstrapResult {
abstract class Failure(val error: String?) : BootstrapResult()
- class UnsupportedAuthFlow : Failure(null)
-
data class GenericError(val failure: Throwable) : Failure(failure.localizedMessage)
data class InvalidPasswordError(val matrixError: MatrixError) : Failure(null)
class FailedToCreateSSSSKey(failure: Throwable) : Failure(failure.localizedMessage)
class FailedToSetDefaultSSSSKey(failure: Throwable) : Failure(failure.localizedMessage)
class FailedToStorePrivateKeyInSSSS(failure: Throwable) : Failure(failure.localizedMessage)
object MissingPrivateKey : Failure(null)
-
- data class PasswordAuthFlowMissing(val sessionId: String) : Failure(null)
}
interface BootstrapProgressListener {
@@ -68,7 +62,7 @@ interface BootstrapProgressListener {
}
data class Params(
- val userPasswordAuth: UserPasswordAuth? = null,
+ val userInteractiveAuthInterceptor: UserInteractiveAuthInterceptor,
val progressListener: BootstrapProgressListener? = null,
val passphrase: String?,
val keySpec: SsssKeySpec? = null,
@@ -101,7 +95,10 @@ class BootstrapCrossSigningTask @Inject constructor(
try {
awaitCallback {
- crossSigningService.initializeCrossSigning(params.userPasswordAuth, it)
+ crossSigningService.initializeCrossSigning(
+ params.userInteractiveAuthInterceptor,
+ it
+ )
}
if (params.setupMode == SetupMode.CROSS_SIGNING_ONLY) {
return BootstrapResult.SuccessCrossSigningOnly
@@ -312,16 +309,6 @@ class BootstrapCrossSigningTask @Inject constructor(
private fun handleInitializeXSigningError(failure: Throwable): BootstrapResult {
if (failure is Failure.ServerError && failure.error.code == MatrixError.M_FORBIDDEN) {
return BootstrapResult.InvalidPasswordError(failure.error)
- } else {
- val registrationFlowResponse = failure.toRegistrationFlowResponse()
- if (registrationFlowResponse != null) {
- return if (registrationFlowResponse.flows.orEmpty().any { it.stages?.contains(LoginFlowTypes.PASSWORD) == true }) {
- BootstrapResult.PasswordAuthFlowMissing(registrationFlowResponse.session ?: "")
- } else {
- // can't do this from here
- BootstrapResult.UnsupportedAuthFlow()
- }
- }
}
return BootstrapResult.GenericError(failure)
}
diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapReAuthFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapReAuthFragment.kt
new file mode 100644
index 0000000000..507050c2e8
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapReAuthFragment.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2020 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.app.features.crypto.recover
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.core.view.isVisible
+import com.airbnb.mvrx.parentFragmentViewModel
+import com.airbnb.mvrx.withState
+import im.vector.app.core.extensions.setTextOrHide
+import im.vector.app.core.platform.VectorBaseFragment
+import im.vector.app.core.resources.ColorProvider
+import im.vector.app.databinding.FragmentBootstrapReauthBinding
+
+import javax.inject.Inject
+
+class BootstrapReAuthFragment @Inject constructor(
+ private val colorProvider: ColorProvider
+) : VectorBaseFragment() {
+
+ override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentBootstrapReauthBinding {
+ return FragmentBootstrapReauthBinding.inflate(inflater, container, false)
+ }
+
+ val sharedViewModel: BootstrapSharedViewModel by parentFragmentViewModel()
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ views.bootstrapRetryButton.debouncedClicks { submit() }
+ views.bootstrapCancelButton.debouncedClicks { cancel() }
+ }
+
+ private fun submit() = withState(sharedViewModel) { state ->
+ if (state.step !is BootstrapStep.AccountReAuth) {
+ return@withState
+ }
+ if (state.passphrase != null) {
+ sharedViewModel.handle(BootstrapActions.DoInitialize(state.passphrase))
+ } else {
+ sharedViewModel.handle(BootstrapActions.DoInitializeGeneratedKey)
+ }
+ }
+
+ private fun cancel() = withState(sharedViewModel) { state ->
+ if (state.step !is BootstrapStep.AccountReAuth) {
+ return@withState
+ }
+ sharedViewModel.handle(BootstrapActions.GoBack)
+ }
+
+ override fun invalidate() = withState(sharedViewModel) { state ->
+ if (state.step !is BootstrapStep.AccountReAuth) {
+ return@withState
+ }
+ val failure = state.step.failure
+ views.reAuthFailureText.setTextOrHide(failure)
+ if (failure == null) {
+ views.waitingProgress.isVisible = true
+ views.bootstrapCancelButton.isVisible = false
+ views.bootstrapRetryButton.isVisible = false
+ } else {
+ views.waitingProgress.isVisible = false
+ views.bootstrapCancelButton.isVisible = true
+ views.bootstrapRetryButton.isVisible = true
+ }
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSharedViewModel.kt
index 2e5097fdb7..42278cd948 100644
--- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSharedViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSharedViewModel.kt
@@ -25,25 +25,36 @@ import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
import com.nulabinc.zxcvbn.Zxcvbn
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
import im.vector.app.R
import im.vector.app.core.error.ErrorFormatter
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.platform.WaitingViewData
import im.vector.app.core.resources.StringProvider
+import im.vector.app.features.auth.ReAuthActivity
import im.vector.app.features.login.ReAuthHelper
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
+import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
+import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.securestorage.RawBytesKeySpec
+import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
+import org.matrix.android.sdk.api.auth.registration.nextUncompletedStage
+import org.matrix.android.sdk.internal.crypto.crosssigning.fromBase64
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersionResult
import org.matrix.android.sdk.internal.crypto.keysbackup.util.extractCurveKeyFromRecoveryKey
-import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth
+import org.matrix.android.sdk.internal.crypto.model.rest.DefaultBaseAuth
+import org.matrix.android.sdk.api.auth.UIABaseAuth
+import org.matrix.android.sdk.api.auth.UserPasswordAuth
import org.matrix.android.sdk.internal.util.awaitCallback
import java.io.OutputStream
+import kotlin.coroutines.Continuation
+import kotlin.coroutines.resume
class BootstrapSharedViewModel @AssistedInject constructor(
@Assisted initialState: BootstrapViewState,
@@ -60,12 +71,15 @@ class BootstrapSharedViewModel @AssistedInject constructor(
private var isBackupCreatedFromPassphrase: Boolean = false
private val zxcvbn = Zxcvbn()
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: BootstrapViewState, args: BootstrapBottomSheet.Args): BootstrapSharedViewModel
}
- private var _pendingSession: String? = null
+// private var _pendingSession: String? = null
+
+ var uiaContinuation: Continuation? = null
+ var pendingAuth: UIABaseAuth? = null
init {
@@ -80,7 +94,7 @@ class BootstrapSharedViewModel @AssistedInject constructor(
SetupMode.CROSS_SIGNING_ONLY -> {
// Go straight to account password
setState {
- copy(step = BootstrapStep.AccountPassword(false))
+ copy(step = BootstrapStep.AccountReAuth())
}
}
SetupMode.NORMAL -> {
@@ -148,10 +162,8 @@ class BootstrapSharedViewModel @AssistedInject constructor(
copy(step = state.step.copy(isPasswordVisible = !state.step.isPasswordVisible))
}
}
- is BootstrapStep.AccountPassword -> {
- setState {
- copy(step = state.step.copy(isPasswordVisible = !state.step.isPasswordVisible))
- }
+ is BootstrapStep.AccountReAuth -> {
+ // nop
}
is BootstrapStep.GetBackupSecretPassForMigration -> {
setState {
@@ -195,16 +207,7 @@ class BootstrapSharedViewModel @AssistedInject constructor(
}
is BootstrapActions.DoInitialize -> {
if (state.passphrase == state.passphraseRepeat) {
- val userPassword = reAuthHelper.data
- if (userPassword == null) {
- setState {
- copy(
- step = BootstrapStep.AccountPassword(false)
- )
- }
- } else {
- startInitializeFlow(userPassword)
- }
+ startInitializeFlow(state)
} else {
setState {
copy(
@@ -214,24 +217,7 @@ class BootstrapSharedViewModel @AssistedInject constructor(
}
}
is BootstrapActions.DoInitializeGeneratedKey -> {
- val userPassword = reAuthHelper.data
- if (userPassword == null) {
- setState {
- copy(
- passphrase = null,
- passphraseRepeat = null,
- step = BootstrapStep.AccountPassword(false)
- )
- }
- } else {
- setState {
- copy(
- passphrase = null,
- passphraseRepeat = null
- )
- }
- startInitializeFlow(userPassword)
- }
+ startInitializeFlow(state)
}
BootstrapActions.RecoveryKeySaved -> {
_viewEvents.post(BootstrapViewEvents.RecoveryKeySaved)
@@ -262,7 +248,7 @@ class BootstrapSharedViewModel @AssistedInject constructor(
}
BootstrapActions.GoToEnterAccountPassword -> {
setState {
- copy(step = BootstrapStep.AccountPassword(false))
+ copy(step = BootstrapStep.AccountReAuth())
}
}
BootstrapActions.HandleForgotBackupPassphrase -> {
@@ -272,15 +258,33 @@ class BootstrapSharedViewModel @AssistedInject constructor(
}
} else return@withState
}
- is BootstrapActions.ReAuth -> {
- startInitializeFlow(action.pass)
- }
+// is BootstrapActions.ReAuth -> {
+// startInitializeFlow(action.pass)
+// }
is BootstrapActions.DoMigrateWithPassphrase -> {
startMigrationFlow(state.step, action.passphrase, null)
}
is BootstrapActions.DoMigrateWithRecoveryKey -> {
startMigrationFlow(state.step, null, action.recoveryKey)
}
+ BootstrapActions.SsoAuthDone -> {
+ uiaContinuation?.resume(DefaultBaseAuth(session = pendingAuth?.session ?: ""))
+ }
+ is BootstrapActions.PasswordAuthDone -> {
+ val decryptedPass = session.loadSecureSecret(action.password.fromBase64().inputStream(), ReAuthActivity.DEFAULT_RESULT_KEYSTORE_ALIAS)
+ uiaContinuation?.resume(
+ UserPasswordAuth(
+ session = pendingAuth?.session,
+ password = decryptedPass,
+ user = session.myUserId
+ )
+ )
+ }
+ BootstrapActions.ReAuthCancelled -> {
+ setState {
+ copy(step = BootstrapStep.AccountReAuth(stringProvider.getString(R.string.authentication_error)))
+ }
+ }
}.exhaustive
}
@@ -292,7 +296,7 @@ class BootstrapSharedViewModel @AssistedInject constructor(
)
}
} else {
- startInitializeFlow(null)
+ startInitializeFlow(it)
}
}
@@ -345,16 +349,16 @@ class BootstrapSharedViewModel @AssistedInject constructor(
migrationRecoveryKey = recoveryKey
)
}
- val userPassword = reAuthHelper.data
- if (userPassword == null) {
- setState {
- copy(
- step = BootstrapStep.AccountPassword(false)
- )
- }
- } else {
- startInitializeFlow(userPassword)
- }
+// val userPassword = reAuthHelper.data
+// if (userPassword == null) {
+// setState {
+// copy(
+// step = BootstrapStep.AccountPassword(false)
+// )
+// }
+// } else {
+ withState { startInitializeFlow(it) }
+// }
}
is BackupToQuadSMigrationTask.Result.Failure -> {
_viewEvents.post(
@@ -371,7 +375,7 @@ class BootstrapSharedViewModel @AssistedInject constructor(
}
}
- private fun startInitializeFlow(userPassword: String?) = withState { state ->
+ private fun startInitializeFlow(state: BootstrapViewState) {
val previousStep = state.step
setState {
@@ -388,19 +392,45 @@ class BootstrapSharedViewModel @AssistedInject constructor(
}
}
- viewModelScope.launch(Dispatchers.IO) {
- val userPasswordAuth = userPassword?.let {
- UserPasswordAuth(
- // Note that _pendingSession may or may not be null, this is OK, it will be managed by the task
- session = _pendingSession,
- user = session.myUserId,
- password = it
- )
+ val interceptor = object : UserInteractiveAuthInterceptor {
+ override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) {
+ when (flowResponse.nextUncompletedStage()) {
+ LoginFlowTypes.PASSWORD -> {
+ pendingAuth = UserPasswordAuth(
+ // Note that _pendingSession may or may not be null, this is OK, it will be managed by the task
+ session = flowResponse.session,
+ user = session.myUserId,
+ password = null
+ )
+ uiaContinuation = promise
+ setState {
+ copy(
+ step = BootstrapStep.AccountReAuth()
+ )
+ }
+ _viewEvents.post(BootstrapViewEvents.RequestReAuth(flowResponse, errCode))
+ }
+ LoginFlowTypes.SSO -> {
+ pendingAuth = DefaultBaseAuth(flowResponse.session)
+ uiaContinuation = promise
+ setState {
+ copy(
+ step = BootstrapStep.AccountReAuth()
+ )
+ }
+ _viewEvents.post(BootstrapViewEvents.RequestReAuth(flowResponse, errCode))
+ }
+ else -> {
+ promise.resumeWith(Result.failure(UnsupportedOperationException()))
+ }
+ }
}
+ }
+ viewModelScope.launch(Dispatchers.IO) {
bootstrapTask.invoke(this,
Params(
- userPasswordAuth = userPasswordAuth,
+ userInteractiveAuthInterceptor = interceptor,
progressListener = progressListener,
passphrase = state.passphrase,
keySpec = state.migrationRecoveryKey?.let { extractCurveKeyFromRecoveryKey(it)?.let { RawBytesKeySpec(it) } },
@@ -409,7 +439,6 @@ class BootstrapSharedViewModel @AssistedInject constructor(
) { bootstrapResult ->
when (bootstrapResult) {
is BootstrapResult.SuccessCrossSigningOnly -> {
- // TPD
_viewEvents.post(BootstrapViewEvents.Dismiss(true))
}
is BootstrapResult.Success -> {
@@ -423,26 +452,11 @@ class BootstrapSharedViewModel @AssistedInject constructor(
)
}
}
- is BootstrapResult.PasswordAuthFlowMissing -> {
- // Ask the password to the user
- _pendingSession = bootstrapResult.sessionId
- setState {
- copy(
- step = BootstrapStep.AccountPassword(false)
- )
- }
- }
- is BootstrapResult.UnsupportedAuthFlow -> {
- _viewEvents.post(BootstrapViewEvents.ModalError(stringProvider.getString(R.string.auth_flow_not_supported)))
- _viewEvents.post(BootstrapViewEvents.Dismiss(false))
- }
is BootstrapResult.InvalidPasswordError -> {
- // it's a bad password
- // We clear the auth session, to avoid 'Requested operation has changed during the UI authentication session' error
- _pendingSession = null
+ // it's a bad password / auth
setState {
copy(
- step = BootstrapStep.AccountPassword(false, stringProvider.getString(R.string.auth_invalid_login_param))
+ step = BootstrapStep.AccountReAuth(stringProvider.getString(R.string.auth_invalid_login_param))
)
}
}
@@ -515,7 +529,7 @@ class BootstrapSharedViewModel @AssistedInject constructor(
)
}
}
- is BootstrapStep.AccountPassword -> {
+ is BootstrapStep.AccountReAuth -> {
_viewEvents.post(BootstrapViewEvents.SkipBootstrap(state.passphrase != null))
}
BootstrapStep.Initializing -> {
diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapStep.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapStep.kt
index 222a5d78c6..09f0e90d5d 100644
--- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapStep.kt
+++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapStep.kt
@@ -52,11 +52,11 @@ package im.vector.app.features.crypto.recover
* │ │ BootstrapStep.ConfirmPassphrase │──┐
* │ └────────────────────────────────────┘ │
* │ │ │
- * │ is password needed? │
+ * │ is password/reauth needed? │
* │ │ │
* │ ▼ │
* │ ┌────────────────────────────────────┐ │
- * │ │ BootstrapStep.AccountPassword │ │
+ * │ │ BootstrapStep.AccountReAuth │ │
* │ └────────────────────────────────────┘ │
* │ │ │
* │ │ │
@@ -94,7 +94,7 @@ sealed class BootstrapStep {
data class SetupPassphrase(val isPasswordVisible: Boolean) : BootstrapStep()
data class ConfirmPassphrase(val isPasswordVisible: Boolean) : BootstrapStep()
- data class AccountPassword(val isPasswordVisible: Boolean, val failure: String? = null) : BootstrapStep()
+ data class AccountReAuth(val failure: String? = null) : BootstrapStep()
abstract class GetBackupSecretForMigration : BootstrapStep()
data class GetBackupSecretPassForMigration(val isPasswordVisible: Boolean, val useKey: Boolean) : GetBackupSecretForMigration()
diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapViewEvents.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapViewEvents.kt
index 10a092ccbb..3f06623ad6 100644
--- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapViewEvents.kt
+++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapViewEvents.kt
@@ -17,10 +17,12 @@
package im.vector.app.features.crypto.recover
import im.vector.app.core.platform.VectorViewEvents
+import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
sealed class BootstrapViewEvents : VectorViewEvents {
data class Dismiss(val success: Boolean) : BootstrapViewEvents()
data class ModalError(val error: String) : BootstrapViewEvents()
object RecoveryKeySaved : BootstrapViewEvents()
data class SkipBootstrap(val genKeyOption: Boolean = true) : BootstrapViewEvents()
+ data class RequestReAuth(val flowResponse: RegistrationFlowResponse, val lastErrorCode: String?) : BootstrapViewEvents()
}
diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt
index 23ed9b6483..04ac79d4a4 100644
--- a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt
@@ -25,8 +25,9 @@ import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.R
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
@@ -159,7 +160,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(
super.onCleared()
}
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: VerificationBottomSheetViewState,
args: VerificationBottomSheet.VerificationArgs): VerificationBottomSheetViewModel
diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/choose/VerificationChooseMethodViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/choose/VerificationChooseMethodViewModel.kt
index 4518a8d6b7..7b9acd2f57 100644
--- a/vector/src/main/java/im/vector/app/features/crypto/verification/choose/VerificationChooseMethodViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/crypto/verification/choose/VerificationChooseMethodViewModel.kt
@@ -19,8 +19,9 @@ import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.di.HasScreenInjector
import im.vector.app.core.platform.EmptyAction
import im.vector.app.core.platform.EmptyViewEvents
@@ -79,7 +80,7 @@ class VerificationChooseMethodViewModel @AssistedInject constructor(
}
}
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: VerificationChooseMethodViewState): VerificationChooseMethodViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/emoji/VerificationEmojiCodeViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/emoji/VerificationEmojiCodeViewModel.kt
index d511123f5e..44f0e752c8 100644
--- a/vector/src/main/java/im/vector/app/features/crypto/verification/emoji/VerificationEmojiCodeViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/crypto/verification/emoji/VerificationEmojiCodeViewModel.kt
@@ -24,8 +24,9 @@ import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.di.HasScreenInjector
import im.vector.app.core.platform.EmptyAction
import im.vector.app.core.platform.EmptyViewEvents
@@ -149,7 +150,7 @@ class VerificationEmojiCodeViewModel @AssistedInject constructor(
}
}
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: VerificationEmojiCodeViewState): VerificationEmojiCodeViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt
index d16ebcdc8b..bf2defafa1 100644
--- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt
@@ -24,8 +24,9 @@ import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
import kotlinx.coroutines.launch
@@ -42,7 +43,7 @@ class DiscoverySettingsViewModel @AssistedInject constructor(
private val session: Session)
: VectorViewModel(initialState) {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: DiscoverySettingsState): DiscoverySettingsViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerViewModel.kt b/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerViewModel.kt
index 0f07a0353f..9455b1bff4 100644
--- a/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerViewModel.kt
@@ -19,8 +19,9 @@ import androidx.lifecycle.viewModelScope
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.R
import im.vector.app.core.di.HasScreenInjector
import im.vector.app.core.extensions.exhaustive
@@ -41,7 +42,7 @@ class SetIdentityServerViewModel @AssistedInject constructor(
stringProvider: StringProvider)
: VectorViewModel(initialState) {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: SetIdentityServerState): SetIdentityServerViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/grouplist/GroupListViewModel.kt b/vector/src/main/java/im/vector/app/features/grouplist/GroupListViewModel.kt
index a17aa4dbf2..3b096adbfb 100644
--- a/vector/src/main/java/im/vector/app/features/grouplist/GroupListViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/grouplist/GroupListViewModel.kt
@@ -22,8 +22,9 @@ import arrow.core.Option
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.R
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.StringProvider
@@ -45,7 +46,7 @@ class GroupListViewModel @AssistedInject constructor(@Assisted initialState: Gro
private val stringProvider: StringProvider
) : VectorViewModel(initialState) {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: GroupListViewState): GroupListViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt
index 90d128320b..afaa290190 100644
--- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt
@@ -20,29 +20,38 @@ import androidx.lifecycle.viewModelScope
import com.airbnb.mvrx.MvRx
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.login.ReAuthHelper
import im.vector.app.features.settings.VectorPreferences
import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
-import org.matrix.android.sdk.api.MatrixCallback
-import org.matrix.android.sdk.api.NoOpMatrixCallback
+import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
+import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
+import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.pushrules.RuleIds
import org.matrix.android.sdk.api.session.InitialSyncProgressService
import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
import org.matrix.android.sdk.api.util.toMatrixItem
+import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
+import org.matrix.android.sdk.api.auth.registration.nextUncompletedStage
import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
-import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth
+import org.matrix.android.sdk.api.auth.UIABaseAuth
+import org.matrix.android.sdk.api.auth.UserPasswordAuth
+import org.matrix.android.sdk.internal.util.awaitCallback
import org.matrix.android.sdk.rx.asObservable
import org.matrix.android.sdk.rx.rx
import timber.log.Timber
+import kotlin.coroutines.Continuation
+import kotlin.coroutines.resume
class HomeActivityViewModel @AssistedInject constructor(
@Assisted initialState: HomeActivityViewState,
@@ -52,7 +61,7 @@ class HomeActivityViewModel @AssistedInject constructor(
private val vectorPreferences: VectorPreferences
) : VectorViewModel(initialState) {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: HomeActivityViewState, args: HomeActivityArgs): HomeActivityViewModel
}
@@ -73,7 +82,6 @@ class HomeActivityViewModel @AssistedInject constructor(
init {
cleanupFiles()
observeInitialSync()
- mayBeInitializeCrossSigning()
checkSessionPushIsOn()
observeCrossSigningReset()
}
@@ -121,10 +129,10 @@ class HomeActivityViewModel @AssistedInject constructor(
// Schedule a check of the bootstrap when the init sync will be finished
checkBootstrap = true
}
- is InitialSyncProgressService.Status.Idle -> {
+ is InitialSyncProgressService.Status.Idle -> {
if (checkBootstrap) {
checkBootstrap = false
- maybeBootstrapCrossSigning()
+ maybeBootstrapCrossSigningAfterInitialSync()
}
}
}
@@ -138,29 +146,6 @@ class HomeActivityViewModel @AssistedInject constructor(
.disposeOnClear()
}
- private fun mayBeInitializeCrossSigning() {
- if (args.accountCreation) {
- val password = reAuthHelper.data ?: return Unit.also {
- Timber.w("No password to init cross signing")
- }
-
- val session = activeSessionHolder.getSafeActiveSession() ?: return Unit.also {
- Timber.w("No session to init cross signing")
- }
-
- // We do not use the viewModel context because we do not want to cancel this action
- Timber.d("Initialize cross signing")
- session.cryptoService().crossSigningService().initializeCrossSigning(
- authParams = UserPasswordAuth(
- session = null,
- user = session.myUserId,
- password = password
- ),
- callback = NoOpMatrixCallback()
- )
- }
- }
-
/**
* After migration from riot to element some users reported that their
* push setting for the session was set to off
@@ -196,56 +181,66 @@ class HomeActivityViewModel @AssistedInject constructor(
}
}
- private fun maybeBootstrapCrossSigning() {
- // In case of account creation, it is already done before
- if (args.accountCreation) return
+ private fun maybeBootstrapCrossSigningAfterInitialSync() {
+ // We do not use the viewModel context because we do not want to tie this action to activity view model
+ GlobalScope.launch(Dispatchers.IO) {
+ val session = activeSessionHolder.getSafeActiveSession() ?: return@launch
- val session = activeSessionHolder.getSafeActiveSession() ?: return
+ tryOrNull("## MaybeBootstrapCrossSigning: Failed to download keys") {
+ awaitCallback> {
+ session.cryptoService().downloadKeys(listOf(session.myUserId), true, it)
+ }
+ }
- // Ensure keys of the user are downloaded
- session.cryptoService().downloadKeys(listOf(session.myUserId), true, object : MatrixCallback> {
- override fun onSuccess(data: MXUsersDevicesMap) {
- // Is there already cross signing keys here?
- val mxCrossSigningInfo = session.cryptoService().crossSigningService().getMyCrossSigningKeys()
- if (mxCrossSigningInfo != null) {
- // Cross-signing is already set up for this user, is it trusted?
- if (!mxCrossSigningInfo.isTrusted()) {
- // New session
- _viewEvents.post(
- HomeActivityViewEvents.OnNewSession(
- session.getUser(session.myUserId)?.toMatrixItem(),
- // If it's an old unverified, we should send requests
- // instead of waiting for an incoming one
- reAuthHelper.data != null
- )
- )
- }
- } else {
- // Initialize cross-signing
- val password = reAuthHelper.data
-
- if (password == null) {
- // Check this is not an SSO account
- if (session.getHomeServerCapabilities().canChangePassword) {
- // Ask password to the user: Upgrade security
- _viewEvents.post(HomeActivityViewEvents.AskPasswordToInitCrossSigning(session.getUser(session.myUserId)?.toMatrixItem()))
- }
- // Else (SSO) just ignore for the moment
- } else {
- // We do not use the viewModel context because we do not want to cancel this action
- Timber.d("Initialize cross signing")
+ // From there we are up to date with server
+ // Is there already cross signing keys here?
+ val mxCrossSigningInfo = session.cryptoService().crossSigningService().getMyCrossSigningKeys()
+ if (mxCrossSigningInfo != null) {
+ // Cross-signing is already set up for this user, is it trusted?
+ if (!mxCrossSigningInfo.isTrusted()) {
+ // New session
+ _viewEvents.post(
+ HomeActivityViewEvents.OnNewSession(
+ session.getUser(session.myUserId)?.toMatrixItem(),
+ // If it's an old unverified, we should send requests
+ // instead of waiting for an incoming one
+ reAuthHelper.data != null
+ )
+ )
+ }
+ } else {
+ // Try to initialize cross signing in background if possible
+ Timber.d("Initialize cross signing...")
+ awaitCallback {
+ try {
session.cryptoService().crossSigningService().initializeCrossSigning(
- authParams = UserPasswordAuth(
- session = null,
- user = session.myUserId,
- password = password
- ),
- callback = NoOpMatrixCallback()
+ object : UserInteractiveAuthInterceptor {
+ override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) {
+ // We missed server grace period or it's not setup, see if we remember locally password
+ if (flowResponse.nextUncompletedStage() == LoginFlowTypes.PASSWORD
+ && errCode == null
+ && reAuthHelper.data != null) {
+ promise.resume(
+ UserPasswordAuth(
+ session = flowResponse.session,
+ user = session.myUserId,
+ password = reAuthHelper.data
+ )
+ )
+ } else {
+ promise.resumeWith(Result.failure(Exception("Cannot silently initialize cross signing, UIA missing")))
+ }
+ }
+ },
+ callback = it
)
+ Timber.d("Initialize cross signing SUCCESS")
+ } catch (failure: Throwable) {
+ Timber.e(failure, "Failed to initialize cross signing")
}
}
}
- })
+ }
}
override fun handle(action: HomeActivityViewActions) {
diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt
index 88c310fde8..c261081055 100644
--- a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt
@@ -19,8 +19,9 @@ package im.vector.app.features.home
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.di.HasScreenInjector
import im.vector.app.core.platform.EmptyViewEvents
import im.vector.app.core.platform.VectorViewModel
@@ -44,7 +45,7 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho
private val stringProvider: StringProvider)
: VectorViewModel(initialState) {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: HomeDetailViewState): HomeDetailViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt
index 3bdcfc4018..f4f16502ab 100644
--- a/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt
@@ -24,8 +24,9 @@ import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.platform.EmptyViewEvents
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.platform.VectorViewModelAction
@@ -65,7 +66,7 @@ class UnknownDeviceDetectorSharedViewModel @AssistedInject constructor(@Assisted
data class IgnoreDevice(val deviceIds: List) : Action()
}
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: UnknownDevicesState): UnknownDeviceDetectorSharedViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsViewModel.kt
index 8a32157097..d3825de4ef 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsViewModel.kt
@@ -19,8 +19,9 @@ package im.vector.app.features.home.room.breadcrumbs
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.platform.EmptyAction
import im.vector.app.core.platform.EmptyViewEvents
import im.vector.app.core.platform.VectorViewModel
@@ -35,7 +36,7 @@ class BreadcrumbsViewModel @AssistedInject constructor(@Assisted initialState: B
private val session: Session)
: VectorViewModel(initialState) {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: BreadcrumbsViewState): BreadcrumbsViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/AutoCompleter.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/AutoCompleter.kt
index d560a675fe..ec2ff82e67 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/AutoCompleter.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/AutoCompleter.kt
@@ -24,8 +24,9 @@ import android.widget.EditText
import com.otaliastudios.autocomplete.Autocomplete
import com.otaliastudios.autocomplete.AutocompleteCallback
import com.otaliastudios.autocomplete.CharPolicy
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.R
import im.vector.app.core.glide.GlideApp
import im.vector.app.core.glide.GlideRequests
@@ -59,7 +60,7 @@ class AutoCompleter @AssistedInject constructor(
private lateinit var autocompleteMemberPresenter: AutocompleteMemberPresenter
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(roomId: String): AutoCompleter
}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt
index e134230c61..2d2059377c 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt
@@ -297,6 +297,8 @@ class RoomDetailFragment @Inject constructor(
private var lockSendButton = false
private val activeCallViewHolder = ActiveCallViewHolder()
+ private lateinit var emojiPopup: EmojiPopup
+
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
sharedActionViewModel = activityViewModelProvider.get(MessageSharedActionViewModel::class.java)
@@ -512,7 +514,7 @@ class RoomDetailFragment @Inject constructor(
}
private fun setupEmojiPopup() {
- val emojiPopup = EmojiPopup
+ emojiPopup = EmojiPopup
.Builder
.fromRootView(views.rootConstraintLayout)
.setKeyboardAnimationStyle(R.style.emoji_fade_animation_style)
@@ -591,6 +593,7 @@ class RoomDetailFragment @Inject constructor(
autoCompleter.clear()
debouncer.cancelAll()
views.timelineRecyclerView.cleanup()
+ emojiPopup.dismiss()
super.onDestroyView()
}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt
index 975eda5d2f..09179a9458 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt
@@ -25,8 +25,9 @@ import com.airbnb.mvrx.Success
import com.airbnb.mvrx.ViewModelContext
import com.jakewharton.rxrelay2.BehaviorRelay
import com.jakewharton.rxrelay2.PublishRelay
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.R
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
@@ -134,7 +135,7 @@ class RoomDetailViewModel @AssistedInject constructor(
private var trackUnreadMessages = AtomicBoolean(false)
private var mostRecentDisplayedEvent: TimelineEvent? = null
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: RoomDetailViewState): RoomDetailViewModel
}
@@ -1395,7 +1396,7 @@ class RoomDetailViewModel @AssistedInject constructor(
snapshot
.takeIf { state.asyncRoomSummary.invoke()?.isEncrypted == false }
?.forEach {
- previewUrlRetriever.getPreviewUrl(it.root, viewModelScope)
+ previewUrlRetriever.getPreviewUrl(it, viewModelScope)
}
}
}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt
index 8a8701e45f..9a57d9480c 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt
@@ -76,10 +76,10 @@ class SearchFragment @Inject constructor(
controller.listener = this
}
- override fun onDestroy() {
- super.onDestroy()
+ override fun onDestroyView() {
views.searchResultRecycler.cleanup()
controller.listener = null
+ super.onDestroyView()
}
override fun invalidate() = withState(searchViewModel) { state ->
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt
index ab440f6b5f..cb93cf95d2 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt
@@ -23,8 +23,9 @@ import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
import kotlinx.coroutines.launch
@@ -46,7 +47,7 @@ class SearchViewModel @AssistedInject constructor(
private var nextBatch: String? = null
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: SearchViewState): SearchViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt
index 2eae58c6b9..5008f0e0aa 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt
@@ -18,8 +18,9 @@ package im.vector.app.features.home.room.detail.timeline.action
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import dagger.Lazy
import im.vector.app.R
import im.vector.app.core.extensions.canReact
@@ -75,7 +76,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
pillsPostProcessorFactory.create(initialState.roomId)
}
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: MessageActionState): MessageActionsViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryViewModel.kt
index e4d124dd94..fff1c8a0ff 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryViewModel.kt
@@ -24,8 +24,9 @@ import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.date.VectorDateFormatter
import im.vector.app.core.platform.EmptyAction
import im.vector.app.core.platform.EmptyViewEvents
@@ -61,7 +62,7 @@ class ViewEditHistoryViewModel @AssistedInject constructor(@Assisted
private val room = session.getRoom(roomId)
?: throw IllegalStateException("Shouldn't use this ViewModel without a room")
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: ViewEditHistoryViewState): ViewEditHistoryViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsViewModel.kt
index a36f656f52..c1f98cc3ab 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsViewModel.kt
@@ -22,8 +22,9 @@ import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.date.DateFormatKind
import im.vector.app.core.date.VectorDateFormatter
import im.vector.app.core.platform.EmptyAction
@@ -68,7 +69,7 @@ class ViewReactionsViewModel @AssistedInject constructor(@Assisted
private val room = session.getRoom(roomId)
?: throw IllegalStateException("Shouldn't use this ViewModel without a room")
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: DisplayReactionsViewState): ViewReactionsViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/url/PreviewUrlRetriever.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/url/PreviewUrlRetriever.kt
index 695661feeb..54d5fd9eb3 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/url/PreviewUrlRetriever.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/url/PreviewUrlRetriever.kt
@@ -16,37 +16,56 @@
package im.vector.app.features.home.room.detail.timeline.url
+import android.os.Handler
+import android.os.Looper
import im.vector.app.BuildConfig
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.cache.CacheStrategy
import org.matrix.android.sdk.api.session.Session
-import org.matrix.android.sdk.api.session.events.model.Event
+import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
+import org.matrix.android.sdk.api.session.room.timeline.getLatestEventId
class PreviewUrlRetriever(session: Session) {
private val mediaService = session.mediaService()
- private val data = mutableMapOf()
+ private data class EventIdPreviewUrlUiState(
+ // Id of the latest event in the case of an edited event, or the eventId for an event which has not been edited
+ val latestEventId: String,
+ val previewUrlUiState: PreviewUrlUiState
+ )
+
+ // Keys are the main eventId
+ private val data = mutableMapOf()
private val listeners = mutableMapOf>()
+ private val uiHandler = Handler(Looper.getMainLooper())
// In memory list
private val blockedUrl = mutableSetOf()
- fun getPreviewUrl(event: Event, coroutineScope: CoroutineScope) {
- val eventId = event.eventId ?: return
+ fun getPreviewUrl(event: TimelineEvent, coroutineScope: CoroutineScope) {
+ val eventId = event.root.eventId ?: return
+ val latestEventId = event.getLatestEventId()
synchronized(data) {
- if (data[eventId] == null) {
+ val current = data[eventId]
+ if (current?.latestEventId != latestEventId) {
+ // The event is not known or it has been edited
// Keep only the first URL for the moment
val url = mediaService.extractUrls(event)
.firstOrNull()
?.takeIf { it !in blockedUrl }
if (url == null) {
- updateState(eventId, PreviewUrlUiState.NoUrl)
+ updateState(eventId, latestEventId, PreviewUrlUiState.NoUrl)
+ null
+ } else if (url != (current?.previewUrlUiState as? PreviewUrlUiState.Data)?.url) {
+ // There is a not known URL, or the Event has been edited and the URL has changed
+ updateState(eventId, latestEventId, PreviewUrlUiState.Loading)
+ url
} else {
- updateState(eventId, PreviewUrlUiState.Loading)
+ // Already handled
+ null
}
- url
} else {
// Already handled
null
@@ -64,15 +83,15 @@ class PreviewUrlRetriever(session: Session) {
synchronized(data) {
// Blocked after the request has been sent?
if (urlToRetrieve in blockedUrl) {
- updateState(eventId, PreviewUrlUiState.NoUrl)
+ updateState(eventId, latestEventId, PreviewUrlUiState.NoUrl)
} else {
- updateState(eventId, PreviewUrlUiState.Data(eventId, urlToRetrieve, it))
+ updateState(eventId, latestEventId, PreviewUrlUiState.Data(eventId, urlToRetrieve, it))
}
}
},
{
synchronized(data) {
- updateState(eventId, PreviewUrlUiState.Error(it))
+ updateState(eventId, latestEventId, PreviewUrlUiState.Error(it))
}
}
)
@@ -86,18 +105,20 @@ class PreviewUrlRetriever(session: Session) {
// Notify the listener
synchronized(data) {
data[eventId]
- ?.takeIf { it is PreviewUrlUiState.Data && it.url == url }
+ ?.takeIf { it.previewUrlUiState is PreviewUrlUiState.Data && it.previewUrlUiState.url == url }
?.let {
- updateState(eventId, PreviewUrlUiState.NoUrl)
+ updateState(eventId, it.latestEventId, PreviewUrlUiState.NoUrl)
}
}
}
- private fun updateState(eventId: String, state: PreviewUrlUiState) {
- data[eventId] = state
+ private fun updateState(eventId: String, latestEventId: String, state: PreviewUrlUiState) {
+ data[eventId] = EventIdPreviewUrlUiState(latestEventId, state)
// Notify the listener
- listeners[eventId].orEmpty().forEach {
- it.onStateUpdated(state)
+ uiHandler.post {
+ listeners[eventId].orEmpty().forEach {
+ it.onStateUpdated(state)
+ }
}
}
@@ -107,7 +128,7 @@ class PreviewUrlRetriever(session: Session) {
// Give the current state if any
synchronized(data) {
- listener.onStateUpdated(data[key] ?: PreviewUrlUiState.Unknown)
+ listener.onStateUpdated(data[key]?.previewUrlUiState ?: PreviewUrlUiState.Unknown)
}
}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsViewModel.kt
index a062b2b034..c51c571815 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsViewModel.kt
@@ -18,8 +18,9 @@ package im.vector.app.features.home.room.list.actions
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.platform.EmptyAction
import im.vector.app.core.platform.EmptyViewEvents
import im.vector.app.core.platform.VectorViewModel
@@ -31,7 +32,7 @@ class RoomListQuickActionsViewModel @AssistedInject constructor(@Assisted initia
private val session: Session
) : VectorViewModel(initialState) {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: RoomListQuickActionsState): RoomListQuickActionsViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/homeserver/HomeServerCapabilitiesViewModel.kt b/vector/src/main/java/im/vector/app/features/homeserver/HomeServerCapabilitiesViewModel.kt
index fe7a8006e0..05fd825558 100644
--- a/vector/src/main/java/im/vector/app/features/homeserver/HomeServerCapabilitiesViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/homeserver/HomeServerCapabilitiesViewModel.kt
@@ -20,8 +20,9 @@ import androidx.lifecycle.viewModelScope
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.di.HasScreenInjector
import im.vector.app.core.platform.EmptyAction
import im.vector.app.core.platform.EmptyViewEvents
@@ -42,7 +43,7 @@ class HomeServerCapabilitiesViewModel @AssistedInject constructor(
private val rawService: RawService
) : VectorViewModel(initialState) {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: HomeServerCapabilitiesViewState): HomeServerCapabilitiesViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/html/PillsPostProcessor.kt b/vector/src/main/java/im/vector/app/features/html/PillsPostProcessor.kt
index c13f5fdfb3..716baff757 100644
--- a/vector/src/main/java/im/vector/app/features/html/PillsPostProcessor.kt
+++ b/vector/src/main/java/im/vector/app/features/html/PillsPostProcessor.kt
@@ -19,8 +19,9 @@ package im.vector.app.features.html
import android.content.Context
import android.text.Spannable
import android.text.Spanned
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.glide.GlideApp
import im.vector.app.features.home.AvatarRenderer
@@ -37,7 +38,7 @@ class PillsPostProcessor @AssistedInject constructor(@Assisted private val roomI
private val sessionHolder: ActiveSessionHolder)
: EventHtmlRenderer.PostProcessor {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(roomId: String?): PillsPostProcessor
}
diff --git a/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomViewModel.kt b/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomViewModel.kt
index 21a998d8e2..a694ee36ba 100644
--- a/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomViewModel.kt
@@ -19,8 +19,9 @@ package im.vector.app.features.invite
import com.airbnb.mvrx.ActivityViewModelContext
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.R
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.StringProvider
@@ -37,7 +38,7 @@ class InviteUsersToRoomViewModel @AssistedInject constructor(@Assisted
private val room = session.getRoom(initialState.roomId)!!
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: InviteUsersToRoomViewState): InviteUsersToRoomViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/link/LinkHandlerActivity.kt b/vector/src/main/java/im/vector/app/features/link/LinkHandlerActivity.kt
index 2f1e8c0b1a..6c0e142b38 100644
--- a/vector/src/main/java/im/vector/app/features/link/LinkHandlerActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/link/LinkHandlerActivity.kt
@@ -19,6 +19,7 @@ package im.vector.app.features.link
import android.content.Intent
import android.net.Uri
import androidx.appcompat.app.AlertDialog
+import androidx.lifecycle.lifecycleScope
import im.vector.app.R
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.di.ScreenComponent
@@ -30,7 +31,7 @@ import im.vector.app.features.login.LoginActivity
import im.vector.app.features.login.LoginConfig
import im.vector.app.features.permalink.PermalinkHandler
import io.reactivex.android.schedulers.AndroidSchedulers
-import org.matrix.android.sdk.api.MatrixCallback
+import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.session.permalinks.PermalinkService
import timber.log.Timber
import java.util.concurrent.TimeUnit
@@ -139,23 +140,30 @@ class LinkHandlerActivity : VectorBaseActivity() {
.setTitle(R.string.dialog_title_warning)
.setMessage(R.string.error_user_already_logged_in)
.setCancelable(false)
- .setPositiveButton(R.string.logout) { _, _ ->
- sessionHolder.getSafeActiveSession()?.signOut(true, object : MatrixCallback {
- override fun onFailure(failure: Throwable) {
- displayError(failure)
- }
-
- override fun onSuccess(data: Unit) {
- Timber.d("## displayAlreadyLoginPopup(): logout succeeded")
- sessionHolder.clearActiveSession()
- startLoginActivity(uri)
- }
- }) ?: finish()
- }
+ .setPositiveButton(R.string.logout) { _, _ -> safeSignout(uri) }
.setNegativeButton(R.string.cancel) { _, _ -> finish() }
.show()
}
+ private fun safeSignout(uri: Uri) {
+ val session = sessionHolder.getSafeActiveSession()
+ if (session == null) {
+ // Should not happen
+ startLoginActivity(uri)
+ } else {
+ lifecycleScope.launch {
+ try {
+ session.signOut(true)
+ Timber.d("## displayAlreadyLoginPopup(): logout succeeded")
+ sessionHolder.clearActiveSession()
+ startLoginActivity(uri)
+ } catch (failure: Throwable) {
+ displayError(failure)
+ }
+ }
+ }
+ }
+
private fun displayError(failure: Throwable) {
AlertDialog.Builder(this)
.setTitle(R.string.dialog_title_error)
diff --git a/vector/src/main/java/im/vector/app/features/login/LoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/LoginViewModel.kt
index a3f1c36d08..7bf0b98841 100644
--- a/vector/src/main/java/im/vector/app/features/login/LoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/LoginViewModel.kt
@@ -26,8 +26,9 @@ import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.R
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.extensions.configureAndStart
@@ -68,7 +69,7 @@ class LoginViewModel @AssistedInject constructor(
private val homeServerHistoryService: HomeServerHistoryService
) : VectorViewModel(initialState) {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: LoginViewState): LoginViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/login/LoginWebFragment.kt b/vector/src/main/java/im/vector/app/features/login/LoginWebFragment.kt
index 4b03c93321..9ea42308eb 100644
--- a/vector/src/main/java/im/vector/app/features/login/LoginWebFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/LoginWebFragment.kt
@@ -155,18 +155,15 @@ class LoginWebFragment @Inject constructor(
// avoid infinite onPageFinished call
if (url.startsWith("http")) {
// Generic method to make a bridge between JS and the UIWebView
- val mxcJavascriptSendObjectMessage = assetReader.readAssetFile("sendObject.js")
- view.loadUrl(mxcJavascriptSendObjectMessage)
+ assetReader.readAssetFile("sendObject.js")?.let { view.loadUrl(it) }
if (state.signMode == SignMode.SignIn) {
// The function the fallback page calls when the login is complete
- val mxcJavascriptOnLogin = assetReader.readAssetFile("onLogin.js")
- view.loadUrl(mxcJavascriptOnLogin)
+ assetReader.readAssetFile("onLogin.js")?.let { view.loadUrl(it) }
} else {
// MODE_REGISTER
// The function the fallback page calls when the registration is complete
- val mxcJavascriptOnRegistered = assetReader.readAssetFile("onRegistered.js")
- view.loadUrl(mxcJavascriptOnRegistered)
+ assetReader.readAssetFile("onRegistered.js")?.let { view.loadUrl(it) }
}
}
}
diff --git a/vector/src/main/java/im/vector/app/features/login/SocialLoginButtonsView.kt b/vector/src/main/java/im/vector/app/features/login/SocialLoginButtonsView.kt
index 9290479a7a..4dc688ad22 100644
--- a/vector/src/main/java/im/vector/app/features/login/SocialLoginButtonsView.kt
+++ b/vector/src/main/java/im/vector/app/features/login/SocialLoginButtonsView.kt
@@ -83,25 +83,28 @@ class SocialLoginButtonsView @JvmOverloads constructor(context: Context, attrs:
ssoIdentityProviders?.forEach { identityProvider ->
// Use some heuristic to render buttons according to branding guidelines
val button: MaterialButton = cachedViews[identityProvider.id]
- ?: when (identityProvider.id) {
- SsoIdentityProvider.ID_GOOGLE -> {
+ ?: when (identityProvider.brand) {
+ SsoIdentityProvider.BRAND_GOOGLE -> {
MaterialButton(context, null, R.attr.vctr_social_login_button_google_style)
}
- SsoIdentityProvider.ID_GITHUB -> {
+ SsoIdentityProvider.BRAND_GITHUB -> {
MaterialButton(context, null, R.attr.vctr_social_login_button_github_style)
}
- SsoIdentityProvider.ID_APPLE -> {
+ SsoIdentityProvider.BRAND_APPLE -> {
MaterialButton(context, null, R.attr.vctr_social_login_button_apple_style)
}
- SsoIdentityProvider.ID_FACEBOOK -> {
+ SsoIdentityProvider.BRAND_FACEBOOK -> {
MaterialButton(context, null, R.attr.vctr_social_login_button_facebook_style)
}
- SsoIdentityProvider.ID_TWITTER -> {
+ SsoIdentityProvider.BRAND_TWITTER -> {
MaterialButton(context, null, R.attr.vctr_social_login_button_twitter_style)
}
+ SsoIdentityProvider.BRAND_GITLAB -> {
+ MaterialButton(context, null, R.attr.vctr_social_login_button_gitlab_style)
+ }
else -> {
// TODO Use iconUrl
- MaterialButton(context, null, R.attr.materialButtonStyle).apply {
+ MaterialButton(context, null, R.attr.materialButtonOutlinedStyle).apply {
transformationMethod = null
textAlignment = View.TEXT_ALIGNMENT_CENTER
}
@@ -131,12 +134,13 @@ class SocialLoginButtonsView @JvmOverloads constructor(context: Context, attrs:
clipChildren = false
if (isInEditMode) {
ssoIdentityProviders = listOf(
- SsoIdentityProvider(SsoIdentityProvider.ID_GOOGLE, "Google", null),
- SsoIdentityProvider(SsoIdentityProvider.ID_FACEBOOK, "Facebook", null),
- SsoIdentityProvider(SsoIdentityProvider.ID_APPLE, "Apple", null),
- SsoIdentityProvider(SsoIdentityProvider.ID_GITHUB, "GitHub", null),
- SsoIdentityProvider(SsoIdentityProvider.ID_TWITTER, "Twitter", null),
- SsoIdentityProvider("Custom_pro", "SSO", null)
+ SsoIdentityProvider("Google", "Google", null, SsoIdentityProvider.BRAND_GOOGLE),
+ SsoIdentityProvider("Facebook", "Facebook", null, SsoIdentityProvider.BRAND_FACEBOOK),
+ SsoIdentityProvider("Apple", "Apple", null, SsoIdentityProvider.BRAND_APPLE),
+ SsoIdentityProvider("GitHub", "GitHub", null, SsoIdentityProvider.BRAND_GITHUB),
+ SsoIdentityProvider("Twitter", "Twitter", null, SsoIdentityProvider.BRAND_TWITTER),
+ SsoIdentityProvider("Gitlab", "Gitlab", null, SsoIdentityProvider.BRAND_GITLAB),
+ SsoIdentityProvider("Custom_pro", "SSO", null, null)
)
}
val typedArray = context.theme.obtainStyledAttributes(attrs, R.styleable.SocialLoginButtonsView, 0, 0)
diff --git a/vector/src/main/java/im/vector/app/features/matrixto/MatrixToBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/matrixto/MatrixToBottomSheetViewModel.kt
index 6e8a530c9a..207256c75a 100644
--- a/vector/src/main/java/im/vector/app/features/matrixto/MatrixToBottomSheetViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/matrixto/MatrixToBottomSheetViewModel.kt
@@ -24,8 +24,9 @@ import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.R
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
@@ -50,7 +51,7 @@ class MatrixToBottomSheetViewModel @AssistedInject constructor(
private val stringProvider: StringProvider,
private val rawService: RawService) : VectorViewModel(initialState) {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: MatrixToBottomSheetState): MatrixToBottomSheetViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt
index 0a6197e424..fded8602c4 100644
--- a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt
+++ b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt
@@ -229,10 +229,13 @@ class DefaultNavigator @Inject constructor(
}
override fun openKeysBackupSetup(context: Context, showManualExport: Boolean) {
- // if cross signing is enabled we should propose full 4S
+ // if cross signing is enabled and trusted or not set up at all we should propose full 4S
sessionHolder.getSafeActiveSession()?.let { session ->
- if (session.cryptoService().crossSigningService().canCrossSign() && context is AppCompatActivity) {
- BootstrapBottomSheet.show(context.supportFragmentManager, SetupMode.NORMAL)
+ if (session.cryptoService().crossSigningService().getMyCrossSigningKeys() == null
+ || session.cryptoService().crossSigningService().canCrossSign()) {
+ (context as? AppCompatActivity)?.let {
+ BootstrapBottomSheet.show(it.supportFragmentManager, SetupMode.NORMAL)
+ }
} else {
context.startActivity(KeysBackupSetupActivity.intent(context, showManualExport))
}
diff --git a/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt b/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt
index 24fb6159f8..28b2a8b4d5 100644
--- a/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt
+++ b/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt
@@ -21,6 +21,7 @@ import android.os.Build
import android.os.Handler
import android.os.Looper
import android.view.View
+import android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
import android.widget.ImageView
import com.tapadoo.alerter.Alerter
import com.tapadoo.alerter.OnHideAlertListener
@@ -165,9 +166,7 @@ class PopupAlertManager @Inject constructor(private val avatarRenderer: Lazy
- var flags = view.systemUiVisibility
- flags = flags and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv()
- view.systemUiVisibility = flags
+ view.windowInsetsController?.setSystemBarsAppearance(0, APPEARANCE_LIGHT_STATUS_BARS)
}
}
@@ -179,9 +178,7 @@ class PopupAlertManager @Inject constructor(private val avatarRenderer: Lazy
- var flags = view.systemUiVisibility
- flags = flags or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
- view.systemUiVisibility = flags
+ view.windowInsetsController?.setSystemBarsAppearance(APPEARANCE_LIGHT_STATUS_BARS, APPEARANCE_LIGHT_STATUS_BARS)
}
}
diff --git a/vector/src/main/java/im/vector/app/features/rageshake/BugReporter.kt b/vector/src/main/java/im/vector/app/features/rageshake/BugReporter.kt
index 7be7624a48..1a03fc6c47 100755
--- a/vector/src/main/java/im/vector/app/features/rageshake/BugReporter.kt
+++ b/vector/src/main/java/im/vector/app/features/rageshake/BugReporter.kt
@@ -21,7 +21,6 @@ import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.Canvas
-import android.os.AsyncTask
import android.os.Build
import android.view.View
import androidx.fragment.app.DialogFragment
@@ -37,6 +36,11 @@ import im.vector.app.features.settings.devtools.GossipingEventsSerializer
import im.vector.app.features.settings.locale.SystemLocaleProvider
import im.vector.app.features.themes.ThemeUtils
import im.vector.app.features.version.VersionProvider
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
import okhttp3.Call
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient
@@ -98,6 +102,8 @@ class BugReporter @Inject constructor(
var screenshot: Bitmap? = null
private set
+ private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
+
private val LOGCAT_CMD_ERROR = arrayOf("logcat", // /< Run 'logcat' command
"-d", // /< Dump the log rather than continue outputting it
"-v", // formatting
@@ -160,13 +166,13 @@ class BugReporter @Inject constructor(
withScreenshot: Boolean,
theBugDescription: String,
listener: IMXBugReportListener?) {
- object : AsyncTask() {
- // enumerate files to delete
- val mBugReportFiles: MutableList = ArrayList()
+ // enumerate files to delete
+ val mBugReportFiles: MutableList = ArrayList()
- override fun doInBackground(vararg voids: Void?): String? {
+ coroutineScope.launch {
+ var serverError: String? = null
+ withContext(Dispatchers.IO) {
var bugDescription = theBugDescription
- var serverError: String? = null
val crashCallStack = getCrashDescription(context)
if (null != crashCallStack) {
@@ -342,7 +348,11 @@ class BugReporter @Inject constructor(
}
Timber.v("## onWrite() : $percentage%")
- publishProgress(percentage)
+ try {
+ listener?.onProgress(percentage)
+ } catch (e: Exception) {
+ Timber.e(e, "## onProgress() : failed")
+ }
}
// build the request
@@ -386,11 +396,13 @@ class BugReporter @Inject constructor(
}
// check if the error message
- try {
- val responseJSON = JSONObject(serverError)
- serverError = responseJSON.getString("error")
- } catch (e: JSONException) {
- Timber.e(e, "doInBackground ; Json conversion failed")
+ serverError?.let {
+ try {
+ val responseJSON = JSONObject(it)
+ serverError = responseJSON.getString("error")
+ } catch (e: JSONException) {
+ Timber.e(e, "doInBackground ; Json conversion failed")
+ }
}
// should never happen
@@ -403,21 +415,9 @@ class BugReporter @Inject constructor(
}
}
}
-
- return serverError
}
- override fun onProgressUpdate(vararg progress: Int?) {
- if (null != listener) {
- try {
- listener.onProgress(progress[0] ?: 0)
- } catch (e: Exception) {
- Timber.e(e, "## onProgress() : failed")
- }
- }
- }
-
- override fun onPostExecute(reason: String?) {
+ withContext(Dispatchers.Main) {
mBugReportCall = null
// delete when the bug report has been successfully sent
@@ -429,17 +429,17 @@ class BugReporter @Inject constructor(
try {
if (mIsCancelled) {
listener.onUploadCancelled()
- } else if (null == reason) {
+ } else if (null == serverError) {
listener.onUploadSucceed()
} else {
- listener.onUploadFailed(reason)
+ listener.onUploadFailed(serverError)
}
} catch (e: Exception) {
Timber.e(e, "## onPostExecute() : failed")
}
}
}
- }.execute()
+ }
}
/**
@@ -457,9 +457,9 @@ class BugReporter @Inject constructor(
activity.startActivity(intent)
}
- // ==============================================================================================================
- // crash report management
- // ==============================================================================================================
+// ==============================================================================================================
+// crash report management
+// ==============================================================================================================
/**
* Provides the crash file
@@ -529,9 +529,9 @@ class BugReporter @Inject constructor(
return null
}
- // ==============================================================================================================
- // Screenshot management
- // ==============================================================================================================
+// ==============================================================================================================
+// Screenshot management
+// ==============================================================================================================
/**
* Take a screenshot of the display.
@@ -598,9 +598,9 @@ class BugReporter @Inject constructor(
}
}
- // ==============================================================================================================
- // Logcat management
- // ==============================================================================================================
+// ==============================================================================================================
+// Logcat management
+// ==============================================================================================================
/**
* Save the logcat
@@ -660,9 +660,9 @@ class BugReporter @Inject constructor(
}
}
- // ==============================================================================================================
- // File compression management
- // ==============================================================================================================
+// ==============================================================================================================
+// File compression management
+// ==============================================================================================================
/**
* GZip a file
diff --git a/vector/src/main/java/im/vector/app/features/reactions/EmojiSearchResultViewModel.kt b/vector/src/main/java/im/vector/app/features/reactions/EmojiSearchResultViewModel.kt
index 98b729603a..ac7aee797a 100644
--- a/vector/src/main/java/im/vector/app/features/reactions/EmojiSearchResultViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/reactions/EmojiSearchResultViewModel.kt
@@ -19,8 +19,9 @@ import com.airbnb.mvrx.ActivityViewModelContext
import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.platform.EmptyViewEvents
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.reactions.data.EmojiDataSource
@@ -36,7 +37,7 @@ class EmojiSearchResultViewModel @AssistedInject constructor(
private val dataSource: EmojiDataSource)
: VectorViewModel(initialState) {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: EmojiSearchResultViewState): EmojiSearchResultViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/room/RequireActiveMembershipViewModel.kt b/vector/src/main/java/im/vector/app/features/room/RequireActiveMembershipViewModel.kt
index 868a4e4dd6..c8b0037311 100644
--- a/vector/src/main/java/im/vector/app/features/room/RequireActiveMembershipViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/room/RequireActiveMembershipViewModel.kt
@@ -21,8 +21,9 @@ import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
import com.jakewharton.rxrelay2.BehaviorRelay
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.R
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
@@ -48,7 +49,7 @@ class RequireActiveMembershipViewModel @AssistedInject constructor(
private val session: Session)
: VectorViewModel(initialState) {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: RequireActiveMembershipViewState): RequireActiveMembershipViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryViewModel.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryViewModel.kt
index b047b2ede9..f5c545c34d 100644
--- a/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryViewModel.kt
@@ -23,8 +23,9 @@ import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.ViewModelContext
import com.airbnb.mvrx.appendAt
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.platform.VectorViewModel
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.extensions.orFalse
@@ -46,7 +47,7 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState:
private val session: Session)
: VectorViewModel(initialState) {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: PublicRoomsViewState): RoomDirectoryViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomViewModel.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomViewModel.kt
index 216a016fbe..3c027c4845 100644
--- a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomViewModel.kt
@@ -25,8 +25,9 @@ import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.raw.wellknown.getElementWellknown
@@ -46,7 +47,7 @@ class CreateRoomViewModel @AssistedInject constructor(@Assisted initialState: Cr
private val rawService: RawService
) : VectorViewModel(initialState) {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: CreateRoomViewState): CreateRoomViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerViewModel.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerViewModel.kt
index 44044304bc..5460411907 100644
--- a/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerViewModel.kt
@@ -21,8 +21,9 @@ import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.platform.EmptyViewEvents
import im.vector.app.core.platform.VectorViewModel
import org.matrix.android.sdk.api.MatrixCallback
@@ -33,7 +34,7 @@ class RoomDirectoryPickerViewModel @AssistedInject constructor(@Assisted initial
private val session: Session)
: VectorViewModel(initialState) {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: RoomDirectoryPickerViewState): RoomDirectoryPickerViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt
index 63b9a50546..c99213d890 100644
--- a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt
@@ -22,8 +22,9 @@ import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.EmptyViewEvents
import im.vector.app.core.platform.VectorViewModel
@@ -46,7 +47,7 @@ class RoomPreviewViewModel @AssistedInject constructor(@Assisted private val ini
private val session: Session)
: VectorViewModel(initialState) {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: RoomPreviewViewState): RoomPreviewViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt
index e29c197ab8..a692eebe40 100644
--- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt
+++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt
@@ -80,8 +80,6 @@ class RoomMemberProfileController @Inject constructor(
action = { callback?.onIgnoreClicked() }
)
if (!state.isMine) {
- buildProfileSection(stringProvider.getString(R.string.room_profile_section_more))
-
buildProfileAction(
id = "direct",
editable = false,
diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt
index 8211a05127..0556b9d2d6 100644
--- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt
@@ -25,8 +25,9 @@ import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.R
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.StringProvider
@@ -61,7 +62,7 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v
private val session: Session)
: VectorViewModel(initialState) {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: RoomMemberProfileViewState): RoomMemberProfileViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt
index 0f1ac3bfea..7d31a5c811 100644
--- a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt
@@ -22,8 +22,9 @@ import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.di.HasScreenInjector
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
@@ -48,7 +49,7 @@ class DeviceListBottomSheetViewModel @AssistedInject constructor(@Assisted priva
private val session: Session)
: VectorViewModel(initialState) {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: DeviceListViewState, args: DeviceListBottomSheet.Args): DeviceListBottomSheetViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt
index cfdf1d9c0b..02a648287f 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt
@@ -21,8 +21,9 @@ import androidx.lifecycle.viewModelScope
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.R
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
@@ -49,7 +50,7 @@ class RoomProfileViewModel @AssistedInject constructor(
private val session: Session
) : VectorViewModel(initialState) {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: RoomProfileViewState): RoomProfileViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt
index f470eeefc2..8832c9f7d8 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt
@@ -24,8 +24,9 @@ import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.powerlevel.PowerLevelsObservableFactory
@@ -44,7 +45,7 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo
private val session: Session)
: VectorViewModel(initialState) {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: RoomAliasViewState): RoomAliasViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetViewModel.kt
index 7f723cae53..e762b52025 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetViewModel.kt
@@ -18,8 +18,9 @@ package im.vector.app.features.roomprofile.alias.detail
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.platform.EmptyAction
import im.vector.app.core.platform.EmptyViewEvents
import im.vector.app.core.platform.VectorViewModel
@@ -30,7 +31,7 @@ class RoomAliasBottomSheetViewModel @AssistedInject constructor(
session: Session
) : VectorViewModel(initialState) {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: RoomAliasBottomSheetState): RoomAliasBottomSheetViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt
index 0cecd22fa0..5663392c6c 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt
@@ -20,8 +20,9 @@ import androidx.lifecycle.viewModelScope
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.R
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
@@ -47,7 +48,7 @@ class RoomBannedMemberListViewModel @AssistedInject constructor(@Assisted initia
private val session: Session)
: VectorViewModel(initialState) {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: RoomBannedMemberListViewState): RoomBannedMemberListViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt
index 9f15e62b3b..de983537fc 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt
@@ -20,8 +20,9 @@ import androidx.lifecycle.viewModelScope
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.EmptyViewEvents
import im.vector.app.core.platform.VectorViewModel
@@ -53,7 +54,7 @@ class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState
private val session: Session)
: VectorViewModel(initialState) {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: RoomMemberListViewState): RoomMemberListViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt
index 183488a5e0..626c20010c 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt
@@ -21,8 +21,9 @@ import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.powerlevel.PowerLevelsObservableFactory
@@ -39,7 +40,7 @@ class RoomPermissionsViewModel @AssistedInject constructor(@Assisted initialStat
private val session: Session)
: VectorViewModel(initialState) {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: RoomPermissionsViewState): RoomPermissionsViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt
index 48ff38f92e..bf227ea5e8 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt
@@ -20,8 +20,9 @@ import androidx.core.net.toFile
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.powerlevel.PowerLevelsObservableFactory
@@ -45,7 +46,7 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState:
private val session: Session)
: VectorViewModel(initialState) {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: RoomSettingsViewState): RoomSettingsViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt
index b62b633a36..cdf139c7f6 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt
@@ -24,8 +24,9 @@ import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
import kotlinx.coroutines.launch
@@ -41,7 +42,7 @@ class RoomUploadsViewModel @AssistedInject constructor(
private val session: Session
) : VectorViewModel(initialState) {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: RoomUploadsViewState): RoomUploadsViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/media/RoomUploadsMediaFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/media/RoomUploadsMediaFragment.kt
index 84a419c6c6..fe6dc86165 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/media/RoomUploadsMediaFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/media/RoomUploadsMediaFragment.kt
@@ -16,6 +16,7 @@
package im.vector.app.features.roomprofile.uploads.media
+import android.os.Build
import android.os.Bundle
import android.util.DisplayMetrics
import android.view.LayoutInflater
@@ -78,9 +79,14 @@ class RoomUploadsMediaFragment @Inject constructor(
controller.listener = this
}
+ @Suppress("DEPRECATION")
private fun getNumberOfColumns(): Int {
val displayMetrics = DisplayMetrics()
- requireActivity().windowManager.defaultDisplay.getMetrics(displayMetrics)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ requireContext().display?.getMetrics(displayMetrics)
+ } else {
+ requireActivity().windowManager.defaultDisplay.getMetrics(displayMetrics)
+ }
return dimensionConverter.pxToDp(displayMetrics.widthPixels) / IMAGE_SIZE_DP
}
diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsSecurityPrivacyFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsSecurityPrivacyFragment.kt
index 727a6f765e..c12df073ee 100644
--- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsSecurityPrivacyFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsSecurityPrivacyFragment.kt
@@ -311,10 +311,6 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor(
}
mCrossSigningStatePreference.isVisible = true
- if (!vectorPreferences.developerMode()) {
- // When not in developer mode, intercept click on this preference
- mCrossSigningStatePreference.onPreferenceClickListener = Preference.OnPreferenceClickListener { true }
- }
}
private val saveMegolmStartForActivityResult = registerStartForActivityResult {
diff --git a/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountAction.kt b/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountAction.kt
new file mode 100644
index 0000000000..c3fa844805
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountAction.kt
@@ -0,0 +1,28 @@
+/*
+ * 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.settings.account.deactivation
+
+import im.vector.app.core.platform.VectorViewModelAction
+
+sealed class DeactivateAccountAction : VectorViewModelAction {
+ object TogglePassword : DeactivateAccountAction()
+ data class DeactivateAccount(val eraseAllData: Boolean) : DeactivateAccountAction()
+
+ object SsoAuthDone: DeactivateAccountAction()
+ data class PasswordAuthDone(val password: String): DeactivateAccountAction()
+ object ReAuthCancelled: DeactivateAccountAction()
+}
diff --git a/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountFragment.kt b/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountFragment.kt
index 3d128eb755..2cc80bfa23 100644
--- a/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountFragment.kt
@@ -16,6 +16,7 @@
package im.vector.app.features.settings.account.deactivation
+import android.app.Activity
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
@@ -23,16 +24,16 @@ import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import com.airbnb.mvrx.fragmentViewModel
-import com.airbnb.mvrx.withState
-import com.jakewharton.rxbinding3.widget.textChanges
import im.vector.app.R
import im.vector.app.core.extensions.exhaustive
-import im.vector.app.core.extensions.showPassword
+import im.vector.app.core.extensions.registerStartForActivityResult
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.FragmentDeactivateAccountBinding
import im.vector.app.features.MainActivity
import im.vector.app.features.MainActivityArgs
+import im.vector.app.features.auth.ReAuthActivity
import im.vector.app.features.settings.VectorSettingsActivity
+import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
import javax.inject.Inject
@@ -46,6 +47,25 @@ class DeactivateAccountFragment @Inject constructor(
return FragmentDeactivateAccountBinding.inflate(inflater, container, false)
}
+ private val reAuthActivityResultLauncher = registerStartForActivityResult { activityResult ->
+ if (activityResult.resultCode == Activity.RESULT_OK) {
+ when (activityResult.data?.extras?.getString(ReAuthActivity.RESULT_FLOW_TYPE)) {
+ LoginFlowTypes.SSO -> {
+ viewModel.handle(DeactivateAccountAction.SsoAuthDone)
+ }
+ LoginFlowTypes.PASSWORD -> {
+ val password = activityResult.data?.extras?.getString(ReAuthActivity.RESULT_VALUE) ?: ""
+ viewModel.handle(DeactivateAccountAction.PasswordAuthDone(password))
+ }
+ else -> {
+ viewModel.handle(DeactivateAccountAction.ReAuthCancelled)
+ }
+ }
+ } else {
+ viewModel.handle(DeactivateAccountAction.ReAuthCancelled)
+ }
+ }
+
override fun onResume() {
super.onResume()
(activity as? AppCompatActivity)?.supportActionBar?.setTitle(R.string.deactivate_account_title)
@@ -66,59 +86,46 @@ class DeactivateAccountFragment @Inject constructor(
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- setupUi()
setupViewListeners()
observeViewEvents()
}
- private fun setupUi() {
- views.deactivateAccountPassword.textChanges()
- .subscribe {
- views.deactivateAccountPasswordTil.error = null
- views.deactivateAccountSubmit.isEnabled = it.isNotEmpty()
- }
- .disposeOnDestroyView()
- }
-
private fun setupViewListeners() {
- views.deactivateAccountPasswordReveal.setOnClickListener {
- viewModel.handle(DeactivateAccountAction.TogglePassword)
- }
-
views.deactivateAccountSubmit.debouncedClicks {
viewModel.handle(DeactivateAccountAction.DeactivateAccount(
- views.deactivateAccountPassword.text.toString(),
- views.deactivateAccountEraseCheckbox.isChecked))
+ views.deactivateAccountEraseCheckbox.isChecked)
+ )
}
}
private fun observeViewEvents() {
viewModel.observeViewEvents {
when (it) {
- is DeactivateAccountViewEvents.Loading -> {
+ is DeactivateAccountViewEvents.Loading -> {
settingsActivity?.ignoreInvalidTokenError = true
showLoadingDialog(it.message)
}
- DeactivateAccountViewEvents.EmptyPassword -> {
+ DeactivateAccountViewEvents.InvalidAuth -> {
+ dismissLoadingDialog()
settingsActivity?.ignoreInvalidTokenError = false
- views.deactivateAccountPasswordTil.error = getString(R.string.error_empty_field_your_password)
}
- DeactivateAccountViewEvents.InvalidPassword -> {
- settingsActivity?.ignoreInvalidTokenError = false
- views.deactivateAccountPasswordTil.error = getString(R.string.settings_fail_to_update_password_invalid_current_password)
- }
- is DeactivateAccountViewEvents.OtherFailure -> {
+ is DeactivateAccountViewEvents.OtherFailure -> {
settingsActivity?.ignoreInvalidTokenError = false
+ dismissLoadingDialog()
displayErrorDialog(it.throwable)
}
- DeactivateAccountViewEvents.Done ->
+ DeactivateAccountViewEvents.Done -> {
MainActivity.restartApp(requireActivity(), MainActivityArgs(clearCredentials = true, isAccountDeactivated = true))
+ }
+ is DeactivateAccountViewEvents.RequestReAuth -> {
+ ReAuthActivity.newIntent(requireContext(),
+ it.registrationFlowResponse,
+ it.lastErrorCode,
+ getString(R.string.deactivate_account_title)).let { intent ->
+ reAuthActivityResultLauncher.launch(intent)
+ }
+ }
}.exhaustive
}
}
-
- override fun invalidate() = withState(viewModel) { state ->
- views.deactivateAccountPassword.showPassword(state.passwordShown)
- views.deactivateAccountPasswordReveal.setImageResource(if (state.passwordShown) R.drawable.ic_eye_closed else R.drawable.ic_eye)
- }
}
diff --git a/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountViewEvents.kt b/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountViewEvents.kt
index 46acb4aee4..1b0ec2de0c 100644
--- a/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountViewEvents.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountViewEvents.kt
@@ -17,14 +17,15 @@
package im.vector.app.features.settings.account.deactivation
import im.vector.app.core.platform.VectorViewEvents
+import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
/**
* Transient events for deactivate account settings screen
*/
sealed class DeactivateAccountViewEvents : VectorViewEvents {
data class Loading(val message: CharSequence? = null) : DeactivateAccountViewEvents()
- object EmptyPassword : DeactivateAccountViewEvents()
- object InvalidPassword : DeactivateAccountViewEvents()
+ object InvalidAuth : DeactivateAccountViewEvents()
data class OtherFailure(val throwable: Throwable) : DeactivateAccountViewEvents()
object Done : DeactivateAccountViewEvents()
+ data class RequestReAuth(val registrationFlowResponse: RegistrationFlowResponse, val lastErrorCode: String?) : DeactivateAccountViewEvents()
}
diff --git a/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountViewModel.kt
index 211559c657..49cb75c9d6 100644
--- a/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountViewModel.kt
@@ -20,38 +20,69 @@ import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
-import im.vector.app.core.platform.VectorViewModelAction
+import im.vector.app.features.auth.ReAuthActivity
import kotlinx.coroutines.launch
-import org.matrix.android.sdk.api.failure.isInvalidPassword
+import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
+import org.matrix.android.sdk.api.failure.isInvalidUIAAuth
import org.matrix.android.sdk.api.session.Session
-import java.lang.Exception
+import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
+import org.matrix.android.sdk.internal.crypto.crosssigning.fromBase64
+import org.matrix.android.sdk.internal.crypto.model.rest.DefaultBaseAuth
+import org.matrix.android.sdk.api.auth.UIABaseAuth
+import org.matrix.android.sdk.api.auth.UserPasswordAuth
+import timber.log.Timber
+import kotlin.coroutines.Continuation
+import kotlin.coroutines.resume
data class DeactivateAccountViewState(
val passwordShown: Boolean = false
) : MvRxState
-sealed class DeactivateAccountAction : VectorViewModelAction {
- object TogglePassword : DeactivateAccountAction()
- data class DeactivateAccount(val password: String, val eraseAllData: Boolean) : DeactivateAccountAction()
-}
-
class DeactivateAccountViewModel @AssistedInject constructor(@Assisted private val initialState: DeactivateAccountViewState,
private val session: Session)
: VectorViewModel(initialState) {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: DeactivateAccountViewState): DeactivateAccountViewModel
}
+ var uiaContinuation: Continuation? = null
+ var pendingAuth: UIABaseAuth? = null
+
override fun handle(action: DeactivateAccountAction) {
when (action) {
- DeactivateAccountAction.TogglePassword -> handleTogglePassword()
+ DeactivateAccountAction.TogglePassword -> handleTogglePassword()
is DeactivateAccountAction.DeactivateAccount -> handleDeactivateAccount(action)
+ DeactivateAccountAction.SsoAuthDone -> {
+ Timber.d("## UIA - FallBack success")
+ if (pendingAuth != null) {
+ uiaContinuation?.resume(pendingAuth!!)
+ } else {
+ uiaContinuation?.resumeWith(Result.failure((IllegalArgumentException())))
+ }
+ }
+ is DeactivateAccountAction.PasswordAuthDone -> {
+ val decryptedPass = session.loadSecureSecret(action.password.fromBase64().inputStream(), ReAuthActivity.DEFAULT_RESULT_KEYSTORE_ALIAS)
+ uiaContinuation?.resume(
+ UserPasswordAuth(
+ session = pendingAuth?.session,
+ password = decryptedPass,
+ user = session.myUserId
+ )
+ )
+ }
+ DeactivateAccountAction.ReAuthCancelled -> {
+ Timber.d("## UIA - Reauth cancelled")
+ uiaContinuation?.resumeWith(Result.failure((Exception())))
+ uiaContinuation = null
+ pendingAuth = null
+ }
}.exhaustive
}
@@ -62,20 +93,22 @@ class DeactivateAccountViewModel @AssistedInject constructor(@Assisted private v
}
private fun handleDeactivateAccount(action: DeactivateAccountAction.DeactivateAccount) {
- if (action.password.isEmpty()) {
- _viewEvents.post(DeactivateAccountViewEvents.EmptyPassword)
- return
- }
-
_viewEvents.post(DeactivateAccountViewEvents.Loading())
viewModelScope.launch {
val event = try {
- session.deactivateAccount(action.password, action.eraseAllData)
+ session.deactivateAccount(
+ object : UserInteractiveAuthInterceptor {
+ override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) {
+ _viewEvents.post(DeactivateAccountViewEvents.RequestReAuth(flowResponse, errCode))
+ pendingAuth = DefaultBaseAuth(session = flowResponse.session)
+ uiaContinuation = promise
+ }
+ }, action.eraseAllData)
DeactivateAccountViewEvents.Done
} catch (failure: Exception) {
- if (failure.isInvalidPassword()) {
- DeactivateAccountViewEvents.InvalidPassword
+ if (failure.isInvalidUIAAuth()) {
+ DeactivateAccountViewEvents.InvalidAuth
} else {
DeactivateAccountViewEvents.OtherFailure(failure)
}
diff --git a/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsAction.kt b/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsAction.kt
index af6ca9f4b7..735c456ff9 100644
--- a/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsAction.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsAction.kt
@@ -18,4 +18,9 @@ package im.vector.app.features.settings.crosssigning
import im.vector.app.core.platform.VectorViewModelAction
-sealed class CrossSigningSettingsAction : VectorViewModelAction
+sealed class CrossSigningSettingsAction : VectorViewModelAction {
+ object InitializeCrossSigning: CrossSigningSettingsAction()
+ object SsoAuthDone: CrossSigningSettingsAction()
+ data class PasswordAuthDone(val password: String): CrossSigningSettingsAction()
+ object ReAuthCancelled: CrossSigningSettingsAction()
+}
diff --git a/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsController.kt b/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsController.kt
index 82279a3906..6425256929 100644
--- a/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsController.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsController.kt
@@ -19,8 +19,11 @@ import com.airbnb.epoxy.TypedEpoxyController
import im.vector.app.R
import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.StringProvider
+import im.vector.app.core.ui.list.genericButtonItem
import im.vector.app.core.ui.list.genericItem
import im.vector.app.core.ui.list.genericItemWithValue
+import im.vector.app.core.ui.list.genericPositiveButtonItem
+import im.vector.app.core.utils.DebouncedClickListener
import im.vector.app.core.utils.DimensionConverter
import me.gujun.android.span.span
import javax.inject.Inject
@@ -31,7 +34,9 @@ class CrossSigningSettingsController @Inject constructor(
private val dimensionConverter: DimensionConverter
) : TypedEpoxyController() {
- interface InteractionListener
+ interface InteractionListener {
+ fun didTapInitializeCrossSigning()
+ }
var interactionListener: InteractionListener? = null
@@ -44,6 +49,13 @@ class CrossSigningSettingsController @Inject constructor(
titleIconResourceId(R.drawable.ic_shield_trusted)
title(stringProvider.getString(R.string.encryption_information_dg_xsigning_complete))
}
+ genericButtonItem {
+ id("Reset")
+ text(stringProvider.getString(R.string.reset_cross_signing))
+ buttonClickAction(DebouncedClickListener({
+ interactionListener?.didTapInitializeCrossSigning()
+ }))
+ }
}
data.xSigningKeysAreTrusted -> {
genericItem {
@@ -51,6 +63,13 @@ class CrossSigningSettingsController @Inject constructor(
titleIconResourceId(R.drawable.ic_shield_custom)
title(stringProvider.getString(R.string.encryption_information_dg_xsigning_trusted))
}
+ genericButtonItem {
+ id("Reset")
+ text(stringProvider.getString(R.string.reset_cross_signing))
+ buttonClickAction(DebouncedClickListener({
+ interactionListener?.didTapInitializeCrossSigning()
+ }))
+ }
}
data.xSigningIsEnableInAccount -> {
genericItem {
@@ -58,12 +77,27 @@ class CrossSigningSettingsController @Inject constructor(
titleIconResourceId(R.drawable.ic_shield_black)
title(stringProvider.getString(R.string.encryption_information_dg_xsigning_not_trusted))
}
+ genericButtonItem {
+ id("Reset")
+ text(stringProvider.getString(R.string.reset_cross_signing))
+ buttonClickAction(DebouncedClickListener({
+ interactionListener?.didTapInitializeCrossSigning()
+ }))
+ }
}
else -> {
genericItem {
id("not")
title(stringProvider.getString(R.string.encryption_information_dg_xsigning_disabled))
}
+
+ genericPositiveButtonItem {
+ id("Initialize")
+ text(stringProvider.getString(R.string.initialize_cross_signing))
+ buttonClickAction(DebouncedClickListener({
+ interactionListener?.didTapInitializeCrossSigning()
+ }))
+ }
}
}
diff --git a/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsFragment.kt b/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsFragment.kt
index 63611efae5..80e44174ff 100644
--- a/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsFragment.kt
@@ -15,20 +15,26 @@
*/
package im.vector.app.features.settings.crosssigning
+import android.app.Activity
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
+import androidx.core.view.isVisible
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
import im.vector.app.R
import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith
import im.vector.app.core.extensions.exhaustive
+import im.vector.app.core.extensions.registerStartForActivityResult
+import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.FragmentGenericRecyclerBinding
+import im.vector.app.features.auth.ReAuthActivity
+import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
import javax.inject.Inject
@@ -47,19 +53,55 @@ class CrossSigningSettingsFragment @Inject constructor(
private val viewModel: CrossSigningSettingsViewModel by fragmentViewModel()
+ private val reAuthActivityResultLauncher = registerStartForActivityResult { activityResult ->
+ if (activityResult.resultCode == Activity.RESULT_OK) {
+ when (activityResult.data?.extras?.getString(ReAuthActivity.RESULT_FLOW_TYPE)) {
+ LoginFlowTypes.SSO -> {
+ viewModel.handle(CrossSigningSettingsAction.SsoAuthDone)
+ }
+ LoginFlowTypes.PASSWORD -> {
+ val password = activityResult.data?.extras?.getString(ReAuthActivity.RESULT_VALUE) ?: ""
+ viewModel.handle(CrossSigningSettingsAction.PasswordAuthDone(password))
+ }
+ else -> {
+ viewModel.handle(CrossSigningSettingsAction.ReAuthCancelled)
+ }
+ }
+// activityResult.data?.extras?.getString(ReAuthActivity.RESULT_TOKEN)?.let { token ->
+// }
+ } else {
+ viewModel.handle(CrossSigningSettingsAction.ReAuthCancelled)
+ }
+ }
+
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupRecyclerView()
- viewModel.observeViewEvents {
- when (it) {
+ viewModel.observeViewEvents { event ->
+ when (event) {
is CrossSigningSettingsViewEvents.Failure -> {
AlertDialog.Builder(requireContext())
.setTitle(R.string.dialog_title_error)
- .setMessage(errorFormatter.toHumanReadable(it.throwable))
+ .setMessage(errorFormatter.toHumanReadable(event.throwable))
.setPositiveButton(R.string.ok, null)
.show()
Unit
}
+ is CrossSigningSettingsViewEvents.RequestReAuth -> {
+ ReAuthActivity.newIntent(requireContext(),
+ event.registrationFlowResponse,
+ event.lastErrorCode,
+ getString(R.string.initialize_cross_signing)).let { intent ->
+ reAuthActivityResultLauncher.launch(intent)
+ }
+ }
+ is CrossSigningSettingsViewEvents.ShowModalWaitingView -> {
+ views.waitingView.waitingView.isVisible = true
+ views.waitingView.waitingStatusText.setTextOrHide(event.status)
+ }
+ CrossSigningSettingsViewEvents.HideModalWaitingView -> {
+ views.waitingView.waitingView.isVisible = false
+ }
}.exhaustive
}
}
@@ -83,4 +125,8 @@ class CrossSigningSettingsFragment @Inject constructor(
controller.interactionListener = null
super.onDestroyView()
}
+
+ override fun didTapInitializeCrossSigning() {
+ viewModel.handle(CrossSigningSettingsAction.InitializeCrossSigning)
+ }
}
diff --git a/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewEvents.kt b/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewEvents.kt
index b81a321f3f..1c11560d40 100644
--- a/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewEvents.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewEvents.kt
@@ -17,10 +17,14 @@
package im.vector.app.features.settings.crosssigning
import im.vector.app.core.platform.VectorViewEvents
+import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
/**
* Transient events for cross signing settings screen
*/
sealed class CrossSigningSettingsViewEvents : VectorViewEvents {
data class Failure(val throwable: Throwable) : CrossSigningSettingsViewEvents()
+ data class RequestReAuth(val registrationFlowResponse: RegistrationFlowResponse, val lastErrorCode: String?) : CrossSigningSettingsViewEvents()
+ data class ShowModalWaitingView(val status: String?) : CrossSigningSettingsViewEvents()
+ object HideModalWaitingView : CrossSigningSettingsViewEvents()
}
diff --git a/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewModel.kt
index 3f70ac1fd7..04100056aa 100644
--- a/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewModel.kt
@@ -15,24 +15,48 @@
*/
package im.vector.app.features.settings.crosssigning
+import androidx.lifecycle.viewModelScope
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import im.vector.app.R
+import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
+import im.vector.app.core.resources.StringProvider
+import im.vector.app.features.auth.ReAuthActivity
+import im.vector.app.features.login.ReAuthHelper
import io.reactivex.Observable
import io.reactivex.functions.BiFunction
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import org.matrix.android.sdk.api.auth.UIABaseAuth
+import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
+import org.matrix.android.sdk.api.auth.UserPasswordAuth
+import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
+import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
+import org.matrix.android.sdk.api.auth.registration.nextUncompletedStage
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo
import org.matrix.android.sdk.api.util.Optional
+import org.matrix.android.sdk.internal.crypto.crosssigning.fromBase64
import org.matrix.android.sdk.internal.crypto.crosssigning.isVerified
+import org.matrix.android.sdk.internal.crypto.model.rest.DefaultBaseAuth
import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo
+import org.matrix.android.sdk.internal.util.awaitCallback
import org.matrix.android.sdk.rx.rx
+import timber.log.Timber
+import kotlin.coroutines.Continuation
+import kotlin.coroutines.resume
-class CrossSigningSettingsViewModel @AssistedInject constructor(@Assisted private val initialState: CrossSigningSettingsViewState,
- private val session: Session)
- : VectorViewModel(initialState) {
+class CrossSigningSettingsViewModel @AssistedInject constructor(
+ @Assisted private val initialState: CrossSigningSettingsViewState,
+ private val session: Session,
+ private val reAuthHelper: ReAuthHelper,
+ private val stringProvider: StringProvider
+) : VectorViewModel(initialState) {
init {
Observable.combineLatest, Optional, Pair, Optional>>(
@@ -57,15 +81,82 @@ class CrossSigningSettingsViewModel @AssistedInject constructor(@Assisted privat
}
}
- @AssistedInject.Factory
+ var uiaContinuation: Continuation? = null
+ var pendingAuth: UIABaseAuth? = null
+
+ @AssistedFactory
interface Factory {
fun create(initialState: CrossSigningSettingsViewState): CrossSigningSettingsViewModel
}
override fun handle(action: CrossSigningSettingsAction) {
- // No op for the moment
- // when (action) {
- // }.exhaustive
+ when (action) {
+ CrossSigningSettingsAction.InitializeCrossSigning -> {
+ _viewEvents.post(CrossSigningSettingsViewEvents.ShowModalWaitingView(null))
+ viewModelScope.launch(Dispatchers.IO) {
+ try {
+ awaitCallback {
+ session.cryptoService().crossSigningService().initializeCrossSigning(
+ object : UserInteractiveAuthInterceptor {
+ override fun performStage(flowResponse: RegistrationFlowResponse,
+ errCode: String?,
+ promise: Continuation) {
+ Timber.d("## UIA : initializeCrossSigning UIA")
+ if (flowResponse.nextUncompletedStage() == LoginFlowTypes.PASSWORD
+ && reAuthHelper.data != null && errCode == null) {
+ UserPasswordAuth(
+ session = null,
+ user = session.myUserId,
+ password = reAuthHelper.data
+ ).let { promise.resume(it) }
+ } else {
+ Timber.d("## UIA : initializeCrossSigning UIA > start reauth activity")
+ _viewEvents.post(CrossSigningSettingsViewEvents.RequestReAuth(flowResponse, errCode))
+ pendingAuth = DefaultBaseAuth(session = flowResponse.session)
+ uiaContinuation = promise
+ }
+ }
+ }, it)
+ }
+ } catch (failure: Throwable) {
+ handleInitializeXSigningError(failure)
+ } finally {
+ _viewEvents.post(CrossSigningSettingsViewEvents.HideModalWaitingView)
+ }
+ }
+ Unit
+ }
+ is CrossSigningSettingsAction.SsoAuthDone -> {
+ Timber.d("## UIA - FallBack success")
+ if (pendingAuth != null) {
+ uiaContinuation?.resume(pendingAuth!!)
+ } else {
+ uiaContinuation?.resumeWith(Result.failure((IllegalArgumentException())))
+ }
+ }
+ is CrossSigningSettingsAction.PasswordAuthDone -> {
+ val decryptedPass = session.loadSecureSecret(action.password.fromBase64().inputStream(), ReAuthActivity.DEFAULT_RESULT_KEYSTORE_ALIAS)
+ uiaContinuation?.resume(
+ UserPasswordAuth(
+ session = pendingAuth?.session,
+ password = decryptedPass,
+ user = session.myUserId
+ )
+ )
+ }
+ CrossSigningSettingsAction.ReAuthCancelled -> {
+ Timber.d("## UIA - Reauth cancelled")
+ _viewEvents.post(CrossSigningSettingsViewEvents.HideModalWaitingView)
+ uiaContinuation?.resumeWith(Result.failure((Exception())))
+ uiaContinuation = null
+ pendingAuth = null
+ }
+ }.exhaustive
+ }
+
+ private fun handleInitializeXSigningError(failure: Throwable) {
+ Timber.e(failure, "## CrossSigning - Failed to initialize cross signing")
+ _viewEvents.post(CrossSigningSettingsViewEvents.Failure(Exception(stringProvider.getString(R.string.failed_to_initialize_cross_signing))))
}
companion object : MvRxViewModelFactory {
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewModel.kt
index ad840d2efb..ee5b0a6092 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewModel.kt
@@ -19,8 +19,9 @@ import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.platform.EmptyAction
import im.vector.app.core.platform.EmptyViewEvents
import im.vector.app.core.platform.VectorViewModel
@@ -33,7 +34,7 @@ class DeviceVerificationInfoBottomSheetViewModel @AssistedInject constructor(@As
val session: Session
) : VectorViewModel(initialState) {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: DeviceVerificationInfoBottomSheetViewState, deviceId: String): DeviceVerificationInfoBottomSheetViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesAction.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesAction.kt
index 2b0991ab4e..46a476c270 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesAction.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesAction.kt
@@ -22,7 +22,7 @@ import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
sealed class DevicesAction : VectorViewModelAction {
object Refresh : DevicesAction()
data class Delete(val deviceId: String) : DevicesAction()
- data class Password(val password: String) : DevicesAction()
+// data class Password(val password: String) : DevicesAction()
data class Rename(val deviceId: String, val newName: String) : DevicesAction()
data class PromptRename(val deviceId: String) : DevicesAction()
@@ -30,4 +30,8 @@ sealed class DevicesAction : VectorViewModelAction {
data class VerifyMyDeviceManually(val deviceId: String) : DevicesAction()
object CompleteSecurity : DevicesAction()
data class MarkAsManuallyVerified(val cryptoDeviceInfo: CryptoDeviceInfo) : DevicesAction()
+
+ object SsoAuthDone: DevicesAction()
+ data class PasswordAuthDone(val password: String): DevicesAction()
+ object ReAuthCancelled: DevicesAction()
}
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewEvents.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewEvents.kt
index 60d7491603..8535c698a7 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewEvents.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewEvents.kt
@@ -19,6 +19,7 @@ package im.vector.app.features.settings.devices
import im.vector.app.core.platform.VectorViewEvents
import org.matrix.android.sdk.api.session.Session
+import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo
@@ -27,9 +28,12 @@ import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo
*/
sealed class DevicesViewEvents : VectorViewEvents {
data class Loading(val message: CharSequence? = null) : DevicesViewEvents()
+// object HideLoading : DevicesViewEvents()
data class Failure(val throwable: Throwable) : DevicesViewEvents()
- object RequestPassword : DevicesViewEvents()
+// object RequestPassword : DevicesViewEvents()
+
+ data class RequestReAuth(val registrationFlowResponse: RegistrationFlowResponse, val lastErrorCode: String?) : DevicesViewEvents()
data class PromptRenameDevice(val deviceInfo: DeviceInfo) : DevicesViewEvents()
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt
index 8c5762afce..b91b5255b6 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt
@@ -26,16 +26,22 @@ import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
-import im.vector.app.core.error.SsoFlowNotSupportedYet
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import im.vector.app.R
import im.vector.app.core.platform.VectorViewModel
+import im.vector.app.core.resources.StringProvider
+import im.vector.app.features.auth.ReAuthActivity
+import im.vector.app.features.login.ReAuthHelper
import io.reactivex.Observable
import io.reactivex.functions.BiFunction
import io.reactivex.subjects.PublishSubject
+import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.NoOpMatrixCallback
+import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.session.Session
@@ -43,13 +49,22 @@ import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod
import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState
+import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
+import org.matrix.android.sdk.api.auth.registration.nextUncompletedStage
import org.matrix.android.sdk.internal.crypto.crosssigning.DeviceTrustLevel
+import org.matrix.android.sdk.internal.crypto.crosssigning.fromBase64
import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
+import org.matrix.android.sdk.internal.crypto.model.rest.DefaultBaseAuth
import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo
+import org.matrix.android.sdk.api.auth.UIABaseAuth
+import org.matrix.android.sdk.api.auth.UserPasswordAuth
import org.matrix.android.sdk.internal.util.awaitCallback
import org.matrix.android.sdk.rx.rx
import timber.log.Timber
import java.util.concurrent.TimeUnit
+import javax.net.ssl.HttpsURLConnection
+import kotlin.coroutines.Continuation
+import kotlin.coroutines.resume
data class DevicesViewState(
val myDeviceId: String = "",
@@ -69,10 +84,15 @@ data class DeviceFullInfo(
class DevicesViewModel @AssistedInject constructor(
@Assisted initialState: DevicesViewState,
- private val session: Session
+ private val session: Session,
+ private val reAuthHelper: ReAuthHelper,
+ private val stringProvider: StringProvider
) : VectorViewModel(initialState), VerificationService.Listener {
- @AssistedInject.Factory
+ var uiaContinuation: Continuation? = null
+ var pendingAuth: UIABaseAuth? = null
+
+ @AssistedFactory
interface Factory {
fun create(initialState: DevicesViewState): DevicesViewModel
}
@@ -86,10 +106,6 @@ class DevicesViewModel @AssistedInject constructor(
}
}
- // temp storage when we ask for the user password
- private var _currentDeviceId: String? = null
- private var _currentSession: String? = null
-
private val refreshPublisher: PublishSubject = PublishSubject.create()
init {
@@ -188,13 +204,43 @@ class DevicesViewModel @AssistedInject constructor(
return when (action) {
is DevicesAction.Refresh -> queryRefreshDevicesList()
is DevicesAction.Delete -> handleDelete(action)
- is DevicesAction.Password -> handlePassword(action)
is DevicesAction.Rename -> handleRename(action)
is DevicesAction.PromptRename -> handlePromptRename(action)
is DevicesAction.VerifyMyDevice -> handleInteractiveVerification(action)
is DevicesAction.CompleteSecurity -> handleCompleteSecurity()
is DevicesAction.MarkAsManuallyVerified -> handleVerifyManually(action)
is DevicesAction.VerifyMyDeviceManually -> handleShowDeviceCryptoInfo(action)
+ is DevicesAction.SsoAuthDone -> {
+ // we should use token based auth
+ // _viewEvents.post(CrossSigningSettingsViewEvents.ShowModalWaitingView(null))
+ // will release the interactive auth interceptor
+ Timber.d("## UIA - FallBack success $pendingAuth , continuation: $uiaContinuation")
+ if (pendingAuth != null) {
+ uiaContinuation?.resume(pendingAuth!!)
+ } else {
+ uiaContinuation?.resumeWith(Result.failure((IllegalArgumentException())))
+ }
+ Unit
+ }
+ is DevicesAction.PasswordAuthDone -> {
+ val decryptedPass = session.loadSecureSecret(action.password.fromBase64().inputStream(), ReAuthActivity.DEFAULT_RESULT_KEYSTORE_ALIAS)
+ uiaContinuation?.resume(
+ UserPasswordAuth(
+ session = pendingAuth?.session,
+ password = decryptedPass,
+ user = session.myUserId
+ )
+ )
+ Unit
+ }
+ DevicesAction.ReAuthCancelled -> {
+ Timber.d("## UIA - Reauth cancelled")
+// _viewEvents.post(DevicesViewEvents.Loading)
+ uiaContinuation?.resumeWith(Result.failure((Exception())))
+ uiaContinuation = null
+ pendingAuth = null
+ Unit
+ }
}
}
@@ -284,95 +330,48 @@ class DevicesViewModel @AssistedInject constructor(
)
}
- session.cryptoService().deleteDevice(deviceId, object : MatrixCallback {
- override fun onFailure(failure: Throwable) {
- var isPasswordRequestFound = false
-
- if (failure is Failure.RegistrationFlowError) {
- // We only support LoginFlowTypes.PASSWORD
- // Check if we can provide the user password
- failure.registrationFlowResponse.flows?.forEach { interactiveAuthenticationFlow ->
- isPasswordRequestFound = isPasswordRequestFound || interactiveAuthenticationFlow.stages?.any { it == LoginFlowTypes.PASSWORD } == true
- }
-
- if (isPasswordRequestFound) {
- _currentDeviceId = deviceId
- _currentSession = failure.registrationFlowResponse.session
-
- setState {
- copy(
- request = Success(Unit)
- )
+ viewModelScope.launch(Dispatchers.IO) {
+ try {
+ awaitCallback {
+ session.cryptoService().deleteDevice(deviceId, object : UserInteractiveAuthInterceptor {
+ override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) {
+ Timber.d("## UIA : deleteDevice UIA")
+ if (flowResponse.nextUncompletedStage() == LoginFlowTypes.PASSWORD && reAuthHelper.data != null && errCode == null) {
+ UserPasswordAuth(
+ session = null,
+ user = session.myUserId,
+ password = reAuthHelper.data
+ ).let { promise.resume(it) }
+ } else {
+ Timber.d("## UIA : deleteDevice UIA > start reauth activity")
+ _viewEvents.post(DevicesViewEvents.RequestReAuth(flowResponse, errCode))
+ pendingAuth = DefaultBaseAuth(session = flowResponse.session)
+ uiaContinuation = promise
+ }
}
-
- _viewEvents.post(DevicesViewEvents.RequestPassword)
- }
+ }, it)
}
-
- if (!isPasswordRequestFound) {
- // LoginFlowTypes.PASSWORD not supported, and this is the only one Element supports so far...
- setState {
- copy(
- request = Fail(failure)
- )
- }
-
- _viewEvents.post(DevicesViewEvents.Failure(SsoFlowNotSupportedYet()))
- }
- }
-
- override fun onSuccess(data: Unit) {
setState {
copy(
- request = Success(data)
+ request = Success(Unit)
)
}
// force settings update
queryRefreshDevicesList()
- }
- })
- }
-
- private fun handlePassword(action: DevicesAction.Password) {
- val currentDeviceId = _currentDeviceId
- if (currentDeviceId.isNullOrBlank()) {
- // Abort
- return
- }
-
- setState {
- copy(
- request = Loading()
- )
- }
-
- session.cryptoService().deleteDeviceWithUserPassword(currentDeviceId, _currentSession, action.password, object : MatrixCallback {
- override fun onSuccess(data: Unit) {
- _currentDeviceId = null
- _currentSession = null
-
- setState {
- copy(
- request = Success(data)
- )
- }
- // force settings update
- queryRefreshDevicesList()
- }
-
- override fun onFailure(failure: Throwable) {
- _currentDeviceId = null
- _currentSession = null
-
- // Password is maybe not good
+ } catch (failure: Throwable) {
setState {
copy(
request = Fail(failure)
)
}
-
- _viewEvents.post(DevicesViewEvents.Failure(failure))
+ if (failure is Failure.OtherServerError && failure.httpCode == HttpsURLConnection.HTTP_UNAUTHORIZED) {
+ _viewEvents.post(DevicesViewEvents.Failure(Exception(stringProvider.getString(R.string.authentication_error))))
+ } else {
+ _viewEvents.post(DevicesViewEvents.Failure(Exception(stringProvider.getString(R.string.matrix_error))))
+ }
+ // ...
+ Timber.e(failure, "failed to delete session")
}
- })
+ }
}
}
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/VectorSettingsDevicesFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/VectorSettingsDevicesFragment.kt
index 1bf538d458..0040f73fec 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/VectorSettingsDevicesFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/VectorSettingsDevicesFragment.kt
@@ -16,6 +16,7 @@
package im.vector.app.features.settings.devices
+import android.app.Activity
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@@ -29,14 +30,16 @@ import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
import im.vector.app.R
import im.vector.app.core.dialogs.ManuallyVerifyDialog
-import im.vector.app.core.dialogs.PromptPasswordDialog
import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith
import im.vector.app.core.extensions.exhaustive
+import im.vector.app.core.extensions.registerStartForActivityResult
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.DialogBaseEditTextBinding
import im.vector.app.databinding.FragmentGenericRecyclerBinding
+import im.vector.app.features.auth.ReAuthActivity
import im.vector.app.features.crypto.verification.VerificationBottomSheet
+import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo
import javax.inject.Inject
@@ -52,7 +55,7 @@ class VectorSettingsDevicesFragment @Inject constructor(
// used to avoid requesting to enter the password for each deletion
// Note: Sonar does not like to use password for member name.
- private var mAccountPass: String = ""
+// private var mAccountPass: String = ""
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentGenericRecyclerBinding {
return FragmentGenericRecyclerBinding.inflate(inflater, container, false)
@@ -71,7 +74,7 @@ class VectorSettingsDevicesFragment @Inject constructor(
when (it) {
is DevicesViewEvents.Loading -> showLoading(it.message)
is DevicesViewEvents.Failure -> showFailure(it.throwable)
- is DevicesViewEvents.RequestPassword -> maybeShowDeleteDeviceWithPasswordDialog()
+ is DevicesViewEvents.RequestReAuth -> askForReAuthentication(it)
is DevicesViewEvents.PromptRenameDevice -> displayDeviceRenameDialog(it.deviceInfo)
is DevicesViewEvents.ShowVerifyDevice -> {
VerificationBottomSheet.withArgs(
@@ -93,13 +96,6 @@ class VectorSettingsDevicesFragment @Inject constructor(
}
}
- override fun showFailure(throwable: Throwable) {
- super.showFailure(throwable)
-
- // Password is maybe not good, for safety measure, reset it here
- mAccountPass = ""
- }
-
override fun onDestroyView() {
devicesController.callback = null
views.genericRecyclerView.cleanup()
@@ -119,14 +115,6 @@ class VectorSettingsDevicesFragment @Inject constructor(
)
}
-// override fun onDeleteDevice(deviceInfo: DeviceInfo) {
-// devicesViewModel.handle(DevicesAction.Delete(deviceInfo))
-// }
-//
-// override fun onRenameDevice(deviceInfo: DeviceInfo) {
-// displayDeviceRenameDialog(deviceInfo)
-// }
-
override fun retry() {
viewModel.handle(DevicesAction.Refresh)
}
@@ -154,17 +142,34 @@ class VectorSettingsDevicesFragment @Inject constructor(
.show()
}
- /**
- * Show a dialog to ask for user password, or use a previously entered password.
- */
- private fun maybeShowDeleteDeviceWithPasswordDialog() {
- if (mAccountPass.isNotEmpty()) {
- viewModel.handle(DevicesAction.Password(mAccountPass))
- } else {
- PromptPasswordDialog().show(requireActivity()) { password ->
- mAccountPass = password
- viewModel.handle(DevicesAction.Password(mAccountPass))
+ private val reAuthActivityResultLauncher = registerStartForActivityResult { activityResult ->
+ if (activityResult.resultCode == Activity.RESULT_OK) {
+ when (activityResult.data?.extras?.getString(ReAuthActivity.RESULT_FLOW_TYPE)) {
+ LoginFlowTypes.SSO -> {
+ viewModel.handle(DevicesAction.SsoAuthDone)
+ }
+ LoginFlowTypes.PASSWORD -> {
+ val password = activityResult.data?.extras?.getString(ReAuthActivity.RESULT_VALUE) ?: ""
+ viewModel.handle(DevicesAction.PasswordAuthDone(password))
+ }
+ else -> {
+ viewModel.handle(DevicesAction.ReAuthCancelled)
+ }
}
+ } else {
+ viewModel.handle(DevicesAction.ReAuthCancelled)
+ }
+ }
+
+ /**
+ * Launch the re auth activity to get credentials
+ */
+ private fun askForReAuthentication(reAuthReq: DevicesViewEvents.RequestReAuth) {
+ ReAuthActivity.newIntent(requireContext(),
+ reAuthReq.registrationFlowResponse,
+ reAuthReq.lastErrorCode,
+ getString(R.string.devices_delete_dialog_title)).let { intent ->
+ reAuthActivityResultLauncher.launch(intent)
}
}
diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataViewModel.kt
index af8d162f15..b2200e6a6d 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataViewModel.kt
@@ -23,8 +23,9 @@ import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.EmptyViewEvents
import im.vector.app.core.platform.VectorViewModel
@@ -63,7 +64,7 @@ class AccountDataViewModel @AssistedInject constructor(@Assisted initialState: A
}
}
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: AccountDataViewState): AccountDataViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsPaperTrailViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsPaperTrailViewModel.kt
index 4249ef09fa..325538ee5e 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsPaperTrailViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsPaperTrailViewModel.kt
@@ -24,8 +24,9 @@ import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.platform.EmptyAction
import im.vector.app.core.platform.EmptyViewEvents
import im.vector.app.core.platform.VectorViewModel
@@ -57,7 +58,7 @@ class GossipingEventsPaperTrailViewModel @AssistedInject constructor(@Assisted i
override fun handle(action: EmptyAction) {}
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: GossipingEventsPaperTrailState): GossipingEventsPaperTrailViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestListViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestListViewModel.kt
index 0b0b923a48..c0a791233f 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestListViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestListViewModel.kt
@@ -24,8 +24,9 @@ import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.platform.EmptyAction
import im.vector.app.core.platform.EmptyViewEvents
import im.vector.app.core.platform.VectorViewModel
@@ -65,7 +66,7 @@ class KeyRequestListViewModel @AssistedInject constructor(@Assisted initialState
override fun handle(action: EmptyAction) {}
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: KeyRequestListViewState): KeyRequestListViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestViewModel.kt
index a7d5e8f1ac..e7a56ef9df 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestViewModel.kt
@@ -27,8 +27,9 @@ import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewEvents
import im.vector.app.core.platform.VectorViewModel
@@ -54,7 +55,7 @@ class KeyRequestViewModel @AssistedInject constructor(
private val session: Session)
: VectorViewModel(initialState) {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: KeyRequestViewState): KeyRequestViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/settings/ignored/IgnoredUsersViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/ignored/IgnoredUsersViewModel.kt
index 98c9f8e4f1..fdc6585829 100644
--- a/vector/src/main/java/im/vector/app/features/settings/ignored/IgnoredUsersViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/ignored/IgnoredUsersViewModel.kt
@@ -25,8 +25,9 @@ import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.platform.VectorViewModelAction
import org.matrix.android.sdk.api.MatrixCallback
@@ -47,7 +48,7 @@ class IgnoredUsersViewModel @AssistedInject constructor(@Assisted initialState:
private val session: Session)
: VectorViewModel(initialState) {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: IgnoredUsersViewState): IgnoredUsersViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/settings/locale/LocalePickerViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/locale/LocalePickerViewModel.kt
index df7cc4ba84..2e59b0ef7d 100644
--- a/vector/src/main/java/im/vector/app/features/settings/locale/LocalePickerViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/locale/LocalePickerViewModel.kt
@@ -22,8 +22,9 @@ import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.configuration.VectorConfiguration
@@ -35,7 +36,7 @@ class LocalePickerViewModel @AssistedInject constructor(
private val vectorConfiguration: VectorConfiguration
) : VectorViewModel(initialState) {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: LocalePickerViewState): LocalePickerViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/settings/push/PushGatewaysViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/push/PushGatewaysViewModel.kt
index 2c8b902188..7981d71ce1 100644
--- a/vector/src/main/java/im/vector/app/features/settings/push/PushGatewaysViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/push/PushGatewaysViewModel.kt
@@ -22,8 +22,9 @@ import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.EmptyViewEvents
import im.vector.app.core.platform.VectorViewModel
@@ -39,7 +40,7 @@ class PushGatewaysViewModel @AssistedInject constructor(@Assisted initialState:
private val session: Session)
: VectorViewModel(initialState) {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: PushGatewayViewState): PushGatewaysViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsAction.kt b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsAction.kt
index 0be3c6a198..d223009e69 100644
--- a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsAction.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsAction.kt
@@ -25,6 +25,11 @@ sealed class ThreePidsSettingsAction : VectorViewModelAction {
data class SubmitCode(val threePid: ThreePid.Msisdn, val code: String) : ThreePidsSettingsAction()
data class ContinueThreePid(val threePid: ThreePid) : ThreePidsSettingsAction()
data class CancelThreePid(val threePid: ThreePid) : ThreePidsSettingsAction()
- data class AccountPassword(val password: String) : ThreePidsSettingsAction()
+
+ // data class AccountPassword(val password: String) : ThreePidsSettingsAction()
data class DeleteThreePid(val threePid: ThreePid) : ThreePidsSettingsAction()
+
+ object SsoAuthDone : ThreePidsSettingsAction()
+ data class PasswordAuthDone(val password: String) : ThreePidsSettingsAction()
+ object ReAuthCancelled : ThreePidsSettingsAction()
}
diff --git a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsFragment.kt b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsFragment.kt
index d6da04affc..0a7489e2cc 100644
--- a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsFragment.kt
@@ -16,6 +16,7 @@
package im.vector.app.features.settings.threepids
+import android.app.Activity
import android.content.DialogInterface
import android.os.Bundle
import android.view.LayoutInflater
@@ -26,7 +27,6 @@ import androidx.appcompat.app.AppCompatActivity
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
import im.vector.app.R
-import im.vector.app.core.dialogs.PromptPasswordDialog
import im.vector.app.core.dialogs.withColoredButton
import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith
@@ -35,10 +35,12 @@ import im.vector.app.core.extensions.getFormattedValue
import im.vector.app.core.extensions.hideKeyboard
import im.vector.app.core.extensions.isEmail
import im.vector.app.core.extensions.isMsisdn
+import im.vector.app.core.extensions.registerStartForActivityResult
import im.vector.app.core.platform.OnBackPressed
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.FragmentGenericRecyclerBinding
-
+import im.vector.app.features.auth.ReAuthActivity
+import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
import org.matrix.android.sdk.api.session.identity.ThreePid
import javax.inject.Inject
@@ -64,15 +66,42 @@ class ThreePidsSettingsFragment @Inject constructor(
viewModel.observeViewEvents {
when (it) {
- is ThreePidsSettingsViewEvents.Failure -> displayErrorDialog(it.throwable)
- ThreePidsSettingsViewEvents.RequestPassword -> askUserPassword()
+ is ThreePidsSettingsViewEvents.Failure -> displayErrorDialog(it.throwable)
+ is ThreePidsSettingsViewEvents.RequestReAuth -> askAuthentication(it)
}.exhaustive
}
}
- private fun askUserPassword() {
- PromptPasswordDialog().show(requireActivity()) { password ->
- viewModel.handle(ThreePidsSettingsAction.AccountPassword(password))
+ // private fun askUserPassword() {
+// PromptPasswordDialog().show(requireActivity()) { password ->
+// viewModel.handle(ThreePidsSettingsAction.AccountPassword(password))
+// }
+// }
+
+ private fun askAuthentication(event: ThreePidsSettingsViewEvents.RequestReAuth) {
+ ReAuthActivity.newIntent(requireContext(),
+ event.registrationFlowResponse,
+ event.lastErrorCode,
+ getString(R.string.settings_add_email_address)).let { intent ->
+ reAuthActivityResultLauncher.launch(intent)
+ }
+ }
+ private val reAuthActivityResultLauncher = registerStartForActivityResult { activityResult ->
+ if (activityResult.resultCode == Activity.RESULT_OK) {
+ when (activityResult.data?.extras?.getString(ReAuthActivity.RESULT_FLOW_TYPE)) {
+ LoginFlowTypes.SSO -> {
+ viewModel.handle(ThreePidsSettingsAction.SsoAuthDone)
+ }
+ LoginFlowTypes.PASSWORD -> {
+ val password = activityResult.data?.extras?.getString(ReAuthActivity.RESULT_VALUE) ?: ""
+ viewModel.handle(ThreePidsSettingsAction.PasswordAuthDone(password))
+ }
+ else -> {
+ viewModel.handle(ThreePidsSettingsAction.ReAuthCancelled)
+ }
+ }
+ } else {
+ viewModel.handle(ThreePidsSettingsAction.ReAuthCancelled)
}
}
diff --git a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewEvents.kt b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewEvents.kt
index 1ac2d10458..0346fd137e 100644
--- a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewEvents.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewEvents.kt
@@ -17,8 +17,10 @@
package im.vector.app.features.settings.threepids
import im.vector.app.core.platform.VectorViewEvents
+import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
sealed class ThreePidsSettingsViewEvents : VectorViewEvents {
data class Failure(val throwable: Throwable) : ThreePidsSettingsViewEvents()
- object RequestPassword : ThreePidsSettingsViewEvents()
+// object RequestPassword : ThreePidsSettingsViewEvents()
+ data class RequestReAuth(val registrationFlowResponse: RegistrationFlowResponse, val lastErrorCode: String?) : ThreePidsSettingsViewEvents()
}
diff --git a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewModel.kt
index 41b6146bb1..a1d4d6227b 100644
--- a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewModel.kt
@@ -23,21 +23,29 @@ import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
import im.vector.app.R
-import im.vector.app.core.error.SsoFlowNotSupportedYet
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.StringProvider
import im.vector.app.core.utils.ReadOnceTrue
+import im.vector.app.features.auth.ReAuthActivity
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.MatrixCallback
-import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
-import org.matrix.android.sdk.api.failure.Failure
+import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
+import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.identity.ThreePid
+import org.matrix.android.sdk.internal.crypto.crosssigning.fromBase64
+import org.matrix.android.sdk.internal.crypto.model.rest.DefaultBaseAuth
+import org.matrix.android.sdk.api.auth.UIABaseAuth
+import org.matrix.android.sdk.api.auth.UserPasswordAuth
import org.matrix.android.sdk.rx.rx
+import timber.log.Timber
+import kotlin.coroutines.Continuation
+import kotlin.coroutines.resume
class ThreePidsSettingsViewModel @AssistedInject constructor(
@Assisted initialState: ThreePidsSettingsViewState,
@@ -47,36 +55,16 @@ class ThreePidsSettingsViewModel @AssistedInject constructor(
// UIA session
private var pendingThreePid: ThreePid? = null
- private var pendingSession: String? = null
+// private var pendingSession: String? = null
private val loadingCallback: MatrixCallback = object : MatrixCallback {
override fun onFailure(failure: Throwable) {
isLoading(false)
-
- if (failure is Failure.RegistrationFlowError) {
- var isPasswordRequestFound = false
-
- // We only support LoginFlowTypes.PASSWORD
- // Check if we can provide the user password
- failure.registrationFlowResponse.flows?.forEach { interactiveAuthenticationFlow ->
- isPasswordRequestFound = isPasswordRequestFound || interactiveAuthenticationFlow.stages?.any { it == LoginFlowTypes.PASSWORD } == true
- }
-
- if (isPasswordRequestFound) {
- pendingSession = failure.registrationFlowResponse.session
- _viewEvents.post(ThreePidsSettingsViewEvents.RequestPassword)
- } else {
- // LoginFlowTypes.PASSWORD not supported, and this is the only one Element supports so far...
- _viewEvents.post(ThreePidsSettingsViewEvents.Failure(SsoFlowNotSupportedYet()))
- }
- } else {
- _viewEvents.post(ThreePidsSettingsViewEvents.Failure(failure))
- }
+ _viewEvents.post(ThreePidsSettingsViewEvents.Failure(failure))
}
override fun onSuccess(data: Unit) {
pendingThreePid = null
- pendingSession = null
isLoading(false)
}
}
@@ -89,7 +77,7 @@ class ThreePidsSettingsViewModel @AssistedInject constructor(
}
}
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: ThreePidsSettingsViewState): ThreePidsSettingsViewModel
}
@@ -141,16 +129,50 @@ class ThreePidsSettingsViewModel @AssistedInject constructor(
override fun handle(action: ThreePidsSettingsAction) {
when (action) {
- is ThreePidsSettingsAction.AddThreePid -> handleAddThreePid(action)
+ is ThreePidsSettingsAction.AddThreePid -> handleAddThreePid(action)
is ThreePidsSettingsAction.ContinueThreePid -> handleContinueThreePid(action)
- is ThreePidsSettingsAction.SubmitCode -> handleSubmitCode(action)
- is ThreePidsSettingsAction.CancelThreePid -> handleCancelThreePid(action)
- is ThreePidsSettingsAction.AccountPassword -> handleAccountPassword(action)
- is ThreePidsSettingsAction.DeleteThreePid -> handleDeleteThreePid(action)
- is ThreePidsSettingsAction.ChangeUiState -> handleChangeUiState(action)
+ is ThreePidsSettingsAction.SubmitCode -> handleSubmitCode(action)
+ is ThreePidsSettingsAction.CancelThreePid -> handleCancelThreePid(action)
+ is ThreePidsSettingsAction.DeleteThreePid -> handleDeleteThreePid(action)
+ is ThreePidsSettingsAction.ChangeUiState -> handleChangeUiState(action)
+ ThreePidsSettingsAction.SsoAuthDone -> {
+ Timber.d("## UIA - FallBack success")
+ if (pendingAuth != null) {
+ uiaContinuation?.resume(pendingAuth!!)
+ } else {
+ uiaContinuation?.resumeWith(Result.failure((IllegalArgumentException())))
+ }
+ }
+ is ThreePidsSettingsAction.PasswordAuthDone -> {
+ val decryptedPass = session.loadSecureSecret(action.password.fromBase64().inputStream(), ReAuthActivity.DEFAULT_RESULT_KEYSTORE_ALIAS)
+ uiaContinuation?.resume(
+ UserPasswordAuth(
+ session = pendingAuth?.session,
+ password = decryptedPass,
+ user = session.myUserId
+ )
+ )
+ }
+ ThreePidsSettingsAction.ReAuthCancelled -> {
+ Timber.d("## UIA - Reauth cancelled")
+ uiaContinuation?.resumeWith(Result.failure((Exception())))
+ uiaContinuation = null
+ pendingAuth = null
+ }
}.exhaustive
}
+ var uiaContinuation: Continuation? = null
+ var pendingAuth: UIABaseAuth? = null
+
+ private val uiaInterceptor = object : UserInteractiveAuthInterceptor {
+ override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) {
+ _viewEvents.post(ThreePidsSettingsViewEvents.RequestReAuth(flowResponse, errCode))
+ pendingAuth = DefaultBaseAuth(session = flowResponse.session)
+ uiaContinuation = promise
+ }
+ }
+
private fun handleSubmitCode(action: ThreePidsSettingsAction.SubmitCode) {
isLoading(true)
setState {
@@ -167,7 +189,7 @@ class ThreePidsSettingsViewModel @AssistedInject constructor(
override fun onSuccess(data: Unit) {
// then finalize
pendingThreePid = action.threePid
- session.finalizeAddingThreePid(action.threePid, null, null, loadingCallback)
+ session.finalizeAddingThreePid(action.threePid, uiaInterceptor, loadingCallback)
}
override fun onFailure(failure: Throwable) {
@@ -231,7 +253,7 @@ class ThreePidsSettingsViewModel @AssistedInject constructor(
isLoading(true)
pendingThreePid = action.threePid
viewModelScope.launch {
- session.finalizeAddingThreePid(action.threePid, null, null, loadingCallback)
+ session.finalizeAddingThreePid(action.threePid, uiaInterceptor, loadingCallback)
}
}
@@ -242,16 +264,14 @@ class ThreePidsSettingsViewModel @AssistedInject constructor(
}
}
- private fun handleAccountPassword(action: ThreePidsSettingsAction.AccountPassword) {
- val safeSession = pendingSession ?: return Unit
- .also { _viewEvents.post(ThreePidsSettingsViewEvents.Failure(IllegalStateException("No pending session"))) }
- val safeThreePid = pendingThreePid ?: return Unit
- .also { _viewEvents.post(ThreePidsSettingsViewEvents.Failure(IllegalStateException("No pending threePid"))) }
- isLoading(true)
- viewModelScope.launch {
- session.finalizeAddingThreePid(safeThreePid, safeSession, action.password, loadingCallback)
- }
- }
+// private fun handleAccountPassword(action: ThreePidsSettingsAction.AccountPassword) {
+// val safeThreePid = pendingThreePid ?: return Unit
+// .also { _viewEvents.post(ThreePidsSettingsViewEvents.Failure(IllegalStateException("No pending threePid"))) }
+// isLoading(true)
+// viewModelScope.launch {
+// session.finalizeAddingThreePid(safeThreePid, uiaInterceptor, loadingCallback)
+// }
+// }
private fun handleDeleteThreePid(action: ThreePidsSettingsAction.DeleteThreePid) {
isLoading(true)
diff --git a/vector/src/main/java/im/vector/app/features/share/IncomingShareViewModel.kt b/vector/src/main/java/im/vector/app/features/share/IncomingShareViewModel.kt
index 9014565e08..4d211d11de 100644
--- a/vector/src/main/java/im/vector/app/features/share/IncomingShareViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/share/IncomingShareViewModel.kt
@@ -20,8 +20,9 @@ import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
import com.jakewharton.rxrelay2.BehaviorRelay
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.extensions.toggle
import im.vector.app.core.platform.VectorViewModel
@@ -42,7 +43,7 @@ class IncomingShareViewModel @AssistedInject constructor(
private val breadcrumbsRoomComparator: BreadcrumbsRoomComparator)
: VectorViewModel(initialState) {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: IncomingShareViewState): IncomingShareViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewModel.kt b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewModel.kt
index 81ac1dbd54..98ec2d07de 100644
--- a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewModel.kt
@@ -16,6 +16,7 @@
package im.vector.app.features.signout.soft
+import androidx.lifecycle.viewModelScope
import com.airbnb.mvrx.ActivityViewModelContext
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading
@@ -23,12 +24,14 @@ import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.extensions.hasUnsavedKeys
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.login.LoginMode
+import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.auth.AuthenticationService
import org.matrix.android.sdk.api.auth.data.LoginFlowResult
@@ -47,7 +50,7 @@ class SoftLogoutViewModel @AssistedInject constructor(
private val authenticationService: AuthenticationService
) : VectorViewModel(initialState) {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: SoftLogoutViewState): SoftLogoutViewModel
}
@@ -173,22 +176,19 @@ class SoftLogoutViewModel @AssistedInject constructor(
asyncLoginAction = Loading()
)
}
- currentTask = session.updateCredentials(action.credentials,
- object : MatrixCallback {
- override fun onFailure(failure: Throwable) {
- _viewEvents.post(SoftLogoutViewEvents.Failure(failure))
- setState {
- copy(
- asyncLoginAction = Uninitialized
- )
- }
- }
-
- override fun onSuccess(data: Unit) {
- onSessionRestored()
- }
+ viewModelScope.launch {
+ try {
+ session.updateCredentials(action.credentials)
+ onSessionRestored()
+ } catch (failure: Throwable) {
+ _viewEvents.post(SoftLogoutViewEvents.Failure(failure))
+ setState {
+ copy(
+ asyncLoginAction = Uninitialized
+ )
}
- )
+ }
+ }
}
}
}
@@ -201,21 +201,18 @@ class SoftLogoutViewModel @AssistedInject constructor(
passwordShown = false
)
}
- currentTask = session.signInAgain(action.password,
- object : MatrixCallback {
- override fun onFailure(failure: Throwable) {
- setState {
- copy(
- asyncLoginAction = Fail(failure)
- )
- }
- }
-
- override fun onSuccess(data: Unit) {
- onSessionRestored()
- }
+ viewModelScope.launch {
+ try {
+ session.signInAgain(action.password)
+ onSessionRestored()
+ } catch (failure: Throwable) {
+ setState {
+ copy(
+ asyncLoginAction = Fail(failure)
+ )
}
- )
+ }
+ }
}
private fun onSessionRestored() {
diff --git a/vector/src/main/java/im/vector/app/features/terms/ReviewTermsViewModel.kt b/vector/src/main/java/im/vector/app/features/terms/ReviewTermsViewModel.kt
index 89d6e970cc..4ecad80876 100644
--- a/vector/src/main/java/im/vector/app/features/terms/ReviewTermsViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/terms/ReviewTermsViewModel.kt
@@ -22,8 +22,9 @@ import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
import kotlinx.coroutines.launch
@@ -35,7 +36,7 @@ class ReviewTermsViewModel @AssistedInject constructor(
private val session: Session
) : VectorViewModel(initialState) {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: ReviewTermsViewState): ReviewTermsViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/usercode/UserCodeSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/usercode/UserCodeSharedViewModel.kt
index 45b6f0ee65..4caa75b39c 100644
--- a/vector/src/main/java/im/vector/app/features/usercode/UserCodeSharedViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/usercode/UserCodeSharedViewModel.kt
@@ -21,8 +21,9 @@ import com.airbnb.mvrx.ActivityViewModelContext
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.R
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.StringProvider
@@ -66,7 +67,7 @@ class UserCodeSharedViewModel @AssistedInject constructor(
}
}
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: UserCodeState): UserCodeSharedViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewModel.kt b/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewModel.kt
index f8eabbaed0..9766d640c7 100644
--- a/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewModel.kt
@@ -21,8 +21,9 @@ import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
import com.jakewharton.rxrelay2.BehaviorRelay
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.extensions.toggle
import im.vector.app.core.platform.VectorViewModel
@@ -51,7 +52,7 @@ class UserListViewModel @AssistedInject constructor(@Assisted initialState: User
private var currentUserSearchDisposable: Disposable? = null
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: UserListViewState): UserListViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/webview/VectorWebViewActivity.kt b/vector/src/main/java/im/vector/app/features/webview/VectorWebViewActivity.kt
index 68c13c300e..e4d2571333 100644
--- a/vector/src/main/java/im/vector/app/features/webview/VectorWebViewActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/webview/VectorWebViewActivity.kt
@@ -64,7 +64,9 @@ class VectorWebViewActivity : VectorBaseActivity()
// Allow use of Local Storage
domStorageEnabled = true
+ @Suppress("DEPRECATION")
allowFileAccessFromFileURLs = true
+ @Suppress("DEPRECATION")
allowUniversalAccessFromFileURLs = true
displayZoomControls = false
@@ -73,7 +75,7 @@ class VectorWebViewActivity : VectorBaseActivity()
val cookieManager = android.webkit.CookieManager.getInstance()
cookieManager.setAcceptThirdPartyCookies(views.simpleWebview, true)
- val url = intent.extras?.getString(EXTRA_URL)
+ val url = intent.extras?.getString(EXTRA_URL) ?: return
val title = intent.extras?.getString(EXTRA_TITLE, USE_TITLE_FROM_WEB_PAGE)
if (title != USE_TITLE_FROM_WEB_PAGE) {
setTitle(title)
diff --git a/vector/src/main/java/im/vector/app/features/widgets/WidgetPostAPIHandler.kt b/vector/src/main/java/im/vector/app/features/widgets/WidgetPostAPIHandler.kt
index 3906ea687c..13d49eb20b 100644
--- a/vector/src/main/java/im/vector/app/features/widgets/WidgetPostAPIHandler.kt
+++ b/vector/src/main/java/im/vector/app/features/widgets/WidgetPostAPIHandler.kt
@@ -17,8 +17,9 @@
package im.vector.app.features.widgets
import android.text.TextUtils
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.R
import im.vector.app.core.resources.StringProvider
import kotlinx.coroutines.GlobalScope
@@ -45,7 +46,7 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo
private val stringProvider: StringProvider,
private val session: Session) : WidgetPostAPIMediator.Handler {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(roomId: String): WidgetPostAPIHandler
}
diff --git a/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt b/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt
index 1bc10e26d4..203b58f68f 100644
--- a/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt
@@ -25,8 +25,9 @@ import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.StringProvider
import im.vector.app.features.widgets.permissions.WidgetPermissionsHelper
@@ -55,7 +56,7 @@ class WidgetViewModel @AssistedInject constructor(@Assisted val initialState: Wi
WidgetPostAPIHandler.NavigationCallback,
IntegrationManagerService.Listener {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: WidgetViewState): WidgetViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewModel.kt b/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewModel.kt
index eb588ec9ae..3accc56680 100644
--- a/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewModel.kt
@@ -20,8 +20,9 @@ import com.airbnb.mvrx.ActivityViewModelContext
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.R
import im.vector.app.core.platform.VectorViewModel
import kotlinx.coroutines.launch
@@ -141,7 +142,7 @@ class RoomWidgetPermissionViewModel @AssistedInject constructor(@Assisted val in
}
}
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: RoomWidgetPermissionViewState): RoomWidgetPermissionViewModel
}
diff --git a/vector/src/main/java/im/vector/app/features/widgets/webview/WidgetWebView.kt b/vector/src/main/java/im/vector/app/features/widgets/webview/WidgetWebView.kt
index 446bc1663f..d30baef55a 100644
--- a/vector/src/main/java/im/vector/app/features/widgets/webview/WidgetWebView.kt
+++ b/vector/src/main/java/im/vector/app/features/widgets/webview/WidgetWebView.kt
@@ -54,7 +54,9 @@ fun WebView.setupForWidget(webViewEventListener: WebViewEventListener) {
// Allow use of Local Storage
settings.domStorageEnabled = true
+ @Suppress("DEPRECATION")
settings.allowFileAccessFromFileURLs = true
+ @Suppress("DEPRECATION")
settings.allowUniversalAccessFromFileURLs = true
settings.displayZoomControls = false
@@ -75,7 +77,6 @@ fun WebView.clearAfterWidget() {
// Make sure you remove the WebView from its parent view before doing anything.
(parent as? ViewGroup)?.removeAllViews()
webChromeClient = null
- webViewClient = null
clearHistory()
// NOTE: clears RAM cache, if you pass true, it will also clear the disk cache.
clearCache(true)
diff --git a/vector/src/main/java/im/vector/app/features/workers/signout/ServerBackupStatusViewModel.kt b/vector/src/main/java/im/vector/app/features/workers/signout/ServerBackupStatusViewModel.kt
index 68f62cffe5..1c3ad7563c 100644
--- a/vector/src/main/java/im/vector/app/features/workers/signout/ServerBackupStatusViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/workers/signout/ServerBackupStatusViewModel.kt
@@ -24,8 +24,9 @@ import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.platform.EmptyAction
import im.vector.app.core.platform.EmptyViewEvents
import im.vector.app.core.platform.VectorViewModel
@@ -69,7 +70,7 @@ class ServerBackupStatusViewModel @AssistedInject constructor(@Assisted initialS
private val session: Session)
: VectorViewModel(initialState), KeysBackupStateListener {
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: ServerBackupStatusViewState): ServerBackupStatusViewModel
}
@@ -114,8 +115,10 @@ class ServerBackupStatusViewModel @AssistedInject constructor(@Assisted initialS
// So recovery is not setup
// Check if cross signing is enabled and local secrets known
- if (crossSigningInfo.getOrNull()?.isTrusted() == true
- && pInfo.getOrNull()?.allKnown().orFalse()
+ if (
+ crossSigningInfo.getOrNull() == null
+ || (crossSigningInfo.getOrNull()?.isTrusted() == true
+ && pInfo.getOrNull()?.allKnown().orFalse())
) {
// So 4S is not setup and we have local secrets,
return@Function4 BannerState.Setup(numberOfKeys = getNumberOfKeysToBackup())
diff --git a/vector/src/main/java/im/vector/app/features/workers/signout/SignoutCheckViewModel.kt b/vector/src/main/java/im/vector/app/features/workers/signout/SignoutCheckViewModel.kt
index 8a1055e780..2f8d45043b 100644
--- a/vector/src/main/java/im/vector/app/features/workers/signout/SignoutCheckViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/workers/signout/SignoutCheckViewModel.kt
@@ -26,8 +26,9 @@ import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import dagger.assisted.AssistedFactory
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewEvents
import im.vector.app.core.platform.VectorViewModel
@@ -63,7 +64,7 @@ class SignoutCheckViewModel @AssistedInject constructor(@Assisted initialState:
data class ExportKeys(val exporter: KeysExporter, val passphrase: String, val uri: Uri) : ViewEvents()
}
- @AssistedInject.Factory
+ @AssistedFactory
interface Factory {
fun create(initialState: SignoutCheckViewState): SignoutCheckViewModel
}
diff --git a/vector/src/main/res/drawable/ic_social_gitlab.xml b/vector/src/main/res/drawable/ic_social_gitlab.xml
new file mode 100644
index 0000000000..9399f6448a
--- /dev/null
+++ b/vector/src/main/res/drawable/ic_social_gitlab.xml
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
diff --git a/vector/src/main/res/layout/activity_home.xml b/vector/src/main/res/layout/activity_home.xml
index 50fc11500a..a41256fb84 100644
--- a/vector/src/main/res/layout/activity_home.xml
+++ b/vector/src/main/res/layout/activity_home.xml
@@ -25,7 +25,7 @@
diff --git a/vector/src/main/res/layout/fragment_bootstrap_reauth.xml b/vector/src/main/res/layout/fragment_bootstrap_reauth.xml
new file mode 100644
index 0000000000..1bc6725c64
--- /dev/null
+++ b/vector/src/main/res/layout/fragment_bootstrap_reauth.xml
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/vector/src/main/res/layout/fragment_deactivate_account.xml b/vector/src/main/res/layout/fragment_deactivate_account.xml
index db85c607e1..4bbf0a496c 100644
--- a/vector/src/main/res/layout/fragment_deactivate_account.xml
+++ b/vector/src/main/res/layout/fragment_deactivate_account.xml
@@ -31,75 +31,14 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/deactivateAccountContent" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ app:layout_constraintTop_toBottomOf="@+id/deactivateAccountEraseCheckbox" />
diff --git a/vector/src/main/res/layout/fragment_login.xml b/vector/src/main/res/layout/fragment_login.xml
index da41878365..1740d26b3b 100644
--- a/vector/src/main/res/layout/fragment_login.xml
+++ b/vector/src/main/res/layout/fragment_login.xml
@@ -143,7 +143,6 @@
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical"
- android:padding="8dp"
android:visibility="gone"
tools:visibility="visible">
diff --git a/vector/src/main/res/layout/fragment_login_signup_signin_selection.xml b/vector/src/main/res/layout/fragment_login_signup_signin_selection.xml
index 097e5c1f52..56d4e37f1e 100644
--- a/vector/src/main/res/layout/fragment_login_signup_signin_selection.xml
+++ b/vector/src/main/res/layout/fragment_login_signup_signin_selection.xml
@@ -88,7 +88,6 @@
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical"
- android:padding="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
diff --git a/vector/src/main/res/layout/fragment_reauth_confirm.xml b/vector/src/main/res/layout/fragment_reauth_confirm.xml
new file mode 100644
index 0000000000..65a2bc01a3
--- /dev/null
+++ b/vector/src/main/res/layout/fragment_reauth_confirm.xml
@@ -0,0 +1,112 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/vector/src/main/res/layout/item_positive_button.xml b/vector/src/main/res/layout/item_positive_button.xml
new file mode 100644
index 0000000000..cdee239e59
--- /dev/null
+++ b/vector/src/main/res/layout/item_positive_button.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/vector/src/main/res/values-sw600dp/dimens.xml b/vector/src/main/res/values-sw600dp/dimens.xml
new file mode 100644
index 0000000000..204d663d9c
--- /dev/null
+++ b/vector/src/main/res/values-sw600dp/dimens.xml
@@ -0,0 +1,5 @@
+
+
+
+ 400dp
+
\ No newline at end of file
diff --git a/vector/src/main/res/values/attrs.xml b/vector/src/main/res/values/attrs.xml
index 51d140ebcf..41b8080fc0 100644
--- a/vector/src/main/res/values/attrs.xml
+++ b/vector/src/main/res/values/attrs.xml
@@ -46,6 +46,7 @@
+
diff --git a/vector/src/main/res/values/dimens.xml b/vector/src/main/res/values/dimens.xml
index ccb7ae7726..06ca39950c 100644
--- a/vector/src/main/res/values/dimens.xml
+++ b/vector/src/main/res/values/dimens.xml
@@ -33,4 +33,7 @@
280dp
+
+ 320dp
+
\ No newline at end of file
diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml
index 1f282115e0..b386c3f1d5 100644
--- a/vector/src/main/res/values/strings.xml
+++ b/vector/src/main/res/values/strings.xml
@@ -333,6 +333,7 @@
Your device is using an outdated TLS security protocol, vulnerable to attack, for your security you will not be able to connect
Invalid username/password
+ Unauthorized, missing valid authentication credentials
The access token specified was not recognised
Malformed JSON
Did not contain valid JSON
@@ -2623,6 +2624,7 @@
Mark as Trusted
Sorry, this operation is not possible yet for accounts connected using Single Sign-On.
+ Failed to set up Cross Signing
Please choose a username.
Please choose a password.
@@ -2792,4 +2794,8 @@
Discard changes
Matrix Link
+
+ Re-Authentication Needed
+ Element requires you to enter your credentials to perform this action.
+ Failed to authenticate
diff --git a/vector/src/main/res/values/styles_social_login.xml b/vector/src/main/res/values/styles_social_login.xml
index 796965cee1..3ad7fbb989 100644
--- a/vector/src/main/res/values/styles_social_login.xml
+++ b/vector/src/main/res/values/styles_social_login.xml
@@ -1,17 +1,20 @@
-
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/vector/src/main/res/values/theme_dark.xml b/vector/src/main/res/values/theme_dark.xml
index 86fbb57608..c31fc8240b 100644
--- a/vector/src/main/res/values/theme_dark.xml
+++ b/vector/src/main/res/values/theme_dark.xml
@@ -200,6 +200,7 @@
- @style/WidgetButtonSocialLogin.Facebook.Dark
- @style/WidgetButtonSocialLogin.Twitter.Dark
- @style/WidgetButtonSocialLogin.Apple.Dark
+ - @style/WidgetButtonSocialLogin.Gitlab.Dark
- @android:color/transparent
diff --git a/vector/src/main/res/values/theme_light.xml b/vector/src/main/res/values/theme_light.xml
index f174bcf758..56faaeb325 100644
--- a/vector/src/main/res/values/theme_light.xml
+++ b/vector/src/main/res/values/theme_light.xml
@@ -202,6 +202,7 @@
- @style/WidgetButtonSocialLogin.Facebook.Light
- @style/WidgetButtonSocialLogin.Twitter.Light
- @style/WidgetButtonSocialLogin.Apple.Light
+ - @style/WidgetButtonSocialLogin.Gitlab.Light
- @color/black_alpha