mirror of
https://github.com/element-hq/element-android
synced 2024-11-24 02:15:35 +03:00
Merge remote-tracking branch 'upstream/develop' into rust
This commit is contained in:
commit
324cdc4db1
63 changed files with 947 additions and 397 deletions
49
CHANGES.md
49
CHANGES.md
|
@ -1,9 +1,47 @@
|
||||||
Changes in Element 1.1.4 (2021-XX-XX)
|
Changes in Element 1.1.7 (2021-XX-XX)
|
||||||
===================================================
|
===================================================
|
||||||
|
|
||||||
Features ✨:
|
Features ✨:
|
||||||
-
|
-
|
||||||
|
|
||||||
|
Improvements 🙌:
|
||||||
|
-
|
||||||
|
|
||||||
|
Bugfix 🐛:
|
||||||
|
- Message states cosmetic changes (#3007)
|
||||||
|
|
||||||
|
Translations 🗣:
|
||||||
|
-
|
||||||
|
|
||||||
|
SDK API changes ⚠️:
|
||||||
|
-
|
||||||
|
|
||||||
|
Build 🧱:
|
||||||
|
- Upgrade to gradle 7
|
||||||
|
|
||||||
|
Test:
|
||||||
|
-
|
||||||
|
|
||||||
|
Other changes:
|
||||||
|
- New store descriptions
|
||||||
|
|
||||||
|
Changes in Element 1.1.6 (2021-04-16)
|
||||||
|
===================================================
|
||||||
|
|
||||||
|
Bugfix 🐛:
|
||||||
|
- Fix crash on the timeline
|
||||||
|
- App crashes on "troubleshoot notifications" button (#3187)
|
||||||
|
|
||||||
|
Changes in Element 1.1.5 (2021-04-15)
|
||||||
|
===================================================
|
||||||
|
|
||||||
|
Bugfix 🐛:
|
||||||
|
- Fix crash during Realm migration
|
||||||
|
- Fix crash when playing video (#3179)
|
||||||
|
|
||||||
|
Changes in Element 1.1.4 (2021-04-09)
|
||||||
|
===================================================
|
||||||
|
|
||||||
Improvements 🙌:
|
Improvements 🙌:
|
||||||
- Split network request `/keys/query` into smaller requests (250 users max) (#2925)
|
- Split network request `/keys/query` into smaller requests (250 users max) (#2925)
|
||||||
- Crypto improvement | Bulk send NO_OLM withheld code
|
- Crypto improvement | Bulk send NO_OLM withheld code
|
||||||
|
@ -18,6 +56,8 @@ Improvements 🙌:
|
||||||
- Room list improvements (paging)
|
- Room list improvements (paging)
|
||||||
- Fix quick click action (#3127)
|
- Fix quick click action (#3127)
|
||||||
- Get Event after a Push for a faster notification display in some conditions
|
- Get Event after a Push for a faster notification display in some conditions
|
||||||
|
- Always try to retry Http requests in case of 429 (#1300)
|
||||||
|
- registration availability endpoint added to matrix-sdk
|
||||||
|
|
||||||
Bugfix 🐛:
|
Bugfix 🐛:
|
||||||
- Fix bad theme change for the MainActivity
|
- Fix bad theme change for the MainActivity
|
||||||
|
@ -26,9 +66,7 @@ Bugfix 🐛:
|
||||||
- Fix avatar rendering for DMs, after initial sync (#2693)
|
- Fix avatar rendering for DMs, after initial sync (#2693)
|
||||||
- Fix mandatory parameter in API (#3065)
|
- Fix mandatory parameter in API (#3065)
|
||||||
- If signout request fails, do not start LoginActivity, but restart the app (#3099)
|
- If signout request fails, do not start LoginActivity, but restart the app (#3099)
|
||||||
|
- Retain keyword order in emoji import script, and update the generated file (#3147)
|
||||||
Translations 🗣:
|
|
||||||
-
|
|
||||||
|
|
||||||
SDK API changes ⚠️:
|
SDK API changes ⚠️:
|
||||||
- Several Services have been migrated to coroutines (#2449)
|
- Several Services have been migrated to coroutines (#2449)
|
||||||
|
@ -37,9 +75,6 @@ SDK API changes ⚠️:
|
||||||
Build 🧱:
|
Build 🧱:
|
||||||
- Properly exclude gms dependencies in fdroid build flavour which were pulled in through the jitsi SDK (#3125)
|
- Properly exclude gms dependencies in fdroid build flavour which were pulled in through the jitsi SDK (#3125)
|
||||||
|
|
||||||
Test:
|
|
||||||
-
|
|
||||||
|
|
||||||
Other changes:
|
Other changes:
|
||||||
- Add version details on the login screen, in debug or developer mode
|
- Add version details on the login screen, in debug or developer mode
|
||||||
- Migrate Retrofit interface to coroutine calls
|
- Migrate Retrofit interface to coroutine calls
|
||||||
|
|
|
@ -69,7 +69,7 @@ dependencies {
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||||
implementation 'androidx.core:core-ktx:1.3.2'
|
implementation 'androidx.core:core-ktx:1.3.2'
|
||||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||||
implementation "androidx.recyclerview:recyclerview:1.2.0-rc01"
|
implementation "androidx.recyclerview:recyclerview:1.2.0"
|
||||||
|
|
||||||
implementation 'com.google.android.material:material:1.3.0'
|
implementation 'com.google.android.material:material:1.3.0'
|
||||||
}
|
}
|
|
@ -17,7 +17,7 @@ buildscript {
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.1.1'
|
classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.1.1'
|
||||||
classpath 'com.google.android.gms:oss-licenses-plugin:0.10.3'
|
classpath 'com.google.android.gms:oss-licenses-plugin:0.10.3'
|
||||||
classpath "com.likethesalad.android:string-reference:1.2.1"
|
classpath "com.likethesalad.android:string-reference:1.2.2"
|
||||||
|
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
// in the individual module build.gradle files
|
// in the individual module build.gradle files
|
||||||
|
@ -51,7 +51,7 @@ allprojects {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
maven {
|
maven {
|
||||||
url "http://dl.bintray.com/piasy/maven"
|
url "https://dl.bintray.com/piasy/maven"
|
||||||
content {
|
content {
|
||||||
includeGroupByRegex "com\\.github\\.piasy"
|
includeGroupByRegex "com\\.github\\.piasy"
|
||||||
}
|
}
|
||||||
|
|
2
fastlane/metadata/android/en-US/changelogs/40101040.txt
Normal file
2
fastlane/metadata/android/en-US/changelogs/40101040.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
Main changes in this version: performance improvement and bug fixes!
|
||||||
|
Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.1.4
|
2
fastlane/metadata/android/en-US/changelogs/40101050.txt
Normal file
2
fastlane/metadata/android/en-US/changelogs/40101050.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
Main changes in this version: hot fixes for 1.1.4
|
||||||
|
Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.1.5
|
2
fastlane/metadata/android/en-US/changelogs/40101060.txt
Normal file
2
fastlane/metadata/android/en-US/changelogs/40101060.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
Main changes in this version: hot fixes for 1.1.5
|
||||||
|
Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.1.6
|
|
@ -1,30 +1,39 @@
|
||||||
Element is a new type of messenger and collaboration app that:
|
Element is both a secure messenger and a productivity team collaboration app that is ideal for group chats while remote working. This chat app uses end-to-end encryption to provide powerful video conferencing, file sharing and voice calls.
|
||||||
|
|
||||||
1. Puts you in control to preserve your privacy
|
<b>Element’s features include:</b>
|
||||||
2. Lets you communicate with anyone in the Matrix network, and even beyond by integrating with apps such as Slack
|
- Advanced online communication tools
|
||||||
3. Protects you from advertising, datamining and walled gardens
|
- Fully encrypted messages to allow safer corporate communication, even for remote workers
|
||||||
4. Secures you through end-to-end encryption, with cross-signing to verify others
|
- Decentralized chat based on the Matrix open source framework
|
||||||
|
- File sharing securely with encrypted data while managing projects
|
||||||
|
- Video chats with Voice over IP and screen sharing
|
||||||
|
- Easy integration with your favourite online collaboration tools, project management tools, VoIP services and other team messaging apps
|
||||||
|
|
||||||
Element is completely different from other messaging and collaboration apps because it is decentralised and open source.
|
Element is completely different from other messaging and collaboration apps. It operates on Matrix, an open network for secure messaging and decentralized communication. It allows self-hosting to give users maximum ownership and control of their data and messages.
|
||||||
|
|
||||||
Element lets you self-host - or choose a host - so that you have privacy, ownership and control of your data and conversations. It gives you access to an open network; so you’re not just stuck speaking to other Element users only. And it is very secure.
|
<b>Privacy and encrypted messaging</b>
|
||||||
|
Element protects you from unwanted ads, data mining and walled gardens. It also secures all your data, one-to-one video and voice communication through end-to-end encryption and cross-signed device verification.
|
||||||
|
|
||||||
Element is able to do all this because it operates on Matrix - the standard for open, decentralised communication.
|
Element gives you control over your privacy while allowing you to communicate securely with anyone on the Matrix network, or other business collaboration tools by integrating with apps such as Slack.
|
||||||
|
|
||||||
Element puts you in control by letting you choose who hosts your conversations. From the Element app, you can choose to host in different ways:
|
<b>Element can be self-hosted</b>
|
||||||
|
To allow more control of your sensitive data and conversations, Element can be self-hosted or you can choose any Matrix-based host - the standard for open source, decentralized communication. Element gives you privacy, security compliance and integration flexibility.
|
||||||
|
|
||||||
|
<b>Own your data</b>
|
||||||
|
You decide where to keep your data and messages. Without the risk of data mining or access from third parties.
|
||||||
|
|
||||||
|
Element puts you in control in different ways:
|
||||||
1. Get a free account on the matrix.org public server hosted by the Matrix developers, or choose from thousands of public servers hosted by volunteers
|
1. Get a free account on the matrix.org public server hosted by the Matrix developers, or choose from thousands of public servers hosted by volunteers
|
||||||
2. Self-host your account by running a server on your own hardware
|
2. Self-host your account by running a server on your own IT infrastructure
|
||||||
3. Sign up for an account on a custom server by simply subscribing to the Element Matrix Services hosting platform
|
3. Sign up for an account on a custom server by simply subscribing to the Element Matrix Services hosting platform
|
||||||
|
|
||||||
<b>Why choose Element?</b>
|
<b>Open messaging and collaboration</b>
|
||||||
|
You can chat with anyone on the Matrix network, whether they’re using Element, another Matrix app or even if they are using a different messaging app.
|
||||||
|
|
||||||
<b>OWN YOUR DATA</b>: You decide where to keep your data and messages. You own it and control it, not some MEGACORP that mines your data or gives access to third parties.
|
<b>Super secure</b>
|
||||||
|
Real end-to-end encryption (only those in the conversation can decrypt messages), and cross-signed device verification.
|
||||||
|
|
||||||
<b>OPEN MESSAGING AND COLLABORATION</b>: You can chat with anyone else in the Matrix network, whether they’re using Element or another Matrix app, and even if they are using a different messaging system of the likes of Slack, IRC or XMPP.
|
<b>Complete communication and integration</b>
|
||||||
|
Messaging, voice and video calls, file sharing, screen sharing and a whole bunch of integrations, bots and widgets. Build rooms, communities, stay in touch and get things done.
|
||||||
|
|
||||||
<b>SUPER-SECURE</b>: Real end-to-end encryption (only those in the conversation can decrypt messages), and cross-signing to verify the devices of conversation participants.
|
<b>Pick up where you left off</b>
|
||||||
|
Stay in touch wherever you are with fully synchronised message history across all your devices and on the web at https://app.element.io
|
||||||
<b>COMPLETE COMMUNICATION</b>: Messaging, voice and video calls, file sharing, screen sharing and a whole bunch of integrations, bots and widgets. Build rooms, communities, stay in touch and get things done.
|
|
||||||
|
|
||||||
<b>EVERYWHERE YOU ARE</b>: Stay in touch wherever you are with fully synchronised message history across all your devices and on the web at https://app.element.io.
|
|
|
@ -1 +1 @@
|
||||||
Secure decentralised chat & VoIP. Keep your data safe from third parties.
|
Group messenger - encrypted messaging, group chat and video calls
|
|
@ -1 +1 @@
|
||||||
Element (previously Riot.im)
|
Element - Secure Messenger
|
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,6 +1,6 @@
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionSha256Sum=9af5c8e7e2cd1a3b0f694a4ac262b9f38c75262e74a9e8b5101af302a6beadd7
|
distributionSha256Sum=81003f83b0056d20eedf48cddd4f52a9813163d4ba185bcf8abd34b8eeea4cbd
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-all.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|
|
@ -1,2 +1 @@
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest package="org.matrix.android.sdk.rx" />
|
||||||
package="org.matrix.android.sdk.rx" />
|
|
||||||
|
|
|
@ -124,8 +124,8 @@ class RxSession(private val session: Session) {
|
||||||
.startWithCallable { session.getPendingThreePids() }
|
.startWithCallable { session.getPendingThreePids() }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createRoom(roomParams: CreateRoomParams): Single<String> = singleBuilder {
|
fun createRoom(roomParams: CreateRoomParams): Single<String> = rxSingle {
|
||||||
session.createRoom(roomParams, it)
|
session.createRoom(roomParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun searchUsersDirectory(search: String,
|
fun searchUsersDirectory(search: String,
|
||||||
|
@ -136,13 +136,13 @@ class RxSession(private val session: Session) {
|
||||||
|
|
||||||
fun joinRoom(roomIdOrAlias: String,
|
fun joinRoom(roomIdOrAlias: String,
|
||||||
reason: String? = null,
|
reason: String? = null,
|
||||||
viaServers: List<String> = emptyList()): Single<Unit> = singleBuilder {
|
viaServers: List<String> = emptyList()): Single<Unit> = rxSingle {
|
||||||
session.joinRoom(roomIdOrAlias, reason, viaServers, it)
|
session.joinRoom(roomIdOrAlias, reason, viaServers)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getRoomIdByAlias(roomAlias: String,
|
fun getRoomIdByAlias(roomAlias: String,
|
||||||
searchOnServer: Boolean): Single<Optional<RoomAliasDescription>> = singleBuilder {
|
searchOnServer: Boolean): Single<Optional<RoomAliasDescription>> = rxSingle {
|
||||||
session.getRoomIdByAlias(roomAlias, searchOnServer, it)
|
session.getRoomIdByAlias(roomAlias, searchOnServer)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getProfileInfo(userId: String): Single<JsonDict> = rxSingle {
|
fun getProfileInfo(userId: String): Single<JsonDict> = rxSingle {
|
||||||
|
|
|
@ -6,10 +6,13 @@ apply plugin: 'realm-android'
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
// mavenCentral()
|
||||||
|
//noinspection GrDeprecatedAPIUsage
|
||||||
|
jcenter()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath "io.realm:realm-gradle-plugin:10.4.0"
|
// Stick to this version until https://github.com/realm/realm-java/issues/7402 is fixed
|
||||||
|
classpath "io.realm:realm-gradle-plugin:10.3.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,7 +129,7 @@ dependencies {
|
||||||
def lifecycle_version = '2.2.0'
|
def lifecycle_version = '2.2.0'
|
||||||
def arch_version = '2.1.0'
|
def arch_version = '2.1.0'
|
||||||
def markwon_version = '3.1.0'
|
def markwon_version = '3.1.0'
|
||||||
def daggerVersion = '2.33'
|
def daggerVersion = '2.34'
|
||||||
def work_version = '2.5.0'
|
def work_version = '2.5.0'
|
||||||
def retrofit_version = '2.9.0'
|
def retrofit_version = '2.9.0'
|
||||||
|
|
||||||
|
|
|
@ -66,8 +66,8 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
|
||||||
fun doE2ETestWithAliceInARoom(encryptedRoom: Boolean = true): CryptoTestData {
|
fun doE2ETestWithAliceInARoom(encryptedRoom: Boolean = true): CryptoTestData {
|
||||||
val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, defaultSessionParams)
|
val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, defaultSessionParams)
|
||||||
|
|
||||||
val roomId = mTestHelper.doSync<String> {
|
val roomId = mTestHelper.runBlockingTest {
|
||||||
aliceSession.createRoom(CreateRoomParams().apply { name = "MyRoom" }, it)
|
aliceSession.createRoom(CreateRoomParams().apply { name = "MyRoom" })
|
||||||
}
|
}
|
||||||
|
|
||||||
if (encryptedRoom) {
|
if (encryptedRoom) {
|
||||||
|
@ -135,7 +135,7 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
|
||||||
bobRoomSummariesLive.observeForever(roomJoinedObserver)
|
bobRoomSummariesLive.observeForever(roomJoinedObserver)
|
||||||
}
|
}
|
||||||
|
|
||||||
mTestHelper.doSync<Unit> { bobSession.joinRoom(aliceRoomId, callback = it) }
|
mTestHelper.runBlockingTest { bobSession.joinRoom(aliceRoomId) }
|
||||||
|
|
||||||
mTestHelper.await(lock)
|
mTestHelper.await(lock)
|
||||||
|
|
||||||
|
@ -176,8 +176,8 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
|
||||||
room.invite(samSession.myUserId, null)
|
room.invite(samSession.myUserId, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
mTestHelper.doSync<Unit> {
|
mTestHelper.runBlockingTest {
|
||||||
samSession.joinRoom(room.roomId, null, emptyList(), it)
|
samSession.joinRoom(room.roomId, null, emptyList())
|
||||||
}
|
}
|
||||||
|
|
||||||
return samSession
|
return samSession
|
||||||
|
@ -256,8 +256,8 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createDM(alice: Session, bob: Session): String {
|
fun createDM(alice: Session, bob: Session): String {
|
||||||
val roomId = mTestHelper.doSync<String> {
|
val roomId = mTestHelper.runBlockingTest {
|
||||||
alice.createDirectRoom(bob.myUserId, it)
|
alice.createDirectRoom(bob.myUserId)
|
||||||
}
|
}
|
||||||
|
|
||||||
mTestHelper.waitWithLatch { latch ->
|
mTestHelper.waitWithLatch { latch ->
|
||||||
|
@ -300,7 +300,7 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
|
||||||
bobRoomSummariesLive.observeForever(newRoomObserver)
|
bobRoomSummariesLive.observeForever(newRoomObserver)
|
||||||
}
|
}
|
||||||
|
|
||||||
mTestHelper.doSync<Unit> { bob.joinRoom(roomId, callback = it) }
|
mTestHelper.runBlockingTest { bob.joinRoom(roomId) }
|
||||||
}
|
}
|
||||||
|
|
||||||
return roomId
|
return roomId
|
||||||
|
@ -398,8 +398,8 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
|
||||||
val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, defaultSessionParams)
|
val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, defaultSessionParams)
|
||||||
aliceSession.cryptoService().setWarnOnUnknownDevices(false)
|
aliceSession.cryptoService().setWarnOnUnknownDevices(false)
|
||||||
|
|
||||||
val roomId = mTestHelper.doSync<String> {
|
val roomId = mTestHelper.runBlockingTest {
|
||||||
aliceSession.createRoom(CreateRoomParams().apply { name = "MyRoom" }, it)
|
aliceSession.createRoom(CreateRoomParams().apply { name = "MyRoom" })
|
||||||
}
|
}
|
||||||
val room = aliceSession.getRoom(roomId)!!
|
val room = aliceSession.getRoom(roomId)!!
|
||||||
|
|
||||||
|
@ -412,7 +412,7 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
|
||||||
val session = mTestHelper.createAccount("User_$index", defaultSessionParams)
|
val session = mTestHelper.createAccount("User_$index", defaultSessionParams)
|
||||||
mTestHelper.runBlockingTest(timeout = 600_000) { room.invite(session.myUserId, null) }
|
mTestHelper.runBlockingTest(timeout = 600_000) { room.invite(session.myUserId, null) }
|
||||||
println("TEST -> " + session.myUserId + " invited")
|
println("TEST -> " + session.myUserId + " invited")
|
||||||
mTestHelper.doSync<Unit> { session.joinRoom(room.roomId, null, emptyList(), it) }
|
mTestHelper.runBlockingTest { session.joinRoom(room.roomId, null, emptyList()) }
|
||||||
println("TEST -> " + session.myUserId + " joined")
|
println("TEST -> " + session.myUserId + " joined")
|
||||||
sessions.add(session)
|
sessions.add(session)
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,6 +66,138 @@ class KeyShareTests : InstrumentedTest {
|
||||||
private val mTestHelper = CommonTestHelper(context())
|
private val mTestHelper = CommonTestHelper(context())
|
||||||
private val mCryptoTestHelper = CryptoTestHelper(mTestHelper)
|
private val mCryptoTestHelper = CryptoTestHelper(mTestHelper)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun test_DoNotSelfShareIfNotTrusted() {
|
||||||
|
val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
||||||
|
|
||||||
|
// Create an encrypted room and add a message
|
||||||
|
val roomId = mTestHelper.runBlockingTest {
|
||||||
|
aliceSession.createRoom(
|
||||||
|
CreateRoomParams().apply {
|
||||||
|
visibility = RoomDirectoryVisibility.PRIVATE
|
||||||
|
enableEncryption()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val room = aliceSession.getRoom(roomId)
|
||||||
|
assertNotNull(room)
|
||||||
|
Thread.sleep(4_000)
|
||||||
|
assertTrue(room?.isEncrypted() == true)
|
||||||
|
val sentEventId = mTestHelper.sendTextMessage(room!!, "My Message", 1).first().eventId
|
||||||
|
|
||||||
|
// Open a new sessionx
|
||||||
|
|
||||||
|
val aliceSession2 = mTestHelper.logIntoAccount(aliceSession.myUserId, SessionTestParams(true))
|
||||||
|
|
||||||
|
val roomSecondSessionPOV = aliceSession2.getRoom(roomId)
|
||||||
|
|
||||||
|
val receivedEvent = roomSecondSessionPOV?.getTimeLineEvent(sentEventId)
|
||||||
|
assertNotNull(receivedEvent)
|
||||||
|
assert(receivedEvent!!.isEncrypted())
|
||||||
|
|
||||||
|
try {
|
||||||
|
aliceSession2.cryptoService().decryptEvent(receivedEvent.root, "foo")
|
||||||
|
fail("should fail")
|
||||||
|
} catch (failure: Throwable) {
|
||||||
|
}
|
||||||
|
|
||||||
|
val outgoingRequestsBefore = aliceSession2.cryptoService().getOutgoingRoomKeyRequests()
|
||||||
|
// Try to request
|
||||||
|
aliceSession2.cryptoService().requestRoomKeyForEvent(receivedEvent.root)
|
||||||
|
|
||||||
|
val waitLatch = CountDownLatch(1)
|
||||||
|
val eventMegolmSessionId = receivedEvent.root.content.toModel<EncryptedEventContent>()?.sessionId
|
||||||
|
|
||||||
|
var outGoingRequestId: String? = null
|
||||||
|
|
||||||
|
mTestHelper.retryPeriodicallyWithLatch(waitLatch) {
|
||||||
|
aliceSession2.cryptoService().getOutgoingRoomKeyRequests()
|
||||||
|
.filter { req ->
|
||||||
|
// filter out request that was known before
|
||||||
|
!outgoingRequestsBefore.any { req.requestId == it.requestId }
|
||||||
|
}
|
||||||
|
.let {
|
||||||
|
val outgoing = it.firstOrNull { it.sessionId == eventMegolmSessionId }
|
||||||
|
outGoingRequestId = outgoing?.requestId
|
||||||
|
outgoing != null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mTestHelper.await(waitLatch)
|
||||||
|
|
||||||
|
Log.v("TEST", "=======> Outgoing requet Id is $outGoingRequestId")
|
||||||
|
|
||||||
|
val outgoingRequestAfter = aliceSession2.cryptoService().getOutgoingRoomKeyRequests()
|
||||||
|
|
||||||
|
// We should have a new request
|
||||||
|
Assert.assertTrue(outgoingRequestAfter.size > outgoingRequestsBefore.size)
|
||||||
|
Assert.assertNotNull(outgoingRequestAfter.first { it.sessionId == eventMegolmSessionId })
|
||||||
|
|
||||||
|
// The first session should see an incoming request
|
||||||
|
// the request should be refused, because the device is not trusted
|
||||||
|
mTestHelper.waitWithLatch { latch ->
|
||||||
|
mTestHelper.retryPeriodicallyWithLatch(latch) {
|
||||||
|
// DEBUG LOGS
|
||||||
|
aliceSession.cryptoService().getIncomingRoomKeyRequests().let {
|
||||||
|
Log.v("TEST", "Incoming request Session 1 (looking for $outGoingRequestId)")
|
||||||
|
Log.v("TEST", "=========================")
|
||||||
|
it.forEach { keyRequest ->
|
||||||
|
Log.v("TEST", "[ts${keyRequest.localCreationTimestamp}] requestId ${keyRequest.requestId}, for sessionId ${keyRequest.requestBody?.sessionId} is ${keyRequest.state}")
|
||||||
|
}
|
||||||
|
Log.v("TEST", "=========================")
|
||||||
|
}
|
||||||
|
|
||||||
|
val incoming = aliceSession.cryptoService().getIncomingRoomKeyRequests().firstOrNull { it.requestId == outGoingRequestId }
|
||||||
|
incoming?.state == GossipingRequestState.REJECTED
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
aliceSession2.cryptoService().decryptEvent(receivedEvent.root, "foo")
|
||||||
|
fail("should fail")
|
||||||
|
} catch (failure: Throwable) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark the device as trusted
|
||||||
|
aliceSession.cryptoService().setDeviceVerification(DeviceTrustLevel(crossSigningVerified = false, locallyVerified = true), aliceSession.myUserId,
|
||||||
|
aliceSession2.sessionParams.deviceId ?: "")
|
||||||
|
|
||||||
|
// Re request
|
||||||
|
aliceSession2.cryptoService().reRequestRoomKeyForEvent(receivedEvent.root)
|
||||||
|
|
||||||
|
mTestHelper.waitWithLatch { latch ->
|
||||||
|
mTestHelper.retryPeriodicallyWithLatch(latch) {
|
||||||
|
aliceSession.cryptoService().getIncomingRoomKeyRequests().let {
|
||||||
|
Log.v("TEST", "Incoming request Session 1")
|
||||||
|
Log.v("TEST", "=========================")
|
||||||
|
it.forEach {
|
||||||
|
Log.v("TEST", "requestId ${it.requestId}, for sessionId ${it.requestBody?.sessionId} is ${it.state}")
|
||||||
|
}
|
||||||
|
Log.v("TEST", "=========================")
|
||||||
|
|
||||||
|
it.any { it.requestBody?.sessionId == eventMegolmSessionId && it.state == GossipingRequestState.ACCEPTED }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread.sleep(6_000)
|
||||||
|
mTestHelper.waitWithLatch { latch ->
|
||||||
|
mTestHelper.retryPeriodicallyWithLatch(latch) {
|
||||||
|
aliceSession2.cryptoService().getOutgoingRoomKeyRequests().let {
|
||||||
|
it.any { it.requestBody?.sessionId == eventMegolmSessionId && it.state == OutgoingGossipingRequestState.CANCELLED }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
aliceSession2.cryptoService().decryptEvent(receivedEvent.root, "foo")
|
||||||
|
} catch (failure: Throwable) {
|
||||||
|
fail("should have been able to decrypt")
|
||||||
|
}
|
||||||
|
|
||||||
|
mTestHelper.signOutAndClose(aliceSession)
|
||||||
|
mTestHelper.signOutAndClose(aliceSession2)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_ShareSSSSSecret() {
|
fun test_ShareSSSSSecret() {
|
||||||
val aliceSession1 = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
val aliceSession1 = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
||||||
|
@ -199,13 +331,12 @@ class KeyShareTests : InstrumentedTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create an encrypted room and send a couple of messages
|
// Create an encrypted room and send a couple of messages
|
||||||
val roomId = mTestHelper.doSync<String> {
|
val roomId = mTestHelper.runBlockingTest {
|
||||||
aliceSession.createRoom(
|
aliceSession.createRoom(
|
||||||
CreateRoomParams().apply {
|
CreateRoomParams().apply {
|
||||||
visibility = RoomDirectoryVisibility.PRIVATE
|
visibility = RoomDirectoryVisibility.PRIVATE
|
||||||
enableEncryption()
|
enableEncryption()
|
||||||
},
|
}
|
||||||
it
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val roomAlicePov = aliceSession.getRoom(roomId)
|
val roomAlicePov = aliceSession.getRoom(roomId)
|
||||||
|
@ -238,8 +369,8 @@ class KeyShareTests : InstrumentedTest {
|
||||||
roomAlicePov.invite(bobSession.myUserId, null)
|
roomAlicePov.invite(bobSession.myUserId, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
mTestHelper.doSync<Unit> {
|
mTestHelper.runBlockingTest {
|
||||||
bobSession.joinRoom(roomAlicePov.roomId, null, emptyList(), it)
|
bobSession.joinRoom(roomAlicePov.roomId, null, emptyList())
|
||||||
}
|
}
|
||||||
|
|
||||||
// we want to discard alice outbound session
|
// we want to discard alice outbound session
|
||||||
|
|
|
@ -16,12 +16,10 @@
|
||||||
|
|
||||||
package org.matrix.android.sdk.api.auth.data
|
package org.matrix.android.sdk.api.auth.data
|
||||||
|
|
||||||
sealed class LoginFlowResult {
|
data class LoginFlowResult(
|
||||||
data class Success(
|
val supportedLoginTypes: List<String>,
|
||||||
val supportedLoginTypes: List<String>,
|
val ssoIdentityProviders: List<SsoIdentityProvider>?,
|
||||||
val ssoIdentityProviders: List<SsoIdentityProvider>?,
|
val isLoginAndRegistrationSupported: Boolean,
|
||||||
val isLoginAndRegistrationSupported: Boolean,
|
val homeServerUrl: String,
|
||||||
val homeServerUrl: String,
|
val isOutdatedHomeserver: Boolean
|
||||||
val isOutdatedHomeserver: Boolean
|
)
|
||||||
) : LoginFlowResult()
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.matrix.android.sdk.api.auth.registration
|
||||||
|
|
||||||
|
import org.matrix.android.sdk.api.failure.Failure
|
||||||
|
|
||||||
|
sealed class RegistrationAvailability {
|
||||||
|
object Available : RegistrationAvailability()
|
||||||
|
data class NotAvailable(val failure: Failure.ServerError) : RegistrationAvailability()
|
||||||
|
}
|
|
@ -36,6 +36,8 @@ interface RegistrationWizard {
|
||||||
|
|
||||||
suspend fun checkIfEmailHasBeenValidated(delayMillis: Long): RegistrationResult
|
suspend fun checkIfEmailHasBeenValidated(delayMillis: Long): RegistrationResult
|
||||||
|
|
||||||
|
suspend fun registrationAvailable(userName: String): RegistrationAvailability
|
||||||
|
|
||||||
val currentThreePid: String?
|
val currentThreePid: String?
|
||||||
|
|
||||||
// True when login and password has been sent with success to the homeserver
|
// True when login and password has been sent with success to the homeserver
|
||||||
|
|
|
@ -65,13 +65,16 @@ fun Throwable.isInvalidUIAAuth(): Boolean {
|
||||||
* Try to convert to a RegistrationFlowResponse. Return null in the cases it's not possible
|
* Try to convert to a RegistrationFlowResponse. Return null in the cases it's not possible
|
||||||
*/
|
*/
|
||||||
fun Throwable.toRegistrationFlowResponse(): RegistrationFlowResponse? {
|
fun Throwable.toRegistrationFlowResponse(): RegistrationFlowResponse? {
|
||||||
return if (this is Failure.OtherServerError && httpCode == 401) {
|
return if (this is Failure.OtherServerError
|
||||||
|
&& httpCode == HttpsURLConnection.HTTP_UNAUTHORIZED /* 401 */) {
|
||||||
tryOrNull {
|
tryOrNull {
|
||||||
MoshiProvider.providesMoshi()
|
MoshiProvider.providesMoshi()
|
||||||
.adapter(RegistrationFlowResponse::class.java)
|
.adapter(RegistrationFlowResponse::class.java)
|
||||||
.fromJson(errorBody)
|
.fromJson(errorBody)
|
||||||
}
|
}
|
||||||
} else if (this is Failure.ServerError && httpCode == 401 && error.code == MatrixError.M_FORBIDDEN) {
|
} else if (this is Failure.ServerError
|
||||||
|
&& httpCode == HttpsURLConnection.HTTP_UNAUTHORIZED /* 401 */
|
||||||
|
&& error.code == MatrixError.M_FORBIDDEN) {
|
||||||
// This happens when the submission for this stage was bad (like bad password)
|
// This happens when the submission for this stage was bad (like bad password)
|
||||||
if (error.session != null && error.flows != null) {
|
if (error.session != null && error.flows != null) {
|
||||||
RegistrationFlowResponse(
|
RegistrationFlowResponse(
|
||||||
|
@ -87,3 +90,11 @@ fun Throwable.toRegistrationFlowResponse(): RegistrationFlowResponse? {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Throwable.isRegistrationAvailabilityError(): Boolean {
|
||||||
|
return this is Failure.ServerError
|
||||||
|
&& httpCode == HttpsURLConnection.HTTP_BAD_REQUEST /* 400 */
|
||||||
|
&& (error.code == MatrixError.M_USER_IN_USE
|
||||||
|
|| error.code == MatrixError.M_INVALID_USERNAME
|
||||||
|
|| error.code == MatrixError.M_EXCLUSIVE)
|
||||||
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@ package org.matrix.android.sdk.api.session.room
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.paging.PagedList
|
import androidx.paging.PagedList
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
|
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
|
import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
|
||||||
|
@ -26,7 +25,6 @@ import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
||||||
import org.matrix.android.sdk.api.session.room.peeking.PeekResult
|
import org.matrix.android.sdk.api.session.room.peeking.PeekResult
|
||||||
import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount
|
import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount
|
||||||
import org.matrix.android.sdk.api.util.Cancelable
|
|
||||||
import org.matrix.android.sdk.api.util.Optional
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
import org.matrix.android.sdk.internal.session.room.alias.RoomAliasDescription
|
import org.matrix.android.sdk.internal.session.room.alias.RoomAliasDescription
|
||||||
|
|
||||||
|
@ -38,22 +36,19 @@ interface RoomService {
|
||||||
/**
|
/**
|
||||||
* Create a room asynchronously
|
* Create a room asynchronously
|
||||||
*/
|
*/
|
||||||
fun createRoom(createRoomParams: CreateRoomParams,
|
suspend fun createRoom(createRoomParams: CreateRoomParams): String
|
||||||
callback: MatrixCallback<String>): Cancelable
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a direct room asynchronously. This is a facility method to create a direct room with the necessary parameters
|
* Create a direct room asynchronously. This is a facility method to create a direct room with the necessary parameters
|
||||||
*/
|
*/
|
||||||
fun createDirectRoom(otherUserId: String,
|
suspend fun createDirectRoom(otherUserId: String): String {
|
||||||
callback: MatrixCallback<String>): Cancelable {
|
|
||||||
return createRoom(
|
return createRoom(
|
||||||
CreateRoomParams()
|
CreateRoomParams()
|
||||||
.apply {
|
.apply {
|
||||||
invitedUserIds.add(otherUserId)
|
invitedUserIds.add(otherUserId)
|
||||||
setDirectMessage()
|
setDirectMessage()
|
||||||
enableEncryptionIfInvitedUsersSupportIt = true
|
enableEncryptionIfInvitedUsersSupportIt = true
|
||||||
},
|
}
|
||||||
callback
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,10 +58,9 @@ interface RoomService {
|
||||||
* @param reason optional reason for joining the room
|
* @param reason optional reason for joining the room
|
||||||
* @param viaServers the servers to attempt to join the room through. One of the servers must be participating in the room.
|
* @param viaServers the servers to attempt to join the room through. One of the servers must be participating in the room.
|
||||||
*/
|
*/
|
||||||
fun joinRoom(roomIdOrAlias: String,
|
suspend fun joinRoom(roomIdOrAlias: String,
|
||||||
reason: String? = null,
|
reason: String? = null,
|
||||||
viaServers: List<String> = emptyList(),
|
viaServers: List<String> = emptyList())
|
||||||
callback: MatrixCallback<Unit>): Cancelable
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a room from a roomId
|
* Get a room from a roomId
|
||||||
|
@ -112,20 +106,18 @@ interface RoomService {
|
||||||
* Inform the Matrix SDK that a room is displayed.
|
* Inform the Matrix SDK that a room is displayed.
|
||||||
* The SDK will update the breadcrumbs in the user account data
|
* The SDK will update the breadcrumbs in the user account data
|
||||||
*/
|
*/
|
||||||
fun onRoomDisplayed(roomId: String): Cancelable
|
suspend fun onRoomDisplayed(roomId: String)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mark all rooms as read
|
* Mark all rooms as read
|
||||||
*/
|
*/
|
||||||
fun markAllAsRead(roomIds: List<String>,
|
suspend fun markAllAsRead(roomIds: List<String>)
|
||||||
callback: MatrixCallback<Unit>): Cancelable
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve a room alias to a room ID.
|
* Resolve a room alias to a room ID.
|
||||||
*/
|
*/
|
||||||
fun getRoomIdByAlias(roomAlias: String,
|
suspend fun getRoomIdByAlias(roomAlias: String,
|
||||||
searchOnServer: Boolean,
|
searchOnServer: Boolean): Optional<RoomAliasDescription>
|
||||||
callback: MatrixCallback<Optional<RoomAliasDescription>>): Cancelable
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete a room alias
|
* Delete a room alias
|
||||||
|
@ -172,14 +164,14 @@ interface RoomService {
|
||||||
/**
|
/**
|
||||||
* Get some state events about a room
|
* Get some state events about a room
|
||||||
*/
|
*/
|
||||||
fun getRoomState(roomId: String, callback: MatrixCallback<List<Event>>)
|
suspend fun getRoomState(roomId: String): List<Event>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use this if you want to get information from a room that you are not yet in (or invited)
|
* Use this if you want to get information from a room that you are not yet in (or invited)
|
||||||
* It might be possible to get some information on this room if it is public or if guest access is allowed
|
* It might be possible to get some information on this room if it is public or if guest access is allowed
|
||||||
* This call will try to gather some information on this room, but it could fail and get nothing more
|
* This call will try to gather some information on this room, but it could fail and get nothing more
|
||||||
*/
|
*/
|
||||||
fun peekRoom(roomIdOrAlias: String, callback: MatrixCallback<PeekResult>)
|
suspend fun peekRoom(roomIdOrAlias: String): PeekResult
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO Doc
|
* TODO Doc
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package org.matrix.android.sdk.internal.auth
|
package org.matrix.android.sdk.internal.auth
|
||||||
|
|
||||||
import org.matrix.android.sdk.api.auth.data.Credentials
|
import org.matrix.android.sdk.api.auth.data.Credentials
|
||||||
|
import org.matrix.android.sdk.internal.auth.data.Availability
|
||||||
import org.matrix.android.sdk.internal.auth.data.LoginFlowResponse
|
import org.matrix.android.sdk.internal.auth.data.LoginFlowResponse
|
||||||
import org.matrix.android.sdk.internal.auth.data.PasswordLoginParams
|
import org.matrix.android.sdk.internal.auth.data.PasswordLoginParams
|
||||||
import org.matrix.android.sdk.internal.auth.data.RiotConfig
|
import org.matrix.android.sdk.internal.auth.data.RiotConfig
|
||||||
|
@ -34,6 +35,7 @@ import retrofit2.http.GET
|
||||||
import retrofit2.http.Headers
|
import retrofit2.http.Headers
|
||||||
import retrofit2.http.POST
|
import retrofit2.http.POST
|
||||||
import retrofit2.http.Path
|
import retrofit2.http.Path
|
||||||
|
import retrofit2.http.Query
|
||||||
import retrofit2.http.Url
|
import retrofit2.http.Url
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -65,6 +67,12 @@ internal interface AuthAPI {
|
||||||
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "register")
|
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "register")
|
||||||
suspend fun register(@Body registrationParams: RegistrationParams): Credentials
|
suspend fun register(@Body registrationParams: RegistrationParams): Credentials
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks to see if a username is available, and valid, for the server.
|
||||||
|
*/
|
||||||
|
@GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "register/available")
|
||||||
|
suspend fun registerAvailable(@Query("username") username: String): Availability
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add 3Pid during registration
|
* Add 3Pid during registration
|
||||||
* Ref: https://gist.github.com/jryans/839a09bf0c5a70e2f36ed990d50ed928
|
* Ref: https://gist.github.com/jryans/839a09bf0c5a70e2f36ed990d50ed928
|
||||||
|
|
|
@ -144,16 +144,14 @@ internal class DefaultAuthenticationService @Inject constructor(
|
||||||
}
|
}
|
||||||
return result.fold(
|
return result.fold(
|
||||||
{
|
{
|
||||||
if (it is LoginFlowResult.Success) {
|
// The homeserver exists and up to date, keep the config
|
||||||
// The homeserver exists and up to date, keep the config
|
// Homeserver url may have been changed, if it was a Riot url
|
||||||
// Homeserver url may have been changed, if it was a Riot url
|
val alteredHomeServerConnectionConfig = homeServerConnectionConfig.copy(
|
||||||
val alteredHomeServerConnectionConfig = homeServerConnectionConfig.copy(
|
homeServerUri = Uri.parse(it.homeServerUrl)
|
||||||
homeServerUri = Uri.parse(it.homeServerUrl)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
pendingSessionData = PendingSessionData(alteredHomeServerConnectionConfig)
|
pendingSessionData = PendingSessionData(alteredHomeServerConnectionConfig)
|
||||||
.also { data -> pendingSessionStore.savePendingSessionData(data) }
|
.also { data -> pendingSessionStore.savePendingSessionData(data) }
|
||||||
}
|
|
||||||
it
|
it
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -307,12 +305,12 @@ internal class DefaultAuthenticationService @Inject constructor(
|
||||||
val loginFlowResponse = executeRequest(null) {
|
val loginFlowResponse = executeRequest(null) {
|
||||||
authAPI.getLoginFlows()
|
authAPI.getLoginFlows()
|
||||||
}
|
}
|
||||||
return LoginFlowResult.Success(
|
return LoginFlowResult(
|
||||||
loginFlowResponse.flows.orEmpty().mapNotNull { it.type },
|
supportedLoginTypes = loginFlowResponse.flows.orEmpty().mapNotNull { it.type },
|
||||||
loginFlowResponse.flows.orEmpty().firstOrNull { it.type == LoginFlowTypes.SSO }?.ssoIdentityProvider,
|
ssoIdentityProviders = loginFlowResponse.flows.orEmpty().firstOrNull { it.type == LoginFlowTypes.SSO }?.ssoIdentityProvider,
|
||||||
versions.isLoginAndRegistrationSupportedBySdk(),
|
isLoginAndRegistrationSupported = versions.isLoginAndRegistrationSupportedBySdk(),
|
||||||
homeServerUrl,
|
homeServerUrl = homeServerUrl,
|
||||||
!versions.isSupportedBySdk()
|
isOutdatedHomeserver = !versions.isSupportedBySdk()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.matrix.android.sdk.internal.auth.data
|
||||||
|
|
||||||
|
import com.squareup.moshi.Json
|
||||||
|
import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
internal data class Availability(
|
||||||
|
/**
|
||||||
|
* A flag to indicate that the username is available. This should always be true when the server replies with 200 OK.
|
||||||
|
*/
|
||||||
|
@Json(name = "available")
|
||||||
|
val available: Boolean? = null
|
||||||
|
)
|
|
@ -19,6 +19,7 @@ package org.matrix.android.sdk.internal.auth.registration
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
|
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.RegisterThreePid
|
||||||
|
import org.matrix.android.sdk.api.auth.registration.RegistrationAvailability
|
||||||
import org.matrix.android.sdk.api.auth.registration.RegistrationResult
|
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.RegistrationWizard
|
||||||
import org.matrix.android.sdk.api.auth.registration.toFlowResult
|
import org.matrix.android.sdk.api.auth.registration.toFlowResult
|
||||||
|
@ -40,9 +41,10 @@ internal class DefaultRegistrationWizard(
|
||||||
|
|
||||||
private var pendingSessionData: PendingSessionData = pendingSessionStore.getPendingSessionData() ?: error("Pending session data should exist here")
|
private var pendingSessionData: PendingSessionData = pendingSessionStore.getPendingSessionData() ?: error("Pending session data should exist here")
|
||||||
|
|
||||||
private val registerTask = DefaultRegisterTask(authAPI)
|
private val registerTask: RegisterTask = DefaultRegisterTask(authAPI)
|
||||||
private val registerAddThreePidTask = DefaultRegisterAddThreePidTask(authAPI)
|
private val registerAvailableTask: RegisterAvailableTask = DefaultRegisterAvailableTask(authAPI)
|
||||||
private val validateCodeTask = DefaultValidateCodeTask(authAPI)
|
private val registerAddThreePidTask: RegisterAddThreePidTask = DefaultRegisterAddThreePidTask(authAPI)
|
||||||
|
private val validateCodeTask: ValidateCodeTask = DefaultValidateCodeTask(authAPI)
|
||||||
|
|
||||||
override val currentThreePid: String?
|
override val currentThreePid: String?
|
||||||
get() {
|
get() {
|
||||||
|
@ -203,4 +205,8 @@ internal class DefaultRegistrationWizard(
|
||||||
val session = sessionCreator.createSession(credentials, pendingSessionData.homeServerConnectionConfig)
|
val session = sessionCreator.createSession(credentials, pendingSessionData.homeServerConnectionConfig)
|
||||||
return RegistrationResult.Success(session)
|
return RegistrationResult.Success(session)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun registrationAvailable(userName: String): RegistrationAvailability {
|
||||||
|
return registerAvailableTask.execute(RegisterAvailableTask.Params(userName))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.matrix.android.sdk.internal.auth.registration
|
||||||
|
|
||||||
|
import org.matrix.android.sdk.api.auth.registration.RegistrationAvailability
|
||||||
|
import org.matrix.android.sdk.api.failure.Failure
|
||||||
|
import org.matrix.android.sdk.api.failure.isRegistrationAvailabilityError
|
||||||
|
import org.matrix.android.sdk.internal.auth.AuthAPI
|
||||||
|
import org.matrix.android.sdk.internal.network.executeRequest
|
||||||
|
import org.matrix.android.sdk.internal.task.Task
|
||||||
|
|
||||||
|
internal interface RegisterAvailableTask : Task<RegisterAvailableTask.Params, RegistrationAvailability> {
|
||||||
|
data class Params(
|
||||||
|
val userName: String
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class DefaultRegisterAvailableTask(private val authAPI: AuthAPI) : RegisterAvailableTask {
|
||||||
|
override suspend fun execute(params: RegisterAvailableTask.Params): RegistrationAvailability {
|
||||||
|
return try {
|
||||||
|
executeRequest(null) {
|
||||||
|
authAPI.registerAvailable(params.userName)
|
||||||
|
}
|
||||||
|
RegistrationAvailability.Available
|
||||||
|
} catch (exception: Throwable) {
|
||||||
|
if (exception.isRegistrationAvailabilityError()) {
|
||||||
|
RegistrationAvailability.NotAvailable(exception as Failure.ServerError)
|
||||||
|
} else {
|
||||||
|
throw exception
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -44,7 +44,7 @@ data class CryptoDeviceInfo(
|
||||||
*/
|
*/
|
||||||
fun fingerprint(): String? {
|
fun fingerprint(): String? {
|
||||||
return keys
|
return keys
|
||||||
?.takeIf { !deviceId.isBlank() }
|
?.takeIf { deviceId.isNotBlank() }
|
||||||
?.get("ed25519:$deviceId")
|
?.get("ed25519:$deviceId")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ data class CryptoDeviceInfo(
|
||||||
*/
|
*/
|
||||||
fun identityKey(): String? {
|
fun identityKey(): String? {
|
||||||
return keys
|
return keys
|
||||||
?.takeIf { !deviceId.isBlank() }
|
?.takeIf { deviceId.isNotBlank() }
|
||||||
?.get("curve25519:$deviceId")
|
?.get("curve25519:$deviceId")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -103,7 +103,7 @@ data class MXDeviceInfo(
|
||||||
*/
|
*/
|
||||||
fun fingerprint(): String? {
|
fun fingerprint(): String? {
|
||||||
return keys
|
return keys
|
||||||
?.takeIf { !deviceId.isBlank() }
|
?.takeIf { deviceId.isNotBlank() }
|
||||||
?.get("ed25519:$deviceId")
|
?.get("ed25519:$deviceId")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,7 +112,7 @@ data class MXDeviceInfo(
|
||||||
*/
|
*/
|
||||||
fun identityKey(): String? {
|
fun identityKey(): String? {
|
||||||
return keys
|
return keys
|
||||||
?.takeIf { !deviceId.isBlank() }
|
?.takeIf { deviceId.isNotBlank() }
|
||||||
?.get("curve25519:$deviceId")
|
?.get("curve25519:$deviceId")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ package org.matrix.android.sdk.internal.network
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import org.matrix.android.sdk.api.failure.Failure
|
import org.matrix.android.sdk.api.failure.Failure
|
||||||
|
import org.matrix.android.sdk.api.failure.MatrixError
|
||||||
import org.matrix.android.sdk.api.failure.getRetryDelay
|
import org.matrix.android.sdk.api.failure.getRetryDelay
|
||||||
import org.matrix.android.sdk.api.failure.shouldBeRetried
|
import org.matrix.android.sdk.api.failure.shouldBeRetried
|
||||||
import org.matrix.android.sdk.internal.network.ssl.CertUtil
|
import org.matrix.android.sdk.internal.network.ssl.CertUtil
|
||||||
|
@ -28,22 +29,21 @@ import java.io.IOException
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute a request from the requestBlock and handle some of the Exception it could generate
|
* Execute a request from the requestBlock and handle some of the Exception it could generate
|
||||||
|
* Ref: https://github.com/matrix-org/matrix-js-sdk/blob/develop/src/scheduler.js#L138-L175
|
||||||
*
|
*
|
||||||
* @param globalErrorReceiver will be use to notify error such as invalid token error. See [GlobalError]
|
* @param globalErrorReceiver will be use to notify error such as invalid token error. See [GlobalError]
|
||||||
* @param canRetry if set to true, the request will be executed again in case of error, after a delay
|
* @param canRetry if set to true, the request will be executed again in case of error, after a delay
|
||||||
* @param initialDelayBeforeRetry the first delay to wait before a request is retried. Will be doubled after each retry
|
|
||||||
* @param maxDelayBeforeRetry the max delay to wait before a retry
|
* @param maxDelayBeforeRetry the max delay to wait before a retry
|
||||||
* @param maxRetriesCount the max number of retries
|
* @param maxRetriesCount the max number of retries
|
||||||
* @param requestBlock a suspend lambda to perform the network request
|
* @param requestBlock a suspend lambda to perform the network request
|
||||||
*/
|
*/
|
||||||
internal suspend inline fun <DATA> executeRequest(globalErrorReceiver: GlobalErrorReceiver?,
|
internal suspend inline fun <DATA> executeRequest(globalErrorReceiver: GlobalErrorReceiver?,
|
||||||
canRetry: Boolean = false,
|
canRetry: Boolean = false,
|
||||||
initialDelayBeforeRetry: Long = 100L,
|
maxDelayBeforeRetry: Long = 32_000L,
|
||||||
maxDelayBeforeRetry: Long = 10_000L,
|
maxRetriesCount: Int = 4,
|
||||||
maxRetriesCount: Int = Int.MAX_VALUE,
|
|
||||||
noinline requestBlock: suspend () -> DATA): DATA {
|
noinline requestBlock: suspend () -> DATA): DATA {
|
||||||
var currentRetryCount = 0
|
var currentRetryCount = 0
|
||||||
var currentDelay = initialDelayBeforeRetry
|
var currentDelay = 1_000L
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
|
@ -72,9 +72,16 @@ internal suspend inline fun <DATA> executeRequest(globalErrorReceiver: GlobalErr
|
||||||
// }
|
// }
|
||||||
?.also { unrecognizedCertificateException -> throw unrecognizedCertificateException }
|
?.also { unrecognizedCertificateException -> throw unrecognizedCertificateException }
|
||||||
|
|
||||||
if (canRetry && currentRetryCount++ < maxRetriesCount && exception.shouldBeRetried()) {
|
currentRetryCount++
|
||||||
// In case of 429, ensure we wait enough
|
|
||||||
delay(currentDelay.coerceAtLeast(exception.getRetryDelay(0)))
|
if (exception is Failure.ServerError
|
||||||
|
&& exception.httpCode == 429
|
||||||
|
&& exception.error.code == MatrixError.M_LIMIT_EXCEEDED
|
||||||
|
&& currentRetryCount < maxRetriesCount) {
|
||||||
|
// 429, we can retry
|
||||||
|
delay(exception.getRetryDelay(1_000))
|
||||||
|
} else if (canRetry && currentRetryCount < maxRetriesCount && exception.shouldBeRetried()) {
|
||||||
|
delay(currentDelay)
|
||||||
currentDelay = currentDelay.times(2L).coerceAtMost(maxDelayBeforeRetry)
|
currentDelay = currentDelay.times(2L).coerceAtMost(maxDelayBeforeRetry)
|
||||||
// Try again (loop)
|
// Try again (loop)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -20,7 +20,6 @@ import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.Transformations
|
import androidx.lifecycle.Transformations
|
||||||
import androidx.paging.PagedList
|
import androidx.paging.PagedList
|
||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
import org.matrix.android.sdk.api.session.room.Room
|
import org.matrix.android.sdk.api.session.room.Room
|
||||||
import org.matrix.android.sdk.api.session.room.RoomService
|
import org.matrix.android.sdk.api.session.room.RoomService
|
||||||
|
@ -32,7 +31,6 @@ import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
||||||
import org.matrix.android.sdk.api.session.room.peeking.PeekResult
|
import org.matrix.android.sdk.api.session.room.peeking.PeekResult
|
||||||
import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount
|
import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount
|
||||||
import org.matrix.android.sdk.api.util.Cancelable
|
|
||||||
import org.matrix.android.sdk.api.util.Optional
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
import org.matrix.android.sdk.api.util.toOptional
|
import org.matrix.android.sdk.api.util.toOptional
|
||||||
import org.matrix.android.sdk.internal.database.mapper.asDomain
|
import org.matrix.android.sdk.internal.database.mapper.asDomain
|
||||||
|
@ -50,8 +48,6 @@ import org.matrix.android.sdk.internal.session.room.peeking.ResolveRoomStateTask
|
||||||
import org.matrix.android.sdk.internal.session.room.read.MarkAllRoomsReadTask
|
import org.matrix.android.sdk.internal.session.room.read.MarkAllRoomsReadTask
|
||||||
import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryDataSource
|
import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryDataSource
|
||||||
import org.matrix.android.sdk.internal.session.user.accountdata.UpdateBreadcrumbsTask
|
import org.matrix.android.sdk.internal.session.user.accountdata.UpdateBreadcrumbsTask
|
||||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
|
||||||
import org.matrix.android.sdk.internal.task.configureWith
|
|
||||||
import org.matrix.android.sdk.internal.util.fetchCopied
|
import org.matrix.android.sdk.internal.util.fetchCopied
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -67,16 +63,11 @@ internal class DefaultRoomService @Inject constructor(
|
||||||
private val peekRoomTask: PeekRoomTask,
|
private val peekRoomTask: PeekRoomTask,
|
||||||
private val roomGetter: RoomGetter,
|
private val roomGetter: RoomGetter,
|
||||||
private val roomSummaryDataSource: RoomSummaryDataSource,
|
private val roomSummaryDataSource: RoomSummaryDataSource,
|
||||||
private val roomChangeMembershipStateDataSource: RoomChangeMembershipStateDataSource,
|
private val roomChangeMembershipStateDataSource: RoomChangeMembershipStateDataSource
|
||||||
private val taskExecutor: TaskExecutor
|
|
||||||
) : RoomService {
|
) : RoomService {
|
||||||
|
|
||||||
override fun createRoom(createRoomParams: CreateRoomParams, callback: MatrixCallback<String>): Cancelable {
|
override suspend fun createRoom(createRoomParams: CreateRoomParams): String {
|
||||||
return createRoomTask
|
return createRoomTask.execute(createRoomParams)
|
||||||
.configureWith(createRoomParams) {
|
|
||||||
this.callback = callback
|
|
||||||
}
|
|
||||||
.executeBy(taskExecutor)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getRoom(roomId: String): Room? {
|
override fun getRoom(roomId: String): Room? {
|
||||||
|
@ -121,34 +112,20 @@ internal class DefaultRoomService @Inject constructor(
|
||||||
return roomSummaryDataSource.getBreadcrumbsLive(queryParams)
|
return roomSummaryDataSource.getBreadcrumbsLive(queryParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRoomDisplayed(roomId: String): Cancelable {
|
override suspend fun onRoomDisplayed(roomId: String) {
|
||||||
return updateBreadcrumbsTask
|
updateBreadcrumbsTask.execute(UpdateBreadcrumbsTask.Params(roomId))
|
||||||
.configureWith(UpdateBreadcrumbsTask.Params(roomId))
|
|
||||||
.executeBy(taskExecutor)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun joinRoom(roomIdOrAlias: String, reason: String?, viaServers: List<String>, callback: MatrixCallback<Unit>): Cancelable {
|
override suspend fun joinRoom(roomIdOrAlias: String, reason: String?, viaServers: List<String>) {
|
||||||
return joinRoomTask
|
joinRoomTask.execute(JoinRoomTask.Params(roomIdOrAlias, reason, viaServers))
|
||||||
.configureWith(JoinRoomTask.Params(roomIdOrAlias, reason, viaServers)) {
|
|
||||||
this.callback = callback
|
|
||||||
}
|
|
||||||
.executeBy(taskExecutor)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun markAllAsRead(roomIds: List<String>, callback: MatrixCallback<Unit>): Cancelable {
|
override suspend fun markAllAsRead(roomIds: List<String>) {
|
||||||
return markAllRoomsReadTask
|
markAllRoomsReadTask.execute(MarkAllRoomsReadTask.Params(roomIds))
|
||||||
.configureWith(MarkAllRoomsReadTask.Params(roomIds)) {
|
|
||||||
this.callback = callback
|
|
||||||
}
|
|
||||||
.executeBy(taskExecutor)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getRoomIdByAlias(roomAlias: String, searchOnServer: Boolean, callback: MatrixCallback<Optional<RoomAliasDescription>>): Cancelable {
|
override suspend fun getRoomIdByAlias(roomAlias: String, searchOnServer: Boolean): Optional<RoomAliasDescription> {
|
||||||
return roomIdByAliasTask
|
return roomIdByAliasTask.execute(GetRoomIdByAliasTask.Params(roomAlias, searchOnServer))
|
||||||
.configureWith(GetRoomIdByAliasTask.Params(roomAlias, searchOnServer)) {
|
|
||||||
this.callback = callback
|
|
||||||
}
|
|
||||||
.executeBy(taskExecutor)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun deleteRoomAlias(roomAlias: String) {
|
override suspend fun deleteRoomAlias(roomAlias: String) {
|
||||||
|
@ -179,19 +156,11 @@ internal class DefaultRoomService @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getRoomState(roomId: String, callback: MatrixCallback<List<Event>>) {
|
override suspend fun getRoomState(roomId: String): List<Event> {
|
||||||
resolveRoomStateTask
|
return resolveRoomStateTask.execute(ResolveRoomStateTask.Params(roomId))
|
||||||
.configureWith(ResolveRoomStateTask.Params(roomId)) {
|
|
||||||
this.callback = callback
|
|
||||||
}
|
|
||||||
.executeBy(taskExecutor)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun peekRoom(roomIdOrAlias: String, callback: MatrixCallback<PeekResult>) {
|
override suspend fun peekRoom(roomIdOrAlias: String): PeekResult {
|
||||||
peekRoomTask
|
return peekRoomTask.execute(PeekRoomTask.Params(roomIdOrAlias))
|
||||||
.configureWith(PeekRoomTask.Params(roomIdOrAlias)) {
|
|
||||||
this.callback = callback
|
|
||||||
}
|
|
||||||
.executeBy(taskExecutor)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,7 +91,7 @@ internal class SendEventWorker(context: Context,
|
||||||
if (/*currentAttemptCount >= MAX_NUMBER_OF_RETRY_BEFORE_FAILING ||**/ !exception.shouldBeRetried()) {
|
if (/*currentAttemptCount >= MAX_NUMBER_OF_RETRY_BEFORE_FAILING ||**/ !exception.shouldBeRetried()) {
|
||||||
Timber.e("## SendEvent: [${System.currentTimeMillis()}] Send event Failed cannot retry ${params.eventId} > ${exception.localizedMessage}")
|
Timber.e("## SendEvent: [${System.currentTimeMillis()}] Send event Failed cannot retry ${params.eventId} > ${exception.localizedMessage}")
|
||||||
localEchoRepository.updateSendState(event.eventId, event.roomId, SendState.UNDELIVERED)
|
localEchoRepository.updateSendState(event.eventId, event.roomId, SendState.UNDELIVERED)
|
||||||
return Result.success()
|
Result.success()
|
||||||
} else {
|
} else {
|
||||||
Timber.e("## SendEvent: [${System.currentTimeMillis()}] Send event Failed schedule retry ${params.eventId} > ${exception.localizedMessage}")
|
Timber.e("## SendEvent: [${System.currentTimeMillis()}] Send event Failed schedule retry ${params.eventId} > ${exception.localizedMessage}")
|
||||||
Result.retry()
|
Result.retry()
|
||||||
|
|
|
@ -114,11 +114,13 @@ for emoji in emoji_picker_datasource_emojis:
|
||||||
|
|
||||||
# If additional keywords exist, add them to emoji_picker_datasource_emojis
|
# If additional keywords exist, add them to emoji_picker_datasource_emojis
|
||||||
# Avoid duplicates and keep order. Put official unicode.com keywords first and extend up with emojilib ones.
|
# Avoid duplicates and keep order. Put official unicode.com keywords first and extend up with emojilib ones.
|
||||||
new_keywords = OrderedDict.fromkeys(emoji_picker_datasource_emojis[emoji]["j"] + emoji_additional_keywords).keys()
|
new_keywords = OrderedDict.fromkeys(emoji_picker_datasource_emojis[emoji]["j"] + emoji_additional_keywords)
|
||||||
# Remove the ones derived from the unicode name
|
# Remove the ones derived from the unicode name
|
||||||
new_keywords = new_keywords - {emoji.replace("-", "_")} - {emoji.replace("-", " ")} - {emoji_name}
|
for keyword in [emoji.replace("-", "_")] + [emoji.replace("-", " ")] + [emoji_name]:
|
||||||
|
if keyword in new_keywords:
|
||||||
|
new_keywords.pop(keyword)
|
||||||
# Write new keywords back
|
# Write new keywords back
|
||||||
emoji_picker_datasource_emojis[emoji]["j"] = list(new_keywords)
|
emoji_picker_datasource_emojis[emoji]["j"] = list(new_keywords.keys())
|
||||||
|
|
||||||
# Filter out components from unicode 13.1 (as they are not suitable for single-emoji reactions)
|
# Filter out components from unicode 13.1 (as they are not suitable for single-emoji reactions)
|
||||||
emoji_picker_datasource['categories'] = [x for x in emoji_picker_datasource['categories'] if x['id'] != 'component']
|
emoji_picker_datasource['categories'] = [x for x in emoji_picker_datasource['categories'] if x['id'] != 'component']
|
||||||
|
|
|
@ -14,7 +14,7 @@ kapt {
|
||||||
// Note: 2 digits max for each value
|
// Note: 2 digits max for each value
|
||||||
ext.versionMajor = 1
|
ext.versionMajor = 1
|
||||||
ext.versionMinor = 1
|
ext.versionMinor = 1
|
||||||
ext.versionPatch = 4
|
ext.versionPatch = 7
|
||||||
|
|
||||||
static def getGitTimestamp() {
|
static def getGitTimestamp() {
|
||||||
def cmd = 'git show -s --format=%ct'
|
def cmd = 'git show -s --format=%ct'
|
||||||
|
@ -297,7 +297,7 @@ dependencies {
|
||||||
def big_image_viewer_version = '1.7.1'
|
def big_image_viewer_version = '1.7.1'
|
||||||
def glide_version = '4.12.0'
|
def glide_version = '4.12.0'
|
||||||
def moshi_version = '1.12.0'
|
def moshi_version = '1.12.0'
|
||||||
def daggerVersion = '2.33'
|
def daggerVersion = '2.34'
|
||||||
def autofill_version = "1.1.0"
|
def autofill_version = "1.1.0"
|
||||||
def work_version = '2.5.0'
|
def work_version = '2.5.0'
|
||||||
def arch_version = '2.1.0'
|
def arch_version = '2.1.0'
|
||||||
|
@ -320,13 +320,13 @@ dependencies {
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version"
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version"
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version"
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version"
|
||||||
|
|
||||||
implementation "androidx.recyclerview:recyclerview:1.2.0-rc01"
|
implementation "androidx.recyclerview:recyclerview:1.2.0"
|
||||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||||
implementation "androidx.fragment:fragment-ktx:$fragment_version"
|
implementation "androidx.fragment:fragment-ktx:$fragment_version"
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
|
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
|
||||||
implementation "androidx.sharetarget:sharetarget:1.1.0"
|
implementation "androidx.sharetarget:sharetarget:1.1.0"
|
||||||
implementation 'androidx.core:core-ktx:1.3.2'
|
implementation 'androidx.core:core-ktx:1.3.2'
|
||||||
implementation "androidx.media:media:1.2.1"
|
implementation "androidx.media:media:1.3.0"
|
||||||
|
|
||||||
implementation "org.threeten:threetenbp:1.4.0:no-tzdb"
|
implementation "org.threeten:threetenbp:1.4.0:no-tzdb"
|
||||||
implementation "com.gabrielittner.threetenbp:lazythreetenbp:0.9.0"
|
implementation "com.gabrielittner.threetenbp:lazythreetenbp:0.9.0"
|
||||||
|
@ -423,7 +423,7 @@ dependencies {
|
||||||
kapt "com.google.dagger:dagger-compiler:$daggerVersion"
|
kapt "com.google.dagger:dagger-compiler:$daggerVersion"
|
||||||
|
|
||||||
// gplay flavor only
|
// gplay flavor only
|
||||||
gplayImplementation('com.google.firebase:firebase-messaging:21.0.1') {
|
gplayImplementation('com.google.firebase:firebase-messaging:21.1.0') {
|
||||||
exclude group: 'com.google.firebase', module: 'firebase-core'
|
exclude group: 'com.google.firebase', module: 'firebase-core'
|
||||||
exclude group: 'com.google.firebase', module: 'firebase-analytics'
|
exclude group: 'com.google.firebase', module: 'firebase-analytics'
|
||||||
exclude group: 'com.google.firebase', module: 'firebase-measurement-connector'
|
exclude group: 'com.google.firebase', module: 'firebase-measurement-connector'
|
||||||
|
|
|
@ -0,0 +1,128 @@
|
||||||
|
/*
|
||||||
|
* 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.roomdirectory
|
||||||
|
|
||||||
|
import im.vector.app.InstrumentedTest
|
||||||
|
import im.vector.app.core.utils.AssetReader
|
||||||
|
import org.amshove.kluent.shouldBe
|
||||||
|
import org.junit.FixMethodOrder
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.junit.runners.JUnit4
|
||||||
|
import org.junit.runners.MethodSorters
|
||||||
|
|
||||||
|
@RunWith(JUnit4::class)
|
||||||
|
@FixMethodOrder(MethodSorters.JVM)
|
||||||
|
class ExplicitTermFilterTest : InstrumentedTest {
|
||||||
|
|
||||||
|
private val explicitTermFilter = ExplicitTermFilter(AssetReader(context()))
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun isValidEmptyTrue() {
|
||||||
|
explicitTermFilter.isValid("") shouldBe true
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun isValidTrue() {
|
||||||
|
explicitTermFilter.isValid("Hello") shouldBe true
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun isValidFalse() {
|
||||||
|
explicitTermFilter.isValid("nsfw") shouldBe false
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun isValidUpCaseFalse() {
|
||||||
|
explicitTermFilter.isValid("Nsfw") shouldBe false
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun isValidMultilineTrue() {
|
||||||
|
explicitTermFilter.isValid("Hello\nWorld") shouldBe true
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun isValidMultilineFalse() {
|
||||||
|
explicitTermFilter.isValid("Hello\nnsfw") shouldBe false
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun isValidMultilineFalse2() {
|
||||||
|
explicitTermFilter.isValid("nsfw\nHello") shouldBe false
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun isValidAnalFalse() {
|
||||||
|
explicitTermFilter.isValid("anal") shouldBe false
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun isValidAnal2False() {
|
||||||
|
explicitTermFilter.isValid("There is some anal in this room") shouldBe false
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun isValidAnalysisTrue() {
|
||||||
|
explicitTermFilter.isValid("analysis") shouldBe true
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun isValidAnalysis2True() {
|
||||||
|
explicitTermFilter.isValid("There is some analysis in the room") shouldBe true
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun isValidSpecialCharFalse() {
|
||||||
|
explicitTermFilter.isValid("18+") shouldBe false
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun isValidSpecialChar2False() {
|
||||||
|
explicitTermFilter.isValid("This is a room with 18+ content") shouldBe false
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun isValidOtherSpecialCharFalse() {
|
||||||
|
explicitTermFilter.isValid("strap-on") shouldBe false
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun isValidOtherSpecialChar2False() {
|
||||||
|
explicitTermFilter.isValid("This is a room with strap-on content") shouldBe false
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun isValid18True() {
|
||||||
|
explicitTermFilter.isValid("18") shouldBe true
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun isValidLastFalse() {
|
||||||
|
explicitTermFilter.isValid("zoo") shouldBe false
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun canSearchForFalse() {
|
||||||
|
explicitTermFilter.canSearchFor("zoo") shouldBe false
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun canSearchForTrue() {
|
||||||
|
explicitTermFilter.canSearchFor("android") shouldBe true
|
||||||
|
}
|
||||||
|
}
|
|
@ -78,6 +78,7 @@ class UiAllScreensSanityTest {
|
||||||
// Last passing:
|
// Last passing:
|
||||||
// 2020-11-09
|
// 2020-11-09
|
||||||
// 2020-12-16 After ViewBinding huge change
|
// 2020-12-16 After ViewBinding huge change
|
||||||
|
// 2021-04-08 Testing 429 change
|
||||||
@Test
|
@Test
|
||||||
fun allScreensTest() {
|
fun allScreensTest() {
|
||||||
// Create an account
|
// Create an account
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="8dp"
|
android:layout_marginStart="8dp"
|
||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
tools:text="@string/verification_emoji_wrench" />
|
tools:text="@string/verification_emoji_spanner" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/sas_emoji_text_id"
|
android:id="@+id/sas_emoji_text_id"
|
||||||
|
@ -47,7 +47,7 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="8dp"
|
android:layout_marginStart="8dp"
|
||||||
android:textSize="12sp"
|
android:textSize="12sp"
|
||||||
tools:text="verification_emoji_wrench" />
|
tools:text="verification_emoji_spanner" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
|
@ -24,9 +24,11 @@ import im.vector.app.core.pushers.PushersManager
|
||||||
import im.vector.app.core.resources.StringProvider
|
import im.vector.app.core.resources.StringProvider
|
||||||
import im.vector.app.features.settings.troubleshoot.TroubleshootTest
|
import im.vector.app.features.settings.troubleshoot.TroubleshootTest
|
||||||
import im.vector.app.push.fcm.FcmHelper
|
import im.vector.app.push.fcm.FcmHelper
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import org.matrix.android.sdk.api.session.pushers.PushGatewayFailure
|
import org.matrix.android.sdk.api.session.pushers.PushGatewayFailure
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -47,22 +49,26 @@ class TestPushFromPushGateway @Inject constructor(private val context: AppCompat
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
action = GlobalScope.launch {
|
action = GlobalScope.launch {
|
||||||
status = runCatching { pushersManager.testPush(fcmToken) }
|
val result = runCatching { pushersManager.testPush(fcmToken) }
|
||||||
.fold(
|
|
||||||
{
|
withContext(Dispatchers.Main) {
|
||||||
// Wait for the push to be received
|
status = result
|
||||||
description = stringProvider.getString(R.string.settings_troubleshoot_test_push_loop_waiting_for_push)
|
.fold(
|
||||||
TestStatus.RUNNING
|
{
|
||||||
},
|
// Wait for the push to be received
|
||||||
{
|
description = stringProvider.getString(R.string.settings_troubleshoot_test_push_loop_waiting_for_push)
|
||||||
description = if (it is PushGatewayFailure.PusherRejected) {
|
TestStatus.RUNNING
|
||||||
stringProvider.getString(R.string.settings_troubleshoot_test_push_loop_failed)
|
},
|
||||||
} else {
|
{
|
||||||
errorFormatter.toHumanReadable(it)
|
description = if (it is PushGatewayFailure.PusherRejected) {
|
||||||
|
stringProvider.getString(R.string.settings_troubleshoot_test_push_loop_failed)
|
||||||
|
} else {
|
||||||
|
errorFormatter.toHumanReadable(it)
|
||||||
|
}
|
||||||
|
TestStatus.FAILED
|
||||||
}
|
}
|
||||||
TestStatus.FAILED
|
)
|
||||||
}
|
}
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
68
vector/src/main/assets/forbidden_terms.txt
Normal file
68
vector/src/main/assets/forbidden_terms.txt
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
anal
|
||||||
|
bbw
|
||||||
|
bdsm
|
||||||
|
beast
|
||||||
|
bestiality
|
||||||
|
blowjob
|
||||||
|
bondage
|
||||||
|
boobs
|
||||||
|
clit
|
||||||
|
cock
|
||||||
|
cuck
|
||||||
|
cum
|
||||||
|
cunt
|
||||||
|
daddy
|
||||||
|
dick
|
||||||
|
dildo
|
||||||
|
erotic
|
||||||
|
exhibitionism
|
||||||
|
faggot
|
||||||
|
femboy
|
||||||
|
fisting
|
||||||
|
flogging
|
||||||
|
fmf
|
||||||
|
foursome
|
||||||
|
futa
|
||||||
|
gangbang
|
||||||
|
gore
|
||||||
|
h3ntai
|
||||||
|
handjob
|
||||||
|
hentai
|
||||||
|
incest
|
||||||
|
jizz
|
||||||
|
kink
|
||||||
|
loli
|
||||||
|
m4f
|
||||||
|
masturbate
|
||||||
|
masturbation
|
||||||
|
mfm
|
||||||
|
milf
|
||||||
|
moresome
|
||||||
|
naked
|
||||||
|
neet
|
||||||
|
nsfw
|
||||||
|
nude
|
||||||
|
nudity
|
||||||
|
orgy
|
||||||
|
pedo
|
||||||
|
pegging
|
||||||
|
penis
|
||||||
|
petplay
|
||||||
|
porn
|
||||||
|
pussy
|
||||||
|
rape
|
||||||
|
rimming
|
||||||
|
sadism
|
||||||
|
sadomasochism
|
||||||
|
sexy
|
||||||
|
shota
|
||||||
|
spank
|
||||||
|
squirt
|
||||||
|
strap-on
|
||||||
|
threesome
|
||||||
|
vagina
|
||||||
|
vibrator
|
||||||
|
voyeur
|
||||||
|
watersports
|
||||||
|
xxx
|
||||||
|
zoo
|
|
@ -28,8 +28,10 @@ import com.bumptech.glide.signature.ObjectKey
|
||||||
import im.vector.app.core.extensions.vectorComponent
|
import im.vector.app.core.extensions.vectorComponent
|
||||||
import im.vector.app.core.files.LocalFilesHelper
|
import im.vector.app.core.files.LocalFilesHelper
|
||||||
import im.vector.app.features.media.ImageContentRenderer
|
import im.vector.app.features.media.ImageContentRenderer
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
@ -121,10 +123,12 @@ class VectorGlideDataFetcher(context: Context,
|
||||||
url = data.url,
|
url = data.url,
|
||||||
elementToDecrypt = data.elementToDecrypt)
|
elementToDecrypt = data.elementToDecrypt)
|
||||||
}
|
}
|
||||||
result.fold(
|
withContext(Dispatchers.Main) {
|
||||||
{ callback.onDataReady(it.inputStream()) },
|
result.fold(
|
||||||
{ callback.onLoadFailed(it as? Exception ?: IOException(it.localizedMessage)) }
|
{ callback.onDataReady(it.inputStream()) },
|
||||||
)
|
{ callback.onLoadFailed(it as? Exception ?: IOException(it.localizedMessage)) }
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// val url = contentUrlResolver.resolveFullSize(data.url)
|
// val url = contentUrlResolver.resolveFullSize(data.url)
|
||||||
// ?: return
|
// ?: return
|
||||||
|
|
|
@ -23,7 +23,7 @@ import javax.inject.Inject
|
||||||
class AppNameProvider @Inject constructor(private val context: Context) {
|
class AppNameProvider @Inject constructor(private val context: Context) {
|
||||||
|
|
||||||
fun getAppName(): String {
|
fun getAppName(): String {
|
||||||
try {
|
return try {
|
||||||
val appPackageName = context.applicationContext.packageName
|
val appPackageName = context.applicationContext.packageName
|
||||||
val pm = context.packageManager
|
val pm = context.packageManager
|
||||||
val appInfo = pm.getApplicationInfo(appPackageName, 0)
|
val appInfo = pm.getApplicationInfo(appPackageName, 0)
|
||||||
|
@ -33,10 +33,10 @@ class AppNameProvider @Inject constructor(private val context: Context) {
|
||||||
if (!appName.matches("\\A\\p{ASCII}*\\z".toRegex())) {
|
if (!appName.matches("\\A\\p{ASCII}*\\z".toRegex())) {
|
||||||
appName = appPackageName
|
appName = appPackageName
|
||||||
}
|
}
|
||||||
return appName
|
appName
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e, "## AppNameProvider() : failed")
|
Timber.e(e, "## AppNameProvider() : failed")
|
||||||
return "ElementAndroid"
|
"ElementAndroid"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -146,7 +146,7 @@ class DialPadFragment : Fragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun poll() {
|
private fun poll() {
|
||||||
if (!input.isEmpty()) {
|
if (input.isNotEmpty()) {
|
||||||
input = input.substring(0, input.length - 1)
|
input = input.substring(0, input.length - 1)
|
||||||
formatter = PhoneNumberUtil.getInstance().getAsYouTypeFormatter(regionCode)
|
formatter = PhoneNumberUtil.getInstance().getAsYouTypeFormatter(regionCode)
|
||||||
if (formatAsYouType) {
|
if (formatAsYouType) {
|
||||||
|
|
|
@ -22,7 +22,6 @@ import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||||
import org.matrix.android.sdk.api.raw.RawService
|
import org.matrix.android.sdk.api.raw.RawService
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
||||||
import org.matrix.android.sdk.internal.util.awaitCallback
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class DirectRoomHelper @Inject constructor(
|
class DirectRoomHelper @Inject constructor(
|
||||||
|
@ -45,9 +44,7 @@ class DirectRoomHelper @Inject constructor(
|
||||||
setDirectMessage()
|
setDirectMessage()
|
||||||
enableEncryptionIfInvitedUsersSupportIt = adminE2EByDefault
|
enableEncryptionIfInvitedUsersSupportIt = adminE2EByDefault
|
||||||
}
|
}
|
||||||
roomId = awaitCallback {
|
roomId = session.createRoom(roomParams)
|
||||||
session.createRoom(roomParams, it)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return roomId
|
return roomId
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,7 +60,7 @@ class IncomingVerificationRequestHandler @Inject constructor(
|
||||||
// TODO maybe check also if
|
// TODO maybe check also if
|
||||||
val uid = "kvr_${tx.transactionId}"
|
val uid = "kvr_${tx.transactionId}"
|
||||||
when (tx.state) {
|
when (tx.state) {
|
||||||
is VerificationTxState.OnStarted -> {
|
is VerificationTxState.OnStarted -> {
|
||||||
// Add a notification for every incoming request
|
// Add a notification for every incoming request
|
||||||
val user = session?.getUser(tx.otherUserId)
|
val user = session?.getUser(tx.otherUserId)
|
||||||
val name = user?.getBestName() ?: tx.otherUserId
|
val name = user?.getBestName() ?: tx.otherUserId
|
||||||
|
@ -119,6 +119,13 @@ class IncomingVerificationRequestHandler @Inject constructor(
|
||||||
Timber.v("## SAS verificationRequestCreated ${pr.transactionId}")
|
Timber.v("## SAS verificationRequestCreated ${pr.transactionId}")
|
||||||
// For incoming request we should prompt (if not in activity where this request apply)
|
// For incoming request we should prompt (if not in activity where this request apply)
|
||||||
if (pr.isIncoming) {
|
if (pr.isIncoming) {
|
||||||
|
// if it's a self verification for my devices, we can discard the review login alert
|
||||||
|
// if not this request will be underneath and not visible by the user...
|
||||||
|
// it will re-appear later
|
||||||
|
if (pr.otherUserId == session?.myUserId) {
|
||||||
|
// XXX this is a bit hard coded :/
|
||||||
|
popupAlertManager.cancelAlert("review_login")
|
||||||
|
}
|
||||||
val user = session?.getUser(pr.otherUserId)?.toMatrixItem()
|
val user = session?.getUser(pr.otherUserId)?.toMatrixItem()
|
||||||
val name = user?.getBestName() ?: pr.otherUserId
|
val name = user?.getBestName() ?: pr.otherUserId
|
||||||
val description = if (name == pr.otherUserId) {
|
val description = if (name == pr.otherUserId) {
|
||||||
|
|
|
@ -249,32 +249,34 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(
|
||||||
pendingRequest = Loading()
|
pendingRequest = Loading()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
session.createDirectRoom(otherUserId, object : MatrixCallback<String> {
|
viewModelScope.launch {
|
||||||
override fun onSuccess(data: String) {
|
val result = runCatching { session.createDirectRoom(otherUserId) }
|
||||||
setState {
|
result.fold(
|
||||||
copy(
|
{ data ->
|
||||||
roomId = data,
|
setState {
|
||||||
pendingRequest = Success(
|
copy(
|
||||||
session
|
roomId = data,
|
||||||
.cryptoService()
|
pendingRequest = Success(
|
||||||
.verificationService()
|
session
|
||||||
.requestKeyVerificationInDMs(
|
.cryptoService()
|
||||||
supportedVerificationMethodsProvider.provide(),
|
.verificationService()
|
||||||
otherUserId,
|
.requestKeyVerificationInDMs(
|
||||||
data,
|
supportedVerificationMethodsProvider.provide(),
|
||||||
pendingLocalId
|
otherUserId,
|
||||||
)
|
data,
|
||||||
|
pendingLocalId
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
}
|
||||||
}
|
},
|
||||||
}
|
{ failure ->
|
||||||
|
setState {
|
||||||
override fun onFailure(failure: Throwable) {
|
copy(pendingRequest = Fail(failure))
|
||||||
setState {
|
}
|
||||||
copy(pendingRequest = Fail(failure))
|
}
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
setState {
|
setState {
|
||||||
copy(
|
copy(
|
||||||
|
|
|
@ -203,9 +203,8 @@ class HomeActivityViewModel @AssistedInject constructor(
|
||||||
_viewEvents.post(
|
_viewEvents.post(
|
||||||
HomeActivityViewEvents.OnNewSession(
|
HomeActivityViewEvents.OnNewSession(
|
||||||
session.getUser(session.myUserId)?.toMatrixItem(),
|
session.getUser(session.myUserId)?.toMatrixItem(),
|
||||||
// If it's an old unverified, we should send requests
|
// Always send request instead of waiting for an incoming as per recent EW changes
|
||||||
// instead of waiting for an incoming one
|
false
|
||||||
reAuthHelper.data != null
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,6 @@ import org.matrix.android.sdk.api.query.RoomCategoryFilter
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||||
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
||||||
import org.matrix.android.sdk.internal.util.awaitCallback
|
|
||||||
import org.matrix.android.sdk.rx.asObservable
|
import org.matrix.android.sdk.rx.asObservable
|
||||||
import org.matrix.android.sdk.rx.rx
|
import org.matrix.android.sdk.rx.rx
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
@ -109,9 +108,7 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho
|
||||||
)
|
)
|
||||||
.map { it.roomId }
|
.map { it.roomId }
|
||||||
try {
|
try {
|
||||||
awaitCallback<Unit> {
|
session.markAllAsRead(roomIds)
|
||||||
session.markAllAsRead(roomIds, it)
|
|
||||||
}
|
|
||||||
} catch (failure: Throwable) {
|
} catch (failure: Throwable) {
|
||||||
Timber.d(failure, "Failed to mark all as read")
|
Timber.d(failure, "Failed to mark all as read")
|
||||||
}
|
}
|
||||||
|
|
|
@ -177,14 +177,16 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||||
observePowerLevel()
|
observePowerLevel()
|
||||||
updateShowDialerOptionState()
|
updateShowDialerOptionState()
|
||||||
room.getRoomSummaryLive()
|
room.getRoomSummaryLive()
|
||||||
|
viewModelScope.launch {
|
||||||
|
tryOrNull { room.markAsRead(ReadService.MarkAsReadParams.READ_RECEIPT) }
|
||||||
|
}
|
||||||
|
// Inform the SDK that the room is displayed
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
try {
|
try {
|
||||||
room.markAsRead(ReadService.MarkAsReadParams.READ_RECEIPT)
|
session.onRoomDisplayed(initialState.roomId)
|
||||||
} catch (_: Exception) {
|
} catch (_: Exception) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Inform the SDK that the room is displayed
|
|
||||||
session.onRoomDisplayed(initialState.roomId)
|
|
||||||
callManager.addPstnSupportListener(this)
|
callManager.addPstnSupportListener(this)
|
||||||
callManager.checkForPSTNSupportIfNeeded()
|
callManager.checkForPSTNSupportIfNeeded()
|
||||||
chatEffectManager.delegate = this
|
chatEffectManager.delegate = this
|
||||||
|
@ -546,10 +548,7 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||||
if (trackUnreadMessages.getAndSet(false)) {
|
if (trackUnreadMessages.getAndSet(false)) {
|
||||||
mostRecentDisplayedEvent?.root?.eventId?.also {
|
mostRecentDisplayedEvent?.root?.eventId?.also {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
try {
|
tryOrNull { room.setReadMarker(it) }
|
||||||
room.setReadMarker(it)
|
|
||||||
} catch (_: Exception) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mostRecentDisplayedEvent = null
|
mostRecentDisplayedEvent = null
|
||||||
|
@ -902,19 +901,19 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleJoinToAnotherRoomSlashCommand(command: ParsedCommand.JoinRoom) {
|
private fun handleJoinToAnotherRoomSlashCommand(command: ParsedCommand.JoinRoom) {
|
||||||
session.joinRoom(command.roomAlias, command.reason, emptyList(), object : MatrixCallback<Unit> {
|
viewModelScope.launch {
|
||||||
override fun onSuccess(data: Unit) {
|
try {
|
||||||
session.getRoomSummary(command.roomAlias)
|
session.joinRoom(command.roomAlias, command.reason, emptyList())
|
||||||
?.roomId
|
} catch (failure: Throwable) {
|
||||||
?.let {
|
|
||||||
_viewEvents.post(RoomDetailViewEvents.JoinRoomCommandSuccess(it))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onFailure(failure: Throwable) {
|
|
||||||
_viewEvents.post(RoomDetailViewEvents.SlashCommandResultError(failure))
|
_viewEvents.post(RoomDetailViewEvents.SlashCommandResultError(failure))
|
||||||
|
return@launch
|
||||||
}
|
}
|
||||||
})
|
session.getRoomSummary(command.roomAlias)
|
||||||
|
?.roomId
|
||||||
|
?.let {
|
||||||
|
_viewEvents.post(RoomDetailViewEvents.JoinRoomCommandSuccess(it))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun legacyRiotQuoteText(quotedText: String?, myText: String): String {
|
private fun legacyRiotQuoteText(quotedText: String?, myText: String): String {
|
||||||
|
@ -1254,7 +1253,7 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
bufferedMostRecentDisplayedEvent.root.eventId?.let { eventId ->
|
bufferedMostRecentDisplayedEvent.root.eventId?.let { eventId ->
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
room.setReadReceipt(eventId)
|
tryOrNull { room.setReadReceipt(eventId) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -1263,10 +1262,7 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||||
|
|
||||||
private fun handleMarkAllAsRead() {
|
private fun handleMarkAllAsRead() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
try {
|
tryOrNull { room.markAsRead(ReadService.MarkAsReadParams.BOTH) }
|
||||||
room.markAsRead(ReadService.MarkAsReadParams.BOTH)
|
|
||||||
} catch (_: Exception) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -91,18 +91,25 @@ class LoginFragment @Inject constructor() : AbstractSSOLoginFragment<FragmentLog
|
||||||
SignMode.SignUp -> {
|
SignMode.SignUp -> {
|
||||||
views.loginField.setAutofillHints(HintConstants.AUTOFILL_HINT_NEW_USERNAME)
|
views.loginField.setAutofillHints(HintConstants.AUTOFILL_HINT_NEW_USERNAME)
|
||||||
views.passwordField.setAutofillHints(HintConstants.AUTOFILL_HINT_NEW_PASSWORD)
|
views.passwordField.setAutofillHints(HintConstants.AUTOFILL_HINT_NEW_PASSWORD)
|
||||||
views.loginSocialLoginButtons.mode = SocialLoginButtonsView.Mode.MODE_SIGN_UP
|
|
||||||
}
|
}
|
||||||
SignMode.SignIn,
|
SignMode.SignIn,
|
||||||
SignMode.SignInWithMatrixId -> {
|
SignMode.SignInWithMatrixId -> {
|
||||||
views.loginField.setAutofillHints(HintConstants.AUTOFILL_HINT_USERNAME)
|
views.loginField.setAutofillHints(HintConstants.AUTOFILL_HINT_USERNAME)
|
||||||
views.passwordField.setAutofillHints(HintConstants.AUTOFILL_HINT_PASSWORD)
|
views.passwordField.setAutofillHints(HintConstants.AUTOFILL_HINT_PASSWORD)
|
||||||
views.loginSocialLoginButtons.mode = SocialLoginButtonsView.Mode.MODE_SIGN_IN
|
|
||||||
}
|
}
|
||||||
}.exhaustive
|
}.exhaustive
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun setupSocialLoginButtons(state: LoginViewState) {
|
||||||
|
views.loginSocialLoginButtons.mode = when (state.signMode) {
|
||||||
|
SignMode.Unknown -> error("developer error")
|
||||||
|
SignMode.SignUp -> SocialLoginButtonsView.Mode.MODE_SIGN_UP
|
||||||
|
SignMode.SignIn,
|
||||||
|
SignMode.SignInWithMatrixId -> SocialLoginButtonsView.Mode.MODE_SIGN_IN
|
||||||
|
}.exhaustive
|
||||||
|
}
|
||||||
|
|
||||||
private fun submit() {
|
private fun submit() {
|
||||||
cleanupUi()
|
cleanupUi()
|
||||||
|
|
||||||
|
@ -277,6 +284,7 @@ class LoginFragment @Inject constructor() : AbstractSSOLoginFragment<FragmentLog
|
||||||
|
|
||||||
setupUi(state)
|
setupUi(state)
|
||||||
setupAutoFill(state)
|
setupAutoFill(state)
|
||||||
|
setupSocialLoginButtons(state)
|
||||||
setupButtons(state)
|
setupButtons(state)
|
||||||
|
|
||||||
when (state.asyncLoginAction) {
|
when (state.asyncLoginAction) {
|
||||||
|
|
|
@ -43,7 +43,6 @@ import kotlinx.coroutines.launch
|
||||||
import org.matrix.android.sdk.api.auth.AuthenticationService
|
import org.matrix.android.sdk.api.auth.AuthenticationService
|
||||||
import org.matrix.android.sdk.api.auth.HomeServerHistoryService
|
import org.matrix.android.sdk.api.auth.HomeServerHistoryService
|
||||||
import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
|
import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
|
||||||
import org.matrix.android.sdk.api.auth.data.LoginFlowResult
|
|
||||||
import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
|
import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
|
||||||
import org.matrix.android.sdk.api.auth.login.LoginWizard
|
import org.matrix.android.sdk.api.auth.login.LoginWizard
|
||||||
import org.matrix.android.sdk.api.auth.registration.FlowResult
|
import org.matrix.android.sdk.api.auth.registration.FlowResult
|
||||||
|
@ -773,34 +772,34 @@ class LoginViewModel @AssistedInject constructor(
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data is LoginFlowResult.Success) {
|
data ?: return@launch
|
||||||
// Valid Homeserver, add it to the history.
|
|
||||||
// Note: we add what the user has input, data.homeServerUrl can be different
|
|
||||||
rememberHomeServer(homeServerConnectionConfig.homeServerUri.toString())
|
|
||||||
|
|
||||||
val loginMode = when {
|
// Valid Homeserver, add it to the history.
|
||||||
// SSO login is taken first
|
// Note: we add what the user has input, data.homeServerUrl can be different
|
||||||
data.supportedLoginTypes.contains(LoginFlowTypes.SSO)
|
rememberHomeServer(homeServerConnectionConfig.homeServerUri.toString())
|
||||||
&& data.supportedLoginTypes.contains(LoginFlowTypes.PASSWORD) -> LoginMode.SsoAndPassword(data.ssoIdentityProviders)
|
|
||||||
data.supportedLoginTypes.contains(LoginFlowTypes.SSO) -> LoginMode.Sso(data.ssoIdentityProviders)
|
|
||||||
data.supportedLoginTypes.contains(LoginFlowTypes.PASSWORD) -> LoginMode.Password
|
|
||||||
else -> LoginMode.Unsupported
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME We should post a view event here normally?
|
val loginMode = when {
|
||||||
setState {
|
// SSO login is taken first
|
||||||
copy(
|
data.supportedLoginTypes.contains(LoginFlowTypes.SSO)
|
||||||
asyncHomeServerLoginFlowRequest = Uninitialized,
|
&& data.supportedLoginTypes.contains(LoginFlowTypes.PASSWORD) -> LoginMode.SsoAndPassword(data.ssoIdentityProviders)
|
||||||
homeServerUrl = data.homeServerUrl,
|
data.supportedLoginTypes.contains(LoginFlowTypes.SSO) -> LoginMode.Sso(data.ssoIdentityProviders)
|
||||||
loginMode = loginMode,
|
data.supportedLoginTypes.contains(LoginFlowTypes.PASSWORD) -> LoginMode.Password
|
||||||
loginModeSupportedTypes = data.supportedLoginTypes.toList()
|
else -> LoginMode.Unsupported
|
||||||
)
|
}
|
||||||
}
|
|
||||||
if ((loginMode == LoginMode.Password && !data.isLoginAndRegistrationSupported)
|
// FIXME We should post a view event here normally?
|
||||||
|| data.isOutdatedHomeserver) {
|
setState {
|
||||||
// Notify the UI
|
copy(
|
||||||
_viewEvents.post(LoginViewEvents.OutdatedHomeserver)
|
asyncHomeServerLoginFlowRequest = Uninitialized,
|
||||||
}
|
homeServerUrl = data.homeServerUrl,
|
||||||
|
loginMode = loginMode,
|
||||||
|
loginModeSupportedTypes = data.supportedLoginTypes.toList()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if ((loginMode == LoginMode.Password && !data.isLoginAndRegistrationSupported)
|
||||||
|
|| data.isOutdatedHomeserver) {
|
||||||
|
// Notify the UI
|
||||||
|
_viewEvents.post(LoginViewEvents.OutdatedHomeserver)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,8 +31,10 @@ import im.vector.lib.attachmentviewer.AttachmentInfo
|
||||||
import im.vector.lib.attachmentviewer.AttachmentSourceProvider
|
import im.vector.lib.attachmentviewer.AttachmentSourceProvider
|
||||||
import im.vector.lib.attachmentviewer.ImageLoaderTarget
|
import im.vector.lib.attachmentviewer.ImageLoaderTarget
|
||||||
import im.vector.lib.attachmentviewer.VideoLoaderTarget
|
import im.vector.lib.attachmentviewer.VideoLoaderTarget
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import org.matrix.android.sdk.api.session.events.model.isVideoMessage
|
import org.matrix.android.sdk.api.session.events.model.isVideoMessage
|
||||||
import org.matrix.android.sdk.api.session.file.FileService
|
import org.matrix.android.sdk.api.session.file.FileService
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||||
|
@ -162,10 +164,12 @@ abstract class BaseAttachmentProvider<Type>(
|
||||||
elementToDecrypt = data.elementToDecrypt
|
elementToDecrypt = data.elementToDecrypt
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
result.fold(
|
withContext(Dispatchers.Main) {
|
||||||
{ target.onVideoFileReady(info.uid, it) },
|
result.fold(
|
||||||
{ target.onVideoFileLoadFailed(info.uid) }
|
{ target.onVideoFileReady(info.uid, it) },
|
||||||
)
|
{ target.onVideoFileLoadFailed(info.uid) }
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,8 +19,10 @@ package im.vector.app.features.media
|
||||||
import im.vector.app.core.date.VectorDateFormatter
|
import im.vector.app.core.date.VectorDateFormatter
|
||||||
import im.vector.app.core.resources.StringProvider
|
import im.vector.app.core.resources.StringProvider
|
||||||
import im.vector.lib.attachmentviewer.AttachmentInfo
|
import im.vector.lib.attachmentviewer.AttachmentInfo
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import org.matrix.android.sdk.api.session.file.FileService
|
import org.matrix.android.sdk.api.session.file.FileService
|
||||||
import org.matrix.android.sdk.api.session.room.Room
|
import org.matrix.android.sdk.api.session.room.Room
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||||
|
@ -87,7 +89,9 @@ class DataAttachmentRoomProvider(
|
||||||
elementToDecrypt = item.elementToDecrypt
|
elementToDecrypt = item.elementToDecrypt
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
callback(result.getOrNull())
|
withContext(Dispatchers.Main) {
|
||||||
|
callback(result.getOrNull())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,8 +19,10 @@ package im.vector.app.features.media
|
||||||
import im.vector.app.core.date.VectorDateFormatter
|
import im.vector.app.core.date.VectorDateFormatter
|
||||||
import im.vector.app.core.resources.StringProvider
|
import im.vector.app.core.resources.StringProvider
|
||||||
import im.vector.lib.attachmentviewer.AttachmentInfo
|
import im.vector.lib.attachmentviewer.AttachmentInfo
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||||
import org.matrix.android.sdk.api.session.file.FileService
|
import org.matrix.android.sdk.api.session.file.FileService
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
||||||
|
@ -134,7 +136,9 @@ class RoomEventsAttachmentProvider(
|
||||||
url = messageContent.getFileUrl(),
|
url = messageContent.getFileUrl(),
|
||||||
elementToDecrypt = messageContent.encryptedFileInfo?.toElementToDecrypt())
|
elementToDecrypt = messageContent.encryptedFileInfo?.toElementToDecrypt())
|
||||||
}
|
}
|
||||||
callback(result.getOrNull())
|
withContext(Dispatchers.Main) {
|
||||||
|
callback(result.getOrNull())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,8 +25,10 @@ import im.vector.app.R
|
||||||
import im.vector.app.core.di.ActiveSessionHolder
|
import im.vector.app.core.di.ActiveSessionHolder
|
||||||
import im.vector.app.core.error.ErrorFormatter
|
import im.vector.app.core.error.ErrorFormatter
|
||||||
import im.vector.app.core.files.LocalFilesHelper
|
import im.vector.app.core.files.LocalFilesHelper
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
import org.matrix.android.sdk.internal.crypto.attachments.ElementToDecrypt
|
import org.matrix.android.sdk.internal.crypto.attachments.ElementToDecrypt
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
@ -83,21 +85,23 @@ class VideoContentRenderer @Inject constructor(private val localFilesHelper: Loc
|
||||||
url = data.url,
|
url = data.url,
|
||||||
elementToDecrypt = data.elementToDecrypt)
|
elementToDecrypt = data.elementToDecrypt)
|
||||||
}
|
}
|
||||||
result.fold(
|
withContext(Dispatchers.Main) {
|
||||||
{ data ->
|
result.fold(
|
||||||
thumbnailView.isVisible = false
|
{ data ->
|
||||||
loadingView.isVisible = false
|
thumbnailView.isVisible = false
|
||||||
videoView.isVisible = true
|
loadingView.isVisible = false
|
||||||
|
videoView.isVisible = true
|
||||||
|
|
||||||
videoView.setVideoPath(data.path)
|
videoView.setVideoPath(data.path)
|
||||||
videoView.start()
|
videoView.start()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
loadingView.isVisible = false
|
loadingView.isVisible = false
|
||||||
errorView.isVisible = true
|
errorView.isVisible = true
|
||||||
errorView.text = errorFormatter.toHumanReadable(it)
|
errorView.text = errorFormatter.toHumanReadable(it)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -124,21 +128,23 @@ class VideoContentRenderer @Inject constructor(private val localFilesHelper: Loc
|
||||||
url = data.url,
|
url = data.url,
|
||||||
elementToDecrypt = null)
|
elementToDecrypt = null)
|
||||||
}
|
}
|
||||||
result.fold(
|
withContext(Dispatchers.Main) {
|
||||||
{ data ->
|
result.fold(
|
||||||
thumbnailView.isVisible = false
|
{ data ->
|
||||||
loadingView.isVisible = false
|
thumbnailView.isVisible = false
|
||||||
videoView.isVisible = true
|
loadingView.isVisible = false
|
||||||
|
videoView.isVisible = true
|
||||||
|
|
||||||
videoView.setVideoPath(data.path)
|
videoView.setVideoPath(data.path)
|
||||||
videoView.start()
|
videoView.start()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
loadingView.isVisible = false
|
loadingView.isVisible = false
|
||||||
errorView.isVisible = true
|
errorView.isVisible = true
|
||||||
errorView.text = errorFormatter.toHumanReadable(it)
|
errorView.text = errorFormatter.toHumanReadable(it)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ import im.vector.app.core.di.ActiveSessionHolder
|
||||||
import im.vector.app.core.extensions.vectorComponent
|
import im.vector.app.core.extensions.vectorComponent
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import org.matrix.android.sdk.api.session.room.Room
|
import org.matrix.android.sdk.api.session.room.Room
|
||||||
import org.matrix.android.sdk.api.session.room.read.ReadService
|
import org.matrix.android.sdk.api.session.room.read.ReadService
|
||||||
|
@ -78,7 +79,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver() {
|
||||||
val room = session.getRoom(roomId)
|
val room = session.getRoom(roomId)
|
||||||
if (room != null) {
|
if (room != null) {
|
||||||
GlobalScope.launch {
|
GlobalScope.launch {
|
||||||
room.join()
|
tryOrNull { room.join() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,7 +90,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver() {
|
||||||
val room = session.getRoom(roomId)
|
val room = session.getRoom(roomId)
|
||||||
if (room != null) {
|
if (room != null) {
|
||||||
GlobalScope.launch {
|
GlobalScope.launch {
|
||||||
room.leave()
|
tryOrNull { room.leave() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -100,10 +101,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver() {
|
||||||
val room = session.getRoom(roomId)
|
val room = session.getRoom(roomId)
|
||||||
if (room != null) {
|
if (room != null) {
|
||||||
GlobalScope.launch {
|
GlobalScope.launch {
|
||||||
try {
|
tryOrNull { room.markAsRead(ReadService.MarkAsReadParams.READ_RECEIPT) }
|
||||||
room.markAsRead(ReadService.MarkAsReadParams.READ_RECEIPT)
|
|
||||||
} catch (_: Exception) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* 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.roomdirectory
|
||||||
|
|
||||||
|
import im.vector.app.core.utils.AssetReader
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class ExplicitTermFilter @Inject constructor(
|
||||||
|
assetReader: AssetReader
|
||||||
|
) {
|
||||||
|
// List of forbidden terms is in file asset forbidden_terms.txt, in lower case
|
||||||
|
private val explicitTerms = assetReader.readAssetFile("forbidden_terms.txt")
|
||||||
|
.orEmpty()
|
||||||
|
.split("\n")
|
||||||
|
.map { it.trim() }
|
||||||
|
.distinct()
|
||||||
|
.filter { it.isNotEmpty() }
|
||||||
|
|
||||||
|
private val explicitContentRegex = explicitTerms
|
||||||
|
.joinToString(prefix = ".*\\b(", separator = "|", postfix = ")\\b.*")
|
||||||
|
.toRegex(RegexOption.IGNORE_CASE)
|
||||||
|
|
||||||
|
fun canSearchFor(term: String): Boolean {
|
||||||
|
return term !in explicitTerms && term != "18+"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isValid(str: String): Boolean {
|
||||||
|
return explicitContentRegex.matches(str.replace("\n", " ")).not()
|
||||||
|
// Special treatment for "18+" since word boundaries does not work here
|
||||||
|
&& str.contains("18+").not()
|
||||||
|
}
|
||||||
|
}
|
|
@ -31,7 +31,6 @@ import im.vector.app.core.platform.VectorViewModel
|
||||||
import im.vector.app.features.settings.VectorPreferences
|
import im.vector.app.features.settings.VectorPreferences
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
|
||||||
import org.matrix.android.sdk.api.extensions.orFalse
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
import org.matrix.android.sdk.api.failure.Failure
|
import org.matrix.android.sdk.api.failure.Failure
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
|
@ -42,12 +41,12 @@ import org.matrix.android.sdk.api.session.room.model.thirdparty.RoomDirectoryDat
|
||||||
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
||||||
import org.matrix.android.sdk.rx.rx
|
import org.matrix.android.sdk.rx.rx
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.Locale
|
|
||||||
|
|
||||||
class RoomDirectoryViewModel @AssistedInject constructor(
|
class RoomDirectoryViewModel @AssistedInject constructor(
|
||||||
@Assisted initialState: PublicRoomsViewState,
|
@Assisted initialState: PublicRoomsViewState,
|
||||||
vectorPreferences: VectorPreferences,
|
vectorPreferences: VectorPreferences,
|
||||||
private val session: Session
|
private val session: Session,
|
||||||
|
private val explicitTermFilter: ExplicitTermFilter
|
||||||
) : VectorViewModel<PublicRoomsViewState, RoomDirectoryAction, RoomDirectoryViewEvents>(initialState) {
|
) : VectorViewModel<PublicRoomsViewState, RoomDirectoryAction, RoomDirectoryViewEvents>(initialState) {
|
||||||
|
|
||||||
@AssistedFactory
|
@AssistedFactory
|
||||||
|
@ -58,11 +57,6 @@ class RoomDirectoryViewModel @AssistedInject constructor(
|
||||||
companion object : MvRxViewModelFactory<RoomDirectoryViewModel, PublicRoomsViewState> {
|
companion object : MvRxViewModelFactory<RoomDirectoryViewModel, PublicRoomsViewState> {
|
||||||
private const val PUBLIC_ROOMS_LIMIT = 20
|
private const val PUBLIC_ROOMS_LIMIT = 20
|
||||||
|
|
||||||
// List of forbidden terms, in lower case
|
|
||||||
private val explicitContentTerms = listOf(
|
|
||||||
"nsfw"
|
|
||||||
)
|
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
override fun create(viewModelContext: ViewModelContext, state: PublicRoomsViewState): RoomDirectoryViewModel? {
|
override fun create(viewModelContext: ViewModelContext, state: PublicRoomsViewState): RoomDirectoryViewModel? {
|
||||||
val activity: RoomDirectoryActivity = (viewModelContext as ActivityViewModelContext).activity()
|
val activity: RoomDirectoryActivity = (viewModelContext as ActivityViewModelContext).activity()
|
||||||
|
@ -166,6 +160,17 @@ class RoomDirectoryViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun load(filter: String, roomDirectoryData: RoomDirectoryData) {
|
private fun load(filter: String, roomDirectoryData: RoomDirectoryData) {
|
||||||
|
if (!showAllRooms && !explicitTermFilter.canSearchFor(filter)) {
|
||||||
|
setState {
|
||||||
|
copy(
|
||||||
|
asyncPublicRoomsRequest = Success(Unit),
|
||||||
|
publicRooms = emptyList(),
|
||||||
|
hasMore = false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
currentJob = viewModelScope.launch {
|
currentJob = viewModelScope.launch {
|
||||||
val data = try {
|
val data = try {
|
||||||
session.getPublicRooms(roomDirectoryData.homeServer,
|
session.getPublicRooms(roomDirectoryData.homeServer,
|
||||||
|
@ -202,11 +207,7 @@ class RoomDirectoryViewModel @AssistedInject constructor(
|
||||||
// Filter
|
// Filter
|
||||||
val newPublicRooms = data.chunk.orEmpty()
|
val newPublicRooms = data.chunk.orEmpty()
|
||||||
.filter {
|
.filter {
|
||||||
showAllRooms
|
showAllRooms || explicitTermFilter.isValid("${it.name.orEmpty()} ${it.topic.orEmpty()}")
|
||||||
|| "${it.name.orEmpty()} ${it.topic.orEmpty()} ${it.canonicalAlias.orEmpty()}".toLowerCase(Locale.ROOT)
|
|
||||||
.let { str ->
|
|
||||||
explicitContentTerms.all { term -> term !in str }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setState {
|
setState {
|
||||||
|
@ -232,17 +233,16 @@ class RoomDirectoryViewModel @AssistedInject constructor(
|
||||||
val viaServers = state.roomDirectoryData.homeServer
|
val viaServers = state.roomDirectoryData.homeServer
|
||||||
?.let { listOf(it) }
|
?.let { listOf(it) }
|
||||||
.orEmpty()
|
.orEmpty()
|
||||||
session.joinRoom(action.roomId, viaServers = viaServers, callback = object : MatrixCallback<Unit> {
|
viewModelScope.launch {
|
||||||
override fun onSuccess(data: Unit) {
|
try {
|
||||||
|
session.joinRoom(action.roomId, viaServers = viaServers)
|
||||||
// We do not update the joiningRoomsIds here, because, the room is not joined yet regarding the sync data.
|
// We do not update the joiningRoomsIds here, because, the room is not joined yet regarding the sync data.
|
||||||
// Instead, we wait for the room to be joined
|
// Instead, we wait for the room to be joined
|
||||||
}
|
} catch (failure: Throwable) {
|
||||||
|
|
||||||
override fun onFailure(failure: Throwable) {
|
|
||||||
// Notify the user
|
// Notify the user
|
||||||
_viewEvents.post(RoomDirectoryViewEvents.Failure(failure))
|
_viewEvents.post(RoomDirectoryViewEvents.Failure(failure))
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCleared() {
|
override fun onCleared() {
|
||||||
|
|
|
@ -34,7 +34,6 @@ import im.vector.app.features.raw.wellknown.getElementWellknown
|
||||||
import im.vector.app.features.raw.wellknown.isE2EByDefault
|
import im.vector.app.features.raw.wellknown.isE2EByDefault
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
|
||||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||||
import org.matrix.android.sdk.api.raw.RawService
|
import org.matrix.android.sdk.api.raw.RawService
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
|
@ -216,19 +215,22 @@ class CreateRoomViewModel @AssistedInject constructor(@Assisted initialState: Cr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
session.createRoom(createRoomParams, object : MatrixCallback<String> {
|
// TODO: Should this be non-cancellable?
|
||||||
override fun onSuccess(data: String) {
|
viewModelScope.launch {
|
||||||
setState {
|
val result = runCatching { session.createRoom(createRoomParams) }
|
||||||
copy(asyncCreateRoomRequest = Success(data))
|
result.fold(
|
||||||
}
|
{ roomId ->
|
||||||
}
|
setState {
|
||||||
|
copy(asyncCreateRoomRequest = Success(roomId))
|
||||||
override fun onFailure(failure: Throwable) {
|
}
|
||||||
setState {
|
},
|
||||||
copy(asyncCreateRoomRequest = Fail(failure))
|
{ failure ->
|
||||||
}
|
setState {
|
||||||
_viewEvents.post(CreateRoomViewEvents.Failure(failure))
|
copy(asyncCreateRoomRequest = Fail(failure))
|
||||||
}
|
}
|
||||||
})
|
_viewEvents.post(CreateRoomViewEvents.Failure(failure))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,6 @@ import im.vector.app.core.platform.VectorViewModel
|
||||||
import im.vector.app.features.roomdirectory.JoinState
|
import im.vector.app.features.roomdirectory.JoinState
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
|
||||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||||
import org.matrix.android.sdk.api.query.QueryStringValue
|
import org.matrix.android.sdk.api.query.QueryStringValue
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
|
@ -39,7 +38,6 @@ import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
|
||||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||||
import org.matrix.android.sdk.api.session.room.peeking.PeekResult
|
import org.matrix.android.sdk.api.session.room.peeking.PeekResult
|
||||||
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
||||||
import org.matrix.android.sdk.internal.util.awaitCallback
|
|
||||||
import org.matrix.android.sdk.rx.rx
|
import org.matrix.android.sdk.rx.rx
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
|
@ -77,9 +75,7 @@ class RoomPreviewViewModel @AssistedInject constructor(@Assisted private val ini
|
||||||
}
|
}
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
val peekResult = tryOrNull {
|
val peekResult = tryOrNull {
|
||||||
awaitCallback<PeekResult> {
|
session.peekRoom(initialState.roomAlias ?: initialState.roomId)
|
||||||
session.peekRoom(initialState.roomAlias ?: initialState.roomId, it)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
when (peekResult) {
|
when (peekResult) {
|
||||||
|
@ -177,15 +173,14 @@ class RoomPreviewViewModel @AssistedInject constructor(@Assisted private val ini
|
||||||
Timber.w("Try to join an already joining room. Should not happen")
|
Timber.w("Try to join an already joining room. Should not happen")
|
||||||
return@withState
|
return@withState
|
||||||
}
|
}
|
||||||
session.joinRoom(state.roomId, viaServers = state.homeServers, callback = object : MatrixCallback<Unit> {
|
viewModelScope.launch {
|
||||||
override fun onSuccess(data: Unit) {
|
try {
|
||||||
|
session.joinRoom(state.roomId, viaServers = state.homeServers)
|
||||||
// We do not update the joiningRoomsIds here, because, the room is not joined yet regarding the sync data.
|
// We do not update the joiningRoomsIds here, because, the room is not joined yet regarding the sync data.
|
||||||
// Instead, we wait for the room to be joined
|
// Instead, we wait for the room to be joined
|
||||||
}
|
} catch (failure: Throwable) {
|
||||||
|
|
||||||
override fun onFailure(failure: Throwable) {
|
|
||||||
setState { copy(lastError = failure) }
|
setState { copy(lastError = failure) }
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,8 +20,11 @@ import androidx.activity.result.ActivityResultLauncher
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.di.ActiveSessionHolder
|
import im.vector.app.core.di.ActiveSessionHolder
|
||||||
import im.vector.app.core.resources.StringProvider
|
import im.vector.app.core.resources.StringProvider
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||||
import org.matrix.android.sdk.api.pushrules.RuleIds
|
import org.matrix.android.sdk.api.pushrules.RuleIds
|
||||||
import org.matrix.android.sdk.api.pushrules.RuleKind
|
import org.matrix.android.sdk.api.pushrules.RuleKind
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -50,10 +53,12 @@ class TestAccountSettings @Inject constructor(private val stringProvider: String
|
||||||
if (manager?.diagStatus == TestStatus.RUNNING) return // wait before all is finished
|
if (manager?.diagStatus == TestStatus.RUNNING) return // wait before all is finished
|
||||||
|
|
||||||
GlobalScope.launch {
|
GlobalScope.launch {
|
||||||
runCatching {
|
tryOrNull {
|
||||||
session.updatePushRuleEnableStatus(RuleKind.OVERRIDE, defaultRule, !defaultRule.enabled)
|
session.updatePushRuleEnableStatus(RuleKind.OVERRIDE, defaultRule, !defaultRule.enabled)
|
||||||
}
|
}
|
||||||
manager?.retry(activityResultLauncher)
|
withContext(Dispatchers.Main) {
|
||||||
|
manager?.retry(activityResultLauncher)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,6 @@ import im.vector.app.core.platform.VectorViewModel
|
||||||
import im.vector.app.features.login.LoginMode
|
import im.vector.app.features.login.LoginMode
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.matrix.android.sdk.api.auth.AuthenticationService
|
import org.matrix.android.sdk.api.auth.AuthenticationService
|
||||||
import org.matrix.android.sdk.api.auth.data.LoginFlowResult
|
|
||||||
import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
|
import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
@ -100,21 +99,21 @@ class SoftLogoutViewModel @AssistedInject constructor(
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data is LoginFlowResult.Success) {
|
data ?: return@launch
|
||||||
val loginMode = when {
|
|
||||||
// SSO login is taken first
|
|
||||||
data.supportedLoginTypes.contains(LoginFlowTypes.SSO)
|
|
||||||
&& data.supportedLoginTypes.contains(LoginFlowTypes.PASSWORD) -> LoginMode.SsoAndPassword(data.ssoIdentityProviders)
|
|
||||||
data.supportedLoginTypes.contains(LoginFlowTypes.SSO) -> LoginMode.Sso(data.ssoIdentityProviders)
|
|
||||||
data.supportedLoginTypes.contains(LoginFlowTypes.PASSWORD) -> LoginMode.Password
|
|
||||||
else -> LoginMode.Unsupported
|
|
||||||
}
|
|
||||||
|
|
||||||
setState {
|
val loginMode = when {
|
||||||
copy(
|
// SSO login is taken first
|
||||||
asyncHomeServerLoginFlowRequest = Success(loginMode)
|
data.supportedLoginTypes.contains(LoginFlowTypes.SSO)
|
||||||
)
|
&& data.supportedLoginTypes.contains(LoginFlowTypes.PASSWORD) -> LoginMode.SsoAndPassword(data.ssoIdentityProviders)
|
||||||
}
|
data.supportedLoginTypes.contains(LoginFlowTypes.SSO) -> LoginMode.Sso(data.ssoIdentityProviders)
|
||||||
|
data.supportedLoginTypes.contains(LoginFlowTypes.PASSWORD) -> LoginMode.Password
|
||||||
|
else -> LoginMode.Unsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
setState {
|
||||||
|
copy(
|
||||||
|
asyncHomeServerLoginFlowRequest = Success(loginMode)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:id="@+id/layout_root"
|
android:id="@+id/layout_root"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
|
@ -145,6 +145,7 @@
|
||||||
android:layout_marginBottom="4dp"
|
android:layout_marginBottom="4dp"
|
||||||
android:contentDescription="@string/event_status_a11y_sending"
|
android:contentDescription="@string/event_status_a11y_sending"
|
||||||
android:src="@drawable/ic_sending_message"
|
android:src="@drawable/ic_sending_message"
|
||||||
|
android:tint="?riotx_text_tertiary"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
@ -158,6 +159,7 @@
|
||||||
android:layout_marginStart="8dp"
|
android:layout_marginStart="8dp"
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="8dp"
|
||||||
android:layout_marginBottom="4dp"
|
android:layout_marginBottom="4dp"
|
||||||
|
android:tint="?riotx_text_tertiary"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -198,10 +198,8 @@
|
||||||
|
|
||||||
<attr name="riotx_text_tertiary" format="color" />
|
<attr name="riotx_text_tertiary" format="color" />
|
||||||
<color name="riotx_text_tertiary_light">#FF8D99A5</color>
|
<color name="riotx_text_tertiary_light">#FF8D99A5</color>
|
||||||
<!-- TODO Pick color from Figma, I do not know where to find it -->
|
<color name="riotx_text_tertiary_dark">#FF8E99A4</color>
|
||||||
<color name="riotx_text_tertiary_dark">#FF8D99A5</color>
|
<color name="riotx_text_tertiary_black">#FF8E99A4</color>
|
||||||
<!-- TODO Pick color from Figma, I do not know where to find it -->
|
|
||||||
<color name="riotx_text_tertiary_black">#FF8D99A5</color>
|
|
||||||
|
|
||||||
<attr name="riotx_text_primary_body_contrast" format="color" />
|
<attr name="riotx_text_primary_body_contrast" format="color" />
|
||||||
<color name="riotx_text_primary_body_contrast_light">#FF61708B</color>
|
<color name="riotx_text_primary_body_contrast_light">#FF61708B</color>
|
||||||
|
|
Loading…
Reference in a new issue