mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-26 03:16:02 +03:00
Merge branch 'release/0.21.0'
This commit is contained in:
commit
d1c4d4b099
411 changed files with 13503 additions and 1602 deletions
|
@ -12,12 +12,15 @@
|
||||||
<w>fdroid</w>
|
<w>fdroid</w>
|
||||||
<w>gplay</w>
|
<w>gplay</w>
|
||||||
<w>hmac</w>
|
<w>hmac</w>
|
||||||
|
<w>homeserver</w>
|
||||||
<w>ktlint</w>
|
<w>ktlint</w>
|
||||||
<w>linkified</w>
|
<w>linkified</w>
|
||||||
<w>linkify</w>
|
<w>linkify</w>
|
||||||
<w>megolm</w>
|
<w>megolm</w>
|
||||||
<w>msisdn</w>
|
<w>msisdn</w>
|
||||||
|
<w>msisdns</w>
|
||||||
<w>pbkdf</w>
|
<w>pbkdf</w>
|
||||||
|
<w>pids</w>
|
||||||
<w>pkcs</w>
|
<w>pkcs</w>
|
||||||
<w>riotx</w>
|
<w>riotx</w>
|
||||||
<w>signin</w>
|
<w>signin</w>
|
||||||
|
|
27
CHANGES.md
27
CHANGES.md
|
@ -1,3 +1,30 @@
|
||||||
|
Changes in RiotX 0.21.0 (2020-05-28)
|
||||||
|
===================================================
|
||||||
|
|
||||||
|
Features ✨:
|
||||||
|
- Identity server support (#607)
|
||||||
|
- Switch language support (#41)
|
||||||
|
- Display list of attachments of a room (#860)
|
||||||
|
|
||||||
|
Improvements 🙌:
|
||||||
|
- Better connectivity lost indicator when airplane mode is on
|
||||||
|
- Add a setting to hide redacted events (#951)
|
||||||
|
- Render formatted_body for m.notice and m.emote (#1196)
|
||||||
|
- Change icon to magnifying-glass to filter room (#1384)
|
||||||
|
|
||||||
|
Bugfix 🐛:
|
||||||
|
- After jump to unread, newer messages are never loaded (#1008)
|
||||||
|
- Fix issues with FontScale switch (#69, #645)
|
||||||
|
- "Seen by" uses 12h time (#1378)
|
||||||
|
- Enable markdown (if active) when sending emote (#734)
|
||||||
|
- Screenshots for Rageshake now includes Dialogs such as BottomSheet (#1349)
|
||||||
|
|
||||||
|
SDK API changes ⚠️:
|
||||||
|
- initialize with proxy configuration
|
||||||
|
|
||||||
|
Other changes:
|
||||||
|
- support new key agreement method for SAS (#1374)
|
||||||
|
|
||||||
Changes in RiotX 0.20.0 (2020-05-15)
|
Changes in RiotX 0.20.0 (2020-05-15)
|
||||||
===================================================
|
===================================================
|
||||||
|
|
||||||
|
|
92
docs/identity_server.md
Normal file
92
docs/identity_server.md
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
# Identity server
|
||||||
|
|
||||||
|
Issue: #607
|
||||||
|
PR: #1354
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
Identity Servers support contact discovery on Matrix by letting people look up Third Party Identifiers to see if the owner has publicly linked them with their Matrix ID.
|
||||||
|
|
||||||
|
## Implementation
|
||||||
|
|
||||||
|
The current implementation was Inspired by the code from Riot-Android.
|
||||||
|
|
||||||
|
Difference though (list not exhaustive):
|
||||||
|
- Only API v2 is supported (see https://matrix.org/docs/spec/identity_service/latest)
|
||||||
|
- Homeserver has to be up to date to support binding (Versions.isLoginAndRegistrationSupportedBySdk() has to return true)
|
||||||
|
- The SDK managed the session and client secret when binding ThreePid. Those data are not exposed to the client.
|
||||||
|
- The SDK supports incremental sendAttempt (this is not used by RiotX)
|
||||||
|
- The "Continue" button is now under the information, and not as the same place that the checkbox
|
||||||
|
- The app can cancel a binding. Current data are erased from DB.
|
||||||
|
- The API (IdentityService) is improved.
|
||||||
|
- A new DB to store data related to the identity server management.
|
||||||
|
|
||||||
|
Missing features (list not exhaustive):
|
||||||
|
- Invite by 3Pid (will be in a dedicated PR)
|
||||||
|
- Add email or phone to account (not P1, can be done on Riot-Web)
|
||||||
|
- List email and phone of the account (could be done in a dedicated PR)
|
||||||
|
- Search contact (not P1)
|
||||||
|
- Logout from identity server when user sign out or deactivate his account.
|
||||||
|
|
||||||
|
## Related MSCs
|
||||||
|
The list can be found here: https://matrix.org/blog/2019/09/27/privacy-improvements-in-synapse-1-4-and-riot-1-4
|
||||||
|
|
||||||
|
## Steps and requirements
|
||||||
|
|
||||||
|
- Only one identity server by account can be set. The user's choice is stored in account data with key `m.identity_server`. But every clients will managed its own token to log in to the identity server
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "m.identity_server",
|
||||||
|
"content": {
|
||||||
|
"base_url": "https://matrix.org"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- The accepted terms are stored in the account data:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "m.accepted_terms",
|
||||||
|
"content": {
|
||||||
|
"accepted": [
|
||||||
|
"https://vector.im/identity-server-privacy-notice-1"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- Default identity server URL, from Wellknown data is proposed to the user.
|
||||||
|
- Identity server can be set
|
||||||
|
- Identity server can be changed on another user's device, so when the change is detected (thanks to account data sync) RiotX should properly disconnect from a previous identity server (I think it was not the case in Riot-Android, where we keep the token forever)
|
||||||
|
- Registration to the identity server is managed with an openId token
|
||||||
|
- Terms of service can be accepted when configuring the identity server.
|
||||||
|
- Terms of service can be accepted after, if they change.
|
||||||
|
- Identity server can be modified
|
||||||
|
- Identity server can be disconnected with a warning dialog, with special content if there are current bound 3pid on this identity server.
|
||||||
|
- Email can be bound
|
||||||
|
- Email can be unbound
|
||||||
|
- Phone can be bound
|
||||||
|
- Phone can be unbound
|
||||||
|
- Look up can be performed, to get matrixIds from local contact book (phone and email): Android permission correctly handled (not done yet)
|
||||||
|
- Look up pepper can be updated if it is rotated on the identity server
|
||||||
|
- Invitation using 3PID can be done (See #548) (not done yet)
|
||||||
|
- Homeserver access-token will never be sent to an identity server
|
||||||
|
- When user sign-out: logout from the identity server if any.
|
||||||
|
- When user deactivate account: logout from the identity server if any.
|
||||||
|
|
||||||
|
## Screens
|
||||||
|
|
||||||
|
### Settings
|
||||||
|
|
||||||
|
Identity server settings can be accessed from the internal setting of the application, both from "Discovery" section and from identity detail section.
|
||||||
|
|
||||||
|
### Discovery screen
|
||||||
|
|
||||||
|
This screen displays the identity server configuration and the binding of the user's ThreePid (email and msisdn). This is the main screen of the feature.
|
||||||
|
|
||||||
|
### Set identity server screen
|
||||||
|
|
||||||
|
This screen is a form to set a new identity server URL
|
||||||
|
|
||||||
|
## Ref:
|
||||||
|
- https://matrix.org/blog/2019/09/27/privacy-improvements-in-synapse-1-4-and-riot-1-4 is a good summary of the role of an Identity server and the proper way to configure and use it in respect to the privacy and the consent of the user.
|
||||||
|
- API documentation: https://matrix.org/docs/spec/identity_service/latest
|
||||||
|
- vector.im TOS: https://vector.im/identity-server-privacy-notice
|
|
@ -21,6 +21,7 @@ import im.vector.matrix.android.api.session.Session
|
||||||
import im.vector.matrix.android.api.session.crypto.crosssigning.MXCrossSigningInfo
|
import im.vector.matrix.android.api.session.crypto.crosssigning.MXCrossSigningInfo
|
||||||
import im.vector.matrix.android.api.session.group.GroupSummaryQueryParams
|
import im.vector.matrix.android.api.session.group.GroupSummaryQueryParams
|
||||||
import im.vector.matrix.android.api.session.group.model.GroupSummary
|
import im.vector.matrix.android.api.session.group.model.GroupSummary
|
||||||
|
import im.vector.matrix.android.api.session.identity.ThreePid
|
||||||
import im.vector.matrix.android.api.session.pushers.Pusher
|
import im.vector.matrix.android.api.session.pushers.Pusher
|
||||||
import im.vector.matrix.android.api.session.room.RoomSummaryQueryParams
|
import im.vector.matrix.android.api.session.room.RoomSummaryQueryParams
|
||||||
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||||
|
@ -94,6 +95,11 @@ class RxSession(private val session: Session) {
|
||||||
return session.getPagedUsersLive(filter, excludedUserIds).asObservable()
|
return session.getPagedUsersLive(filter, excludedUserIds).asObservable()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun liveThreePIds(refreshData: Boolean): Observable<List<ThreePid>> {
|
||||||
|
return session.getThreePidsLive(refreshData).asObservable()
|
||||||
|
.startWithCallable { session.getThreePids() }
|
||||||
|
}
|
||||||
|
|
||||||
fun createRoom(roomParams: CreateRoomParams): Single<String> = singleBuilder {
|
fun createRoom(roomParams: CreateRoomParams): Single<String> = singleBuilder {
|
||||||
session.createRoom(roomParams, it)
|
session.createRoom(roomParams, it)
|
||||||
}
|
}
|
||||||
|
|
|
@ -158,6 +158,9 @@ dependencies {
|
||||||
// Bus
|
// Bus
|
||||||
implementation 'org.greenrobot:eventbus:3.1.1'
|
implementation 'org.greenrobot:eventbus:3.1.1'
|
||||||
|
|
||||||
|
// Phone number https://github.com/google/libphonenumber
|
||||||
|
implementation 'com.googlecode.libphonenumber:libphonenumber:8.10.23'
|
||||||
|
|
||||||
debugImplementation 'com.airbnb.okreplay:okreplay:1.5.0'
|
debugImplementation 'com.airbnb.okreplay:okreplay:1.5.0'
|
||||||
releaseImplementation 'com.airbnb.okreplay:noop:1.5.0'
|
releaseImplementation 'com.airbnb.okreplay:noop:1.5.0'
|
||||||
androidTestImplementation 'com.airbnb.okreplay:espresso:1.5.0'
|
androidTestImplementation 'com.airbnb.okreplay:espresso:1.5.0'
|
||||||
|
|
|
@ -28,10 +28,10 @@ import im.vector.matrix.android.api.auth.data.LoginFlowResult
|
||||||
import im.vector.matrix.android.api.auth.registration.RegistrationResult
|
import im.vector.matrix.android.api.auth.registration.RegistrationResult
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.session.Session
|
||||||
import im.vector.matrix.android.api.session.events.model.EventType
|
import im.vector.matrix.android.api.session.events.model.EventType
|
||||||
import im.vector.matrix.android.api.session.events.model.LocalEcho
|
|
||||||
import im.vector.matrix.android.api.session.events.model.toModel
|
import im.vector.matrix.android.api.session.events.model.toModel
|
||||||
import im.vector.matrix.android.api.session.room.Room
|
import im.vector.matrix.android.api.session.room.Room
|
||||||
import im.vector.matrix.android.api.session.room.model.message.MessageContent
|
import im.vector.matrix.android.api.session.room.model.message.MessageContent
|
||||||
|
import im.vector.matrix.android.api.session.room.send.SendState
|
||||||
import im.vector.matrix.android.api.session.room.timeline.Timeline
|
import im.vector.matrix.android.api.session.room.timeline.Timeline
|
||||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||||
import im.vector.matrix.android.api.session.room.timeline.TimelineSettings
|
import im.vector.matrix.android.api.session.room.timeline.TimelineSettings
|
||||||
|
@ -88,7 +88,8 @@ class CommonTestHelper(context: Context) {
|
||||||
fun syncSession(session: Session) {
|
fun syncSession(session: Session) {
|
||||||
val lock = CountDownLatch(1)
|
val lock = CountDownLatch(1)
|
||||||
|
|
||||||
session.open()
|
GlobalScope.launch(Dispatchers.Main) { session.open() }
|
||||||
|
|
||||||
session.startSync(true)
|
session.startSync(true)
|
||||||
|
|
||||||
val syncLiveData = runBlocking(Dispatchers.Main) {
|
val syncLiveData = runBlocking(Dispatchers.Main) {
|
||||||
|
@ -116,7 +117,7 @@ class CommonTestHelper(context: Context) {
|
||||||
*/
|
*/
|
||||||
fun sendTextMessage(room: Room, message: String, nbOfMessages: Int): List<TimelineEvent> {
|
fun sendTextMessage(room: Room, message: String, nbOfMessages: Int): List<TimelineEvent> {
|
||||||
val sentEvents = ArrayList<TimelineEvent>(nbOfMessages)
|
val sentEvents = ArrayList<TimelineEvent>(nbOfMessages)
|
||||||
val latch = CountDownLatch(nbOfMessages)
|
val latch = CountDownLatch(1)
|
||||||
val timelineListener = object : Timeline.Listener {
|
val timelineListener = object : Timeline.Listener {
|
||||||
override fun onTimelineFailure(throwable: Throwable) {
|
override fun onTimelineFailure(throwable: Throwable) {
|
||||||
}
|
}
|
||||||
|
@ -127,7 +128,7 @@ class CommonTestHelper(context: Context) {
|
||||||
|
|
||||||
override fun onTimelineUpdated(snapshot: List<TimelineEvent>) {
|
override fun onTimelineUpdated(snapshot: List<TimelineEvent>) {
|
||||||
val newMessages = snapshot
|
val newMessages = snapshot
|
||||||
.filter { LocalEcho.isLocalEchoId(it.eventId).not() }
|
.filter { it.root.sendState == SendState.SYNCED }
|
||||||
.filter { it.root.getClearType() == EventType.MESSAGE }
|
.filter { it.root.getClearType() == EventType.MESSAGE }
|
||||||
.filter { it.root.getClearContent().toModel<MessageContent>()?.body?.startsWith(message) == true }
|
.filter { it.root.getClearContent().toModel<MessageContent>()?.body?.startsWith(message) == true }
|
||||||
|
|
||||||
|
@ -143,7 +144,8 @@ class CommonTestHelper(context: Context) {
|
||||||
for (i in 0 until nbOfMessages) {
|
for (i in 0 until nbOfMessages) {
|
||||||
room.sendTextMessage(message + " #" + (i + 1))
|
room.sendTextMessage(message + " #" + (i + 1))
|
||||||
}
|
}
|
||||||
await(latch)
|
// Wait 3 second more per message
|
||||||
|
await(latch, timeout = TestConstants.timeOutMillis + 3_000L * nbOfMessages)
|
||||||
timeline.removeListener(timelineListener)
|
timeline.removeListener(timelineListener)
|
||||||
timeline.dispose()
|
timeline.dispose()
|
||||||
|
|
||||||
|
@ -291,6 +293,24 @@ class CommonTestHelper(context: Context) {
|
||||||
return requestFailure!!
|
return requestFailure!!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun createEventListener(latch: CountDownLatch, predicate: (List<TimelineEvent>) -> Boolean): Timeline.Listener {
|
||||||
|
return object : Timeline.Listener {
|
||||||
|
override fun onTimelineFailure(throwable: Throwable) {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNewTimelineEvents(eventIds: List<String>) {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTimelineUpdated(snapshot: List<TimelineEvent>) {
|
||||||
|
if (predicate(snapshot)) {
|
||||||
|
latch.countDown()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Await for a latch and ensure the result is true
|
* Await for a latch and ensure the result is true
|
||||||
*
|
*
|
||||||
|
@ -349,3 +369,13 @@ class CommonTestHelper(context: Context) {
|
||||||
session.close()
|
session.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun List<TimelineEvent>.checkSendOrder(baseTextMessage: String, numberOfMessages: Int, startIndex: Int): Boolean {
|
||||||
|
return drop(startIndex)
|
||||||
|
.take(numberOfMessages)
|
||||||
|
.foldRightIndexed(true) { index, timelineEvent, acc ->
|
||||||
|
val body = timelineEvent.root.content.toModel<MessageContent>()?.body
|
||||||
|
val currentMessageSuffix = numberOfMessages - index
|
||||||
|
acc && (body == null || body.startsWith(baseTextMessage) && body.endsWith("#$currentMessageSuffix"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -53,17 +53,19 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
|
||||||
/**
|
/**
|
||||||
* @return alice session
|
* @return alice session
|
||||||
*/
|
*/
|
||||||
fun doE2ETestWithAliceInARoom(): 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.doSync<String> {
|
||||||
aliceSession.createRoom(CreateRoomParams(name = "MyRoom"), it)
|
aliceSession.createRoom(CreateRoomParams(name = "MyRoom"), it)
|
||||||
}
|
}
|
||||||
|
|
||||||
val room = aliceSession.getRoom(roomId)!!
|
if (encryptedRoom) {
|
||||||
|
val room = aliceSession.getRoom(roomId)!!
|
||||||
|
|
||||||
mTestHelper.doSync<Unit> {
|
mTestHelper.doSync<Unit> {
|
||||||
room.enableEncryption(callback = it)
|
room.enableEncryption(callback = it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return CryptoTestData(aliceSession, roomId)
|
return CryptoTestData(aliceSession, roomId)
|
||||||
|
@ -72,8 +74,8 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
|
||||||
/**
|
/**
|
||||||
* @return alice and bob sessions
|
* @return alice and bob sessions
|
||||||
*/
|
*/
|
||||||
fun doE2ETestWithAliceAndBobInARoom(): CryptoTestData {
|
fun doE2ETestWithAliceAndBobInARoom(encryptedRoom: Boolean = true): CryptoTestData {
|
||||||
val cryptoTestData = doE2ETestWithAliceInARoom()
|
val cryptoTestData = doE2ETestWithAliceInARoom(encryptedRoom)
|
||||||
val aliceSession = cryptoTestData.firstSession
|
val aliceSession = cryptoTestData.firstSession
|
||||||
val aliceRoomId = cryptoTestData.roomId
|
val aliceRoomId = cryptoTestData.roomId
|
||||||
|
|
||||||
|
@ -246,7 +248,7 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
|
||||||
assertNotNull(eventWireContent.get("session_id"))
|
assertNotNull(eventWireContent.get("session_id"))
|
||||||
assertNotNull(eventWireContent.get("sender_key"))
|
assertNotNull(eventWireContent.get("sender_key"))
|
||||||
|
|
||||||
assertEquals(senderSession.sessionParams.credentials.deviceId, eventWireContent.get("device_id"))
|
assertEquals(senderSession.sessionParams.deviceId, eventWireContent.get("device_id"))
|
||||||
|
|
||||||
assertNotNull(event.eventId)
|
assertNotNull(event.eventId)
|
||||||
assertEquals(roomId, event.roomId)
|
assertEquals(roomId, event.roomId)
|
||||||
|
|
|
@ -122,7 +122,7 @@ class XSigningTest : InstrumentedTest {
|
||||||
// We will want to test that in alice POV, this new device would be trusted by cross signing
|
// We will want to test that in alice POV, this new device would be trusted by cross signing
|
||||||
|
|
||||||
val bobSession2 = mTestHelper.logIntoAccount(bobUserId, SessionTestParams(true))
|
val bobSession2 = mTestHelper.logIntoAccount(bobUserId, SessionTestParams(true))
|
||||||
val bobSecondDeviceId = bobSession2.sessionParams.credentials.deviceId!!
|
val bobSecondDeviceId = bobSession2.sessionParams.deviceId!!
|
||||||
|
|
||||||
// Check that bob first session sees the new login
|
// Check that bob first session sees the new login
|
||||||
val data = mTestHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> {
|
val data = mTestHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> {
|
||||||
|
|
|
@ -148,7 +148,7 @@ class KeyShareTests : InstrumentedTest {
|
||||||
|
|
||||||
// Mark the device as trusted
|
// Mark the device as trusted
|
||||||
aliceSession.cryptoService().setDeviceVerification(DeviceTrustLevel(crossSigningVerified = false, locallyVerified = true), aliceSession.myUserId,
|
aliceSession.cryptoService().setDeviceVerification(DeviceTrustLevel(crossSigningVerified = false, locallyVerified = true), aliceSession.myUserId,
|
||||||
aliceSession2.sessionParams.credentials.deviceId ?: "")
|
aliceSession2.sessionParams.deviceId ?: "")
|
||||||
|
|
||||||
// Re request
|
// Re request
|
||||||
aliceSession2.cryptoService().reRequestRoomKeyForEvent(receivedEvent.root)
|
aliceSession2.cryptoService().reRequestRoomKeyForEvent(receivedEvent.root)
|
||||||
|
@ -253,12 +253,12 @@ class KeyShareTests : InstrumentedTest {
|
||||||
})
|
})
|
||||||
|
|
||||||
val txId: String = "m.testVerif12"
|
val txId: String = "m.testVerif12"
|
||||||
aliceVerificationService2.beginKeyVerification(VerificationMethod.SAS, aliceSession1.myUserId, aliceSession1.sessionParams.credentials.deviceId
|
aliceVerificationService2.beginKeyVerification(VerificationMethod.SAS, aliceSession1.myUserId, aliceSession1.sessionParams.deviceId
|
||||||
?: "", txId)
|
?: "", txId)
|
||||||
|
|
||||||
mTestHelper.waitWithLatch { latch ->
|
mTestHelper.waitWithLatch { latch ->
|
||||||
mTestHelper.retryPeriodicallyWithLatch(latch) {
|
mTestHelper.retryPeriodicallyWithLatch(latch) {
|
||||||
aliceSession1.cryptoService().getDeviceInfo(aliceSession1.myUserId, aliceSession2.sessionParams.credentials.deviceId ?: "")?.isVerified == true
|
aliceSession1.cryptoService().getDeviceInfo(aliceSession1.myUserId, aliceSession2.sessionParams.deviceId ?: "")?.isVerified == true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -835,7 +835,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
assertTrue(signature.valid)
|
assertTrue(signature.valid)
|
||||||
assertNotNull(signature.device)
|
assertNotNull(signature.device)
|
||||||
assertEquals(cryptoTestData.firstSession.cryptoService().getMyDevice().deviceId, signature.deviceId)
|
assertEquals(cryptoTestData.firstSession.cryptoService().getMyDevice().deviceId, signature.deviceId)
|
||||||
assertEquals(signature.device!!.deviceId, cryptoTestData.firstSession.sessionParams.credentials.deviceId)
|
assertEquals(signature.device!!.deviceId, cryptoTestData.firstSession.sessionParams.deviceId)
|
||||||
|
|
||||||
stateObserver.stopAndCheckStates(null)
|
stateObserver.stopAndCheckStates(null)
|
||||||
cryptoTestData.cleanUp(mTestHelper)
|
cryptoTestData.cleanUp(mTestHelper)
|
||||||
|
@ -997,7 +997,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
keysBackup.backupAllGroupSessions(null, it)
|
keysBackup.backupAllGroupSessions(null, it)
|
||||||
}
|
}
|
||||||
|
|
||||||
val oldDeviceId = cryptoTestData.firstSession.sessionParams.credentials.deviceId!!
|
val oldDeviceId = cryptoTestData.firstSession.sessionParams.deviceId!!
|
||||||
val oldKeyBackupVersion = keysBackup.currentBackupVersion
|
val oldKeyBackupVersion = keysBackup.currentBackupVersion
|
||||||
val aliceUserId = cryptoTestData.firstSession.myUserId
|
val aliceUserId = cryptoTestData.firstSession.myUserId
|
||||||
|
|
||||||
|
|
|
@ -468,14 +468,19 @@ class SASTest : InstrumentedTest {
|
||||||
|
|
||||||
val aliceSASLatch = CountDownLatch(1)
|
val aliceSASLatch = CountDownLatch(1)
|
||||||
val aliceListener = object : VerificationService.Listener {
|
val aliceListener = object : VerificationService.Listener {
|
||||||
|
var matchOnce = true
|
||||||
override fun transactionUpdated(tx: VerificationTransaction) {
|
override fun transactionUpdated(tx: VerificationTransaction) {
|
||||||
val uxState = (tx as OutgoingSasVerificationTransaction).uxState
|
val uxState = (tx as OutgoingSasVerificationTransaction).uxState
|
||||||
|
Log.v("TEST", "== aliceState ${uxState.name}")
|
||||||
when (uxState) {
|
when (uxState) {
|
||||||
OutgoingSasVerificationTransaction.UxState.SHOW_SAS -> {
|
OutgoingSasVerificationTransaction.UxState.SHOW_SAS -> {
|
||||||
tx.userHasVerifiedShortCode()
|
tx.userHasVerifiedShortCode()
|
||||||
}
|
}
|
||||||
OutgoingSasVerificationTransaction.UxState.VERIFIED -> {
|
OutgoingSasVerificationTransaction.UxState.VERIFIED -> {
|
||||||
aliceSASLatch.countDown()
|
if (matchOnce) {
|
||||||
|
matchOnce = false
|
||||||
|
aliceSASLatch.countDown()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else -> Unit
|
else -> Unit
|
||||||
}
|
}
|
||||||
|
@ -485,14 +490,23 @@ class SASTest : InstrumentedTest {
|
||||||
|
|
||||||
val bobSASLatch = CountDownLatch(1)
|
val bobSASLatch = CountDownLatch(1)
|
||||||
val bobListener = object : VerificationService.Listener {
|
val bobListener = object : VerificationService.Listener {
|
||||||
|
var acceptOnce = true
|
||||||
|
var matchOnce = true
|
||||||
override fun transactionUpdated(tx: VerificationTransaction) {
|
override fun transactionUpdated(tx: VerificationTransaction) {
|
||||||
val uxState = (tx as IncomingSasVerificationTransaction).uxState
|
val uxState = (tx as IncomingSasVerificationTransaction).uxState
|
||||||
|
Log.v("TEST", "== bobState ${uxState.name}")
|
||||||
when (uxState) {
|
when (uxState) {
|
||||||
IncomingSasVerificationTransaction.UxState.SHOW_ACCEPT -> {
|
IncomingSasVerificationTransaction.UxState.SHOW_ACCEPT -> {
|
||||||
tx.performAccept()
|
if (acceptOnce) {
|
||||||
|
acceptOnce = false
|
||||||
|
tx.performAccept()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
IncomingSasVerificationTransaction.UxState.SHOW_SAS -> {
|
IncomingSasVerificationTransaction.UxState.SHOW_SAS -> {
|
||||||
tx.userHasVerifiedShortCode()
|
if (matchOnce) {
|
||||||
|
matchOnce = false
|
||||||
|
tx.userHasVerifiedShortCode()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
IncomingSasVerificationTransaction.UxState.VERIFIED -> {
|
IncomingSasVerificationTransaction.UxState.VERIFIED -> {
|
||||||
bobSASLatch.countDown()
|
bobSASLatch.countDown()
|
||||||
|
@ -579,7 +593,7 @@ class SASTest : InstrumentedTest {
|
||||||
requestID!!,
|
requestID!!,
|
||||||
cryptoTestData.roomId,
|
cryptoTestData.roomId,
|
||||||
bobSession.myUserId,
|
bobSession.myUserId,
|
||||||
bobSession.sessionParams.credentials.deviceId!!,
|
bobSession.sessionParams.deviceId!!,
|
||||||
null)
|
null)
|
||||||
|
|
||||||
bobVerificationService.beginKeyVerificationInDMs(
|
bobVerificationService.beginKeyVerificationInDMs(
|
||||||
|
@ -587,7 +601,7 @@ class SASTest : InstrumentedTest {
|
||||||
requestID!!,
|
requestID!!,
|
||||||
cryptoTestData.roomId,
|
cryptoTestData.roomId,
|
||||||
aliceSession.myUserId,
|
aliceSession.myUserId,
|
||||||
aliceSession.sessionParams.credentials.deviceId!!,
|
aliceSession.sessionParams.deviceId!!,
|
||||||
null)
|
null)
|
||||||
|
|
||||||
// we should reach SHOW SAS on both
|
// we should reach SHOW SAS on both
|
||||||
|
|
|
@ -0,0 +1,183 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.matrix.android.session.room.timeline
|
||||||
|
|
||||||
|
import im.vector.matrix.android.InstrumentedTest
|
||||||
|
import im.vector.matrix.android.api.extensions.orFalse
|
||||||
|
import im.vector.matrix.android.api.session.events.model.EventType
|
||||||
|
import im.vector.matrix.android.api.session.events.model.toModel
|
||||||
|
import im.vector.matrix.android.api.session.room.model.message.MessageContent
|
||||||
|
import im.vector.matrix.android.api.session.room.timeline.Timeline
|
||||||
|
import im.vector.matrix.android.api.session.room.timeline.TimelineSettings
|
||||||
|
import im.vector.matrix.android.common.CommonTestHelper
|
||||||
|
import im.vector.matrix.android.common.CryptoTestHelper
|
||||||
|
import im.vector.matrix.android.common.checkSendOrder
|
||||||
|
import org.amshove.kluent.shouldBeFalse
|
||||||
|
import org.amshove.kluent.shouldBeTrue
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
|
import org.junit.FixMethodOrder
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.junit.runners.JUnit4
|
||||||
|
import org.junit.runners.MethodSorters
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.util.concurrent.CountDownLatch
|
||||||
|
|
||||||
|
@RunWith(JUnit4::class)
|
||||||
|
@FixMethodOrder(MethodSorters.JVM)
|
||||||
|
class TimelineBackToPreviousLastForwardTest : InstrumentedTest {
|
||||||
|
|
||||||
|
private val commonTestHelper = CommonTestHelper(context())
|
||||||
|
private val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This test ensure that if we have a chunk in the timeline which is due to a sync, and we click to permalink of an
|
||||||
|
* even contained in a previous lastForward chunk, we will be able to go back to the live
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
fun backToPreviousLastForwardTest() {
|
||||||
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(false)
|
||||||
|
|
||||||
|
val aliceSession = cryptoTestData.firstSession
|
||||||
|
val bobSession = cryptoTestData.secondSession!!
|
||||||
|
val aliceRoomId = cryptoTestData.roomId
|
||||||
|
|
||||||
|
aliceSession.cryptoService().setWarnOnUnknownDevices(false)
|
||||||
|
bobSession.cryptoService().setWarnOnUnknownDevices(false)
|
||||||
|
|
||||||
|
val roomFromAlicePOV = aliceSession.getRoom(aliceRoomId)!!
|
||||||
|
val roomFromBobPOV = bobSession.getRoom(aliceRoomId)!!
|
||||||
|
|
||||||
|
val bobTimeline = roomFromBobPOV.createTimeline(null, TimelineSettings(30))
|
||||||
|
bobTimeline.start()
|
||||||
|
|
||||||
|
var roomCreationEventId: String? = null
|
||||||
|
|
||||||
|
run {
|
||||||
|
val lock = CountDownLatch(1)
|
||||||
|
val eventsListener = commonTestHelper.createEventListener(lock) { snapshot ->
|
||||||
|
Timber.e("Bob timeline updated: with ${snapshot.size} events:")
|
||||||
|
snapshot.forEach {
|
||||||
|
Timber.w(" event ${it.root}")
|
||||||
|
}
|
||||||
|
|
||||||
|
roomCreationEventId = snapshot.lastOrNull()?.root?.eventId
|
||||||
|
// Ok, we have the 8 first messages of the initial sync (room creation and bob join event)
|
||||||
|
snapshot.size == 8
|
||||||
|
}
|
||||||
|
|
||||||
|
bobTimeline.addListener(eventsListener)
|
||||||
|
commonTestHelper.await(lock)
|
||||||
|
bobTimeline.removeAllListeners()
|
||||||
|
|
||||||
|
bobTimeline.hasMoreToLoad(Timeline.Direction.BACKWARDS).shouldBeFalse()
|
||||||
|
bobTimeline.hasMoreToLoad(Timeline.Direction.FORWARDS).shouldBeFalse()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bob stop to sync
|
||||||
|
bobSession.stopSync()
|
||||||
|
|
||||||
|
val messageRoot = "First messages from Alice"
|
||||||
|
|
||||||
|
// Alice sends 30 messages
|
||||||
|
commonTestHelper.sendTextMessage(
|
||||||
|
roomFromAlicePOV,
|
||||||
|
messageRoot,
|
||||||
|
30)
|
||||||
|
|
||||||
|
// Bob start to sync
|
||||||
|
bobSession.startSync(true)
|
||||||
|
|
||||||
|
run {
|
||||||
|
val lock = CountDownLatch(1)
|
||||||
|
val eventsListener = commonTestHelper.createEventListener(lock) { snapshot ->
|
||||||
|
Timber.e("Bob timeline updated: with ${snapshot.size} events:")
|
||||||
|
snapshot.forEach {
|
||||||
|
Timber.w(" event ${it.root}")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ok, we have the 10 last messages from Alice.
|
||||||
|
snapshot.size == 10
|
||||||
|
&& snapshot.all { it.root.content.toModel<MessageContent>()?.body?.startsWith(messageRoot).orFalse() }
|
||||||
|
}
|
||||||
|
|
||||||
|
bobTimeline.addListener(eventsListener)
|
||||||
|
commonTestHelper.await(lock)
|
||||||
|
bobTimeline.removeAllListeners()
|
||||||
|
|
||||||
|
bobTimeline.hasMoreToLoad(Timeline.Direction.BACKWARDS).shouldBeTrue()
|
||||||
|
bobTimeline.hasMoreToLoad(Timeline.Direction.FORWARDS).shouldBeFalse()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bob navigate to the first event (room creation event), so inside the previous last forward chunk
|
||||||
|
run {
|
||||||
|
val lock = CountDownLatch(1)
|
||||||
|
val eventsListener = commonTestHelper.createEventListener(lock) { snapshot ->
|
||||||
|
Timber.e("Bob timeline updated: with ${snapshot.size} events:")
|
||||||
|
snapshot.forEach {
|
||||||
|
Timber.w(" event ${it.root}")
|
||||||
|
}
|
||||||
|
|
||||||
|
// The event is in db, so it is fetch and auto pagination occurs, half of the number of events we have for this chunk (?)
|
||||||
|
snapshot.size == 4
|
||||||
|
}
|
||||||
|
|
||||||
|
bobTimeline.addListener(eventsListener)
|
||||||
|
|
||||||
|
// Restart the timeline to the first sent event, which is already in the database, so pagination should start automatically
|
||||||
|
assertTrue(roomFromBobPOV.getTimeLineEvent(roomCreationEventId!!) != null)
|
||||||
|
|
||||||
|
bobTimeline.restartWithEventId(roomCreationEventId)
|
||||||
|
|
||||||
|
commonTestHelper.await(lock)
|
||||||
|
bobTimeline.removeAllListeners()
|
||||||
|
|
||||||
|
bobTimeline.hasMoreToLoad(Timeline.Direction.FORWARDS).shouldBeTrue()
|
||||||
|
bobTimeline.hasMoreToLoad(Timeline.Direction.BACKWARDS).shouldBeFalse()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bob scroll to the future
|
||||||
|
run {
|
||||||
|
val lock = CountDownLatch(1)
|
||||||
|
val eventsListener = commonTestHelper.createEventListener(lock) { snapshot ->
|
||||||
|
Timber.e("Bob timeline updated: with ${snapshot.size} events:")
|
||||||
|
snapshot.forEach {
|
||||||
|
Timber.w(" event ${it.root}")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bob can see the first event of the room (so Back pagination has worked)
|
||||||
|
snapshot.lastOrNull()?.root?.getClearType() == EventType.STATE_ROOM_CREATE
|
||||||
|
// 8 for room creation item, and 30 for the forward pagination
|
||||||
|
&& snapshot.size == 38
|
||||||
|
&& snapshot.checkSendOrder(messageRoot, 30, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
bobTimeline.addListener(eventsListener)
|
||||||
|
|
||||||
|
bobTimeline.paginate(Timeline.Direction.FORWARDS, 50)
|
||||||
|
|
||||||
|
commonTestHelper.await(lock)
|
||||||
|
bobTimeline.removeAllListeners()
|
||||||
|
|
||||||
|
bobTimeline.hasMoreToLoad(Timeline.Direction.FORWARDS).shouldBeFalse()
|
||||||
|
bobTimeline.hasMoreToLoad(Timeline.Direction.BACKWARDS).shouldBeFalse()
|
||||||
|
}
|
||||||
|
bobTimeline.dispose()
|
||||||
|
|
||||||
|
cryptoTestData.cleanUp(commonTestHelper)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,190 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.matrix.android.session.room.timeline
|
||||||
|
|
||||||
|
import im.vector.matrix.android.InstrumentedTest
|
||||||
|
import im.vector.matrix.android.api.extensions.orFalse
|
||||||
|
import im.vector.matrix.android.api.session.events.model.EventType
|
||||||
|
import im.vector.matrix.android.api.session.events.model.toModel
|
||||||
|
import im.vector.matrix.android.api.session.room.model.message.MessageContent
|
||||||
|
import im.vector.matrix.android.api.session.room.timeline.Timeline
|
||||||
|
import im.vector.matrix.android.api.session.room.timeline.TimelineSettings
|
||||||
|
import im.vector.matrix.android.common.CommonTestHelper
|
||||||
|
import im.vector.matrix.android.common.CryptoTestHelper
|
||||||
|
import im.vector.matrix.android.common.checkSendOrder
|
||||||
|
import org.amshove.kluent.shouldBeFalse
|
||||||
|
import org.amshove.kluent.shouldBeTrue
|
||||||
|
import org.junit.FixMethodOrder
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.junit.runners.JUnit4
|
||||||
|
import org.junit.runners.MethodSorters
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.util.concurrent.CountDownLatch
|
||||||
|
|
||||||
|
@RunWith(JUnit4::class)
|
||||||
|
@FixMethodOrder(MethodSorters.JVM)
|
||||||
|
class TimelineForwardPaginationTest : InstrumentedTest {
|
||||||
|
|
||||||
|
private val commonTestHelper = CommonTestHelper(context())
|
||||||
|
private val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This test ensure that if we click to permalink, we will be able to go back to the live
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
fun forwardPaginationTest() {
|
||||||
|
val numberOfMessagesToSend = 90
|
||||||
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceInARoom(false)
|
||||||
|
|
||||||
|
val aliceSession = cryptoTestData.firstSession
|
||||||
|
val aliceRoomId = cryptoTestData.roomId
|
||||||
|
|
||||||
|
aliceSession.cryptoService().setWarnOnUnknownDevices(false)
|
||||||
|
|
||||||
|
val roomFromAlicePOV = aliceSession.getRoom(aliceRoomId)!!
|
||||||
|
|
||||||
|
// Alice sends X messages
|
||||||
|
val message = "Message from Alice"
|
||||||
|
val sentMessages = commonTestHelper.sendTextMessage(
|
||||||
|
roomFromAlicePOV,
|
||||||
|
message,
|
||||||
|
numberOfMessagesToSend)
|
||||||
|
|
||||||
|
// Alice clear the cache
|
||||||
|
commonTestHelper.doSync<Unit> {
|
||||||
|
aliceSession.clearCache(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
// And restarts the sync
|
||||||
|
aliceSession.startSync(true)
|
||||||
|
|
||||||
|
val aliceTimeline = roomFromAlicePOV.createTimeline(null, TimelineSettings(30))
|
||||||
|
aliceTimeline.start()
|
||||||
|
|
||||||
|
// Alice sees the 10 last message of the room, and can only navigate BACKWARD
|
||||||
|
run {
|
||||||
|
val lock = CountDownLatch(1)
|
||||||
|
val eventsListener = commonTestHelper.createEventListener(lock) { snapshot ->
|
||||||
|
Timber.e("Alice timeline updated: with ${snapshot.size} events:")
|
||||||
|
snapshot.forEach {
|
||||||
|
Timber.w(" event ${it.root.content}")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ok, we have the 10 last messages of the initial sync
|
||||||
|
snapshot.size == 10
|
||||||
|
&& snapshot.all { it.root.content.toModel<MessageContent>()?.body?.startsWith(message).orFalse() }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open the timeline at last sent message
|
||||||
|
aliceTimeline.addListener(eventsListener)
|
||||||
|
commonTestHelper.await(lock)
|
||||||
|
aliceTimeline.removeAllListeners()
|
||||||
|
|
||||||
|
aliceTimeline.hasMoreToLoad(Timeline.Direction.BACKWARDS).shouldBeTrue()
|
||||||
|
aliceTimeline.hasMoreToLoad(Timeline.Direction.FORWARDS).shouldBeFalse()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alice navigates to the first message of the room, which is not in its database. A GET /context is performed
|
||||||
|
// Then she can paginate BACKWARD and FORWARD
|
||||||
|
run {
|
||||||
|
val lock = CountDownLatch(1)
|
||||||
|
val aliceEventsListener = commonTestHelper.createEventListener(lock) { snapshot ->
|
||||||
|
Timber.e("Alice timeline updated: with ${snapshot.size} events:")
|
||||||
|
snapshot.forEach {
|
||||||
|
Timber.w(" event ${it.root.content}")
|
||||||
|
}
|
||||||
|
|
||||||
|
// The event is not in db, so it is fetch alone
|
||||||
|
snapshot.size == 1
|
||||||
|
&& snapshot.all { it.root.content.toModel<MessageContent>()?.body?.startsWith("Message from Alice").orFalse() }
|
||||||
|
}
|
||||||
|
|
||||||
|
aliceTimeline.addListener(aliceEventsListener)
|
||||||
|
|
||||||
|
// Restart the timeline to the first sent event
|
||||||
|
aliceTimeline.restartWithEventId(sentMessages.last().eventId)
|
||||||
|
|
||||||
|
commonTestHelper.await(lock)
|
||||||
|
aliceTimeline.removeAllListeners()
|
||||||
|
|
||||||
|
aliceTimeline.hasMoreToLoad(Timeline.Direction.FORWARDS).shouldBeTrue()
|
||||||
|
aliceTimeline.hasMoreToLoad(Timeline.Direction.BACKWARDS).shouldBeTrue()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alice paginates BACKWARD and FORWARD of 50 events each
|
||||||
|
// Then she can only navigate FORWARD
|
||||||
|
run {
|
||||||
|
val lock = CountDownLatch(1)
|
||||||
|
val aliceEventsListener = commonTestHelper.createEventListener(lock) { snapshot ->
|
||||||
|
Timber.e("Alice timeline updated: with ${snapshot.size} events:")
|
||||||
|
snapshot.forEach {
|
||||||
|
Timber.w(" event ${it.root.content}")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alice can see the first event of the room (so Back pagination has worked)
|
||||||
|
snapshot.lastOrNull()?.root?.getClearType() == EventType.STATE_ROOM_CREATE
|
||||||
|
// 6 for room creation item (backward pagination), 1 for the context, and 50 for the forward pagination
|
||||||
|
&& snapshot.size == 6 + 1 + 50
|
||||||
|
}
|
||||||
|
|
||||||
|
aliceTimeline.addListener(aliceEventsListener)
|
||||||
|
|
||||||
|
// Restart the timeline to the first sent event
|
||||||
|
// We ask to load event backward and forward
|
||||||
|
aliceTimeline.paginate(Timeline.Direction.BACKWARDS, 50)
|
||||||
|
aliceTimeline.paginate(Timeline.Direction.FORWARDS, 50)
|
||||||
|
|
||||||
|
commonTestHelper.await(lock)
|
||||||
|
aliceTimeline.removeAllListeners()
|
||||||
|
|
||||||
|
aliceTimeline.hasMoreToLoad(Timeline.Direction.FORWARDS).shouldBeTrue()
|
||||||
|
aliceTimeline.hasMoreToLoad(Timeline.Direction.BACKWARDS).shouldBeFalse()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alice paginates once again FORWARD for 50 events
|
||||||
|
// All the timeline is retrieved, she cannot paginate anymore in both direction
|
||||||
|
run {
|
||||||
|
val lock = CountDownLatch(1)
|
||||||
|
val aliceEventsListener = commonTestHelper.createEventListener(lock) { snapshot ->
|
||||||
|
Timber.e("Alice timeline updated: with ${snapshot.size} events:")
|
||||||
|
snapshot.forEach {
|
||||||
|
Timber.w(" event ${it.root.content}")
|
||||||
|
}
|
||||||
|
// 6 for room creation item (backward pagination),and numberOfMessagesToSend (all the message of the room)
|
||||||
|
snapshot.size == 6 + numberOfMessagesToSend
|
||||||
|
&& snapshot.checkSendOrder(message, numberOfMessagesToSend, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
aliceTimeline.addListener(aliceEventsListener)
|
||||||
|
|
||||||
|
// Ask for a forward pagination
|
||||||
|
aliceTimeline.paginate(Timeline.Direction.FORWARDS, 50)
|
||||||
|
|
||||||
|
commonTestHelper.await(lock)
|
||||||
|
aliceTimeline.removeAllListeners()
|
||||||
|
|
||||||
|
// The timeline is fully loaded
|
||||||
|
aliceTimeline.hasMoreToLoad(Timeline.Direction.FORWARDS).shouldBeFalse()
|
||||||
|
aliceTimeline.hasMoreToLoad(Timeline.Direction.BACKWARDS).shouldBeFalse()
|
||||||
|
}
|
||||||
|
|
||||||
|
aliceTimeline.dispose()
|
||||||
|
|
||||||
|
cryptoTestData.cleanUp(commonTestHelper)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,241 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.matrix.android.session.room.timeline
|
||||||
|
|
||||||
|
import im.vector.matrix.android.InstrumentedTest
|
||||||
|
import im.vector.matrix.android.api.extensions.orFalse
|
||||||
|
import im.vector.matrix.android.api.session.events.model.EventType
|
||||||
|
import im.vector.matrix.android.api.session.events.model.toModel
|
||||||
|
import im.vector.matrix.android.api.session.room.model.message.MessageContent
|
||||||
|
import im.vector.matrix.android.api.session.room.timeline.Timeline
|
||||||
|
import im.vector.matrix.android.api.session.room.timeline.TimelineSettings
|
||||||
|
import im.vector.matrix.android.common.CommonTestHelper
|
||||||
|
import im.vector.matrix.android.common.CryptoTestHelper
|
||||||
|
import im.vector.matrix.android.common.checkSendOrder
|
||||||
|
import org.amshove.kluent.shouldBeFalse
|
||||||
|
import org.amshove.kluent.shouldBeTrue
|
||||||
|
import org.junit.FixMethodOrder
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.junit.runners.JUnit4
|
||||||
|
import org.junit.runners.MethodSorters
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.util.concurrent.CountDownLatch
|
||||||
|
|
||||||
|
@RunWith(JUnit4::class)
|
||||||
|
@FixMethodOrder(MethodSorters.JVM)
|
||||||
|
class TimelinePreviousLastForwardTest : InstrumentedTest {
|
||||||
|
|
||||||
|
private val commonTestHelper = CommonTestHelper(context())
|
||||||
|
private val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This test ensure that if we have a chunk in the timeline which is due to a sync, and we click to permalink, we will be able to go back to the live
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
fun previousLastForwardTest() {
|
||||||
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(false)
|
||||||
|
|
||||||
|
val aliceSession = cryptoTestData.firstSession
|
||||||
|
val bobSession = cryptoTestData.secondSession!!
|
||||||
|
val aliceRoomId = cryptoTestData.roomId
|
||||||
|
|
||||||
|
aliceSession.cryptoService().setWarnOnUnknownDevices(false)
|
||||||
|
bobSession.cryptoService().setWarnOnUnknownDevices(false)
|
||||||
|
|
||||||
|
val roomFromAlicePOV = aliceSession.getRoom(aliceRoomId)!!
|
||||||
|
val roomFromBobPOV = bobSession.getRoom(aliceRoomId)!!
|
||||||
|
|
||||||
|
val bobTimeline = roomFromBobPOV.createTimeline(null, TimelineSettings(30))
|
||||||
|
bobTimeline.start()
|
||||||
|
|
||||||
|
run {
|
||||||
|
val lock = CountDownLatch(1)
|
||||||
|
val eventsListener = commonTestHelper.createEventListener(lock) { snapshot ->
|
||||||
|
Timber.e("Bob timeline updated: with ${snapshot.size} events:")
|
||||||
|
snapshot.forEach {
|
||||||
|
Timber.w(" event ${it.root}")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ok, we have the 8 first messages of the initial sync (room creation and bob invite and join events)
|
||||||
|
snapshot.size == 8
|
||||||
|
}
|
||||||
|
|
||||||
|
bobTimeline.addListener(eventsListener)
|
||||||
|
commonTestHelper.await(lock)
|
||||||
|
bobTimeline.removeAllListeners()
|
||||||
|
|
||||||
|
bobTimeline.hasMoreToLoad(Timeline.Direction.BACKWARDS).shouldBeFalse()
|
||||||
|
bobTimeline.hasMoreToLoad(Timeline.Direction.FORWARDS).shouldBeFalse()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bob stop to sync
|
||||||
|
bobSession.stopSync()
|
||||||
|
|
||||||
|
val firstMessage = "First messages from Alice"
|
||||||
|
// Alice sends 30 messages
|
||||||
|
val firstMessageFromAliceId = commonTestHelper.sendTextMessage(
|
||||||
|
roomFromAlicePOV,
|
||||||
|
firstMessage,
|
||||||
|
30)
|
||||||
|
.last()
|
||||||
|
.eventId
|
||||||
|
|
||||||
|
// Bob start to sync
|
||||||
|
bobSession.startSync(true)
|
||||||
|
|
||||||
|
run {
|
||||||
|
val lock = CountDownLatch(1)
|
||||||
|
val eventsListener = commonTestHelper.createEventListener(lock) { snapshot ->
|
||||||
|
Timber.e("Bob timeline updated: with ${snapshot.size} events:")
|
||||||
|
snapshot.forEach {
|
||||||
|
Timber.w(" event ${it.root}")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ok, we have the 10 last messages from Alice. This will be our future previous lastForward chunk
|
||||||
|
snapshot.size == 10
|
||||||
|
&& snapshot.all { it.root.content.toModel<MessageContent>()?.body?.startsWith(firstMessage).orFalse() }
|
||||||
|
}
|
||||||
|
|
||||||
|
bobTimeline.addListener(eventsListener)
|
||||||
|
commonTestHelper.await(lock)
|
||||||
|
bobTimeline.removeAllListeners()
|
||||||
|
|
||||||
|
bobTimeline.hasMoreToLoad(Timeline.Direction.BACKWARDS).shouldBeTrue()
|
||||||
|
bobTimeline.hasMoreToLoad(Timeline.Direction.FORWARDS).shouldBeFalse()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bob stop to sync
|
||||||
|
bobSession.stopSync()
|
||||||
|
|
||||||
|
val secondMessage = "Second messages from Alice"
|
||||||
|
// Alice sends again 30 messages
|
||||||
|
commonTestHelper.sendTextMessage(
|
||||||
|
roomFromAlicePOV,
|
||||||
|
secondMessage,
|
||||||
|
30)
|
||||||
|
|
||||||
|
// Bob start to sync
|
||||||
|
bobSession.startSync(true)
|
||||||
|
|
||||||
|
run {
|
||||||
|
val lock = CountDownLatch(1)
|
||||||
|
val eventsListener = commonTestHelper.createEventListener(lock) { snapshot ->
|
||||||
|
Timber.e("Bob timeline updated: with ${snapshot.size} events:")
|
||||||
|
snapshot.forEach {
|
||||||
|
Timber.w(" event ${it.root}")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ok, we have the 10 last messages from Alice. This will be our future previous lastForward chunk
|
||||||
|
snapshot.size == 10
|
||||||
|
&& snapshot.all { it.root.content.toModel<MessageContent>()?.body?.startsWith(secondMessage).orFalse() }
|
||||||
|
}
|
||||||
|
|
||||||
|
bobTimeline.addListener(eventsListener)
|
||||||
|
commonTestHelper.await(lock)
|
||||||
|
bobTimeline.removeAllListeners()
|
||||||
|
|
||||||
|
bobTimeline.hasMoreToLoad(Timeline.Direction.BACKWARDS).shouldBeTrue()
|
||||||
|
bobTimeline.hasMoreToLoad(Timeline.Direction.FORWARDS).shouldBeFalse()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bob navigate to the first message sent from Alice
|
||||||
|
run {
|
||||||
|
val lock = CountDownLatch(1)
|
||||||
|
val eventsListener = commonTestHelper.createEventListener(lock) { snapshot ->
|
||||||
|
Timber.e("Bob timeline updated: with ${snapshot.size} events:")
|
||||||
|
snapshot.forEach {
|
||||||
|
Timber.w(" event ${it.root}")
|
||||||
|
}
|
||||||
|
|
||||||
|
// The event is not in db, so it is fetch
|
||||||
|
snapshot.size == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
bobTimeline.addListener(eventsListener)
|
||||||
|
|
||||||
|
// Restart the timeline to the first sent event, and paginate in both direction
|
||||||
|
bobTimeline.restartWithEventId(firstMessageFromAliceId)
|
||||||
|
bobTimeline.paginate(Timeline.Direction.BACKWARDS, 50)
|
||||||
|
bobTimeline.paginate(Timeline.Direction.FORWARDS, 50)
|
||||||
|
|
||||||
|
commonTestHelper.await(lock)
|
||||||
|
bobTimeline.removeAllListeners()
|
||||||
|
|
||||||
|
bobTimeline.hasMoreToLoad(Timeline.Direction.FORWARDS).shouldBeTrue()
|
||||||
|
bobTimeline.hasMoreToLoad(Timeline.Direction.BACKWARDS).shouldBeTrue()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Paginate in both direction
|
||||||
|
run {
|
||||||
|
val lock = CountDownLatch(1)
|
||||||
|
val eventsListener = commonTestHelper.createEventListener(lock) { snapshot ->
|
||||||
|
Timber.e("Bob timeline updated: with ${snapshot.size} events:")
|
||||||
|
snapshot.forEach {
|
||||||
|
Timber.w(" event ${it.root}")
|
||||||
|
}
|
||||||
|
|
||||||
|
snapshot.size == 8 + 1 + 35
|
||||||
|
}
|
||||||
|
|
||||||
|
bobTimeline.addListener(eventsListener)
|
||||||
|
|
||||||
|
// Paginate in both direction
|
||||||
|
bobTimeline.paginate(Timeline.Direction.BACKWARDS, 50)
|
||||||
|
// Ensure the chunk in the middle is included in the next pagination
|
||||||
|
bobTimeline.paginate(Timeline.Direction.FORWARDS, 35)
|
||||||
|
|
||||||
|
commonTestHelper.await(lock)
|
||||||
|
bobTimeline.removeAllListeners()
|
||||||
|
|
||||||
|
bobTimeline.hasMoreToLoad(Timeline.Direction.FORWARDS).shouldBeTrue()
|
||||||
|
bobTimeline.hasMoreToLoad(Timeline.Direction.BACKWARDS).shouldBeFalse()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bob scroll to the future, till the live
|
||||||
|
run {
|
||||||
|
val lock = CountDownLatch(1)
|
||||||
|
val eventsListener = commonTestHelper.createEventListener(lock) { snapshot ->
|
||||||
|
Timber.e("Bob timeline updated: with ${snapshot.size} events:")
|
||||||
|
snapshot.forEach {
|
||||||
|
Timber.w(" event ${it.root}")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bob can see the first event of the room (so Back pagination has worked)
|
||||||
|
snapshot.lastOrNull()?.root?.getClearType() == EventType.STATE_ROOM_CREATE
|
||||||
|
// 8 for room creation item 60 message from Alice
|
||||||
|
&& snapshot.size == 8 + 60
|
||||||
|
&& snapshot.checkSendOrder(secondMessage, 30, 0)
|
||||||
|
&& snapshot.checkSendOrder(firstMessage, 30, 30)
|
||||||
|
}
|
||||||
|
|
||||||
|
bobTimeline.addListener(eventsListener)
|
||||||
|
|
||||||
|
bobTimeline.paginate(Timeline.Direction.FORWARDS, 50)
|
||||||
|
|
||||||
|
commonTestHelper.await(lock)
|
||||||
|
bobTimeline.removeAllListeners()
|
||||||
|
|
||||||
|
bobTimeline.hasMoreToLoad(Timeline.Direction.FORWARDS).shouldBeFalse()
|
||||||
|
bobTimeline.hasMoreToLoad(Timeline.Direction.BACKWARDS).shouldBeFalse()
|
||||||
|
}
|
||||||
|
|
||||||
|
bobTimeline.dispose()
|
||||||
|
|
||||||
|
cryptoTestData.cleanUp(commonTestHelper)
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,7 +23,6 @@ import androidx.work.WorkManager
|
||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
import im.vector.matrix.android.BuildConfig
|
import im.vector.matrix.android.BuildConfig
|
||||||
import im.vector.matrix.android.api.auth.AuthenticationService
|
import im.vector.matrix.android.api.auth.AuthenticationService
|
||||||
import im.vector.matrix.android.api.crypto.MXCryptoConfig
|
|
||||||
import im.vector.matrix.android.internal.SessionManager
|
import im.vector.matrix.android.internal.SessionManager
|
||||||
import im.vector.matrix.android.internal.crypto.attachments.ElementToDecrypt
|
import im.vector.matrix.android.internal.crypto.attachments.ElementToDecrypt
|
||||||
import im.vector.matrix.android.internal.crypto.attachments.MXEncryptedAttachments
|
import im.vector.matrix.android.internal.crypto.attachments.MXEncryptedAttachments
|
||||||
|
@ -32,20 +31,10 @@ import im.vector.matrix.android.internal.network.UserAgentHolder
|
||||||
import im.vector.matrix.android.internal.util.BackgroundDetectionObserver
|
import im.vector.matrix.android.internal.util.BackgroundDetectionObserver
|
||||||
import org.matrix.olm.OlmManager
|
import org.matrix.olm.OlmManager
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
data class MatrixConfiguration(
|
|
||||||
val applicationFlavor: String = "Default-application-flavor",
|
|
||||||
val cryptoConfig: MXCryptoConfig = MXCryptoConfig()
|
|
||||||
) {
|
|
||||||
|
|
||||||
interface Provider {
|
|
||||||
fun providesMatrixConfiguration(): MatrixConfiguration
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the main entry point to the matrix sdk.
|
* This is the main entry point to the matrix sdk.
|
||||||
* To get the singleton instance, use getInstance static method.
|
* To get the singleton instance, use getInstance static method.
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.matrix.android.api
|
||||||
|
|
||||||
|
import im.vector.matrix.android.api.crypto.MXCryptoConfig
|
||||||
|
import java.net.Proxy
|
||||||
|
|
||||||
|
data class MatrixConfiguration(
|
||||||
|
val applicationFlavor: String = "Default-application-flavor",
|
||||||
|
val cryptoConfig: MXCryptoConfig = MXCryptoConfig(),
|
||||||
|
/**
|
||||||
|
* Optional proxy to connect to the matrix servers
|
||||||
|
* You can create one using for instance Proxy(proxyType, InetSocketAddress(hostname, port)
|
||||||
|
*/
|
||||||
|
val proxy: Proxy? = null
|
||||||
|
) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can be implemented by your Application class
|
||||||
|
*/
|
||||||
|
interface Provider {
|
||||||
|
fun providesMatrixConfiguration(): MatrixConfiguration
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,7 +20,6 @@ import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.auth.data.Credentials
|
import im.vector.matrix.android.api.auth.data.Credentials
|
||||||
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
|
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
|
||||||
import im.vector.matrix.android.api.auth.data.LoginFlowResult
|
import im.vector.matrix.android.api.auth.data.LoginFlowResult
|
||||||
import im.vector.matrix.android.api.auth.data.SessionParams
|
|
||||||
import im.vector.matrix.android.api.auth.login.LoginWizard
|
import im.vector.matrix.android.api.auth.login.LoginWizard
|
||||||
import im.vector.matrix.android.api.auth.registration.RegistrationWizard
|
import im.vector.matrix.android.api.auth.registration.RegistrationWizard
|
||||||
import im.vector.matrix.android.api.auth.wellknown.WellknownResult
|
import im.vector.matrix.android.api.auth.wellknown.WellknownResult
|
||||||
|
@ -37,6 +36,11 @@ interface AuthenticationService {
|
||||||
*/
|
*/
|
||||||
fun getLoginFlow(homeServerConnectionConfig: HomeServerConnectionConfig, callback: MatrixCallback<LoginFlowResult>): Cancelable
|
fun getLoginFlow(homeServerConnectionConfig: HomeServerConnectionConfig, callback: MatrixCallback<LoginFlowResult>): Cancelable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request the supported login flows for the corresponding sessionId.
|
||||||
|
*/
|
||||||
|
fun getLoginFlowOfSession(sessionId: String, callback: MatrixCallback<LoginFlowResult>): Cancelable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a LoginWizard, to login to the homeserver. The login flow has to be retrieved first.
|
* Return a LoginWizard, to login to the homeserver. The login flow has to be retrieved first.
|
||||||
*/
|
*/
|
||||||
|
@ -74,15 +78,6 @@ interface AuthenticationService {
|
||||||
*/
|
*/
|
||||||
fun getLastAuthenticatedSession(): Session?
|
fun getLastAuthenticatedSession(): Session?
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an authenticated session. You should at least call authenticate one time before.
|
|
||||||
* If you logout, this session will no longer be valid.
|
|
||||||
*
|
|
||||||
* @param sessionParams the sessionParams to open with.
|
|
||||||
* @return the associated session if any, or null
|
|
||||||
*/
|
|
||||||
fun getSession(sessionParams: SessionParams): Session?
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a session after a SSO successful login
|
* Create a session after a SSO successful login
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -21,7 +21,48 @@ package im.vector.matrix.android.api.auth.data
|
||||||
* You don't have to manually instantiate it.
|
* You don't have to manually instantiate it.
|
||||||
*/
|
*/
|
||||||
data class SessionParams(
|
data class SessionParams(
|
||||||
|
/**
|
||||||
|
* Please consider using shortcuts instead
|
||||||
|
*/
|
||||||
val credentials: Credentials,
|
val credentials: Credentials,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Please consider using shortcuts instead
|
||||||
|
*/
|
||||||
val homeServerConnectionConfig: HomeServerConnectionConfig,
|
val homeServerConnectionConfig: HomeServerConnectionConfig,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set to false if the current token is not valid anymore. Application should not have to use this info.
|
||||||
|
*/
|
||||||
val isTokenValid: Boolean
|
val isTokenValid: Boolean
|
||||||
)
|
) {
|
||||||
|
/*
|
||||||
|
* Shortcuts. Usually the application should only need to use these shortcuts
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The userId of the session (Ex: "@user:domain.org")
|
||||||
|
*/
|
||||||
|
val userId = credentials.userId
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The deviceId of the session (Ex: "ABCDEFGH")
|
||||||
|
*/
|
||||||
|
val deviceId = credentials.deviceId
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current homeserver Url. It can be different that the homeserver url entered
|
||||||
|
* during login phase, because a redirection may have occurred
|
||||||
|
*/
|
||||||
|
val homeServerUrl = homeServerConnectionConfig.homeServerUri.toString()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current homeserver host
|
||||||
|
*/
|
||||||
|
val homeServerHost = homeServerConnectionConfig.homeServerUri.host
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default identity server url if any, returned by the homeserver during login phase
|
||||||
|
*/
|
||||||
|
val defaultIdentityServerUrl = homeServerConnectionConfig.identityServerUri?.toString()
|
||||||
|
}
|
||||||
|
|
|
@ -19,11 +19,11 @@ package im.vector.matrix.android.api.crypto
|
||||||
/**
|
/**
|
||||||
* Class to define the parameters used to customize or configure the end-to-end crypto.
|
* Class to define the parameters used to customize or configure the end-to-end crypto.
|
||||||
*/
|
*/
|
||||||
data class MXCryptoConfig(
|
data class MXCryptoConfig constructor(
|
||||||
// Tell whether the encryption of the event content is enabled for the invited members.
|
// Tell whether the encryption of the event content is enabled for the invited members.
|
||||||
// SDK clients can disable this by settings it to false.
|
// SDK clients can disable this by settings it to false.
|
||||||
// Note that the encryption for the invited members will be blocked if the history visibility is "joined".
|
// Note that the encryption for the invited members will be blocked if the history visibility is "joined".
|
||||||
var enableEncryptionForInvitedMembers: Boolean = true,
|
val enableEncryptionForInvitedMembers: Boolean = true,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If set to true, the SDK will automatically ignore room key request (gossiping)
|
* If set to true, the SDK will automatically ignore room key request (gossiping)
|
||||||
|
@ -31,6 +31,5 @@ data class MXCryptoConfig(
|
||||||
* If set to false, the request will be forwarded to the application layer; in this
|
* If set to false, the request will be forwarded to the application layer; in this
|
||||||
* case the application can decide to prompt the user.
|
* case the application can decide to prompt the user.
|
||||||
*/
|
*/
|
||||||
var discardRoomKeyRequestsFromUntrustedDevices : Boolean = true
|
val discardRoomKeyRequestsFromUntrustedDevices: Boolean = true
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -39,7 +39,10 @@ data class MatrixError(
|
||||||
// For M_LIMIT_EXCEEDED
|
// For M_LIMIT_EXCEEDED
|
||||||
@Json(name = "retry_after_ms") val retryAfterMillis: Long? = null,
|
@Json(name = "retry_after_ms") val retryAfterMillis: Long? = null,
|
||||||
// For M_UNKNOWN_TOKEN
|
// For M_UNKNOWN_TOKEN
|
||||||
@Json(name = "soft_logout") val isSoftLogout: Boolean = false
|
@Json(name = "soft_logout") val isSoftLogout: Boolean = false,
|
||||||
|
// For M_INVALID_PEPPER
|
||||||
|
// {"error": "pepper does not match 'erZvr'", "lookup_pepper": "pQgMS", "algorithm": "sha256", "errcode": "M_INVALID_PEPPER"}
|
||||||
|
@Json(name = "lookup_pepper") val newLookupPepper: String? = null
|
||||||
) {
|
) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -129,6 +132,11 @@ data class MatrixError(
|
||||||
/** (Not documented yet) */
|
/** (Not documented yet) */
|
||||||
const val M_WRONG_ROOM_KEYS_VERSION = "M_WRONG_ROOM_KEYS_VERSION"
|
const val M_WRONG_ROOM_KEYS_VERSION = "M_WRONG_ROOM_KEYS_VERSION"
|
||||||
|
|
||||||
|
const val M_TERMS_NOT_SIGNED = "M_TERMS_NOT_SIGNED"
|
||||||
|
|
||||||
|
// For identity service
|
||||||
|
const val M_INVALID_PEPPER = "M_INVALID_PEPPER"
|
||||||
|
|
||||||
// Possible value for "limit_type"
|
// Possible value for "limit_type"
|
||||||
const val LIMIT_TYPE_MAU = "monthly_active_user"
|
const val LIMIT_TYPE_MAU = "monthly_active_user"
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ import im.vector.matrix.android.api.session.crypto.CryptoService
|
||||||
import im.vector.matrix.android.api.session.file.FileService
|
import im.vector.matrix.android.api.session.file.FileService
|
||||||
import im.vector.matrix.android.api.session.group.GroupService
|
import im.vector.matrix.android.api.session.group.GroupService
|
||||||
import im.vector.matrix.android.api.session.homeserver.HomeServerCapabilitiesService
|
import im.vector.matrix.android.api.session.homeserver.HomeServerCapabilitiesService
|
||||||
|
import im.vector.matrix.android.api.session.identity.IdentityService
|
||||||
import im.vector.matrix.android.api.session.profile.ProfileService
|
import im.vector.matrix.android.api.session.profile.ProfileService
|
||||||
import im.vector.matrix.android.api.session.pushers.PushersService
|
import im.vector.matrix.android.api.session.pushers.PushersService
|
||||||
import im.vector.matrix.android.api.session.room.RoomDirectoryService
|
import im.vector.matrix.android.api.session.room.RoomDirectoryService
|
||||||
|
@ -39,6 +40,7 @@ import im.vector.matrix.android.api.session.securestorage.SharedSecretStorageSer
|
||||||
import im.vector.matrix.android.api.session.signout.SignOutService
|
import im.vector.matrix.android.api.session.signout.SignOutService
|
||||||
import im.vector.matrix.android.api.session.sync.FilterService
|
import im.vector.matrix.android.api.session.sync.FilterService
|
||||||
import im.vector.matrix.android.api.session.sync.SyncState
|
import im.vector.matrix.android.api.session.sync.SyncState
|
||||||
|
import im.vector.matrix.android.api.session.terms.TermsService
|
||||||
import im.vector.matrix.android.api.session.user.UserService
|
import im.vector.matrix.android.api.session.user.UserService
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -54,6 +56,7 @@ interface Session :
|
||||||
SignOutService,
|
SignOutService,
|
||||||
FilterService,
|
FilterService,
|
||||||
FileService,
|
FileService,
|
||||||
|
TermsService,
|
||||||
ProfileService,
|
ProfileService,
|
||||||
PushRuleService,
|
PushRuleService,
|
||||||
PushersService,
|
PushersService,
|
||||||
|
@ -77,7 +80,7 @@ interface Session :
|
||||||
* Useful shortcut to get access to the userId
|
* Useful shortcut to get access to the userId
|
||||||
*/
|
*/
|
||||||
val myUserId: String
|
val myUserId: String
|
||||||
get() = sessionParams.credentials.userId
|
get() = sessionParams.userId
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The sessionId
|
* The sessionId
|
||||||
|
@ -145,6 +148,11 @@ interface Session :
|
||||||
*/
|
*/
|
||||||
fun cryptoService(): CryptoService
|
fun cryptoService(): CryptoService
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the identity service associated with the session
|
||||||
|
*/
|
||||||
|
fun identityService(): IdentityService
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a listener to the session.
|
* Add a listener to the session.
|
||||||
* @param listener the listener to add.
|
* @param listener the listener to add.
|
||||||
|
|
|
@ -220,3 +220,11 @@ fun Event.isImageMessage(): Boolean {
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Event.isVideoMessage(): Boolean {
|
||||||
|
return getClearType() == EventType.MESSAGE
|
||||||
|
&& when (getClearContent()?.toModel<MessageContent>()?.msgType) {
|
||||||
|
MessageType.MSGTYPE_VIDEO -> true
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -24,7 +24,15 @@ data class HomeServerCapabilities(
|
||||||
/**
|
/**
|
||||||
* Max size of file which can be uploaded to the homeserver in bytes. [MAX_UPLOAD_FILE_SIZE_UNKNOWN] if unknown or not retrieved yet
|
* Max size of file which can be uploaded to the homeserver in bytes. [MAX_UPLOAD_FILE_SIZE_UNKNOWN] if unknown or not retrieved yet
|
||||||
*/
|
*/
|
||||||
val maxUploadFileSize: Long = MAX_UPLOAD_FILE_SIZE_UNKNOWN
|
val maxUploadFileSize: Long = MAX_UPLOAD_FILE_SIZE_UNKNOWN,
|
||||||
|
/**
|
||||||
|
* Last version identity server and binding supported
|
||||||
|
*/
|
||||||
|
val lastVersionIdentityServerSupported: Boolean = false,
|
||||||
|
/**
|
||||||
|
* Default identity server url, provided in Wellknown
|
||||||
|
*/
|
||||||
|
val defaultIdentityServerUrl: String? = null
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
const val MAX_UPLOAD_FILE_SIZE_UNKNOWN = -1L
|
const val MAX_UPLOAD_FILE_SIZE_UNKNOWN = -1L
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.matrix.android.api.session.identity
|
||||||
|
|
||||||
|
data class FoundThreePid(
|
||||||
|
val threePid: ThreePid,
|
||||||
|
val matrixId: String
|
||||||
|
)
|
|
@ -0,0 +1,109 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.matrix.android.api.session.identity
|
||||||
|
|
||||||
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
|
import im.vector.matrix.android.api.util.Cancelable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides access to the identity server configuration and services identity server can provide
|
||||||
|
*/
|
||||||
|
interface IdentityService {
|
||||||
|
/**
|
||||||
|
* Return the default identity server of the user, which may have been provided at login time by the homeserver,
|
||||||
|
* or by the Well-known setup of the homeserver
|
||||||
|
* It may be different from the current configured identity server
|
||||||
|
*/
|
||||||
|
fun getDefaultIdentityServer(): String?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the current identity server URL used by this account. Returns null if no identity server is configured.
|
||||||
|
*/
|
||||||
|
fun getCurrentIdentityServerUrl(): String?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the identity server is valid
|
||||||
|
* See https://matrix.org/docs/spec/identity_service/latest#status-check
|
||||||
|
* RiotX SDK only supports identity server API v2
|
||||||
|
*/
|
||||||
|
fun isValidIdentityServer(url: String, callback: MatrixCallback<Unit>): Cancelable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the identity server url.
|
||||||
|
* If successful, any previous identity server will be disconnected.
|
||||||
|
* In case of error, any previous identity server will remain configured.
|
||||||
|
* @param url the new url.
|
||||||
|
* @param callback will notify the user if change is successful. The String will be the final url of the identity server.
|
||||||
|
* The SDK can prepend "https://" for instance.
|
||||||
|
*/
|
||||||
|
fun setNewIdentityServer(url: String, callback: MatrixCallback<String>): Cancelable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disconnect (logout) from the current identity server
|
||||||
|
*/
|
||||||
|
fun disconnect(callback: MatrixCallback<Unit>): Cancelable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This will ask the identity server to send an email or an SMS to let the user confirm he owns the ThreePid
|
||||||
|
*/
|
||||||
|
fun startBindThreePid(threePid: ThreePid, callback: MatrixCallback<Unit>): Cancelable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This will cancel a pending binding of threePid.
|
||||||
|
*/
|
||||||
|
fun cancelBindThreePid(threePid: ThreePid, callback: MatrixCallback<Unit>): Cancelable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This will ask the identity server to send an new email or a new SMS to let the user confirm he owns the ThreePid
|
||||||
|
*/
|
||||||
|
fun sendAgainValidationCode(threePid: ThreePid, callback: MatrixCallback<Unit>): Cancelable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submit the code that the identity server has sent to the user (in email or SMS)
|
||||||
|
* Once successful, you will have to call [finalizeBindThreePid]
|
||||||
|
* @param code the code sent to the user
|
||||||
|
*/
|
||||||
|
fun submitValidationToken(threePid: ThreePid, code: String, callback: MatrixCallback<Unit>): Cancelable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This will perform the actual association of ThreePid and Matrix account
|
||||||
|
*/
|
||||||
|
fun finalizeBindThreePid(threePid: ThreePid, callback: MatrixCallback<Unit>): Cancelable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unbind a threePid
|
||||||
|
* The request will actually be done on the homeserver
|
||||||
|
*/
|
||||||
|
fun unbindThreePid(threePid: ThreePid, callback: MatrixCallback<Unit>): Cancelable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search MatrixId of users providing email and phone numbers
|
||||||
|
*/
|
||||||
|
fun lookUp(threePids: List<ThreePid>, callback: MatrixCallback<List<FoundThreePid>>): Cancelable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the status of the current user's threePid
|
||||||
|
* A lookup will be performed, but also pending binding state will be restored
|
||||||
|
*
|
||||||
|
* @param threePids the list of threePid the user owns (retrieved form the homeserver)
|
||||||
|
* @param callback onSuccess will be called with a map of ThreePid -> SharedState
|
||||||
|
*/
|
||||||
|
fun getShareStatus(threePids: List<ThreePid>, callback: MatrixCallback<Map<ThreePid, SharedState>>): Cancelable
|
||||||
|
|
||||||
|
fun addListener(listener: IdentityServiceListener)
|
||||||
|
fun removeListener(listener: IdentityServiceListener)
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.matrix.android.api.session.identity
|
||||||
|
|
||||||
|
sealed class IdentityServiceError : Throwable() {
|
||||||
|
object OutdatedIdentityServer : IdentityServiceError()
|
||||||
|
object OutdatedHomeServer : IdentityServiceError()
|
||||||
|
object NoIdentityServerConfigured : IdentityServiceError()
|
||||||
|
object TermsNotSignedException : IdentityServiceError()
|
||||||
|
object BulkLookupSha256NotSupported : IdentityServiceError()
|
||||||
|
object BindingError : IdentityServiceError()
|
||||||
|
object NoCurrentBindingError : IdentityServiceError()
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.matrix.android.api.session.identity
|
||||||
|
|
||||||
|
interface IdentityServiceListener {
|
||||||
|
fun onIdentityServerChange()
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.matrix.android.api.session.identity
|
||||||
|
|
||||||
|
enum class SharedState {
|
||||||
|
SHARED,
|
||||||
|
NOT_SHARED,
|
||||||
|
BINDING_IN_PROGRESS
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.matrix.android.api.session.identity
|
||||||
|
|
||||||
|
import com.google.i18n.phonenumbers.NumberParseException
|
||||||
|
import com.google.i18n.phonenumbers.PhoneNumberUtil
|
||||||
|
import im.vector.matrix.android.internal.session.profile.ThirdPartyIdentifier
|
||||||
|
|
||||||
|
sealed class ThreePid(open val value: String) {
|
||||||
|
data class Email(val email: String) : ThreePid(email)
|
||||||
|
data class Msisdn(val msisdn: String) : ThreePid(msisdn)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun ThreePid.toMedium(): String {
|
||||||
|
return when (this) {
|
||||||
|
is ThreePid.Email -> ThirdPartyIdentifier.MEDIUM_EMAIL
|
||||||
|
is ThreePid.Msisdn -> ThirdPartyIdentifier.MEDIUM_MSISDN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(NumberParseException::class)
|
||||||
|
internal fun ThreePid.Msisdn.getCountryCode(): String {
|
||||||
|
return with(PhoneNumberUtil.getInstance()) {
|
||||||
|
getRegionCodeForCountryCode(parse("+$msisdn", null).countryCode)
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,7 +17,9 @@
|
||||||
|
|
||||||
package im.vector.matrix.android.api.session.profile
|
package im.vector.matrix.android.api.session.profile
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
|
import im.vector.matrix.android.api.session.identity.ThreePid
|
||||||
import im.vector.matrix.android.api.util.Cancelable
|
import im.vector.matrix.android.api.util.Cancelable
|
||||||
import im.vector.matrix.android.api.util.JsonDict
|
import im.vector.matrix.android.api.util.JsonDict
|
||||||
import im.vector.matrix.android.api.util.Optional
|
import im.vector.matrix.android.api.util.Optional
|
||||||
|
@ -53,4 +55,15 @@ interface ProfileService {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
fun getProfile(userId: String, matrixCallback: MatrixCallback<JsonDict>): Cancelable
|
fun getProfile(userId: String, matrixCallback: MatrixCallback<JsonDict>): Cancelable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current user 3Pids
|
||||||
|
*/
|
||||||
|
fun getThreePids(): List<ThreePid>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current user 3Pids Live
|
||||||
|
* @param refreshData set to true to fetch data from the homeserver
|
||||||
|
*/
|
||||||
|
fun getThreePidsLive(refreshData: Boolean): LiveData<List<ThreePid>>
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ import im.vector.matrix.android.api.session.room.send.SendService
|
||||||
import im.vector.matrix.android.api.session.room.state.StateService
|
import im.vector.matrix.android.api.session.room.state.StateService
|
||||||
import im.vector.matrix.android.api.session.room.timeline.TimelineService
|
import im.vector.matrix.android.api.session.room.timeline.TimelineService
|
||||||
import im.vector.matrix.android.api.session.room.typing.TypingService
|
import im.vector.matrix.android.api.session.room.typing.TypingService
|
||||||
|
import im.vector.matrix.android.api.session.room.uploads.UploadsService
|
||||||
import im.vector.matrix.android.api.util.Optional
|
import im.vector.matrix.android.api.util.Optional
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -42,6 +43,7 @@ interface Room :
|
||||||
TypingService,
|
TypingService,
|
||||||
MembershipService,
|
MembershipService,
|
||||||
StateService,
|
StateService,
|
||||||
|
UploadsService,
|
||||||
ReportingService,
|
ReportingService,
|
||||||
RelationService,
|
RelationService,
|
||||||
RoomCryptoService,
|
RoomCryptoService,
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.matrix.android.api.session.room.model.message
|
||||||
|
|
||||||
|
interface MessageContentWithFormattedBody : MessageContent {
|
||||||
|
/**
|
||||||
|
* The format used in the formatted_body. Currently only "org.matrix.custom.html" is supported.
|
||||||
|
*/
|
||||||
|
val format: String?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The formatted version of the body. This is required if format is specified.
|
||||||
|
*/
|
||||||
|
val formattedBody: String?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the formattedBody, only if not blank and if the format is equal to "org.matrix.custom.html"
|
||||||
|
*/
|
||||||
|
val matrixFormattedBody: String?
|
||||||
|
get() = formattedBody?.takeIf { it.isNotBlank() && format == MessageFormat.FORMAT_MATRIX_HTML }
|
||||||
|
}
|
|
@ -34,15 +34,15 @@ data class MessageEmoteContent(
|
||||||
@Json(name = "body") override val body: String,
|
@Json(name = "body") override val body: String,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The format used in the formatted_body. Currently only org.matrix.custom.html is supported.
|
* The format used in the formatted_body. Currently only "org.matrix.custom.html" is supported.
|
||||||
*/
|
*/
|
||||||
@Json(name = "format") val format: String? = null,
|
@Json(name = "format") override val format: String? = null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The formatted version of the body. This is required if format is specified.
|
* The formatted version of the body. This is required if format is specified.
|
||||||
*/
|
*/
|
||||||
@Json(name = "formatted_body") val formattedBody: String? = null,
|
@Json(name = "formatted_body") override val formattedBody: String? = null,
|
||||||
|
|
||||||
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
|
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
|
||||||
@Json(name = "m.new_content") override val newContent: Content? = null
|
@Json(name = "m.new_content") override val newContent: Content? = null
|
||||||
) : MessageContent
|
) : MessageContentWithFormattedBody
|
||||||
|
|
|
@ -34,15 +34,15 @@ data class MessageNoticeContent(
|
||||||
@Json(name = "body") override val body: String,
|
@Json(name = "body") override val body: String,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The format used in the formatted_body. Currently only org.matrix.custom.html is supported.
|
* The format used in the formatted_body. Currently only "org.matrix.custom.html" is supported.
|
||||||
*/
|
*/
|
||||||
@Json(name = "format") val format: String? = null,
|
@Json(name = "format") override val format: String? = null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The formatted version of the body. This is required if format is specified.
|
* The formatted version of the body. This is required if format is specified.
|
||||||
*/
|
*/
|
||||||
@Json(name = "formatted_body") val formattedBody: String? = null,
|
@Json(name = "formatted_body") override val formattedBody: String? = null,
|
||||||
|
|
||||||
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
|
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
|
||||||
@Json(name = "m.new_content") override val newContent: Content? = null
|
@Json(name = "m.new_content") override val newContent: Content? = null
|
||||||
) : MessageContent
|
) : MessageContentWithFormattedBody
|
||||||
|
|
|
@ -34,15 +34,15 @@ data class MessageTextContent(
|
||||||
@Json(name = "body") override val body: String,
|
@Json(name = "body") override val body: String,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The format used in the formatted_body. Currently only org.matrix.custom.html is supported.
|
* The format used in the formatted_body. Currently only "org.matrix.custom.html" is supported.
|
||||||
*/
|
*/
|
||||||
@Json(name = "format") val format: String? = null,
|
@Json(name = "format") override val format: String? = null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The formatted version of the body. This is required if format is specified.
|
* The formatted version of the body. This is required if format is specified.
|
||||||
*/
|
*/
|
||||||
@Json(name = "formatted_body") val formattedBody: String? = null,
|
@Json(name = "formatted_body") override val formattedBody: String? = null,
|
||||||
|
|
||||||
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
|
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
|
||||||
@Json(name = "m.new_content") override val newContent: Content? = null
|
@Json(name = "m.new_content") override val newContent: Content? = null
|
||||||
) : MessageContent
|
) : MessageContentWithFormattedBody
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.matrix.android.api.session.room.sender
|
||||||
|
|
||||||
|
data class SenderInfo(
|
||||||
|
val userId: String,
|
||||||
|
/**
|
||||||
|
* Consider using [disambiguatedDisplayName]
|
||||||
|
*/
|
||||||
|
val displayName: String?,
|
||||||
|
val isUniqueDisplayName: Boolean,
|
||||||
|
val avatarUrl: String?
|
||||||
|
) {
|
||||||
|
val disambiguatedDisplayName: String
|
||||||
|
get() = when {
|
||||||
|
displayName.isNullOrBlank() -> userId
|
||||||
|
isUniqueDisplayName -> displayName
|
||||||
|
else -> "$displayName ($userId)"
|
||||||
|
}
|
||||||
|
}
|
|
@ -58,7 +58,7 @@ interface Timeline {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the timeline can be enriched by paginating.
|
* Check if the timeline can be enriched by paginating.
|
||||||
* @param the direction to check in
|
* @param direction the direction to check in
|
||||||
* @return true if timeline can be enriched
|
* @return true if timeline can be enriched
|
||||||
*/
|
*/
|
||||||
fun hasMoreToLoad(direction: Direction): Boolean
|
fun hasMoreToLoad(direction: Direction): Boolean
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package im.vector.matrix.android.api.session.room.timeline
|
package im.vector.matrix.android.api.session.room.timeline
|
||||||
|
|
||||||
|
import im.vector.matrix.android.BuildConfig
|
||||||
import im.vector.matrix.android.api.session.events.model.Event
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
import im.vector.matrix.android.api.session.events.model.EventType
|
import im.vector.matrix.android.api.session.events.model.EventType
|
||||||
import im.vector.matrix.android.api.session.events.model.RelationType
|
import im.vector.matrix.android.api.session.events.model.RelationType
|
||||||
|
@ -25,6 +26,7 @@ import im.vector.matrix.android.api.session.room.model.ReadReceipt
|
||||||
import im.vector.matrix.android.api.session.room.model.message.MessageContent
|
import im.vector.matrix.android.api.session.room.model.message.MessageContent
|
||||||
import im.vector.matrix.android.api.session.room.model.message.MessageStickerContent
|
import im.vector.matrix.android.api.session.room.model.message.MessageStickerContent
|
||||||
import im.vector.matrix.android.api.session.room.model.message.isReply
|
import im.vector.matrix.android.api.session.room.model.message.isReply
|
||||||
|
import im.vector.matrix.android.api.session.room.sender.SenderInfo
|
||||||
import im.vector.matrix.android.api.util.ContentUtils.extractUsefulTextFromReply
|
import im.vector.matrix.android.api.util.ContentUtils.extractUsefulTextFromReply
|
||||||
import im.vector.matrix.android.internal.crypto.model.event.EncryptedEventContent
|
import im.vector.matrix.android.internal.crypto.model.event.EncryptedEventContent
|
||||||
|
|
||||||
|
@ -38,13 +40,17 @@ data class TimelineEvent(
|
||||||
val localId: Long,
|
val localId: Long,
|
||||||
val eventId: String,
|
val eventId: String,
|
||||||
val displayIndex: Int,
|
val displayIndex: Int,
|
||||||
val senderName: String?,
|
val senderInfo: SenderInfo,
|
||||||
val isUniqueDisplayName: Boolean,
|
|
||||||
val senderAvatar: String?,
|
|
||||||
val annotations: EventAnnotationsSummary? = null,
|
val annotations: EventAnnotationsSummary? = null,
|
||||||
val readReceipts: List<ReadReceipt> = emptyList()
|
val readReceipts: List<ReadReceipt> = emptyList()
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (BuildConfig.DEBUG) {
|
||||||
|
assert(eventId == root.eventId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val metadata = HashMap<String, Any>()
|
val metadata = HashMap<String, Any>()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -62,14 +68,6 @@ data class TimelineEvent(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getDisambiguatedDisplayName(): String {
|
|
||||||
return when {
|
|
||||||
senderName.isNullOrBlank() -> root.senderId ?: ""
|
|
||||||
isUniqueDisplayName -> senderName
|
|
||||||
else -> "$senderName (${root.senderId})"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the metadata associated with a key.
|
* Get the metadata associated with a key.
|
||||||
* @param key the key to get the metadata
|
* @param key the key to get the metadata
|
||||||
|
|
|
@ -28,6 +28,10 @@ data class TimelineSettings(
|
||||||
* A flag to filter edit events
|
* A flag to filter edit events
|
||||||
*/
|
*/
|
||||||
val filterEdits: Boolean = false,
|
val filterEdits: Boolean = false,
|
||||||
|
/**
|
||||||
|
* A flag to filter redacted events
|
||||||
|
*/
|
||||||
|
val filterRedacted: Boolean = false,
|
||||||
/**
|
/**
|
||||||
* A flag to filter by types. It should be used with [allowedTypes] field
|
* A flag to filter by types. It should be used with [allowedTypes] field
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.matrix.android.api.session.room.uploads
|
||||||
|
|
||||||
|
data class GetUploadsResult(
|
||||||
|
// List of fetched Events, most recent first
|
||||||
|
val uploadEvents: List<UploadEvent>,
|
||||||
|
// token to get more events
|
||||||
|
val nextToken: String,
|
||||||
|
// True if there are more event to load
|
||||||
|
val hasMore: Boolean
|
||||||
|
)
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.matrix.android.api.session.room.uploads
|
||||||
|
|
||||||
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
|
import im.vector.matrix.android.api.session.room.model.message.MessageWithAttachmentContent
|
||||||
|
import im.vector.matrix.android.api.session.room.sender.SenderInfo
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper around on Event.
|
||||||
|
* Similar to [im.vector.matrix.android.api.session.room.timeline.TimelineEvent], contains an Event with extra useful data
|
||||||
|
*/
|
||||||
|
data class UploadEvent(
|
||||||
|
val root: Event,
|
||||||
|
val eventId: String,
|
||||||
|
val contentWithAttachmentContent: MessageWithAttachmentContent,
|
||||||
|
val senderInfo: SenderInfo
|
||||||
|
)
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.matrix.android.api.session.room.uploads
|
||||||
|
|
||||||
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
|
import im.vector.matrix.android.api.util.Cancelable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interface defines methods to get event with uploads (= attachments) sent to a room. It's implemented at the room level.
|
||||||
|
*/
|
||||||
|
interface UploadsService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a list of events containing URL sent to a room, from most recent to oldest one
|
||||||
|
* @param numberOfEvents the expected number of events to retrieve. The result can contain less events.
|
||||||
|
* @param since token to get next page, or null to get the first page
|
||||||
|
*/
|
||||||
|
fun getUploads(numberOfEvents: Int,
|
||||||
|
since: String?,
|
||||||
|
callback: MatrixCallback<GetUploadsResult>): Cancelable
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.matrix.android.api.session.terms
|
||||||
|
|
||||||
|
import im.vector.matrix.android.internal.session.terms.TermsResponse
|
||||||
|
|
||||||
|
data class GetTermsResponse(
|
||||||
|
val serverResponse: TermsResponse,
|
||||||
|
val alreadyAcceptedTermUrls: Set<String>
|
||||||
|
)
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.matrix.android.api.session.terms
|
||||||
|
|
||||||
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
|
import im.vector.matrix.android.api.util.Cancelable
|
||||||
|
|
||||||
|
interface TermsService {
|
||||||
|
enum class ServiceType {
|
||||||
|
IntegrationManager,
|
||||||
|
IdentityService
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getTerms(serviceType: ServiceType,
|
||||||
|
baseUrl: String,
|
||||||
|
callback: MatrixCallback<GetTermsResponse>): Cancelable
|
||||||
|
|
||||||
|
fun agreeToTerms(serviceType: ServiceType,
|
||||||
|
baseUrl: String,
|
||||||
|
agreedUrls: List<String>,
|
||||||
|
token: String?,
|
||||||
|
callback: MatrixCallback<Unit>): Cancelable
|
||||||
|
}
|
|
@ -21,6 +21,7 @@ import im.vector.matrix.android.api.session.group.model.GroupSummary
|
||||||
import im.vector.matrix.android.api.session.room.model.RoomMemberSummary
|
import im.vector.matrix.android.api.session.room.model.RoomMemberSummary
|
||||||
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||||
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom
|
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom
|
||||||
|
import im.vector.matrix.android.api.session.room.sender.SenderInfo
|
||||||
import im.vector.matrix.android.api.session.user.model.User
|
import im.vector.matrix.android.api.session.user.model.User
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
|
@ -154,3 +155,5 @@ fun RoomSummary.toRoomAliasMatrixItem() = MatrixItem.RoomAliasItem(canonicalAlia
|
||||||
fun PublicRoom.toMatrixItem() = MatrixItem.RoomItem(roomId, name ?: getPrimaryAlias() ?: "", avatarUrl)
|
fun PublicRoom.toMatrixItem() = MatrixItem.RoomItem(roomId, name ?: getPrimaryAlias() ?: "", avatarUrl)
|
||||||
|
|
||||||
fun RoomMemberSummary.toMatrixItem() = MatrixItem.UserItem(userId, displayName, avatarUrl)
|
fun RoomMemberSummary.toMatrixItem() = MatrixItem.UserItem(userId, displayName, avatarUrl)
|
||||||
|
|
||||||
|
fun SenderInfo.toMatrixItem() = MatrixItem.UserItem(userId, disambiguatedDisplayName, avatarUrl)
|
||||||
|
|
|
@ -25,16 +25,15 @@ import im.vector.matrix.android.internal.auth.db.AuthRealmMigration
|
||||||
import im.vector.matrix.android.internal.auth.db.AuthRealmModule
|
import im.vector.matrix.android.internal.auth.db.AuthRealmModule
|
||||||
import im.vector.matrix.android.internal.auth.db.RealmPendingSessionStore
|
import im.vector.matrix.android.internal.auth.db.RealmPendingSessionStore
|
||||||
import im.vector.matrix.android.internal.auth.db.RealmSessionParamsStore
|
import im.vector.matrix.android.internal.auth.db.RealmSessionParamsStore
|
||||||
import im.vector.matrix.android.internal.auth.wellknown.DefaultDirectLoginTask
|
import im.vector.matrix.android.internal.auth.login.DefaultDirectLoginTask
|
||||||
import im.vector.matrix.android.internal.auth.wellknown.DefaultGetWellknownTask
|
import im.vector.matrix.android.internal.auth.login.DirectLoginTask
|
||||||
import im.vector.matrix.android.internal.auth.wellknown.DirectLoginTask
|
|
||||||
import im.vector.matrix.android.internal.auth.wellknown.GetWellknownTask
|
|
||||||
import im.vector.matrix.android.internal.database.RealmKeysUtils
|
import im.vector.matrix.android.internal.database.RealmKeysUtils
|
||||||
import im.vector.matrix.android.internal.di.AuthDatabase
|
import im.vector.matrix.android.internal.di.AuthDatabase
|
||||||
|
import im.vector.matrix.android.internal.wellknown.WellknownModule
|
||||||
import io.realm.RealmConfiguration
|
import io.realm.RealmConfiguration
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@Module
|
@Module(includes = [WellknownModule::class])
|
||||||
internal abstract class AuthModule {
|
internal abstract class AuthModule {
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
|
@ -74,9 +73,6 @@ internal abstract class AuthModule {
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun bindSessionCreator(creator: DefaultSessionCreator): SessionCreator
|
abstract fun bindSessionCreator(creator: DefaultSessionCreator): SessionCreator
|
||||||
|
|
||||||
@Binds
|
|
||||||
abstract fun bindGetWellknownTask(task: DefaultGetWellknownTask): GetWellknownTask
|
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun bindDirectLoginTask(task: DefaultDirectLoginTask): DirectLoginTask
|
abstract fun bindDirectLoginTask(task: DefaultDirectLoginTask): DirectLoginTask
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,6 @@ import im.vector.matrix.android.api.auth.AuthenticationService
|
||||||
import im.vector.matrix.android.api.auth.data.Credentials
|
import im.vector.matrix.android.api.auth.data.Credentials
|
||||||
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
|
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
|
||||||
import im.vector.matrix.android.api.auth.data.LoginFlowResult
|
import im.vector.matrix.android.api.auth.data.LoginFlowResult
|
||||||
import im.vector.matrix.android.api.auth.data.SessionParams
|
|
||||||
import im.vector.matrix.android.api.auth.data.Versions
|
import im.vector.matrix.android.api.auth.data.Versions
|
||||||
import im.vector.matrix.android.api.auth.data.isLoginAndRegistrationSupportedBySdk
|
import im.vector.matrix.android.api.auth.data.isLoginAndRegistrationSupportedBySdk
|
||||||
import im.vector.matrix.android.api.auth.data.isSupportedBySdk
|
import im.vector.matrix.android.api.auth.data.isSupportedBySdk
|
||||||
|
@ -33,14 +32,14 @@ import im.vector.matrix.android.api.auth.wellknown.WellknownResult
|
||||||
import im.vector.matrix.android.api.failure.Failure
|
import im.vector.matrix.android.api.failure.Failure
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.session.Session
|
||||||
import im.vector.matrix.android.api.util.Cancelable
|
import im.vector.matrix.android.api.util.Cancelable
|
||||||
|
import im.vector.matrix.android.api.util.NoOpCancellable
|
||||||
import im.vector.matrix.android.internal.SessionManager
|
import im.vector.matrix.android.internal.SessionManager
|
||||||
import im.vector.matrix.android.internal.auth.data.LoginFlowResponse
|
import im.vector.matrix.android.internal.auth.data.LoginFlowResponse
|
||||||
import im.vector.matrix.android.internal.auth.data.RiotConfig
|
import im.vector.matrix.android.internal.auth.data.RiotConfig
|
||||||
import im.vector.matrix.android.internal.auth.db.PendingSessionData
|
import im.vector.matrix.android.internal.auth.db.PendingSessionData
|
||||||
import im.vector.matrix.android.internal.auth.login.DefaultLoginWizard
|
import im.vector.matrix.android.internal.auth.login.DefaultLoginWizard
|
||||||
|
import im.vector.matrix.android.internal.auth.login.DirectLoginTask
|
||||||
import im.vector.matrix.android.internal.auth.registration.DefaultRegistrationWizard
|
import im.vector.matrix.android.internal.auth.registration.DefaultRegistrationWizard
|
||||||
import im.vector.matrix.android.internal.auth.wellknown.DirectLoginTask
|
|
||||||
import im.vector.matrix.android.internal.auth.wellknown.GetWellknownTask
|
|
||||||
import im.vector.matrix.android.internal.di.Unauthenticated
|
import im.vector.matrix.android.internal.di.Unauthenticated
|
||||||
import im.vector.matrix.android.internal.network.RetrofitFactory
|
import im.vector.matrix.android.internal.network.RetrofitFactory
|
||||||
import im.vector.matrix.android.internal.network.executeRequest
|
import im.vector.matrix.android.internal.network.executeRequest
|
||||||
|
@ -50,7 +49,7 @@ import im.vector.matrix.android.internal.task.launchToCallback
|
||||||
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
||||||
import im.vector.matrix.android.internal.util.exhaustive
|
import im.vector.matrix.android.internal.util.exhaustive
|
||||||
import im.vector.matrix.android.internal.util.toCancelable
|
import im.vector.matrix.android.internal.util.toCancelable
|
||||||
import kotlinx.coroutines.GlobalScope
|
import im.vector.matrix.android.internal.wellknown.GetWellknownTask
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
|
@ -87,14 +86,21 @@ internal class DefaultAuthenticationService @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getSession(sessionParams: SessionParams): Session? {
|
override fun getLoginFlowOfSession(sessionId: String, callback: MatrixCallback<LoginFlowResult>): Cancelable {
|
||||||
return sessionManager.getOrCreateSession(sessionParams)
|
val homeServerConnectionConfig = sessionParamsStore.get(sessionId)?.homeServerConnectionConfig
|
||||||
|
|
||||||
|
return if (homeServerConnectionConfig == null) {
|
||||||
|
callback.onFailure(IllegalStateException("Session not found"))
|
||||||
|
NoOpCancellable
|
||||||
|
} else {
|
||||||
|
getLoginFlow(homeServerConnectionConfig, callback)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getLoginFlow(homeServerConnectionConfig: HomeServerConnectionConfig, callback: MatrixCallback<LoginFlowResult>): Cancelable {
|
override fun getLoginFlow(homeServerConnectionConfig: HomeServerConnectionConfig, callback: MatrixCallback<LoginFlowResult>): Cancelable {
|
||||||
pendingSessionData = null
|
pendingSessionData = null
|
||||||
|
|
||||||
return GlobalScope.launch(coroutineDispatchers.main) {
|
return taskExecutor.executorScope.launch(coroutineDispatchers.main) {
|
||||||
pendingSessionStore.delete()
|
pendingSessionStore.delete()
|
||||||
|
|
||||||
val result = runCatching {
|
val result = runCatching {
|
||||||
|
@ -246,7 +252,8 @@ internal class DefaultAuthenticationService @Inject constructor(
|
||||||
retrofitFactory,
|
retrofitFactory,
|
||||||
coroutineDispatchers,
|
coroutineDispatchers,
|
||||||
sessionCreator,
|
sessionCreator,
|
||||||
pendingSessionStore
|
pendingSessionStore,
|
||||||
|
taskExecutor.executorScope
|
||||||
).also {
|
).also {
|
||||||
currentRegistrationWizard = it
|
currentRegistrationWizard = it
|
||||||
}
|
}
|
||||||
|
@ -266,7 +273,8 @@ internal class DefaultAuthenticationService @Inject constructor(
|
||||||
retrofitFactory,
|
retrofitFactory,
|
||||||
coroutineDispatchers,
|
coroutineDispatchers,
|
||||||
sessionCreator,
|
sessionCreator,
|
||||||
pendingSessionStore
|
pendingSessionStore,
|
||||||
|
taskExecutor.executorScope
|
||||||
).also {
|
).also {
|
||||||
currentLoginWizard = it
|
currentLoginWizard = it
|
||||||
}
|
}
|
||||||
|
@ -283,7 +291,7 @@ internal class DefaultAuthenticationService @Inject constructor(
|
||||||
pendingSessionData = pendingSessionData?.homeServerConnectionConfig
|
pendingSessionData = pendingSessionData?.homeServerConnectionConfig
|
||||||
?.let { PendingSessionData(it) }
|
?.let { PendingSessionData(it) }
|
||||||
.also {
|
.also {
|
||||||
GlobalScope.launch(coroutineDispatchers.main) {
|
taskExecutor.executorScope.launch(coroutineDispatchers.main) {
|
||||||
if (it == null) {
|
if (it == null) {
|
||||||
// Should not happen
|
// Should not happen
|
||||||
pendingSessionStore.delete()
|
pendingSessionStore.delete()
|
||||||
|
@ -300,7 +308,7 @@ internal class DefaultAuthenticationService @Inject constructor(
|
||||||
|
|
||||||
pendingSessionData = null
|
pendingSessionData = null
|
||||||
|
|
||||||
GlobalScope.launch(coroutineDispatchers.main) {
|
taskExecutor.executorScope.launch(coroutineDispatchers.main) {
|
||||||
pendingSessionStore.delete()
|
pendingSessionStore.delete()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -308,7 +316,7 @@ internal class DefaultAuthenticationService @Inject constructor(
|
||||||
override fun createSessionFromSso(homeServerConnectionConfig: HomeServerConnectionConfig,
|
override fun createSessionFromSso(homeServerConnectionConfig: HomeServerConnectionConfig,
|
||||||
credentials: Credentials,
|
credentials: Credentials,
|
||||||
callback: MatrixCallback<Session>): Cancelable {
|
callback: MatrixCallback<Session>): Cancelable {
|
||||||
return GlobalScope.launchToCallback(coroutineDispatchers.main, callback) {
|
return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, callback) {
|
||||||
createSessionFromSso(credentials, homeServerConnectionConfig)
|
createSessionFromSso(credentials, homeServerConnectionConfig)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ internal class SessionParamsMapper @Inject constructor(moshi: Moshi) {
|
||||||
}
|
}
|
||||||
return SessionParamsEntity(
|
return SessionParamsEntity(
|
||||||
sessionParams.credentials.sessionId(),
|
sessionParams.credentials.sessionId(),
|
||||||
sessionParams.credentials.userId,
|
sessionParams.userId,
|
||||||
credentialsJson,
|
credentialsJson,
|
||||||
homeServerConnectionConfigJson,
|
homeServerConnectionConfigJson,
|
||||||
sessionParams.isTokenValid)
|
sessionParams.isTokenValid)
|
||||||
|
|
|
@ -38,7 +38,7 @@ import im.vector.matrix.android.internal.network.RetrofitFactory
|
||||||
import im.vector.matrix.android.internal.network.executeRequest
|
import im.vector.matrix.android.internal.network.executeRequest
|
||||||
import im.vector.matrix.android.internal.task.launchToCallback
|
import im.vector.matrix.android.internal.task.launchToCallback
|
||||||
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
|
|
||||||
|
@ -47,7 +47,8 @@ internal class DefaultLoginWizard(
|
||||||
retrofitFactory: RetrofitFactory,
|
retrofitFactory: RetrofitFactory,
|
||||||
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||||
private val sessionCreator: SessionCreator,
|
private val sessionCreator: SessionCreator,
|
||||||
private val pendingSessionStore: PendingSessionStore
|
private val pendingSessionStore: PendingSessionStore,
|
||||||
|
private val coroutineScope: CoroutineScope
|
||||||
) : LoginWizard {
|
) : LoginWizard {
|
||||||
|
|
||||||
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")
|
||||||
|
@ -59,7 +60,7 @@ internal class DefaultLoginWizard(
|
||||||
password: String,
|
password: String,
|
||||||
deviceName: String,
|
deviceName: String,
|
||||||
callback: MatrixCallback<Session>): Cancelable {
|
callback: MatrixCallback<Session>): Cancelable {
|
||||||
return GlobalScope.launchToCallback(coroutineDispatchers.main, callback) {
|
return coroutineScope.launchToCallback(coroutineDispatchers.main, callback) {
|
||||||
loginInternal(login, password, deviceName)
|
loginInternal(login, password, deviceName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,7 +81,7 @@ internal class DefaultLoginWizard(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun resetPassword(email: String, newPassword: String, callback: MatrixCallback<Unit>): Cancelable {
|
override fun resetPassword(email: String, newPassword: String, callback: MatrixCallback<Unit>): Cancelable {
|
||||||
return GlobalScope.launchToCallback(coroutineDispatchers.main, callback) {
|
return coroutineScope.launchToCallback(coroutineDispatchers.main, callback) {
|
||||||
resetPasswordInternal(email, newPassword)
|
resetPasswordInternal(email, newPassword)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -108,7 +109,7 @@ internal class DefaultLoginWizard(
|
||||||
callback.onFailure(IllegalStateException("developer error, no reset password in progress"))
|
callback.onFailure(IllegalStateException("developer error, no reset password in progress"))
|
||||||
return NoOpCancellable
|
return NoOpCancellable
|
||||||
}
|
}
|
||||||
return GlobalScope.launchToCallback(coroutineDispatchers.main, callback) {
|
return coroutineScope.launchToCallback(coroutineDispatchers.main, callback) {
|
||||||
resetPasswordMailConfirmedInternal(safeResetPasswordData)
|
resetPasswordMailConfirmedInternal(safeResetPasswordData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package im.vector.matrix.android.internal.auth.wellknown
|
package im.vector.matrix.android.internal.auth.login
|
||||||
|
|
||||||
import dagger.Lazy
|
import dagger.Lazy
|
||||||
import im.vector.matrix.android.api.auth.data.Credentials
|
import im.vector.matrix.android.api.auth.data.Credentials
|
|
@ -33,7 +33,7 @@ import im.vector.matrix.android.internal.auth.db.PendingSessionData
|
||||||
import im.vector.matrix.android.internal.network.RetrofitFactory
|
import im.vector.matrix.android.internal.network.RetrofitFactory
|
||||||
import im.vector.matrix.android.internal.task.launchToCallback
|
import im.vector.matrix.android.internal.task.launchToCallback
|
||||||
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
|
|
||||||
|
@ -45,7 +45,8 @@ internal class DefaultRegistrationWizard(
|
||||||
private val retrofitFactory: RetrofitFactory,
|
private val retrofitFactory: RetrofitFactory,
|
||||||
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||||
private val sessionCreator: SessionCreator,
|
private val sessionCreator: SessionCreator,
|
||||||
private val pendingSessionStore: PendingSessionStore
|
private val pendingSessionStore: PendingSessionStore,
|
||||||
|
private val coroutineScope: CoroutineScope
|
||||||
) : RegistrationWizard {
|
) : RegistrationWizard {
|
||||||
|
|
||||||
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")
|
||||||
|
@ -72,7 +73,7 @@ internal class DefaultRegistrationWizard(
|
||||||
|
|
||||||
override fun getRegistrationFlow(callback: MatrixCallback<RegistrationResult>): Cancelable {
|
override fun getRegistrationFlow(callback: MatrixCallback<RegistrationResult>): Cancelable {
|
||||||
val params = RegistrationParams()
|
val params = RegistrationParams()
|
||||||
return GlobalScope.launchToCallback(coroutineDispatchers.main, callback) {
|
return coroutineScope.launchToCallback(coroutineDispatchers.main, callback) {
|
||||||
performRegistrationRequest(params)
|
performRegistrationRequest(params)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,7 +87,7 @@ internal class DefaultRegistrationWizard(
|
||||||
password = password,
|
password = password,
|
||||||
initialDeviceDisplayName = initialDeviceDisplayName
|
initialDeviceDisplayName = initialDeviceDisplayName
|
||||||
)
|
)
|
||||||
return GlobalScope.launchToCallback(coroutineDispatchers.main, callback) {
|
return coroutineScope.launchToCallback(coroutineDispatchers.main, callback) {
|
||||||
performRegistrationRequest(params)
|
performRegistrationRequest(params)
|
||||||
.also {
|
.also {
|
||||||
pendingSessionData = pendingSessionData.copy(isRegistrationStarted = true)
|
pendingSessionData = pendingSessionData.copy(isRegistrationStarted = true)
|
||||||
|
@ -101,7 +102,7 @@ internal class DefaultRegistrationWizard(
|
||||||
return NoOpCancellable
|
return NoOpCancellable
|
||||||
}
|
}
|
||||||
val params = RegistrationParams(auth = AuthParams.createForCaptcha(safeSession, response))
|
val params = RegistrationParams(auth = AuthParams.createForCaptcha(safeSession, response))
|
||||||
return GlobalScope.launchToCallback(coroutineDispatchers.main, callback) {
|
return coroutineScope.launchToCallback(coroutineDispatchers.main, callback) {
|
||||||
performRegistrationRequest(params)
|
performRegistrationRequest(params)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -112,13 +113,13 @@ internal class DefaultRegistrationWizard(
|
||||||
return NoOpCancellable
|
return NoOpCancellable
|
||||||
}
|
}
|
||||||
val params = RegistrationParams(auth = AuthParams(type = LoginFlowTypes.TERMS, session = safeSession))
|
val params = RegistrationParams(auth = AuthParams(type = LoginFlowTypes.TERMS, session = safeSession))
|
||||||
return GlobalScope.launchToCallback(coroutineDispatchers.main, callback) {
|
return coroutineScope.launchToCallback(coroutineDispatchers.main, callback) {
|
||||||
performRegistrationRequest(params)
|
performRegistrationRequest(params)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun addThreePid(threePid: RegisterThreePid, callback: MatrixCallback<RegistrationResult>): Cancelable {
|
override fun addThreePid(threePid: RegisterThreePid, callback: MatrixCallback<RegistrationResult>): Cancelable {
|
||||||
return GlobalScope.launchToCallback(coroutineDispatchers.main, callback) {
|
return coroutineScope.launchToCallback(coroutineDispatchers.main, callback) {
|
||||||
pendingSessionData = pendingSessionData.copy(currentThreePidData = null)
|
pendingSessionData = pendingSessionData.copy(currentThreePidData = null)
|
||||||
.also { pendingSessionStore.savePendingSessionData(it) }
|
.also { pendingSessionStore.savePendingSessionData(it) }
|
||||||
|
|
||||||
|
@ -131,7 +132,7 @@ internal class DefaultRegistrationWizard(
|
||||||
callback.onFailure(IllegalStateException("developer error, call createAccount() method first"))
|
callback.onFailure(IllegalStateException("developer error, call createAccount() method first"))
|
||||||
return NoOpCancellable
|
return NoOpCancellable
|
||||||
}
|
}
|
||||||
return GlobalScope.launchToCallback(coroutineDispatchers.main, callback) {
|
return coroutineScope.launchToCallback(coroutineDispatchers.main, callback) {
|
||||||
sendThreePid(safeCurrentThreePid)
|
sendThreePid(safeCurrentThreePid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -177,13 +178,13 @@ internal class DefaultRegistrationWizard(
|
||||||
callback.onFailure(IllegalStateException("developer error, no pending three pid"))
|
callback.onFailure(IllegalStateException("developer error, no pending three pid"))
|
||||||
return NoOpCancellable
|
return NoOpCancellable
|
||||||
}
|
}
|
||||||
return GlobalScope.launchToCallback(coroutineDispatchers.main, callback) {
|
return coroutineScope.launchToCallback(coroutineDispatchers.main, callback) {
|
||||||
performRegistrationRequest(safeParam, delayMillis)
|
performRegistrationRequest(safeParam, delayMillis)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun handleValidateThreePid(code: String, callback: MatrixCallback<RegistrationResult>): Cancelable {
|
override fun handleValidateThreePid(code: String, callback: MatrixCallback<RegistrationResult>): Cancelable {
|
||||||
return GlobalScope.launchToCallback(coroutineDispatchers.main, callback) {
|
return coroutineScope.launchToCallback(coroutineDispatchers.main, callback) {
|
||||||
validateThreePid(code)
|
validateThreePid(code)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -199,7 +200,7 @@ internal class DefaultRegistrationWizard(
|
||||||
code = code
|
code = code
|
||||||
)
|
)
|
||||||
val validationResponse = validateCodeTask.execute(ValidateCodeTask.Params(url, validationBody))
|
val validationResponse = validateCodeTask.execute(ValidateCodeTask.Params(url, validationBody))
|
||||||
if (validationResponse.success == true) {
|
if (validationResponse.isSuccess()) {
|
||||||
// The entered code is correct
|
// The entered code is correct
|
||||||
// Same than validate email
|
// Same than validate email
|
||||||
return performRegistrationRequest(registrationParams, 3_000)
|
return performRegistrationRequest(registrationParams, 3_000)
|
||||||
|
@ -214,7 +215,7 @@ internal class DefaultRegistrationWizard(
|
||||||
callback.onFailure(IllegalStateException("developer error, call createAccount() method first"))
|
callback.onFailure(IllegalStateException("developer error, call createAccount() method first"))
|
||||||
return NoOpCancellable
|
return NoOpCancellable
|
||||||
}
|
}
|
||||||
return GlobalScope.launchToCallback(coroutineDispatchers.main, callback) {
|
return coroutineScope.launchToCallback(coroutineDispatchers.main, callback) {
|
||||||
val params = RegistrationParams(auth = AuthParams(type = LoginFlowTypes.DUMMY, session = safeSession))
|
val params = RegistrationParams(auth = AuthParams(type = LoginFlowTypes.DUMMY, session = safeSession))
|
||||||
performRegistrationRequest(params)
|
performRegistrationRequest(params)
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.matrix.androidsdk.rest.model.login
|
package im.vector.matrix.android.internal.auth.registration
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.android.parcel.Parcelize
|
||||||
|
|
|
@ -76,7 +76,7 @@ fun RegistrationFlowResponse.toFlowResult(): FlowResult {
|
||||||
this.flows?.forEach { it.stages?.mapTo(allFlowTypes) { type -> type } }
|
this.flows?.forEach { it.stages?.mapTo(allFlowTypes) { type -> type } }
|
||||||
|
|
||||||
allFlowTypes.forEach { type ->
|
allFlowTypes.forEach { type ->
|
||||||
val isMandatory = flows?.all { type in it.stages ?: emptyList() } == true
|
val isMandatory = flows?.all { type in it.stages.orEmpty() } == true
|
||||||
|
|
||||||
val stage = when (type) {
|
val stage = when (type) {
|
||||||
LoginFlowTypes.RECAPTCHA -> Stage.ReCaptcha(isMandatory, ((params?.get(type) as? Map<*, *>)?.get("public_key") as? String)
|
LoginFlowTypes.RECAPTCHA -> Stage.ReCaptcha(isMandatory, ((params?.get(type) as? Map<*, *>)?.get("public_key") as? String)
|
||||||
|
@ -88,7 +88,7 @@ fun RegistrationFlowResponse.toFlowResult(): FlowResult {
|
||||||
else -> Stage.Other(isMandatory, type, (params?.get(type) as? Map<*, *>))
|
else -> Stage.Other(isMandatory, type, (params?.get(type) as? Map<*, *>))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type in completedStages ?: emptyList()) {
|
if (type in completedStages.orEmpty()) {
|
||||||
completedStage.add(stage)
|
completedStage.add(stage)
|
||||||
} else {
|
} else {
|
||||||
missingStage.add(stage)
|
missingStage.add(stage)
|
||||||
|
|
|
@ -18,9 +18,12 @@ package im.vector.matrix.android.internal.auth.registration
|
||||||
|
|
||||||
import com.squareup.moshi.Json
|
import com.squareup.moshi.Json
|
||||||
import com.squareup.moshi.JsonClass
|
import com.squareup.moshi.JsonClass
|
||||||
|
import im.vector.matrix.android.api.extensions.orFalse
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
data class SuccessResult(
|
data class SuccessResult(
|
||||||
@Json(name = "success")
|
@Json(name = "success")
|
||||||
val success: Boolean?
|
val success: Boolean?
|
||||||
)
|
) {
|
||||||
|
fun isSuccess() = success.orFalse()
|
||||||
|
}
|
||||||
|
|
|
@ -262,7 +262,7 @@ internal class DefaultCryptoService @Inject constructor(
|
||||||
|
|
||||||
override fun onSuccess(data: DevicesListResponse) {
|
override fun onSuccess(data: DevicesListResponse) {
|
||||||
// Save in local DB
|
// Save in local DB
|
||||||
cryptoStore.saveMyDevicesInfo(data.devices ?: emptyList())
|
cryptoStore.saveMyDevicesInfo(data.devices.orEmpty())
|
||||||
callback.onSuccess(data)
|
callback.onSuccess(data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -446,7 +446,7 @@ internal class DefaultCryptoService @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getCryptoDeviceInfo(userId: String): List<CryptoDeviceInfo> {
|
override fun getCryptoDeviceInfo(userId: String): List<CryptoDeviceInfo> {
|
||||||
return cryptoStore.getUserDeviceList(userId) ?: emptyList()
|
return cryptoStore.getUserDeviceList(userId).orEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getLiveCryptoDeviceInfo(): LiveData<List<CryptoDeviceInfo>> {
|
override fun getLiveCryptoDeviceInfo(): LiveData<List<CryptoDeviceInfo>> {
|
||||||
|
|
|
@ -137,7 +137,7 @@ internal class OneTimeKeysUploader @Inject constructor(
|
||||||
private suspend fun uploadOneTimeKeys(oneTimeKeys: Map<String, Map<String, String>>?): KeysUploadResponse {
|
private suspend fun uploadOneTimeKeys(oneTimeKeys: Map<String, Map<String, String>>?): KeysUploadResponse {
|
||||||
val oneTimeJson = mutableMapOf<String, Any>()
|
val oneTimeJson = mutableMapOf<String, Any>()
|
||||||
|
|
||||||
val curve25519Map = oneTimeKeys?.get(OlmAccount.JSON_KEY_ONE_TIME_KEY) ?: emptyMap()
|
val curve25519Map = oneTimeKeys?.get(OlmAccount.JSON_KEY_ONE_TIME_KEY).orEmpty()
|
||||||
|
|
||||||
curve25519Map.forEach { (key_id, value) ->
|
curve25519Map.forEach { (key_id, value) ->
|
||||||
val k = mutableMapOf<String, Any>()
|
val k = mutableMapOf<String, Any>()
|
||||||
|
|
|
@ -34,7 +34,7 @@ internal class EnsureOlmSessionsForUsersAction @Inject constructor(private val o
|
||||||
suspend fun handle(users: List<String>): MXUsersDevicesMap<MXOlmSessionResult> {
|
suspend fun handle(users: List<String>): MXUsersDevicesMap<MXOlmSessionResult> {
|
||||||
Timber.v("## ensureOlmSessionsForUsers() : ensureOlmSessionsForUsers $users")
|
Timber.v("## ensureOlmSessionsForUsers() : ensureOlmSessionsForUsers $users")
|
||||||
val devicesByUser = users.associateWith { userId ->
|
val devicesByUser = users.associateWith { userId ->
|
||||||
val devices = cryptoStore.getUserDevices(userId)?.values ?: emptyList()
|
val devices = cryptoStore.getUserDevices(userId)?.values.orEmpty()
|
||||||
|
|
||||||
devices.filter {
|
devices.filter {
|
||||||
// Don't bother setting up session to ourself
|
// Don't bother setting up session to ourself
|
||||||
|
|
|
@ -103,7 +103,7 @@ internal class MXMegolmDecryption(private val userId: String,
|
||||||
senderCurve25519Key = olmDecryptionResult.senderKey,
|
senderCurve25519Key = olmDecryptionResult.senderKey,
|
||||||
claimedEd25519Key = olmDecryptionResult.keysClaimed?.get("ed25519"),
|
claimedEd25519Key = olmDecryptionResult.keysClaimed?.get("ed25519"),
|
||||||
forwardingCurve25519KeyChain = olmDecryptionResult.forwardingCurve25519KeyChain
|
forwardingCurve25519KeyChain = olmDecryptionResult.forwardingCurve25519KeyChain
|
||||||
?: emptyList()
|
.orEmpty()
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON)
|
throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON)
|
||||||
|
|
|
@ -44,7 +44,7 @@ internal class MXOlmEncryption(
|
||||||
ensureSession(userIds)
|
ensureSession(userIds)
|
||||||
val deviceInfos = ArrayList<CryptoDeviceInfo>()
|
val deviceInfos = ArrayList<CryptoDeviceInfo>()
|
||||||
for (userId in userIds) {
|
for (userId in userIds) {
|
||||||
val devices = cryptoStore.getUserDevices(userId)?.values ?: emptyList()
|
val devices = cryptoStore.getUserDevices(userId)?.values.orEmpty()
|
||||||
for (device in devices) {
|
for (device in devices) {
|
||||||
val key = device.identityKey()
|
val key = device.identityKey()
|
||||||
if (key == olmDevice.deviceCurve25519Key) {
|
if (key == olmDevice.deviceCurve25519Key) {
|
||||||
|
|
|
@ -199,7 +199,7 @@ internal object MXEncryptedAttachments {
|
||||||
.replace('_', '/')
|
.replace('_', '/')
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun base64ToBase64Url(base64: String): String {
|
internal fun base64ToBase64Url(base64: String): String {
|
||||||
return base64.replace("\n".toRegex(), "")
|
return base64.replace("\n".toRegex(), "")
|
||||||
.replace("\\+".toRegex(), "-")
|
.replace("\\+".toRegex(), "-")
|
||||||
.replace('/', '_')
|
.replace('/', '_')
|
||||||
|
|
|
@ -47,7 +47,7 @@ internal object CryptoInfoMapper {
|
||||||
return CryptoCrossSigningKey(
|
return CryptoCrossSigningKey(
|
||||||
userId = keyInfo.userId,
|
userId = keyInfo.userId,
|
||||||
usages = keyInfo.usages,
|
usages = keyInfo.usages,
|
||||||
keys = keyInfo.keys ?: emptyMap(),
|
keys = keyInfo.keys.orEmpty(),
|
||||||
signatures = keyInfo.signatures,
|
signatures = keyInfo.signatures,
|
||||||
trustLevel = null
|
trustLevel = null
|
||||||
)
|
)
|
||||||
|
|
|
@ -450,7 +450,7 @@ internal class RealmCryptoStore @Inject constructor(
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return Transformations.map(liveData) {
|
return Transformations.map(liveData) {
|
||||||
it.firstOrNull() ?: emptyList()
|
it.firstOrNull().orEmpty()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -480,7 +480,7 @@ internal class RealmCryptoStore @Inject constructor(
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return Transformations.map(liveData) {
|
return Transformations.map(liveData) {
|
||||||
it.firstOrNull() ?: emptyList()
|
it.firstOrNull().orEmpty()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -200,6 +200,7 @@ internal class RealmCryptoStoreMigration @Inject constructor(private val crossSi
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun migrateTo3(realm: DynamicRealm) {
|
private fun migrateTo3(realm: DynamicRealm) {
|
||||||
|
Timber.d("Step 2 -> 3")
|
||||||
Timber.d("Updating CryptoMetadataEntity table")
|
Timber.d("Updating CryptoMetadataEntity table")
|
||||||
realm.schema.get("CryptoMetadataEntity")
|
realm.schema.get("CryptoMetadataEntity")
|
||||||
?.addField(CryptoMetadataEntityFields.KEY_BACKUP_RECOVERY_KEY, String::class.java)
|
?.addField(CryptoMetadataEntityFields.KEY_BACKUP_RECOVERY_KEY, String::class.java)
|
||||||
|
@ -207,6 +208,7 @@ internal class RealmCryptoStoreMigration @Inject constructor(private val crossSi
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun migrateTo4(realm: DynamicRealm) {
|
private fun migrateTo4(realm: DynamicRealm) {
|
||||||
|
Timber.d("Step 3 -> 4")
|
||||||
Timber.d("Updating KeyInfoEntity table")
|
Timber.d("Updating KeyInfoEntity table")
|
||||||
val keyInfoEntities = realm.where("KeyInfoEntity").findAll()
|
val keyInfoEntities = realm.where("KeyInfoEntity").findAll()
|
||||||
try {
|
try {
|
||||||
|
@ -238,6 +240,7 @@ internal class RealmCryptoStoreMigration @Inject constructor(private val crossSi
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun migrateTo5(realm: DynamicRealm) {
|
private fun migrateTo5(realm: DynamicRealm) {
|
||||||
|
Timber.d("Step 4 -> 5")
|
||||||
realm.schema.create("MyDeviceLastSeenInfoEntity")
|
realm.schema.create("MyDeviceLastSeenInfoEntity")
|
||||||
.addField(MyDeviceLastSeenInfoEntityFields.DEVICE_ID, String::class.java)
|
.addField(MyDeviceLastSeenInfoEntityFields.DEVICE_ID, String::class.java)
|
||||||
.addPrimaryKey(MyDeviceLastSeenInfoEntityFields.DEVICE_ID)
|
.addPrimaryKey(MyDeviceLastSeenInfoEntityFields.DEVICE_ID)
|
||||||
|
|
|
@ -78,7 +78,7 @@ internal open class OutgoingGossipingRequestEntity(
|
||||||
GossipRequestType.KEY -> {
|
GossipRequestType.KEY -> {
|
||||||
OutgoingRoomKeyRequest(
|
OutgoingRoomKeyRequest(
|
||||||
requestBody = getRequestedKeyInfo(),
|
requestBody = getRequestedKeyInfo(),
|
||||||
recipients = getRecipients() ?: emptyMap(),
|
recipients = getRecipients().orEmpty(),
|
||||||
requestId = requestId ?: "",
|
requestId = requestId ?: "",
|
||||||
state = requestState
|
state = requestState
|
||||||
)
|
)
|
||||||
|
@ -86,7 +86,7 @@ internal open class OutgoingGossipingRequestEntity(
|
||||||
GossipRequestType.SECRET -> {
|
GossipRequestType.SECRET -> {
|
||||||
OutgoingSecretRequest(
|
OutgoingSecretRequest(
|
||||||
secretName = getRequestedSecretName(),
|
secretName = getRequestedSecretName(),
|
||||||
recipients = getRecipients() ?: emptyMap(),
|
recipients = getRecipients().orEmpty(),
|
||||||
requestId = requestId ?: "",
|
requestId = requestId ?: "",
|
||||||
state = requestState
|
state = requestState
|
||||||
)
|
)
|
||||||
|
|
|
@ -19,6 +19,7 @@ package im.vector.matrix.android.internal.crypto.tools
|
||||||
import org.matrix.olm.OlmPkDecryption
|
import org.matrix.olm.OlmPkDecryption
|
||||||
import org.matrix.olm.OlmPkEncryption
|
import org.matrix.olm.OlmPkEncryption
|
||||||
import org.matrix.olm.OlmPkSigning
|
import org.matrix.olm.OlmPkSigning
|
||||||
|
import org.matrix.olm.OlmUtility
|
||||||
|
|
||||||
fun <T> withOlmEncryption(block: (OlmPkEncryption) -> T): T {
|
fun <T> withOlmEncryption(block: (OlmPkEncryption) -> T): T {
|
||||||
val olmPkEncryption = OlmPkEncryption()
|
val olmPkEncryption = OlmPkEncryption()
|
||||||
|
@ -46,3 +47,12 @@ fun <T> withOlmSigning(block: (OlmPkSigning) -> T): T {
|
||||||
olmPkSigning.releaseSigning()
|
olmPkSigning.releaseSigning()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun <T> withOlmUtility(block: (OlmUtility) -> T): T {
|
||||||
|
val olmUtility = OlmUtility()
|
||||||
|
try {
|
||||||
|
return block(olmUtility)
|
||||||
|
} finally {
|
||||||
|
olmUtility.releaseUtility()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -198,18 +198,8 @@ internal class DefaultIncomingSASDefaultVerificationTransaction(
|
||||||
// using the result as the shared secret.
|
// using the result as the shared secret.
|
||||||
|
|
||||||
getSAS().setTheirPublicKey(otherKey)
|
getSAS().setTheirPublicKey(otherKey)
|
||||||
// (Note: In all of the following HKDF is as defined in RFC 5869, and uses the previously agreed-on hash function as the hash function,
|
|
||||||
// the shared secret as the input keying material, no salt, and with the input parameter set to the concatenation of:
|
shortCodeBytes = calculateSASBytes()
|
||||||
// - the string “MATRIX_KEY_VERIFICATION_SAS”,
|
|
||||||
// - the Matrix ID of the user who sent the m.key.verification.start message,
|
|
||||||
// - the device ID of the device that sent the m.key.verification.start message,
|
|
||||||
// - the Matrix ID of the user who sent the m.key.verification.accept message,
|
|
||||||
// - he device ID of the device that sent the m.key.verification.accept message
|
|
||||||
// - the transaction ID.
|
|
||||||
val sasInfo = "MATRIX_KEY_VERIFICATION_SAS$otherUserId$otherDeviceId$userId$deviceId$transactionId"
|
|
||||||
// decimal: generate five bytes by using HKDF.
|
|
||||||
// emoji: generate six bytes by using HKDF.
|
|
||||||
shortCodeBytes = getSAS().generateShortCode(sasInfo, 6)
|
|
||||||
|
|
||||||
if (BuildConfig.LOG_PRIVATE_DATA) {
|
if (BuildConfig.LOG_PRIVATE_DATA) {
|
||||||
Timber.v("************ BOB CODE ${getDecimalCodeRepresentation(shortCodeBytes!!)}")
|
Timber.v("************ BOB CODE ${getDecimalCodeRepresentation(shortCodeBytes!!)}")
|
||||||
|
@ -219,6 +209,35 @@ internal class DefaultIncomingSASDefaultVerificationTransaction(
|
||||||
state = VerificationTxState.ShortCodeReady
|
state = VerificationTxState.ShortCodeReady
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun calculateSASBytes(): ByteArray {
|
||||||
|
when (accepted?.keyAgreementProtocol) {
|
||||||
|
KEY_AGREEMENT_V1 -> {
|
||||||
|
// (Note: In all of the following HKDF is as defined in RFC 5869, and uses the previously agreed-on hash function as the hash function,
|
||||||
|
// the shared secret as the input keying material, no salt, and with the input parameter set to the concatenation of:
|
||||||
|
// - the string “MATRIX_KEY_VERIFICATION_SAS”,
|
||||||
|
// - the Matrix ID of the user who sent the m.key.verification.start message,
|
||||||
|
// - the device ID of the device that sent the m.key.verification.start message,
|
||||||
|
// - the Matrix ID of the user who sent the m.key.verification.accept message,
|
||||||
|
// - he device ID of the device that sent the m.key.verification.accept message
|
||||||
|
// - the transaction ID.
|
||||||
|
val sasInfo = "MATRIX_KEY_VERIFICATION_SAS$otherUserId$otherDeviceId$userId$deviceId$transactionId"
|
||||||
|
|
||||||
|
// decimal: generate five bytes by using HKDF.
|
||||||
|
// emoji: generate six bytes by using HKDF.
|
||||||
|
return getSAS().generateShortCode(sasInfo, 6)
|
||||||
|
}
|
||||||
|
KEY_AGREEMENT_V2 -> {
|
||||||
|
// Adds the SAS public key, and separate by |
|
||||||
|
val sasInfo = "MATRIX_KEY_VERIFICATION_SAS|$otherUserId|$otherDeviceId|$otherKey|$userId|$deviceId|${getSAS().publicKey}|$transactionId"
|
||||||
|
return getSAS().generateShortCode(sasInfo, 6)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
// Protocol has been checked earlier
|
||||||
|
throw IllegalArgumentException()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onKeyVerificationMac(vMac: ValidVerificationInfoMac) {
|
override fun onKeyVerificationMac(vMac: ValidVerificationInfoMac) {
|
||||||
Timber.v("## SAS I: received mac for request id:$transactionId")
|
Timber.v("## SAS I: received mac for request id:$transactionId")
|
||||||
// Check for state?
|
// Check for state?
|
||||||
|
|
|
@ -121,7 +121,7 @@ internal class DefaultOutgoingSASDefaultVerificationTransaction(
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// val requestMessage = KeyVerificationRequest(
|
// val requestMessage = KeyVerificationRequest(
|
||||||
// fromDevice = session.sessionParams.credentials.deviceId ?: "",
|
// fromDevice = session.sessionParams.deviceId ?: "",
|
||||||
// methods = listOf(KeyVerificationStart.VERIF_METHOD_SAS),
|
// methods = listOf(KeyVerificationStart.VERIF_METHOD_SAS),
|
||||||
// timestamp = System.currentTimeMillis().toInt(),
|
// timestamp = System.currentTimeMillis().toInt(),
|
||||||
// transactionId = transactionId
|
// transactionId = transactionId
|
||||||
|
@ -193,18 +193,7 @@ internal class DefaultOutgoingSASDefaultVerificationTransaction(
|
||||||
|
|
||||||
if (accepted!!.commitment.equals(otherCommitment)) {
|
if (accepted!!.commitment.equals(otherCommitment)) {
|
||||||
getSAS().setTheirPublicKey(otherKey)
|
getSAS().setTheirPublicKey(otherKey)
|
||||||
// (Note: In all of the following HKDF is as defined in RFC 5869, and uses the previously agreed-on hash function as the hash function,
|
shortCodeBytes = calculateSASBytes()
|
||||||
// the shared secret as the input keying material, no salt, and with the input parameter set to the concatenation of:
|
|
||||||
// - the string “MATRIX_KEY_VERIFICATION_SAS”,
|
|
||||||
// - the Matrix ID of the user who sent the m.key.verification.start message,
|
|
||||||
// - the device ID of the device that sent the m.key.verification.start message,
|
|
||||||
// - the Matrix ID of the user who sent the m.key.verification.accept message,
|
|
||||||
// - he device ID of the device that sent the m.key.verification.accept message
|
|
||||||
// - the transaction ID.
|
|
||||||
val sasInfo = "MATRIX_KEY_VERIFICATION_SAS$userId$deviceId$otherUserId$otherDeviceId$transactionId"
|
|
||||||
// decimal: generate five bytes by using HKDF.
|
|
||||||
// emoji: generate six bytes by using HKDF.
|
|
||||||
shortCodeBytes = getSAS().generateShortCode(sasInfo, 6)
|
|
||||||
state = VerificationTxState.ShortCodeReady
|
state = VerificationTxState.ShortCodeReady
|
||||||
} else {
|
} else {
|
||||||
// bad commitment
|
// bad commitment
|
||||||
|
@ -212,14 +201,45 @@ internal class DefaultOutgoingSASDefaultVerificationTransaction(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun calculateSASBytes(): ByteArray {
|
||||||
|
when (accepted?.keyAgreementProtocol) {
|
||||||
|
KEY_AGREEMENT_V1 -> {
|
||||||
|
// (Note: In all of the following HKDF is as defined in RFC 5869, and uses the previously agreed-on hash function as the hash function,
|
||||||
|
// the shared secret as the input keying material, no salt, and with the input parameter set to the concatenation of:
|
||||||
|
// - the string “MATRIX_KEY_VERIFICATION_SAS”,
|
||||||
|
// - the Matrix ID of the user who sent the m.key.verification.start message,
|
||||||
|
// - the device ID of the device that sent the m.key.verification.start message,
|
||||||
|
// - the Matrix ID of the user who sent the m.key.verification.accept message,
|
||||||
|
// - he device ID of the device that sent the m.key.verification.accept message
|
||||||
|
// - the transaction ID.
|
||||||
|
val sasInfo = "MATRIX_KEY_VERIFICATION_SAS$userId$deviceId$otherUserId$otherDeviceId$transactionId"
|
||||||
|
|
||||||
|
// decimal: generate five bytes by using HKDF.
|
||||||
|
// emoji: generate six bytes by using HKDF.
|
||||||
|
return getSAS().generateShortCode(sasInfo, 6)
|
||||||
|
}
|
||||||
|
KEY_AGREEMENT_V2 -> {
|
||||||
|
// Adds the SAS public key, and separate by |
|
||||||
|
val sasInfo = "MATRIX_KEY_VERIFICATION_SAS|$userId|$deviceId|${getSAS().publicKey}|$otherUserId|$otherDeviceId|$otherKey|$transactionId"
|
||||||
|
return getSAS().generateShortCode(sasInfo, 6)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
// Protocol has been checked earlier
|
||||||
|
throw IllegalArgumentException()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onKeyVerificationMac(vMac: ValidVerificationInfoMac) {
|
override fun onKeyVerificationMac(vMac: ValidVerificationInfoMac) {
|
||||||
Timber.v("## SAS O: onKeyVerificationMac id:$transactionId")
|
Timber.v("## SAS O: onKeyVerificationMac id:$transactionId")
|
||||||
|
// There is starting to be a huge amount of state / race here :/
|
||||||
if (state != VerificationTxState.OnKeyReceived
|
if (state != VerificationTxState.OnKeyReceived
|
||||||
&& state != VerificationTxState.ShortCodeReady
|
&& state != VerificationTxState.ShortCodeReady
|
||||||
&& state != VerificationTxState.ShortCodeAccepted
|
&& state != VerificationTxState.ShortCodeAccepted
|
||||||
|
&& state != VerificationTxState.KeySent
|
||||||
&& state != VerificationTxState.SendingMac
|
&& state != VerificationTxState.SendingMac
|
||||||
&& state != VerificationTxState.MacSent) {
|
&& state != VerificationTxState.MacSent) {
|
||||||
Timber.e("## SAS O: received key from invalid state $state")
|
Timber.e("## SAS O: received mac from invalid state $state")
|
||||||
cancel(CancelCode.UnexpectedMessage)
|
cancel(CancelCode.UnexpectedMessage)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,9 +81,9 @@ import im.vector.matrix.android.internal.crypto.verification.qrcode.generateShar
|
||||||
import im.vector.matrix.android.internal.di.DeviceId
|
import im.vector.matrix.android.internal.di.DeviceId
|
||||||
import im.vector.matrix.android.internal.di.UserId
|
import im.vector.matrix.android.internal.di.UserId
|
||||||
import im.vector.matrix.android.internal.session.SessionScope
|
import im.vector.matrix.android.internal.session.SessionScope
|
||||||
|
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||||
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.GlobalScope
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
@ -104,7 +104,8 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
private val verificationTransportRoomMessageFactory: VerificationTransportRoomMessageFactory,
|
private val verificationTransportRoomMessageFactory: VerificationTransportRoomMessageFactory,
|
||||||
private val verificationTransportToDeviceFactory: VerificationTransportToDeviceFactory,
|
private val verificationTransportToDeviceFactory: VerificationTransportToDeviceFactory,
|
||||||
private val crossSigningService: CrossSigningService,
|
private val crossSigningService: CrossSigningService,
|
||||||
private val cryptoCoroutineScope: CoroutineScope
|
private val cryptoCoroutineScope: CoroutineScope,
|
||||||
|
private val taskExecutor: TaskExecutor
|
||||||
) : DefaultVerificationTransaction.Listener, VerificationService {
|
) : DefaultVerificationTransaction.Listener, VerificationService {
|
||||||
|
|
||||||
private val uiHandler = Handler(Looper.getMainLooper())
|
private val uiHandler = Handler(Looper.getMainLooper())
|
||||||
|
@ -161,7 +162,7 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onRoomEvent(event: Event) {
|
fun onRoomEvent(event: Event) {
|
||||||
GlobalScope.launch(coroutineDispatchers.crypto) {
|
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
|
||||||
when (event.getClearType()) {
|
when (event.getClearType()) {
|
||||||
EventType.KEY_VERIFICATION_START -> {
|
EventType.KEY_VERIFICATION_START -> {
|
||||||
onRoomStartRequestReceived(event)
|
onRoomStartRequestReceived(event)
|
||||||
|
@ -301,7 +302,7 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
// We don't want to block here
|
// We don't want to block here
|
||||||
val otherDeviceId = validRequestInfo.fromDevice
|
val otherDeviceId = validRequestInfo.fromDevice
|
||||||
|
|
||||||
GlobalScope.launch {
|
cryptoCoroutineScope.launch {
|
||||||
if (checkKeysAreDownloaded(senderId, otherDeviceId) == null) {
|
if (checkKeysAreDownloaded(senderId, otherDeviceId) == null) {
|
||||||
Timber.e("## Verification device $otherDeviceId is not known")
|
Timber.e("## Verification device $otherDeviceId is not known")
|
||||||
}
|
}
|
||||||
|
@ -340,7 +341,7 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
// We don't want to block here
|
// We don't want to block here
|
||||||
GlobalScope.launch {
|
taskExecutor.executorScope.launch {
|
||||||
if (checkKeysAreDownloaded(senderId, validRequestInfo.fromDevice) == null) {
|
if (checkKeysAreDownloaded(senderId, validRequestInfo.fromDevice) == null) {
|
||||||
Timber.e("## SAS Verification device ${validRequestInfo.fromDevice} is not known")
|
Timber.e("## SAS Verification device ${validRequestInfo.fromDevice} is not known")
|
||||||
}
|
}
|
||||||
|
@ -610,7 +611,7 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
|
|
||||||
if (validCancelReq == null) {
|
if (validCancelReq == null) {
|
||||||
// ignore
|
// ignore
|
||||||
Timber.e("## SAS Received invalid key request")
|
Timber.e("## SAS Received invalid cancel request")
|
||||||
// TODO should we cancel?
|
// TODO should we cancel?
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,6 @@ import im.vector.matrix.android.api.session.events.model.EventType
|
||||||
import im.vector.matrix.android.internal.crypto.IncomingGossipingRequestManager
|
import im.vector.matrix.android.internal.crypto.IncomingGossipingRequestManager
|
||||||
import im.vector.matrix.android.internal.crypto.OutgoingGossipingRequestManager
|
import im.vector.matrix.android.internal.crypto.OutgoingGossipingRequestManager
|
||||||
import im.vector.matrix.android.internal.crypto.actions.SetDeviceVerificationAction
|
import im.vector.matrix.android.internal.crypto.actions.SetDeviceVerificationAction
|
||||||
import im.vector.matrix.android.internal.crypto.model.MXKey
|
|
||||||
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
||||||
import im.vector.matrix.android.internal.extensions.toUnsignedInt
|
import im.vector.matrix.android.internal.extensions.toUnsignedInt
|
||||||
import im.vector.matrix.android.internal.util.withoutPrefix
|
import im.vector.matrix.android.internal.util.withoutPrefix
|
||||||
|
@ -66,8 +65,11 @@ internal abstract class SASDefaultVerificationTransaction(
|
||||||
const val SAS_MAC_SHA256_LONGKDF = "hmac-sha256"
|
const val SAS_MAC_SHA256_LONGKDF = "hmac-sha256"
|
||||||
const val SAS_MAC_SHA256 = "hkdf-hmac-sha256"
|
const val SAS_MAC_SHA256 = "hkdf-hmac-sha256"
|
||||||
|
|
||||||
|
// Deprecated maybe removed later, use V2
|
||||||
|
const val KEY_AGREEMENT_V1 = "curve25519"
|
||||||
|
const val KEY_AGREEMENT_V2 = "curve25519-hkdf-sha256"
|
||||||
// ordered by preferred order
|
// ordered by preferred order
|
||||||
val KNOWN_AGREEMENT_PROTOCOLS = listOf(MXKey.KEY_CURVE_25519_TYPE)
|
val KNOWN_AGREEMENT_PROTOCOLS = listOf(KEY_AGREEMENT_V2, KEY_AGREEMENT_V1)
|
||||||
// ordered by preferred order
|
// ordered by preferred order
|
||||||
val KNOWN_HASHES = listOf("sha256")
|
val KNOWN_HASHES = listOf("sha256")
|
||||||
// ordered by preferred order
|
// ordered by preferred order
|
||||||
|
|
|
@ -48,10 +48,11 @@ import im.vector.matrix.android.internal.di.SessionId
|
||||||
import im.vector.matrix.android.internal.di.UserId
|
import im.vector.matrix.android.internal.di.UserId
|
||||||
import im.vector.matrix.android.internal.di.WorkManagerProvider
|
import im.vector.matrix.android.internal.di.WorkManagerProvider
|
||||||
import im.vector.matrix.android.internal.session.room.send.LocalEchoEventFactory
|
import im.vector.matrix.android.internal.session.room.send.LocalEchoEventFactory
|
||||||
|
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||||
import im.vector.matrix.android.internal.util.StringProvider
|
import im.vector.matrix.android.internal.util.StringProvider
|
||||||
import im.vector.matrix.android.internal.worker.WorkerParamsFactory
|
import im.vector.matrix.android.internal.worker.WorkerParamsFactory
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
@ -66,7 +67,8 @@ internal class VerificationTransportRoomMessage(
|
||||||
private val userDeviceId: String?,
|
private val userDeviceId: String?,
|
||||||
private val roomId: String,
|
private val roomId: String,
|
||||||
private val localEchoEventFactory: LocalEchoEventFactory,
|
private val localEchoEventFactory: LocalEchoEventFactory,
|
||||||
private val tx: DefaultVerificationTransaction?
|
private val tx: DefaultVerificationTransaction?,
|
||||||
|
private val coroutineScope: CoroutineScope
|
||||||
) : VerificationTransport {
|
) : VerificationTransport {
|
||||||
|
|
||||||
override fun <T> sendToOther(type: String,
|
override fun <T> sendToOther(type: String,
|
||||||
|
@ -131,7 +133,7 @@ internal class VerificationTransportRoomMessage(
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO listen to DB to get synced info
|
// TODO listen to DB to get synced info
|
||||||
GlobalScope.launch(Dispatchers.Main) {
|
coroutineScope.launch(Dispatchers.Main) {
|
||||||
workLiveData.observeForever(observer)
|
workLiveData.observeForever(observer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -212,7 +214,7 @@ internal class VerificationTransportRoomMessage(
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO listen to DB to get synced info
|
// TODO listen to DB to get synced info
|
||||||
GlobalScope.launch(Dispatchers.Main) {
|
coroutineScope.launch(Dispatchers.Main) {
|
||||||
workLiveData.observeForever(observer)
|
workLiveData.observeForever(observer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -265,7 +267,7 @@ internal class VerificationTransportRoomMessage(
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO listen to DB to get synced info
|
// TODO listen to DB to get synced info
|
||||||
GlobalScope.launch(Dispatchers.Main) {
|
coroutineScope.launch(Dispatchers.Main) {
|
||||||
workLiveData.observeForever(observer)
|
workLiveData.observeForever(observer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -384,9 +386,19 @@ internal class VerificationTransportRoomMessageFactory @Inject constructor(
|
||||||
private val userId: String,
|
private val userId: String,
|
||||||
@DeviceId
|
@DeviceId
|
||||||
private val deviceId: String?,
|
private val deviceId: String?,
|
||||||
private val localEchoEventFactory: LocalEchoEventFactory) {
|
private val localEchoEventFactory: LocalEchoEventFactory,
|
||||||
|
private val taskExecutor: TaskExecutor
|
||||||
|
) {
|
||||||
|
|
||||||
fun createTransport(roomId: String, tx: DefaultVerificationTransaction?): VerificationTransportRoomMessage {
|
fun createTransport(roomId: String, tx: DefaultVerificationTransaction?): VerificationTransportRoomMessage {
|
||||||
return VerificationTransportRoomMessage(workManagerProvider, stringProvider, sessionId, userId, deviceId, roomId, localEchoEventFactory, tx)
|
return VerificationTransportRoomMessage(workManagerProvider,
|
||||||
|
stringProvider,
|
||||||
|
sessionId,
|
||||||
|
userId,
|
||||||
|
deviceId,
|
||||||
|
roomId,
|
||||||
|
localEchoEventFactory,
|
||||||
|
tx,
|
||||||
|
taskExecutor.executorScope)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,10 +60,9 @@ internal fun ChunkEntity.merge(roomId: String, chunkToMerge: ChunkEntity, direct
|
||||||
chunkToMerge.stateEvents.forEach { stateEvent ->
|
chunkToMerge.stateEvents.forEach { stateEvent ->
|
||||||
addStateEvent(roomId, stateEvent, direction)
|
addStateEvent(roomId, stateEvent, direction)
|
||||||
}
|
}
|
||||||
return eventsToMerge
|
eventsToMerge.forEach {
|
||||||
.forEach {
|
addTimelineEventFromMerge(localRealm, it, direction)
|
||||||
addTimelineEventFromMerge(localRealm, it, direction)
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun ChunkEntity.addStateEvent(roomId: String, stateEvent: EventEntity, direction: PaginationDirection) {
|
internal fun ChunkEntity.addStateEvent(roomId: String, stateEvent: EventEntity, direction: PaginationDirection) {
|
||||||
|
|
|
@ -18,6 +18,7 @@ package im.vector.matrix.android.internal.database.helper
|
||||||
|
|
||||||
import im.vector.matrix.android.internal.database.model.TimelineEventEntity
|
import im.vector.matrix.android.internal.database.model.TimelineEventEntity
|
||||||
import im.vector.matrix.android.internal.database.model.TimelineEventEntityFields
|
import im.vector.matrix.android.internal.database.model.TimelineEventEntityFields
|
||||||
|
import im.vector.matrix.android.internal.extensions.assertIsManaged
|
||||||
import io.realm.Realm
|
import io.realm.Realm
|
||||||
|
|
||||||
internal fun TimelineEventEntity.Companion.nextId(realm: Realm): Long {
|
internal fun TimelineEventEntity.Companion.nextId(realm: Realm): Long {
|
||||||
|
@ -28,3 +29,11 @@ internal fun TimelineEventEntity.Companion.nextId(realm: Realm): Long {
|
||||||
currentIdNum.toLong() + 1
|
currentIdNum.toLong() + 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun TimelineEventEntity.deleteOnCascade() {
|
||||||
|
assertIsManaged()
|
||||||
|
root?.deleteFromRealm()
|
||||||
|
annotations?.deleteFromRealm()
|
||||||
|
readReceipts?.deleteFromRealm()
|
||||||
|
deleteFromRealm()
|
||||||
|
}
|
||||||
|
|
|
@ -20,21 +20,16 @@ import im.vector.matrix.android.api.session.homeserver.HomeServerCapabilities
|
||||||
import im.vector.matrix.android.internal.database.model.HomeServerCapabilitiesEntity
|
import im.vector.matrix.android.internal.database.model.HomeServerCapabilitiesEntity
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HomeServerCapabilitiesEntity <-> HomeSeverCapabilities
|
* HomeServerCapabilitiesEntity -> HomeSeverCapabilities
|
||||||
*/
|
*/
|
||||||
internal object HomeServerCapabilitiesMapper {
|
internal object HomeServerCapabilitiesMapper {
|
||||||
|
|
||||||
fun map(entity: HomeServerCapabilitiesEntity): HomeServerCapabilities {
|
fun map(entity: HomeServerCapabilitiesEntity): HomeServerCapabilities {
|
||||||
return HomeServerCapabilities(
|
return HomeServerCapabilities(
|
||||||
canChangePassword = entity.canChangePassword,
|
canChangePassword = entity.canChangePassword,
|
||||||
maxUploadFileSize = entity.maxUploadFileSize
|
maxUploadFileSize = entity.maxUploadFileSize,
|
||||||
)
|
lastVersionIdentityServerSupported = entity.lastVersionIdentityServerSupported,
|
||||||
}
|
defaultIdentityServerUrl = entity.defaultIdentityServerUrl
|
||||||
|
|
||||||
fun map(domain: HomeServerCapabilities): HomeServerCapabilitiesEntity {
|
|
||||||
return HomeServerCapabilitiesEntity(
|
|
||||||
canChangePassword = domain.canChangePassword,
|
|
||||||
maxUploadFileSize = domain.maxUploadFileSize
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ internal object PushRulesMapper {
|
||||||
|
|
||||||
private fun fromActionStr(actionsStr: String?): List<Any> {
|
private fun fromActionStr(actionsStr: String?): List<Any> {
|
||||||
try {
|
try {
|
||||||
return actionsStr?.let { moshiActionsAdapter.fromJson(it) } ?: emptyList()
|
return actionsStr?.let { moshiActionsAdapter.fromJson(it) }.orEmpty()
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
Timber.e(e, "## failed to map push rule actions <$actionsStr>")
|
Timber.e(e, "## failed to map push rule actions <$actionsStr>")
|
||||||
return emptyList()
|
return emptyList()
|
||||||
|
|
|
@ -49,7 +49,7 @@ internal class RoomSummaryMapper @Inject constructor(private val timelineEventMa
|
||||||
membership = roomSummaryEntity.membership,
|
membership = roomSummaryEntity.membership,
|
||||||
versioningState = roomSummaryEntity.versioningState,
|
versioningState = roomSummaryEntity.versioningState,
|
||||||
readMarkerId = roomSummaryEntity.readMarkerId,
|
readMarkerId = roomSummaryEntity.readMarkerId,
|
||||||
userDrafts = roomSummaryEntity.userDrafts?.userDrafts?.map { DraftMapper.map(it) } ?: emptyList(),
|
userDrafts = roomSummaryEntity.userDrafts?.userDrafts?.map { DraftMapper.map(it) }.orEmpty(),
|
||||||
canonicalAlias = roomSummaryEntity.canonicalAlias,
|
canonicalAlias = roomSummaryEntity.canonicalAlias,
|
||||||
aliases = roomSummaryEntity.aliases.toList(),
|
aliases = roomSummaryEntity.aliases.toList(),
|
||||||
isEncrypted = roomSummaryEntity.isEncrypted,
|
isEncrypted = roomSummaryEntity.isEncrypted,
|
||||||
|
|
|
@ -18,7 +18,7 @@ package im.vector.matrix.android.internal.database.mapper
|
||||||
|
|
||||||
import im.vector.matrix.android.api.session.events.model.Event
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
import im.vector.matrix.android.api.session.room.model.ReadReceipt
|
import im.vector.matrix.android.api.session.room.model.ReadReceipt
|
||||||
|
import im.vector.matrix.android.api.session.room.sender.SenderInfo
|
||||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||||
import im.vector.matrix.android.internal.database.model.TimelineEventEntity
|
import im.vector.matrix.android.internal.database.model.TimelineEventEntity
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -41,15 +41,18 @@ internal class TimelineEventMapper @Inject constructor(private val readReceiptsS
|
||||||
annotations = timelineEventEntity.annotations?.asDomain(),
|
annotations = timelineEventEntity.annotations?.asDomain(),
|
||||||
localId = timelineEventEntity.localId,
|
localId = timelineEventEntity.localId,
|
||||||
displayIndex = timelineEventEntity.displayIndex,
|
displayIndex = timelineEventEntity.displayIndex,
|
||||||
senderName = timelineEventEntity.senderName,
|
senderInfo = SenderInfo(
|
||||||
isUniqueDisplayName = timelineEventEntity.isUniqueDisplayName,
|
userId = timelineEventEntity.root?.sender ?: "",
|
||||||
senderAvatar = timelineEventEntity.senderAvatar,
|
displayName = timelineEventEntity.senderName,
|
||||||
|
isUniqueDisplayName = timelineEventEntity.isUniqueDisplayName,
|
||||||
|
avatarUrl = timelineEventEntity.senderAvatar
|
||||||
|
),
|
||||||
readReceipts = readReceipts
|
readReceipts = readReceipts
|
||||||
?.distinctBy {
|
?.distinctBy {
|
||||||
it.user
|
it.user
|
||||||
}?.sortedByDescending {
|
}?.sortedByDescending {
|
||||||
it.originServerTs
|
it.originServerTs
|
||||||
} ?: emptyList()
|
}.orEmpty()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,15 +23,20 @@ import io.realm.annotations.Index
|
||||||
import io.realm.annotations.LinkingObjects
|
import io.realm.annotations.LinkingObjects
|
||||||
|
|
||||||
internal open class ChunkEntity(@Index var prevToken: String? = null,
|
internal open class ChunkEntity(@Index var prevToken: String? = null,
|
||||||
|
// Because of gaps we can have several chunks with nextToken == null
|
||||||
@Index var nextToken: String? = null,
|
@Index var nextToken: String? = null,
|
||||||
var stateEvents: RealmList<EventEntity> = RealmList(),
|
var stateEvents: RealmList<EventEntity> = RealmList(),
|
||||||
var timelineEvents: RealmList<TimelineEventEntity> = RealmList(),
|
var timelineEvents: RealmList<TimelineEventEntity> = RealmList(),
|
||||||
|
// Only one chunk will have isLastForward == true
|
||||||
@Index var isLastForward: Boolean = false,
|
@Index var isLastForward: Boolean = false,
|
||||||
@Index var isLastBackward: Boolean = false
|
@Index var isLastBackward: Boolean = false
|
||||||
) : RealmObject() {
|
) : RealmObject() {
|
||||||
|
|
||||||
fun identifier() = "${prevToken}_$nextToken"
|
fun identifier() = "${prevToken}_$nextToken"
|
||||||
|
|
||||||
|
// If true, then this chunk was previously a last forward chunk
|
||||||
|
fun hasBeenALastForwardChunk() = nextToken == null && !isLastForward
|
||||||
|
|
||||||
@LinkingObjects("chunks")
|
@LinkingObjects("chunks")
|
||||||
val room: RealmResults<RoomEntity>? = null
|
val room: RealmResults<RoomEntity>? = null
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,8 @@ import io.realm.RealmObject
|
||||||
internal open class HomeServerCapabilitiesEntity(
|
internal open class HomeServerCapabilitiesEntity(
|
||||||
var canChangePassword: Boolean = true,
|
var canChangePassword: Boolean = true,
|
||||||
var maxUploadFileSize: Long = HomeServerCapabilities.MAX_UPLOAD_FILE_SIZE_UNKNOWN,
|
var maxUploadFileSize: Long = HomeServerCapabilities.MAX_UPLOAD_FILE_SIZE_UNKNOWN,
|
||||||
|
var lastVersionIdentityServerSupported: Boolean = false,
|
||||||
|
var defaultIdentityServerUrl: String? = null,
|
||||||
var lastUpdatedTimestamp: Long = 0L
|
var lastUpdatedTimestamp: Long = 0L
|
||||||
) : RealmObject() {
|
) : RealmObject() {
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@ import io.realm.annotations.RealmModule
|
||||||
UserEntity::class,
|
UserEntity::class,
|
||||||
IgnoredUserEntity::class,
|
IgnoredUserEntity::class,
|
||||||
BreadcrumbsEntity::class,
|
BreadcrumbsEntity::class,
|
||||||
|
UserThreePidEntity::class,
|
||||||
EventAnnotationsSummaryEntity::class,
|
EventAnnotationsSummaryEntity::class,
|
||||||
ReactionAggregatedSummaryEntity::class,
|
ReactionAggregatedSummaryEntity::class,
|
||||||
EditAggregatedSummaryEntity::class,
|
EditAggregatedSummaryEntity::class,
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package im.vector.matrix.android.internal.database.model
|
||||||
|
|
||||||
|
import io.realm.RealmObject
|
||||||
|
|
||||||
|
internal open class UserThreePidEntity(
|
||||||
|
var medium: String = "",
|
||||||
|
var address: String = "",
|
||||||
|
var validatedAt: Long = 0,
|
||||||
|
var addedAt: Long = 0
|
||||||
|
) : RealmObject()
|
|
@ -41,7 +41,7 @@ internal fun ChunkEntity.Companion.find(realm: Realm, roomId: String, prevToken:
|
||||||
return query.findFirst()
|
return query.findFirst()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun ChunkEntity.Companion.findLastLiveChunkFromRoom(realm: Realm, roomId: String): ChunkEntity? {
|
internal fun ChunkEntity.Companion.findLastForwardChunkOfRoom(realm: Realm, roomId: String): ChunkEntity? {
|
||||||
return where(realm, roomId)
|
return where(realm, roomId)
|
||||||
.equalTo(ChunkEntityFields.IS_LAST_FORWARD, true)
|
.equalTo(ChunkEntityFields.IS_LAST_FORWARD, true)
|
||||||
.findFirst()
|
.findFirst()
|
||||||
|
|
|
@ -35,7 +35,7 @@ internal fun FilterEntity.Companion.get(realm: Realm): FilterEntity? {
|
||||||
internal fun FilterEntity.Companion.getOrCreate(realm: Realm): FilterEntity {
|
internal fun FilterEntity.Companion.getOrCreate(realm: Realm): FilterEntity {
|
||||||
return get(realm) ?: realm.createObject<FilterEntity>()
|
return get(realm) ?: realm.createObject<FilterEntity>()
|
||||||
.apply {
|
.apply {
|
||||||
filterBodyJson = FilterFactory.createDefaultFilterBody().toJSONString()
|
filterBodyJson = FilterFactory.createDefaultFilter().toJSONString()
|
||||||
roomEventFilterJson = FilterFactory.createDefaultRoomFilter().toJSONString()
|
roomEventFilterJson = FilterFactory.createDefaultRoomFilter().toJSONString()
|
||||||
filterId = ""
|
filterId = ""
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ internal fun isEventRead(monarchy: Monarchy,
|
||||||
var isEventRead = false
|
var isEventRead = false
|
||||||
|
|
||||||
monarchy.doWithRealm { realm ->
|
monarchy.doWithRealm { realm ->
|
||||||
val liveChunk = ChunkEntity.findLastLiveChunkFromRoom(realm, roomId) ?: return@doWithRealm
|
val liveChunk = ChunkEntity.findLastForwardChunkOfRoom(realm, roomId) ?: return@doWithRealm
|
||||||
val eventToCheck = liveChunk.timelineEvents.find(eventId)
|
val eventToCheck = liveChunk.timelineEvents.find(eventId)
|
||||||
isEventRead = if (eventToCheck == null || eventToCheck.root?.sender == userId) {
|
isEventRead = if (eventToCheck == null || eventToCheck.root?.sender == userId) {
|
||||||
true
|
true
|
||||||
|
|
|
@ -59,11 +59,11 @@ internal fun TimelineEventEntity.Companion.latestEvent(realm: Realm,
|
||||||
filterTypes: List<String> = emptyList()): TimelineEventEntity? {
|
filterTypes: List<String> = emptyList()): TimelineEventEntity? {
|
||||||
val roomEntity = RoomEntity.where(realm, roomId).findFirst() ?: return null
|
val roomEntity = RoomEntity.where(realm, roomId).findFirst() ?: return null
|
||||||
val sendingTimelineEvents = roomEntity.sendingTimelineEvents.where().filterTypes(filterTypes)
|
val sendingTimelineEvents = roomEntity.sendingTimelineEvents.where().filterTypes(filterTypes)
|
||||||
val liveEvents = ChunkEntity.findLastLiveChunkFromRoom(realm, roomId)?.timelineEvents?.where()?.filterTypes(filterTypes)
|
val liveEvents = ChunkEntity.findLastForwardChunkOfRoom(realm, roomId)?.timelineEvents?.where()?.filterTypes(filterTypes)
|
||||||
if (filterContentRelation) {
|
if (filterContentRelation) {
|
||||||
liveEvents
|
liveEvents
|
||||||
?.not()?.like(TimelineEventEntityFields.ROOT.CONTENT, FilterContent.EDIT_TYPE)
|
?.not()?.like(TimelineEventEntityFields.ROOT.CONTENT, TimelineEventFilter.Content.EDIT)
|
||||||
?.not()?.like(TimelineEventEntityFields.ROOT.CONTENT, FilterContent.RESPONSE_TYPE)
|
?.not()?.like(TimelineEventEntityFields.ROOT.CONTENT, TimelineEventFilter.Content.RESPONSE)
|
||||||
}
|
}
|
||||||
val query = if (includesSending && sendingTimelineEvents.findAll().isNotEmpty()) {
|
val query = if (includesSending && sendingTimelineEvents.findAll().isNotEmpty()) {
|
||||||
sendingTimelineEvents
|
sendingTimelineEvents
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2019 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.matrix.android.internal.database.query
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query strings used to filter the timeline events regarding the Json raw string of the Event
|
||||||
|
*/
|
||||||
|
internal object TimelineEventFilter {
|
||||||
|
/**
|
||||||
|
* To apply to Event.content
|
||||||
|
*/
|
||||||
|
internal object Content {
|
||||||
|
internal const val EDIT = """{*"m.relates_to"*"rel_type":*"m.replace"*}"""
|
||||||
|
internal const val RESPONSE = """{*"m.relates_to"*"rel_type":*"m.response"*}"""
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To apply to Event.decryptionResultJson
|
||||||
|
*/
|
||||||
|
internal object DecryptedContent {
|
||||||
|
internal const val URL = """{*"file":*"url":*}"""
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To apply to Event.unsigned
|
||||||
|
*/
|
||||||
|
internal object Unsigned {
|
||||||
|
internal const val REDACTED = """{*"redacted_because":*}"""
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,8 +20,12 @@ import javax.inject.Qualifier
|
||||||
|
|
||||||
@Qualifier
|
@Qualifier
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
annotation class Authenticated
|
internal annotation class Authenticated
|
||||||
|
|
||||||
@Qualifier
|
@Qualifier
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
annotation class Unauthenticated
|
internal annotation class AuthenticatedIdentity
|
||||||
|
|
||||||
|
@Qualifier
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
internal annotation class Unauthenticated
|
||||||
|
|
|
@ -20,12 +20,16 @@ import javax.inject.Qualifier
|
||||||
|
|
||||||
@Qualifier
|
@Qualifier
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
annotation class AuthDatabase
|
internal annotation class AuthDatabase
|
||||||
|
|
||||||
@Qualifier
|
@Qualifier
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
annotation class SessionDatabase
|
internal annotation class SessionDatabase
|
||||||
|
|
||||||
@Qualifier
|
@Qualifier
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
annotation class CryptoDatabase
|
internal annotation class CryptoDatabase
|
||||||
|
|
||||||
|
@Qualifier
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
internal annotation class IdentityDatabase
|
||||||
|
|
|
@ -20,16 +20,16 @@ import javax.inject.Qualifier
|
||||||
|
|
||||||
@Qualifier
|
@Qualifier
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
annotation class SessionFilesDirectory
|
internal annotation class SessionFilesDirectory
|
||||||
|
|
||||||
@Qualifier
|
@Qualifier
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
annotation class SessionCacheDirectory
|
internal annotation class SessionCacheDirectory
|
||||||
|
|
||||||
@Qualifier
|
@Qualifier
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
annotation class CacheDirectory
|
internal annotation class CacheDirectory
|
||||||
|
|
||||||
@Qualifier
|
@Qualifier
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
annotation class ExternalFilesDirectory
|
internal annotation class ExternalFilesDirectory
|
||||||
|
|
|
@ -21,6 +21,7 @@ import com.squareup.moshi.Moshi
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
import dagger.Provides
|
import dagger.Provides
|
||||||
import im.vector.matrix.android.BuildConfig
|
import im.vector.matrix.android.BuildConfig
|
||||||
|
import im.vector.matrix.android.api.MatrixConfiguration
|
||||||
import im.vector.matrix.android.internal.network.TimeOutInterceptor
|
import im.vector.matrix.android.internal.network.TimeOutInterceptor
|
||||||
import im.vector.matrix.android.internal.network.UserAgentInterceptor
|
import im.vector.matrix.android.internal.network.UserAgentInterceptor
|
||||||
import im.vector.matrix.android.internal.network.interceptors.CurlLoggingInterceptor
|
import im.vector.matrix.android.internal.network.interceptors.CurlLoggingInterceptor
|
||||||
|
@ -64,7 +65,8 @@ internal object NetworkModule {
|
||||||
@Provides
|
@Provides
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@Unauthenticated
|
@Unauthenticated
|
||||||
fun providesOkHttpClient(stethoInterceptor: StethoInterceptor,
|
fun providesOkHttpClient(matrixConfiguration: MatrixConfiguration,
|
||||||
|
stethoInterceptor: StethoInterceptor,
|
||||||
timeoutInterceptor: TimeOutInterceptor,
|
timeoutInterceptor: TimeOutInterceptor,
|
||||||
userAgentInterceptor: UserAgentInterceptor,
|
userAgentInterceptor: UserAgentInterceptor,
|
||||||
httpLoggingInterceptor: HttpLoggingInterceptor,
|
httpLoggingInterceptor: HttpLoggingInterceptor,
|
||||||
|
@ -82,6 +84,9 @@ internal object NetworkModule {
|
||||||
if (BuildConfig.LOG_PRIVATE_DATA) {
|
if (BuildConfig.LOG_PRIVATE_DATA) {
|
||||||
addInterceptor(curlLoggingInterceptor)
|
addInterceptor(curlLoggingInterceptor)
|
||||||
}
|
}
|
||||||
|
matrixConfiguration.proxy?.let {
|
||||||
|
proxy(it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.addInterceptor(okReplayInterceptor)
|
.addInterceptor(okReplayInterceptor)
|
||||||
.build()
|
.build()
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.matrix.android.internal.extensions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ex: "abcdef".subStringBetween("a", "f") -> "bcde"
|
||||||
|
* Ex: "abcdefff".subStringBetween("a", "f") -> "bcdeff"
|
||||||
|
* Ex: "aaabcdef".subStringBetween("a", "f") -> "aabcde"
|
||||||
|
*/
|
||||||
|
internal fun String.subStringBetween(prefix: String, suffix: String) = substringAfter(prefix).substringBeforeLast(suffix)
|
|
@ -16,20 +16,16 @@
|
||||||
|
|
||||||
package im.vector.matrix.android.internal.network
|
package im.vector.matrix.android.internal.network
|
||||||
|
|
||||||
import im.vector.matrix.android.internal.auth.SessionParamsStore
|
import im.vector.matrix.android.internal.network.token.AccessTokenProvider
|
||||||
import im.vector.matrix.android.internal.di.SessionId
|
|
||||||
import okhttp3.Interceptor
|
import okhttp3.Interceptor
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
internal class AccessTokenInterceptor @Inject constructor(
|
internal class AccessTokenInterceptor(private val accessTokenProvider: AccessTokenProvider) : Interceptor {
|
||||||
@SessionId private val sessionId: String,
|
|
||||||
private val sessionParamsStore: SessionParamsStore) : Interceptor {
|
|
||||||
|
|
||||||
override fun intercept(chain: Interceptor.Chain): Response {
|
override fun intercept(chain: Interceptor.Chain): Response {
|
||||||
var request = chain.request()
|
var request = chain.request()
|
||||||
|
|
||||||
accessToken?.let {
|
accessTokenProvider.getToken()?.let {
|
||||||
val newRequestBuilder = request.newBuilder()
|
val newRequestBuilder = request.newBuilder()
|
||||||
// Add the access token to all requests if it is set
|
// Add the access token to all requests if it is set
|
||||||
newRequestBuilder.addHeader(HttpHeaders.Authorization, "Bearer $it")
|
newRequestBuilder.addHeader(HttpHeaders.Authorization, "Bearer $it")
|
||||||
|
@ -38,7 +34,4 @@ internal class AccessTokenInterceptor @Inject constructor(
|
||||||
|
|
||||||
return chain.proceed(request)
|
return chain.proceed(request)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val accessToken
|
|
||||||
get() = sessionParamsStore.get(sessionId)?.credentials?.accessToken
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,8 +28,9 @@ internal object NetworkConstants {
|
||||||
const val URI_API_MEDIA_PREFIX_PATH_R0 = "$URI_API_MEDIA_PREFIX_PATH/r0/"
|
const val URI_API_MEDIA_PREFIX_PATH_R0 = "$URI_API_MEDIA_PREFIX_PATH/r0/"
|
||||||
|
|
||||||
// Identity server
|
// Identity server
|
||||||
const val URI_IDENTITY_PATH = "_matrix/identity/api/v1/"
|
const val URI_IDENTITY_PREFIX_PATH = "_matrix/identity/v2"
|
||||||
const val URI_IDENTITY_PATH_V2 = "_matrix/identity/v2/"
|
const val URI_IDENTITY_PATH_V2 = "$URI_IDENTITY_PREFIX_PATH/"
|
||||||
|
|
||||||
const val URI_API_PREFIX_IDENTITY = "_matrix/identity/api/v1"
|
// TODO Ganfra, use correct value
|
||||||
|
const val URI_INTEGRATION_MANAGER_PATH = "TODO/"
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.matrix.android.internal.network.httpclient
|
||||||
|
|
||||||
|
import im.vector.matrix.android.internal.network.AccessTokenInterceptor
|
||||||
|
import im.vector.matrix.android.internal.network.interceptors.CurlLoggingInterceptor
|
||||||
|
import im.vector.matrix.android.internal.network.token.AccessTokenProvider
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
|
||||||
|
internal fun OkHttpClient.addAccessTokenInterceptor(accessTokenProvider: AccessTokenProvider): OkHttpClient {
|
||||||
|
return newBuilder()
|
||||||
|
.apply {
|
||||||
|
// Remove the previous CurlLoggingInterceptor, to add it after the accessTokenInterceptor
|
||||||
|
val existingCurlInterceptors = interceptors().filterIsInstance<CurlLoggingInterceptor>()
|
||||||
|
interceptors().removeAll(existingCurlInterceptors)
|
||||||
|
|
||||||
|
addInterceptor(AccessTokenInterceptor(accessTokenProvider))
|
||||||
|
|
||||||
|
// Re add eventually the curl logging interceptors
|
||||||
|
existingCurlInterceptors.forEach {
|
||||||
|
addInterceptor(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.build()
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.matrix.android.internal.network.token
|
||||||
|
|
||||||
|
internal interface AccessTokenProvider {
|
||||||
|
fun getToken(): String?
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.matrix.android.internal.network.token
|
||||||
|
|
||||||
|
import im.vector.matrix.android.internal.auth.SessionParamsStore
|
||||||
|
import im.vector.matrix.android.internal.di.SessionId
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
internal class HomeserverAccessTokenProvider @Inject constructor(
|
||||||
|
@SessionId private val sessionId: String,
|
||||||
|
private val sessionParamsStore: SessionParamsStore
|
||||||
|
) : AccessTokenProvider {
|
||||||
|
override fun getToken() = sessionParamsStore.get(sessionId)?.credentials?.accessToken
|
||||||
|
}
|
|
@ -28,10 +28,10 @@ import im.vector.matrix.android.internal.di.ExternalFilesDirectory
|
||||||
import im.vector.matrix.android.internal.di.SessionCacheDirectory
|
import im.vector.matrix.android.internal.di.SessionCacheDirectory
|
||||||
import im.vector.matrix.android.internal.di.Unauthenticated
|
import im.vector.matrix.android.internal.di.Unauthenticated
|
||||||
import im.vector.matrix.android.internal.extensions.foldToCallback
|
import im.vector.matrix.android.internal.extensions.foldToCallback
|
||||||
|
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||||
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
||||||
import im.vector.matrix.android.internal.util.toCancelable
|
import im.vector.matrix.android.internal.util.toCancelable
|
||||||
import im.vector.matrix.android.internal.util.writeToFile
|
import im.vector.matrix.android.internal.util.writeToFile
|
||||||
import kotlinx.coroutines.GlobalScope
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
|
@ -51,7 +51,9 @@ internal class DefaultFileService @Inject constructor(
|
||||||
private val contentUrlResolver: ContentUrlResolver,
|
private val contentUrlResolver: ContentUrlResolver,
|
||||||
@Unauthenticated
|
@Unauthenticated
|
||||||
private val okHttpClient: OkHttpClient,
|
private val okHttpClient: OkHttpClient,
|
||||||
private val coroutineDispatchers: MatrixCoroutineDispatchers) : FileService {
|
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||||
|
private val taskExecutor: TaskExecutor
|
||||||
|
) : FileService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Download file in the cache folder, and eventually decrypt it
|
* Download file in the cache folder, and eventually decrypt it
|
||||||
|
@ -63,7 +65,7 @@ internal class DefaultFileService @Inject constructor(
|
||||||
url: String?,
|
url: String?,
|
||||||
elementToDecrypt: ElementToDecrypt?,
|
elementToDecrypt: ElementToDecrypt?,
|
||||||
callback: MatrixCallback<File>): Cancelable {
|
callback: MatrixCallback<File>): Cancelable {
|
||||||
return GlobalScope.launch(coroutineDispatchers.main) {
|
return taskExecutor.executorScope.launch(coroutineDispatchers.main) {
|
||||||
withContext(coroutineDispatchers.io) {
|
withContext(coroutineDispatchers.io) {
|
||||||
Try {
|
Try {
|
||||||
val folder = File(sessionCacheDirectory, "MF")
|
val folder = File(sessionCacheDirectory, "MF")
|
||||||
|
|
|
@ -43,6 +43,7 @@ import im.vector.matrix.android.api.session.securestorage.SharedSecretStorageSer
|
||||||
import im.vector.matrix.android.api.session.signout.SignOutService
|
import im.vector.matrix.android.api.session.signout.SignOutService
|
||||||
import im.vector.matrix.android.api.session.sync.FilterService
|
import im.vector.matrix.android.api.session.sync.FilterService
|
||||||
import im.vector.matrix.android.api.session.sync.SyncState
|
import im.vector.matrix.android.api.session.sync.SyncState
|
||||||
|
import im.vector.matrix.android.api.session.terms.TermsService
|
||||||
import im.vector.matrix.android.api.session.user.UserService
|
import im.vector.matrix.android.api.session.user.UserService
|
||||||
import im.vector.matrix.android.internal.auth.SessionParamsStore
|
import im.vector.matrix.android.internal.auth.SessionParamsStore
|
||||||
import im.vector.matrix.android.internal.crypto.DefaultCryptoService
|
import im.vector.matrix.android.internal.crypto.DefaultCryptoService
|
||||||
|
@ -50,12 +51,14 @@ import im.vector.matrix.android.internal.crypto.crosssigning.ShieldTrustUpdater
|
||||||
import im.vector.matrix.android.internal.database.LiveEntityObserver
|
import im.vector.matrix.android.internal.database.LiveEntityObserver
|
||||||
import im.vector.matrix.android.internal.di.SessionId
|
import im.vector.matrix.android.internal.di.SessionId
|
||||||
import im.vector.matrix.android.internal.di.WorkManagerProvider
|
import im.vector.matrix.android.internal.di.WorkManagerProvider
|
||||||
|
import im.vector.matrix.android.internal.session.identity.DefaultIdentityService
|
||||||
import im.vector.matrix.android.internal.session.room.timeline.TimelineEventDecryptor
|
import im.vector.matrix.android.internal.session.room.timeline.TimelineEventDecryptor
|
||||||
import im.vector.matrix.android.internal.session.sync.SyncTokenStore
|
import im.vector.matrix.android.internal.session.sync.SyncTokenStore
|
||||||
import im.vector.matrix.android.internal.session.sync.job.SyncThread
|
import im.vector.matrix.android.internal.session.sync.job.SyncThread
|
||||||
import im.vector.matrix.android.internal.session.sync.job.SyncWorker
|
import im.vector.matrix.android.internal.session.sync.job.SyncWorker
|
||||||
|
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||||
|
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.greenrobot.eventbus.EventBus
|
import org.greenrobot.eventbus.EventBus
|
||||||
import org.greenrobot.eventbus.Subscribe
|
import org.greenrobot.eventbus.Subscribe
|
||||||
|
@ -82,6 +85,7 @@ internal class DefaultSession @Inject constructor(
|
||||||
private val signOutService: Lazy<SignOutService>,
|
private val signOutService: Lazy<SignOutService>,
|
||||||
private val pushRuleService: Lazy<PushRuleService>,
|
private val pushRuleService: Lazy<PushRuleService>,
|
||||||
private val pushersService: Lazy<PushersService>,
|
private val pushersService: Lazy<PushersService>,
|
||||||
|
private val termsService: Lazy<TermsService>,
|
||||||
private val cryptoService: Lazy<DefaultCryptoService>,
|
private val cryptoService: Lazy<DefaultCryptoService>,
|
||||||
private val fileService: Lazy<FileService>,
|
private val fileService: Lazy<FileService>,
|
||||||
private val secureStorageService: Lazy<SecureStorageService>,
|
private val secureStorageService: Lazy<SecureStorageService>,
|
||||||
|
@ -97,8 +101,11 @@ internal class DefaultSession @Inject constructor(
|
||||||
private val _sharedSecretStorageService: Lazy<SharedSecretStorageService>,
|
private val _sharedSecretStorageService: Lazy<SharedSecretStorageService>,
|
||||||
private val accountService: Lazy<AccountService>,
|
private val accountService: Lazy<AccountService>,
|
||||||
private val timelineEventDecryptor: TimelineEventDecryptor,
|
private val timelineEventDecryptor: TimelineEventDecryptor,
|
||||||
private val shieldTrustUpdater: ShieldTrustUpdater)
|
private val shieldTrustUpdater: ShieldTrustUpdater,
|
||||||
: Session,
|
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||||
|
private val defaultIdentityService: DefaultIdentityService,
|
||||||
|
private val taskExecutor: TaskExecutor
|
||||||
|
) : Session,
|
||||||
RoomService by roomService.get(),
|
RoomService by roomService.get(),
|
||||||
RoomDirectoryService by roomDirectoryService.get(),
|
RoomDirectoryService by roomDirectoryService.get(),
|
||||||
GroupService by groupService.get(),
|
GroupService by groupService.get(),
|
||||||
|
@ -108,6 +115,7 @@ internal class DefaultSession @Inject constructor(
|
||||||
PushRuleService by pushRuleService.get(),
|
PushRuleService by pushRuleService.get(),
|
||||||
PushersService by pushersService.get(),
|
PushersService by pushersService.get(),
|
||||||
FileService by fileService.get(),
|
FileService by fileService.get(),
|
||||||
|
TermsService by termsService.get(),
|
||||||
InitialSyncProgressService by initialSyncProgressService.get(),
|
InitialSyncProgressService by initialSyncProgressService.get(),
|
||||||
SecureStorageService by secureStorageService.get(),
|
SecureStorageService by secureStorageService.get(),
|
||||||
HomeServerCapabilitiesService by homeServerCapabilitiesService.get(),
|
HomeServerCapabilitiesService by homeServerCapabilitiesService.get(),
|
||||||
|
@ -133,6 +141,7 @@ internal class DefaultSession @Inject constructor(
|
||||||
eventBus.register(this)
|
eventBus.register(this)
|
||||||
timelineEventDecryptor.start()
|
timelineEventDecryptor.start()
|
||||||
shieldTrustUpdater.start()
|
shieldTrustUpdater.start()
|
||||||
|
defaultIdentityService.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun requireBackgroundSync() {
|
override fun requireBackgroundSync() {
|
||||||
|
@ -175,6 +184,10 @@ internal class DefaultSession @Inject constructor(
|
||||||
isOpen = false
|
isOpen = false
|
||||||
eventBus.unregister(this)
|
eventBus.unregister(this)
|
||||||
shieldTrustUpdater.stop()
|
shieldTrustUpdater.stop()
|
||||||
|
taskExecutor.executorScope.launch(coroutineDispatchers.main) {
|
||||||
|
// This has to be done on main thread
|
||||||
|
defaultIdentityService.stop()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getSyncStateLive(): LiveData<SyncState> {
|
override fun getSyncStateLive(): LiveData<SyncState> {
|
||||||
|
@ -204,7 +217,7 @@ internal class DefaultSession @Inject constructor(
|
||||||
if (globalError is GlobalError.InvalidToken
|
if (globalError is GlobalError.InvalidToken
|
||||||
&& globalError.softLogout) {
|
&& globalError.softLogout) {
|
||||||
// Mark the token has invalid
|
// Mark the token has invalid
|
||||||
GlobalScope.launch(Dispatchers.IO) {
|
taskExecutor.executorScope.launch(Dispatchers.IO) {
|
||||||
sessionParamsStore.setTokenInvalid(sessionId)
|
sessionParamsStore.setTokenInvalid(sessionId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -218,6 +231,8 @@ internal class DefaultSession @Inject constructor(
|
||||||
|
|
||||||
override fun cryptoService(): CryptoService = cryptoService.get()
|
override fun cryptoService(): CryptoService = cryptoService.get()
|
||||||
|
|
||||||
|
override fun identityService() = defaultIdentityService
|
||||||
|
|
||||||
override fun addListener(listener: Session.Listener) {
|
override fun addListener(listener: Session.Listener) {
|
||||||
sessionListeners.addListener(listener)
|
sessionListeners.addListener(listener)
|
||||||
}
|
}
|
||||||
|
@ -228,6 +243,6 @@ internal class DefaultSession @Inject constructor(
|
||||||
|
|
||||||
// For easy debugging
|
// For easy debugging
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "$myUserId - ${sessionParams.credentials.deviceId}"
|
return "$myUserId - ${sessionParams.deviceId}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,7 @@ import im.vector.matrix.android.internal.session.filter.FilterModule
|
||||||
import im.vector.matrix.android.internal.session.group.GetGroupDataWorker
|
import im.vector.matrix.android.internal.session.group.GetGroupDataWorker
|
||||||
import im.vector.matrix.android.internal.session.group.GroupModule
|
import im.vector.matrix.android.internal.session.group.GroupModule
|
||||||
import im.vector.matrix.android.internal.session.homeserver.HomeServerCapabilitiesModule
|
import im.vector.matrix.android.internal.session.homeserver.HomeServerCapabilitiesModule
|
||||||
|
import im.vector.matrix.android.internal.session.identity.IdentityModule
|
||||||
import im.vector.matrix.android.internal.session.openid.OpenIdModule
|
import im.vector.matrix.android.internal.session.openid.OpenIdModule
|
||||||
import im.vector.matrix.android.internal.session.profile.ProfileModule
|
import im.vector.matrix.android.internal.session.profile.ProfileModule
|
||||||
import im.vector.matrix.android.internal.session.pushers.AddHttpPusherWorker
|
import im.vector.matrix.android.internal.session.pushers.AddHttpPusherWorker
|
||||||
|
@ -51,6 +52,7 @@ import im.vector.matrix.android.internal.session.sync.SyncModule
|
||||||
import im.vector.matrix.android.internal.session.sync.SyncTask
|
import im.vector.matrix.android.internal.session.sync.SyncTask
|
||||||
import im.vector.matrix.android.internal.session.sync.SyncTokenStore
|
import im.vector.matrix.android.internal.session.sync.SyncTokenStore
|
||||||
import im.vector.matrix.android.internal.session.sync.job.SyncWorker
|
import im.vector.matrix.android.internal.session.sync.job.SyncWorker
|
||||||
|
import im.vector.matrix.android.internal.session.terms.TermsModule
|
||||||
import im.vector.matrix.android.internal.session.user.UserModule
|
import im.vector.matrix.android.internal.session.user.UserModule
|
||||||
import im.vector.matrix.android.internal.session.user.accountdata.AccountDataModule
|
import im.vector.matrix.android.internal.session.user.accountdata.AccountDataModule
|
||||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||||
|
@ -72,6 +74,8 @@ import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
||||||
CryptoModule::class,
|
CryptoModule::class,
|
||||||
PushersModule::class,
|
PushersModule::class,
|
||||||
OpenIdModule::class,
|
OpenIdModule::class,
|
||||||
|
IdentityModule::class,
|
||||||
|
TermsModule::class,
|
||||||
AccountDataModule::class,
|
AccountDataModule::class,
|
||||||
ProfileModule::class,
|
ProfileModule::class,
|
||||||
SessionAssistedInjectModule::class,
|
SessionAssistedInjectModule::class,
|
||||||
|
|
|
@ -50,14 +50,15 @@ import im.vector.matrix.android.internal.di.Unauthenticated
|
||||||
import im.vector.matrix.android.internal.di.UserId
|
import im.vector.matrix.android.internal.di.UserId
|
||||||
import im.vector.matrix.android.internal.di.UserMd5
|
import im.vector.matrix.android.internal.di.UserMd5
|
||||||
import im.vector.matrix.android.internal.eventbus.EventBusTimberLogger
|
import im.vector.matrix.android.internal.eventbus.EventBusTimberLogger
|
||||||
import im.vector.matrix.android.internal.network.AccessTokenInterceptor
|
|
||||||
import im.vector.matrix.android.internal.network.DefaultNetworkConnectivityChecker
|
import im.vector.matrix.android.internal.network.DefaultNetworkConnectivityChecker
|
||||||
import im.vector.matrix.android.internal.network.FallbackNetworkCallbackStrategy
|
import im.vector.matrix.android.internal.network.FallbackNetworkCallbackStrategy
|
||||||
import im.vector.matrix.android.internal.network.NetworkCallbackStrategy
|
import im.vector.matrix.android.internal.network.NetworkCallbackStrategy
|
||||||
import im.vector.matrix.android.internal.network.NetworkConnectivityChecker
|
import im.vector.matrix.android.internal.network.NetworkConnectivityChecker
|
||||||
import im.vector.matrix.android.internal.network.PreferredNetworkCallbackStrategy
|
import im.vector.matrix.android.internal.network.PreferredNetworkCallbackStrategy
|
||||||
import im.vector.matrix.android.internal.network.RetrofitFactory
|
import im.vector.matrix.android.internal.network.RetrofitFactory
|
||||||
import im.vector.matrix.android.internal.network.interceptors.CurlLoggingInterceptor
|
import im.vector.matrix.android.internal.network.httpclient.addAccessTokenInterceptor
|
||||||
|
import im.vector.matrix.android.internal.network.token.AccessTokenProvider
|
||||||
|
import im.vector.matrix.android.internal.network.token.HomeserverAccessTokenProvider
|
||||||
import im.vector.matrix.android.internal.session.group.GroupSummaryUpdater
|
import im.vector.matrix.android.internal.session.group.GroupSummaryUpdater
|
||||||
import im.vector.matrix.android.internal.session.homeserver.DefaultHomeServerCapabilitiesService
|
import im.vector.matrix.android.internal.session.homeserver.DefaultHomeServerCapabilitiesService
|
||||||
import im.vector.matrix.android.internal.session.room.EventRelationsAggregationUpdater
|
import im.vector.matrix.android.internal.session.room.EventRelationsAggregationUpdater
|
||||||
|
@ -175,21 +176,8 @@ internal abstract class SessionModule {
|
||||||
@SessionScope
|
@SessionScope
|
||||||
@Authenticated
|
@Authenticated
|
||||||
fun providesOkHttpClient(@Unauthenticated okHttpClient: OkHttpClient,
|
fun providesOkHttpClient(@Unauthenticated okHttpClient: OkHttpClient,
|
||||||
accessTokenInterceptor: AccessTokenInterceptor): OkHttpClient {
|
@Authenticated accessTokenProvider: AccessTokenProvider): OkHttpClient {
|
||||||
return okHttpClient.newBuilder()
|
return okHttpClient.addAccessTokenInterceptor(accessTokenProvider)
|
||||||
.apply {
|
|
||||||
// Remove the previous CurlLoggingInterceptor, to add it after the accessTokenInterceptor
|
|
||||||
val existingCurlInterceptors = interceptors().filterIsInstance<CurlLoggingInterceptor>()
|
|
||||||
interceptors().removeAll(existingCurlInterceptors)
|
|
||||||
|
|
||||||
addInterceptor(accessTokenInterceptor)
|
|
||||||
|
|
||||||
// Re add eventually the curl logging interceptors
|
|
||||||
existingCurlInterceptors.forEach {
|
|
||||||
addInterceptor(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.build()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
|
@ -233,44 +221,48 @@ internal abstract class SessionModule {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@Authenticated
|
||||||
|
abstract fun bindAccessTokenProvider(provider: HomeserverAccessTokenProvider): AccessTokenProvider
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun bindSession(session: DefaultSession): Session
|
abstract fun bindSession(session: DefaultSession): Session
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun bindNetworkConnectivityChecker(networkConnectivityChecker: DefaultNetworkConnectivityChecker): NetworkConnectivityChecker
|
abstract fun bindNetworkConnectivityChecker(checker: DefaultNetworkConnectivityChecker): NetworkConnectivityChecker
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@IntoSet
|
@IntoSet
|
||||||
abstract fun bindGroupSummaryUpdater(groupSummaryUpdater: GroupSummaryUpdater): LiveEntityObserver
|
abstract fun bindGroupSummaryUpdater(updater: GroupSummaryUpdater): LiveEntityObserver
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@IntoSet
|
@IntoSet
|
||||||
abstract fun bindEventsPruner(eventsPruner: EventsPruner): LiveEntityObserver
|
abstract fun bindEventsPruner(pruner: EventsPruner): LiveEntityObserver
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@IntoSet
|
@IntoSet
|
||||||
abstract fun bindEventRelationsAggregationUpdater(eventRelationsAggregationUpdater: EventRelationsAggregationUpdater): LiveEntityObserver
|
abstract fun bindEventRelationsAggregationUpdater(updater: EventRelationsAggregationUpdater): LiveEntityObserver
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@IntoSet
|
@IntoSet
|
||||||
abstract fun bindRoomTombstoneEventLiveObserver(roomTombstoneEventLiveObserver: RoomTombstoneEventLiveObserver): LiveEntityObserver
|
abstract fun bindRoomTombstoneEventLiveObserver(observer: RoomTombstoneEventLiveObserver): LiveEntityObserver
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@IntoSet
|
@IntoSet
|
||||||
abstract fun bindRoomCreateEventLiveObserver(roomCreateEventLiveObserver: RoomCreateEventLiveObserver): LiveEntityObserver
|
abstract fun bindRoomCreateEventLiveObserver(observer: RoomCreateEventLiveObserver): LiveEntityObserver
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@IntoSet
|
@IntoSet
|
||||||
abstract fun bindVerificationMessageLiveObserver(verificationMessageLiveObserver: VerificationMessageLiveObserver): LiveEntityObserver
|
abstract fun bindVerificationMessageLiveObserver(observer: VerificationMessageLiveObserver): LiveEntityObserver
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun bindInitialSyncProgressService(initialSyncProgressService: DefaultInitialSyncProgressService): InitialSyncProgressService
|
abstract fun bindInitialSyncProgressService(service: DefaultInitialSyncProgressService): InitialSyncProgressService
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun bindSecureStorageService(secureStorageService: DefaultSecureStorageService): SecureStorageService
|
abstract fun bindSecureStorageService(service: DefaultSecureStorageService): SecureStorageService
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun bindHomeServerCapabilitiesService(homeServerCapabilitiesService: DefaultHomeServerCapabilitiesService): HomeServerCapabilitiesService
|
abstract fun bindHomeServerCapabilitiesService(service: DefaultHomeServerCapabilitiesService): HomeServerCapabilitiesService
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun bindAccountDataService(service: DefaultAccountDataService): AccountDataService
|
abstract fun bindAccountDataService(service: DefaultAccountDataService): AccountDataService
|
||||||
|
|
|
@ -19,8 +19,10 @@ package im.vector.matrix.android.internal.session.account
|
||||||
import im.vector.matrix.android.internal.di.UserId
|
import im.vector.matrix.android.internal.di.UserId
|
||||||
import im.vector.matrix.android.internal.network.executeRequest
|
import im.vector.matrix.android.internal.network.executeRequest
|
||||||
import im.vector.matrix.android.internal.session.cleanup.CleanupSession
|
import im.vector.matrix.android.internal.session.cleanup.CleanupSession
|
||||||
|
import im.vector.matrix.android.internal.session.identity.IdentityDisconnectTask
|
||||||
import im.vector.matrix.android.internal.task.Task
|
import im.vector.matrix.android.internal.task.Task
|
||||||
import org.greenrobot.eventbus.EventBus
|
import org.greenrobot.eventbus.EventBus
|
||||||
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal interface DeactivateAccountTask : Task<DeactivateAccountTask.Params, Unit> {
|
internal interface DeactivateAccountTask : Task<DeactivateAccountTask.Params, Unit> {
|
||||||
|
@ -34,6 +36,7 @@ internal class DefaultDeactivateAccountTask @Inject constructor(
|
||||||
private val accountAPI: AccountAPI,
|
private val accountAPI: AccountAPI,
|
||||||
private val eventBus: EventBus,
|
private val eventBus: EventBus,
|
||||||
@UserId private val userId: String,
|
@UserId private val userId: String,
|
||||||
|
private val identityDisconnectTask: IdentityDisconnectTask,
|
||||||
private val cleanupSession: CleanupSession
|
private val cleanupSession: CleanupSession
|
||||||
) : DeactivateAccountTask {
|
) : DeactivateAccountTask {
|
||||||
|
|
||||||
|
@ -44,6 +47,10 @@ internal class DefaultDeactivateAccountTask @Inject constructor(
|
||||||
apiCall = accountAPI.deactivate(deactivateAccountParams)
|
apiCall = accountAPI.deactivate(deactivateAccountParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Logout from identity server if any, ignoring errors
|
||||||
|
runCatching { identityDisconnectTask.execute(Unit) }
|
||||||
|
.onFailure { Timber.w(it, "Unable to disconnect identity server") }
|
||||||
|
|
||||||
cleanupSession.handle()
|
cleanupSession.handle()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue