forked from GitHub-Mirror/riotX-android
Compare commits
136 Commits
feature/fi
...
v0.6.1
Author | SHA1 | Date | |
---|---|---|---|
ec0974f72c | |||
1e963bc0dc | |||
cc832633a5 | |||
eadea9016b | |||
6422d946c9 | |||
5cc3dc00e3 | |||
5a2a9f908a | |||
c1f2e9f171 | |||
620ba279d8 | |||
3fcfa33364 | |||
546da0f173 | |||
001711d5a3 | |||
8e1a964679 | |||
b25a130db1 | |||
8a9e6497e8 | |||
47e3797b7e | |||
5cbc90e06a | |||
e04bf31faa | |||
d25cf79b07 | |||
faa8e6bbb2 | |||
d3d4deb884 | |||
f6b8e0c479 | |||
2a726f54a2 | |||
1197d4021d | |||
03f8120b7d | |||
acd7a709de | |||
5651ea515b | |||
9794b3a49d | |||
b3e1c3969d | |||
f24bed17a2 | |||
a993a30203 | |||
91cc78d2ad | |||
562acc9702 | |||
dfab88ed95 | |||
36866dd24e | |||
c728834273 | |||
f5020d0f63 | |||
7da9cafcc2 | |||
6f09eea248 | |||
468bd5bcc9 | |||
3169093c50 | |||
d60d766354 | |||
0ffb5e627e | |||
b4a13f9504 | |||
ffa8b7e73a | |||
528958b3de | |||
3ffe2f7d40 | |||
bf42b73713 | |||
ed93f4a6c1 | |||
b3d649a4d9 | |||
3739e50d46 | |||
9bf484cf1e | |||
6c2faff1f0 | |||
07fca0922b | |||
282de21708 | |||
ba9d119892 | |||
4453f0ced9 | |||
77168bfd6a | |||
25e9a179d2 | |||
73ec0f5a83 | |||
993fa74252 | |||
38fc4984fe | |||
695d8cce00 | |||
07e99901e1 | |||
20f53e9a58 | |||
ced72aff4f | |||
fdaaca49c2 | |||
3485f023b0 | |||
384dd100e9 | |||
1ba8a58219 | |||
c8010561fc | |||
1f127335bc | |||
138a210a73 | |||
ca6bcde82d | |||
6bda437f5d | |||
3e6b65e174 | |||
137dcab734 | |||
b22b8fba02 | |||
3ccdf4a244 | |||
5fbd271b1c | |||
db8ea0f5e8 | |||
a47a3ead1f | |||
05b2092ffc | |||
6249a59203 | |||
618e9a4f52 | |||
f2c8d4ad02 | |||
be524472ec | |||
1b82a1a24d | |||
cf0b331c3b | |||
2a92a3dc80 | |||
012840abba | |||
a5975a099e | |||
38da4b9ee5 | |||
242e60fcaa | |||
a23be05cbf | |||
ed39b02924 | |||
fe931b5361 | |||
90d9cd0587 | |||
9cedb18921 | |||
e89ba7b87b | |||
902657c22a | |||
eec2abf164 | |||
6879cc8ca8 | |||
fd6bbbd3b5 | |||
0ff0b014a9 | |||
a89f0ddd1d | |||
fdc9e84dd5 | |||
58f878fca9 | |||
88095e4bd9 | |||
47d22a3d5e | |||
28e82cb8ea | |||
35817245cb | |||
75266f42bb | |||
95c4c9ce56 | |||
ce5570105d | |||
188a9aebfa | |||
c95223f5d2 | |||
ef0362ba9c | |||
ea242f6737 | |||
cbc08d834b | |||
0ab6b33fb6 | |||
1b394527b6 | |||
a8f1388721 | |||
166be4e289 | |||
b49ccefe63 | |||
825760d17e | |||
b5af62c3ea | |||
a51d96bf00 | |||
7e142d201d | |||
2be6058971 | |||
49d73f360e | |||
9cd69d1e33 | |||
456908c851 | |||
215324a03e | |||
02e342849f | |||
df6080b1da |
@ -1,6 +1,7 @@
|
|||||||
# Use Docker file from https://hub.docker.com/r/runmymind/docker-android-sdk
|
# Use Docker file from https://hub.docker.com/r/runmymind/docker-android-sdk
|
||||||
# Last docker plugin version can be found here:
|
# Last docker plugin version can be found here:
|
||||||
# https://github.com/buildkite-plugins/docker-buildkite-plugin/releases
|
# https://github.com/buildkite-plugins/docker-buildkite-plugin/releases
|
||||||
|
# We propagate the environment to the container (sse https://github.com/buildkite-plugins/docker-buildkite-plugin#propagate-environment-optional-boolean)
|
||||||
|
|
||||||
# Build debug version of the RiotX application, from the develop branch and the features branches
|
# Build debug version of the RiotX application, from the develop branch and the features branches
|
||||||
|
|
||||||
@ -18,6 +19,7 @@ steps:
|
|||||||
plugins:
|
plugins:
|
||||||
- docker#v3.1.0:
|
- docker#v3.1.0:
|
||||||
image: "runmymind/docker-android-sdk"
|
image: "runmymind/docker-android-sdk"
|
||||||
|
propagate-environment: true
|
||||||
|
|
||||||
- label: "Assemble FDroid Debug version"
|
- label: "Assemble FDroid Debug version"
|
||||||
agents:
|
agents:
|
||||||
@ -32,6 +34,7 @@ steps:
|
|||||||
plugins:
|
plugins:
|
||||||
- docker#v3.1.0:
|
- docker#v3.1.0:
|
||||||
image: "runmymind/docker-android-sdk"
|
image: "runmymind/docker-android-sdk"
|
||||||
|
propagate-environment: true
|
||||||
|
|
||||||
- label: "Build Google Play unsigned APK"
|
- label: "Build Google Play unsigned APK"
|
||||||
agents:
|
agents:
|
||||||
@ -46,6 +49,7 @@ steps:
|
|||||||
plugins:
|
plugins:
|
||||||
- docker#v3.1.0:
|
- docker#v3.1.0:
|
||||||
image: "runmymind/docker-android-sdk"
|
image: "runmymind/docker-android-sdk"
|
||||||
|
propagate-environment: true
|
||||||
|
|
||||||
# Code quality
|
# Code quality
|
||||||
|
|
||||||
|
70
CHANGES.md
70
CHANGES.md
@ -1,25 +1,73 @@
|
|||||||
Changes in RiotX 0.4.0 (2019-XX-XX)
|
Changes in RiotX 0.6.1 (2019-09-24)
|
||||||
|
===================================================
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Fix crash: MergedHeaderItem was missing dimensionConverter
|
||||||
|
|
||||||
|
Changes in RiotX 0.6.0 (2019-09-24)
|
||||||
|
===================================================
|
||||||
|
|
||||||
|
Features:
|
||||||
|
- Save draft of a message when exiting a room with non empty composer (#329)
|
||||||
|
|
||||||
|
Improvements:
|
||||||
|
- Add unread indent on room list (#485)
|
||||||
|
- Message Editing: Update notifications (#128)
|
||||||
|
- Remove any notification of a redacted event (#563)
|
||||||
|
|
||||||
|
Other changes:
|
||||||
|
- Fix a few accessibility issues
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Fix characters erased from the Search field when the result are coming (#545)
|
||||||
|
- "No connection" banner was displayed by mistake
|
||||||
|
- Leaving community (from another client) has no effect on RiotX (#497)
|
||||||
|
- Push rules was not retrieved after a clear cache
|
||||||
|
- m.notice messages trigger push notifications (#238)
|
||||||
|
- Embiggen messages with multiple emojis also for edited messages (#458)
|
||||||
|
|
||||||
|
Build:
|
||||||
|
- Fix (again) issue with bad versionCode generated by Buildkite (#553)
|
||||||
|
|
||||||
|
Changes in RiotX 0.5.0 (2019-09-17)
|
||||||
|
===================================================
|
||||||
|
|
||||||
|
Features:
|
||||||
|
- Implementation of login to homeserver with SSO (#557)
|
||||||
|
- Handle M_CONSENT_NOT_GIVEN error (#64)
|
||||||
|
- Auto configure homeserver and identity server URLs of LoginActivity with a magic link
|
||||||
|
|
||||||
|
Improvements:
|
||||||
|
- Reduce default release build log level, and lab option to enable more logs.
|
||||||
|
- Display a no network indicator when there is no network (#559)
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Fix crash due to missing informationData (#535)
|
||||||
|
- Progress in initial sync dialog is decreasing for a step and should not (#532)
|
||||||
|
- Fix rendering issue of accepted third party invitation event
|
||||||
|
- All current notifications were dismissed by mistake when the app is launched from the launcher
|
||||||
|
|
||||||
|
Build:
|
||||||
|
- Fix issue with version name (#533)
|
||||||
|
- Fix issue with bad versionCode generated by Buildkite (#553)
|
||||||
|
|
||||||
|
Changes in RiotX 0.4.0 (2019-08-30)
|
||||||
===================================================
|
===================================================
|
||||||
|
|
||||||
Features:
|
Features:
|
||||||
- Display read receipts in timeline (#81)
|
- Display read receipts in timeline (#81)
|
||||||
|
|
||||||
Improvements:
|
Improvements:
|
||||||
-
|
- Reactions: Reinstate the ability to react with non-unicode keys (#307)
|
||||||
|
|
||||||
Other changes:
|
|
||||||
-
|
|
||||||
|
|
||||||
Bugfix:
|
Bugfix:
|
||||||
- Fix text diff linebreak display (#441)
|
- Fix text diff linebreak display (#441)
|
||||||
- Date change message repeats for each redaction until a normal message (#358)
|
- Date change message repeats for each redaction until a normal message (#358)
|
||||||
- Slide-in reply icon is distorted (#423)
|
- Slide-in reply icon is distorted (#423)
|
||||||
|
- Regression / e2e replies not encrypted
|
||||||
Translations:
|
- Some video won't play
|
||||||
-
|
- Privacy: remove log of notifiable event (#519)
|
||||||
|
- Fix crash with EmojiCompat (#530)
|
||||||
Build:
|
|
||||||
-
|
|
||||||
|
|
||||||
Changes in RiotX 0.3.0 (2019-08-08)
|
Changes in RiotX 0.3.0 (2019-08-08)
|
||||||
===================================================
|
===================================================
|
||||||
|
@ -20,6 +20,7 @@ import im.vector.matrix.android.api.session.room.Room
|
|||||||
import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary
|
import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary
|
||||||
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.model.RoomSummary
|
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||||
|
import im.vector.matrix.android.api.session.room.send.UserDraft
|
||||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||||
import io.reactivex.Observable
|
import io.reactivex.Observable
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
@ -54,6 +55,10 @@ class RxRoom(private val room: Room) {
|
|||||||
return room.getEventReadReceiptsLive(eventId).asObservable()
|
return room.getEventReadReceiptsLive(eventId).asObservable()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun liveDrafts(): Observable<List<UserDraft>> {
|
||||||
|
return room.getDraftsLive().asObservable()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Room.rx(): RxRoom {
|
fun Room.rx(): RxRoom {
|
||||||
|
@ -139,6 +139,9 @@ dependencies {
|
|||||||
implementation 'com.jakewharton.timber:timber:4.7.1'
|
implementation 'com.jakewharton.timber:timber:4.7.1'
|
||||||
implementation 'com.facebook.stetho:stetho-okhttp3:1.5.0'
|
implementation 'com.facebook.stetho:stetho-okhttp3:1.5.0'
|
||||||
|
|
||||||
|
// Bus
|
||||||
|
implementation 'org.greenrobot:eventbus:3.1.1'
|
||||||
|
|
||||||
debugImplementation 'com.airbnb.okreplay:okreplay:1.4.0'
|
debugImplementation 'com.airbnb.okreplay:okreplay:1.4.0'
|
||||||
releaseImplementation 'com.airbnb.okreplay:noop:1.4.0'
|
releaseImplementation 'com.airbnb.okreplay:noop:1.4.0'
|
||||||
androidTestImplementation 'com.airbnb.okreplay:espresso:1.4.0'
|
androidTestImplementation 'com.airbnb.okreplay:espresso:1.4.0'
|
||||||
|
@ -21,7 +21,7 @@ import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
|||||||
import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStore
|
import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStore
|
||||||
import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStoreModule
|
import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStoreModule
|
||||||
import io.realm.RealmConfiguration
|
import io.realm.RealmConfiguration
|
||||||
import java.util.*
|
import kotlin.random.Random
|
||||||
|
|
||||||
internal class CryptoStoreHelper {
|
internal class CryptoStoreHelper {
|
||||||
|
|
||||||
@ -35,7 +35,7 @@ internal class CryptoStoreHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun createCredential() = Credentials(
|
fun createCredential() = Credentials(
|
||||||
userId = "userId_" + Random().nextInt(),
|
userId = "userId_" + Random.nextInt(),
|
||||||
homeServer = "http://matrix.org",
|
homeServer = "http://matrix.org",
|
||||||
accessToken = "access_token",
|
accessToken = "access_token",
|
||||||
refreshToken = null,
|
refreshToken = null,
|
||||||
|
@ -17,16 +17,23 @@
|
|||||||
package im.vector.matrix.android.api.auth
|
package im.vector.matrix.android.api.auth
|
||||||
|
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
|
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.SessionParams
|
import im.vector.matrix.android.api.auth.data.SessionParams
|
||||||
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.internal.auth.data.LoginFlowResponse
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This interface defines methods to authenticate to a matrix server.
|
* This interface defines methods to authenticate to a matrix server.
|
||||||
*/
|
*/
|
||||||
interface Authenticator {
|
interface Authenticator {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request the supported login flows for this homeserver
|
||||||
|
*/
|
||||||
|
fun getLoginFlow(homeServerConnectionConfig: HomeServerConnectionConfig, callback: MatrixCallback<LoginFlowResponse>): Cancelable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param homeServerConnectionConfig this param is used to configure the Homeserver
|
* @param homeServerConnectionConfig this param is used to configure the Homeserver
|
||||||
* @param login the login field
|
* @param login the login field
|
||||||
@ -56,4 +63,9 @@ interface Authenticator {
|
|||||||
* @return the associated session if any, or null
|
* @return the associated session if any, or null
|
||||||
*/
|
*/
|
||||||
fun getSession(sessionParams: SessionParams): Session?
|
fun getSession(sessionParams: SessionParams): Session?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a session after a SSO successful login
|
||||||
|
*/
|
||||||
|
fun createSessionFromSso(credentials: Credentials, homeServerConnectionConfig: HomeServerConnectionConfig): Session
|
||||||
}
|
}
|
@ -17,7 +17,6 @@
|
|||||||
package im.vector.matrix.android.api.comparators
|
package im.vector.matrix.android.api.comparators
|
||||||
|
|
||||||
import im.vector.matrix.android.api.interfaces.DatedObject
|
import im.vector.matrix.android.api.interfaces.DatedObject
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
object DatedObjectComparators {
|
object DatedObjectComparators {
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ package im.vector.matrix.android.api.extensions
|
|||||||
import im.vector.matrix.android.api.comparators.DatedObjectComparators
|
import im.vector.matrix.android.api.comparators.DatedObjectComparators
|
||||||
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
|
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
|
||||||
import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo
|
import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo
|
||||||
import java.util.*
|
import java.util.Collections
|
||||||
|
|
||||||
/* ==========================================================================================
|
/* ==========================================================================================
|
||||||
* MXDeviceInfo
|
* MXDeviceInfo
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* 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.api.failure
|
||||||
|
|
||||||
|
// This data class will be sent to the bus
|
||||||
|
data class ConsentNotGivenError(
|
||||||
|
val consentUri: String
|
||||||
|
)
|
@ -31,6 +31,7 @@ import java.io.IOException
|
|||||||
*/
|
*/
|
||||||
sealed class Failure(cause: Throwable? = null) : Throwable(cause = cause) {
|
sealed class Failure(cause: Throwable? = null) : Throwable(cause = cause) {
|
||||||
data class Unknown(val throwable: Throwable? = null) : Failure(throwable)
|
data class Unknown(val throwable: Throwable? = null) : Failure(throwable)
|
||||||
|
data class Cancelled(val throwable: Throwable? = null) : Failure(throwable)
|
||||||
data class NetworkConnection(val ioException: IOException? = null) : Failure(ioException)
|
data class NetworkConnection(val ioException: IOException? = null) : Failure(ioException)
|
||||||
data class ServerError(val error: MatrixError, val httpCode: Int) : Failure(RuntimeException(error.toString()))
|
data class ServerError(val error: MatrixError, val httpCode: Int) : Failure(RuntimeException(error.toString()))
|
||||||
// When server send an error, but it cannot be interpreted as a MatrixError
|
// When server send an error, but it cannot be interpreted as a MatrixError
|
||||||
|
@ -19,77 +19,92 @@ import im.vector.matrix.android.api.pushrules.rest.PushRule
|
|||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
|
|
||||||
class Action(val type: Type) {
|
sealed class Action {
|
||||||
|
object Notify : Action()
|
||||||
enum class Type(val value: String) {
|
object DoNotNotify : Action()
|
||||||
NOTIFY("notify"),
|
data class Sound(val sound: String) : Action()
|
||||||
DONT_NOTIFY("dont_notify"),
|
data class Highlight(val highlight: Boolean) : Action()
|
||||||
COALESCE("coalesce"),
|
}
|
||||||
SET_TWEAK("set_tweak");
|
|
||||||
|
|
||||||
companion object {
|
private const val ACTION_NOTIFY = "notify"
|
||||||
|
private const val ACTION_DONT_NOTIFY = "dont_notify"
|
||||||
fun safeValueOf(value: String): Type? {
|
private const val ACTION_COALESCE = "coalesce"
|
||||||
try {
|
|
||||||
return valueOf(value)
|
// Ref: https://matrix.org/docs/spec/client_server/latest#tweaks
|
||||||
} catch (e: IllegalArgumentException) {
|
private const val ACTION_OBJECT_SET_TWEAK_KEY = "set_tweak"
|
||||||
return null
|
|
||||||
}
|
private const val ACTION_OBJECT_SET_TWEAK_VALUE_SOUND = "sound"
|
||||||
}
|
private const val ACTION_OBJECT_SET_TWEAK_VALUE_HIGHLIGHT = "highlight"
|
||||||
}
|
|
||||||
}
|
private const val ACTION_OBJECT_VALUE_KEY = "value"
|
||||||
|
private const val ACTION_OBJECT_VALUE_VALUE_DEFAULT = "default"
|
||||||
var tweak_action: String? = null
|
|
||||||
var stringValue: String? = null
|
/**
|
||||||
var boolValue: Boolean? = null
|
* Ref: https://matrix.org/docs/spec/client_server/latest#actions
|
||||||
|
*
|
||||||
companion object {
|
* Convert
|
||||||
fun mapFrom(pushRule: PushRule): List<Action>? {
|
* <pre>
|
||||||
val actions = ArrayList<Action>()
|
* "actions": [
|
||||||
pushRule.actions.forEach { actionStrOrObj ->
|
* "notify",
|
||||||
if (actionStrOrObj is String) {
|
* {
|
||||||
when (actionStrOrObj) {
|
* "set_tweak": "sound",
|
||||||
Action.Type.NOTIFY.value -> Action(Action.Type.NOTIFY)
|
* "value": "default"
|
||||||
Action.Type.DONT_NOTIFY.value -> Action(Action.Type.DONT_NOTIFY)
|
* },
|
||||||
else -> {
|
* {
|
||||||
Timber.w("Unsupported action type ${actionStrOrObj}")
|
* "set_tweak": "highlight"
|
||||||
null
|
* }
|
||||||
}
|
* ]
|
||||||
}?.let {
|
*
|
||||||
actions.add(it)
|
* To
|
||||||
}
|
* [
|
||||||
} else if (actionStrOrObj is Map<*, *>) {
|
* Action.Notify,
|
||||||
val tweakAction = actionStrOrObj["set_tweak"] as? String
|
* Action.Sound("default"),
|
||||||
when (tweakAction) {
|
* Action.Highlight(true)
|
||||||
"sound" -> {
|
* ]
|
||||||
(actionStrOrObj["value"] as? String)?.let { stringValue ->
|
*
|
||||||
Action(Action.Type.SET_TWEAK).also {
|
* </pre>
|
||||||
it.tweak_action = "sound"
|
*/
|
||||||
it.stringValue = stringValue
|
fun PushRule.getActions(): List<Action> {
|
||||||
actions.add(it)
|
val result = ArrayList<Action>()
|
||||||
}
|
|
||||||
}
|
actions.forEach { actionStrOrObj ->
|
||||||
}
|
when (actionStrOrObj) {
|
||||||
"highlight" -> {
|
ACTION_NOTIFY -> Action.Notify
|
||||||
(actionStrOrObj["value"] as? Boolean)?.let { boolValue ->
|
ACTION_DONT_NOTIFY -> Action.DoNotNotify
|
||||||
Action(Action.Type.SET_TWEAK).also {
|
is Map<*, *> -> {
|
||||||
it.tweak_action = "highlight"
|
when (actionStrOrObj[ACTION_OBJECT_SET_TWEAK_KEY]) {
|
||||||
it.boolValue = boolValue
|
ACTION_OBJECT_SET_TWEAK_VALUE_SOUND -> {
|
||||||
actions.add(it)
|
(actionStrOrObj[ACTION_OBJECT_VALUE_KEY] as? String)?.let { stringValue ->
|
||||||
}
|
Action.Sound(stringValue)
|
||||||
}
|
}
|
||||||
}
|
// When the value is not there, default sound (not specified by the spec)
|
||||||
else -> {
|
?: Action.Sound(ACTION_OBJECT_VALUE_VALUE_DEFAULT)
|
||||||
Timber.w("Unsupported action type ${actionStrOrObj}")
|
|
||||||
}
|
}
|
||||||
}
|
ACTION_OBJECT_SET_TWEAK_VALUE_HIGHLIGHT -> {
|
||||||
} else {
|
(actionStrOrObj[ACTION_OBJECT_VALUE_KEY] as? Boolean)?.let { boolValue ->
|
||||||
Timber.w("Unsupported action type ${actionStrOrObj}")
|
Action.Highlight(boolValue)
|
||||||
return null
|
}
|
||||||
}
|
// When the value is not there, default is true, says the spec
|
||||||
}
|
?: Action.Highlight(true)
|
||||||
return if (actions.isEmpty()) null else actions
|
}
|
||||||
}
|
else -> {
|
||||||
}
|
Timber.w("Unsupported set_tweak value ${actionStrOrObj[ACTION_OBJECT_SET_TWEAK_KEY]}")
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
Timber.w("Unsupported action type $actionStrOrObj")
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}?.let {
|
||||||
|
result.add(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,14 +25,14 @@ interface PushRuleService {
|
|||||||
/**
|
/**
|
||||||
* Fetch the push rules from the server
|
* Fetch the push rules from the server
|
||||||
*/
|
*/
|
||||||
fun fetchPushRules(scope: String = "global")
|
fun fetchPushRules(scope: String = RuleScope.GLOBAL)
|
||||||
|
|
||||||
//TODO get push rule set
|
//TODO get push rule set
|
||||||
fun getPushRules(scope: String = "global"): List<PushRule>
|
fun getPushRules(scope: String = RuleScope.GLOBAL): List<PushRule>
|
||||||
|
|
||||||
//TODO update rule
|
//TODO update rule
|
||||||
|
|
||||||
fun updatePushRuleEnableStatus(kind: String, pushRule: PushRule, enabled: Boolean, callback: MatrixCallback<Unit>): Cancelable
|
fun updatePushRuleEnableStatus(kind: RuleKind, pushRule: PushRule, enabled: Boolean, callback: MatrixCallback<Unit>): Cancelable
|
||||||
|
|
||||||
fun addPushRuleListener(listener: PushRuleListener)
|
fun addPushRuleListener(listener: PushRuleListener)
|
||||||
|
|
||||||
@ -43,6 +43,7 @@ interface PushRuleService {
|
|||||||
interface PushRuleListener {
|
interface PushRuleListener {
|
||||||
fun onMatchRule(event: Event, actions: List<Action>)
|
fun onMatchRule(event: Event, actions: List<Action>)
|
||||||
fun onRoomLeft(roomId: String)
|
fun onRoomLeft(roomId: String)
|
||||||
|
fun onEventRedacted(redactedEventId: String)
|
||||||
fun batchFinish()
|
fun batchFinish()
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -18,18 +18,17 @@ package im.vector.matrix.android.api.pushrules
|
|||||||
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.RoomService
|
import im.vector.matrix.android.api.session.room.RoomService
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.regex.Pattern
|
|
||||||
|
|
||||||
private val regex = Pattern.compile("^(==|<=|>=|<|>)?(\\d*)$")
|
private val regex = Regex("^(==|<=|>=|<|>)?(\\d*)$")
|
||||||
|
|
||||||
class RoomMemberCountCondition(val `is`: String) : Condition(Kind.room_member_count) {
|
class RoomMemberCountCondition(val iz: String) : Condition(Kind.room_member_count) {
|
||||||
|
|
||||||
override fun isSatisfied(conditionResolver: ConditionResolver): Boolean {
|
override fun isSatisfied(conditionResolver: ConditionResolver): Boolean {
|
||||||
return conditionResolver.resolveRoomMemberCountCondition(this)
|
return conditionResolver.resolveRoomMemberCountCondition(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun technicalDescription(): String {
|
override fun technicalDescription(): String {
|
||||||
return "Room member count is $`is`"
|
return "Room member count is $iz"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isSatisfied(event: Event, session: RoomService?): Boolean {
|
fun isSatisfied(event: Event, session: RoomService?): Boolean {
|
||||||
@ -56,12 +55,9 @@ class RoomMemberCountCondition(val `is`: String) : Condition(Kind.room_member_co
|
|||||||
*/
|
*/
|
||||||
private fun parseIsField(): Pair<String?, Int>? {
|
private fun parseIsField(): Pair<String?, Int>? {
|
||||||
try {
|
try {
|
||||||
val match = regex.matcher(`is`)
|
val match = regex.find(iz) ?: return null
|
||||||
if (match.find()) {
|
val (prefix, count) = match.destructured
|
||||||
val prefix = match.group(1)
|
return prefix to count.toInt()
|
||||||
val count = match.group(2).toInt()
|
|
||||||
return prefix to count
|
|
||||||
}
|
|
||||||
} catch (t: Throwable) {
|
} catch (t: Throwable) {
|
||||||
Timber.d(t)
|
Timber.d(t)
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ object RuleIds {
|
|||||||
|
|
||||||
// Default Underride Rules
|
// Default Underride Rules
|
||||||
const val RULE_ID_CALL = ".m.rule.call"
|
const val RULE_ID_CALL = ".m.rule.call"
|
||||||
const val RULE_ID_one_to_one_encrypted_room = ".m.rule.encrypted_room_one_to_one"
|
const val RULE_ID_ONE_TO_ONE_ENCRYPTED_ROOM = ".m.rule.encrypted_room_one_to_one"
|
||||||
const val RULE_ID_ONE_TO_ONE_ROOM = ".m.rule.room_one_to_one"
|
const val RULE_ID_ONE_TO_ONE_ROOM = ".m.rule.room_one_to_one"
|
||||||
const val RULE_ID_ALL_OTHER_MESSAGES_ROOMS = ".m.rule.message"
|
const val RULE_ID_ALL_OTHER_MESSAGES_ROOMS = ".m.rule.message"
|
||||||
const val RULE_ID_ENCRYPTED = ".m.rule.encrypted"
|
const val RULE_ID_ENCRYPTED = ".m.rule.encrypted"
|
||||||
|
@ -15,12 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
package im.vector.matrix.android.api.pushrules
|
package im.vector.matrix.android.api.pushrules
|
||||||
|
|
||||||
|
object RuleScope {
|
||||||
enum class RulesetKey(val value: String) {
|
const val GLOBAL = "global"
|
||||||
CONTENT("content"),
|
}
|
||||||
OVERRIDE("override"),
|
|
||||||
ROOM("room"),
|
|
||||||
SENDER("sender"),
|
|
||||||
UNDERRIDE("underride"),
|
|
||||||
UNKNOWN("")
|
|
||||||
}
|
|
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* 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.api.pushrules
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ref: https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-pushrules
|
||||||
|
*/
|
||||||
|
enum class RuleSetKey(val value: String) {
|
||||||
|
CONTENT("content"),
|
||||||
|
OVERRIDE("override"),
|
||||||
|
ROOM("room"),
|
||||||
|
SENDER("sender"),
|
||||||
|
UNDERRIDE("underride")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ref: https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-pushrules-scope-kind-ruleid
|
||||||
|
*/
|
||||||
|
typealias RuleKind = RuleSetKey
|
@ -71,7 +71,7 @@ data class PushCondition(
|
|||||||
this.key?.let { SenderNotificationPermissionCondition(it) }
|
this.key?.let { SenderNotificationPermissionCondition(it) }
|
||||||
}
|
}
|
||||||
Condition.Kind.UNRECOGNIZE -> {
|
Condition.Kind.UNRECOGNIZE -> {
|
||||||
Timber.e("Unknwon kind $kind")
|
Timber.e("Unknown kind $kind")
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,10 +20,10 @@ import androidx.lifecycle.LiveData
|
|||||||
|
|
||||||
interface InitialSyncProgressService {
|
interface InitialSyncProgressService {
|
||||||
|
|
||||||
fun getLiveStatus() : LiveData<Status?>
|
fun getInitialSyncProgressStatus() : LiveData<Status?>
|
||||||
|
|
||||||
data class Status(
|
data class Status(
|
||||||
@StringRes val statusText: Int?,
|
@StringRes val statusText: Int,
|
||||||
val percentProgress: Int = 0
|
val percentProgress: Int = 0
|
||||||
)
|
)
|
||||||
}
|
}
|
@ -29,6 +29,7 @@ import im.vector.matrix.android.api.session.group.GroupService
|
|||||||
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
|
||||||
import im.vector.matrix.android.api.session.room.RoomService
|
import im.vector.matrix.android.api.session.room.RoomService
|
||||||
|
import im.vector.matrix.android.api.session.securestorage.SecureStorageService
|
||||||
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
|
||||||
@ -50,7 +51,8 @@ interface Session :
|
|||||||
FileService,
|
FileService,
|
||||||
PushRuleService,
|
PushRuleService,
|
||||||
PushersService,
|
PushersService,
|
||||||
InitialSyncProgressService {
|
InitialSyncProgressService,
|
||||||
|
SecureStorageService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The params associated to the session
|
* The params associated to the session
|
||||||
@ -87,7 +89,7 @@ interface Session :
|
|||||||
/**
|
/**
|
||||||
* This method start the sync thread.
|
* This method start the sync thread.
|
||||||
*/
|
*/
|
||||||
fun startSync(fromForeground : Boolean)
|
fun startSync(fromForeground: Boolean)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method stop the sync thread.
|
* This method stop the sync thread.
|
||||||
|
@ -109,8 +109,6 @@ interface CryptoService {
|
|||||||
|
|
||||||
fun downloadKeys(userIds: List<String>, forceDownload: Boolean, callback: MatrixCallback<MXUsersDevicesMap<MXDeviceInfo>>)
|
fun downloadKeys(userIds: List<String>, forceDownload: Boolean, callback: MatrixCallback<MXUsersDevicesMap<MXDeviceInfo>>)
|
||||||
|
|
||||||
fun clearCryptoCache(callback: MatrixCallback<Unit>)
|
|
||||||
|
|
||||||
fun addNewSessionListener(newSessionListener: NewSessionListener)
|
fun addNewSessionListener(newSessionListener: NewSessionListener)
|
||||||
|
|
||||||
fun removeSessionListener(listener: NewSessionListener)
|
fun removeSessionListener(listener: NewSessionListener)
|
||||||
|
@ -16,12 +16,15 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.api.session.group.model
|
package im.vector.matrix.android.api.session.group.model
|
||||||
|
|
||||||
|
import im.vector.matrix.android.api.session.room.model.Membership
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class holds some data of a group.
|
* This class holds some data of a group.
|
||||||
* It can be retrieved through [im.vector.matrix.android.api.session.group.GroupService]
|
* It can be retrieved through [im.vector.matrix.android.api.session.group.GroupService]
|
||||||
*/
|
*/
|
||||||
data class GroupSummary(
|
data class GroupSummary(
|
||||||
val groupId: String,
|
val groupId: String,
|
||||||
|
val membership: Membership,
|
||||||
val displayName: String = "",
|
val displayName: String = "",
|
||||||
val shortDescription: String = "",
|
val shortDescription: String = "",
|
||||||
val avatarUrl: String = "",
|
val avatarUrl: String = "",
|
||||||
|
@ -16,9 +16,6 @@
|
|||||||
package im.vector.matrix.android.api.session.pushers
|
package im.vector.matrix.android.api.session.pushers
|
||||||
|
|
||||||
data class Pusher(
|
data class Pusher(
|
||||||
|
|
||||||
val userId: String,
|
|
||||||
|
|
||||||
val pushKey: String,
|
val pushKey: String,
|
||||||
val kind: String,
|
val kind: String,
|
||||||
val appId: String,
|
val appId: String,
|
||||||
|
@ -17,7 +17,7 @@ package im.vector.matrix.android.api.session.pushers
|
|||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import java.util.*
|
import java.util.UUID
|
||||||
|
|
||||||
|
|
||||||
interface PushersService {
|
interface PushersService {
|
||||||
|
@ -22,6 +22,7 @@ import im.vector.matrix.android.api.session.room.members.MembershipService
|
|||||||
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.relation.RelationService
|
import im.vector.matrix.android.api.session.room.model.relation.RelationService
|
||||||
import im.vector.matrix.android.api.session.room.read.ReadService
|
import im.vector.matrix.android.api.session.room.read.ReadService
|
||||||
|
import im.vector.matrix.android.api.session.room.send.DraftService
|
||||||
import im.vector.matrix.android.api.session.room.send.SendService
|
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
|
||||||
@ -32,6 +33,7 @@ import im.vector.matrix.android.api.session.room.timeline.TimelineService
|
|||||||
interface Room :
|
interface Room :
|
||||||
TimelineService,
|
TimelineService,
|
||||||
SendService,
|
SendService,
|
||||||
|
DraftService,
|
||||||
ReadService,
|
ReadService,
|
||||||
MembershipService,
|
MembershipService,
|
||||||
StateService,
|
StateService,
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package im.vector.matrix.android.api.session.room.model
|
package im.vector.matrix.android.api.session.room.model
|
||||||
|
|
||||||
import im.vector.matrix.android.api.session.room.model.tag.RoomTag
|
import im.vector.matrix.android.api.session.room.model.tag.RoomTag
|
||||||
|
import im.vector.matrix.android.api.session.room.send.UserDraft
|
||||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -29,13 +30,15 @@ data class RoomSummary(
|
|||||||
val topic: String = "",
|
val topic: String = "",
|
||||||
val avatarUrl: String = "",
|
val avatarUrl: String = "",
|
||||||
val isDirect: Boolean = false,
|
val isDirect: Boolean = false,
|
||||||
val latestEvent: TimelineEvent? = null,
|
val latestPreviewableEvent: TimelineEvent? = null,
|
||||||
val otherMemberIds: List<String> = emptyList(),
|
val otherMemberIds: List<String> = emptyList(),
|
||||||
val notificationCount: Int = 0,
|
val notificationCount: Int = 0,
|
||||||
val highlightCount: Int = 0,
|
val highlightCount: Int = 0,
|
||||||
|
val hasUnreadMessages: Boolean = false,
|
||||||
val tags: List<RoomTag> = emptyList(),
|
val tags: List<RoomTag> = emptyList(),
|
||||||
val membership: Membership = Membership.NONE,
|
val membership: Membership = Membership.NONE,
|
||||||
val versioningState: VersioningState = VersioningState.NONE
|
val versioningState: VersioningState = VersioningState.NONE,
|
||||||
|
val userDrafts: List<UserDraft> = emptyList()
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val isVersioned: Boolean
|
val isVersioned: Boolean
|
||||||
|
@ -20,7 +20,6 @@ import android.util.Patterns
|
|||||||
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.MatrixPatterns.isUserId
|
import im.vector.matrix.android.api.MatrixPatterns.isUserId
|
||||||
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.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
|
||||||
@ -29,7 +28,6 @@ import im.vector.matrix.android.api.session.room.model.PowerLevels
|
|||||||
import im.vector.matrix.android.api.session.room.model.RoomDirectoryVisibility
|
import im.vector.matrix.android.api.session.room.model.RoomDirectoryVisibility
|
||||||
import im.vector.matrix.android.api.session.room.model.RoomHistoryVisibility
|
import im.vector.matrix.android.api.session.room.model.RoomHistoryVisibility
|
||||||
import im.vector.matrix.android.internal.auth.data.ThreePidMedium
|
import im.vector.matrix.android.internal.auth.data.ThreePidMedium
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parameter to create a room, with facilities functions to configure it
|
* Parameter to create a room, with facilities functions to configure it
|
||||||
@ -133,7 +131,7 @@ class CreateRoomParams {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (null == initialStates) {
|
if (null == initialStates) {
|
||||||
initialStates = Arrays.asList<Event>(algoEvent)
|
initialStates = mutableListOf(algoEvent)
|
||||||
} else {
|
} else {
|
||||||
initialStates!!.add(algoEvent)
|
initialStates!!.add(algoEvent)
|
||||||
}
|
}
|
||||||
@ -166,7 +164,7 @@ class CreateRoomParams {
|
|||||||
content = contentMap.toContent())
|
content = contentMap.toContent())
|
||||||
|
|
||||||
if (null == initialStates) {
|
if (null == initialStates) {
|
||||||
initialStates = Arrays.asList<Event>(historyVisibilityEvent)
|
initialStates = mutableListOf(historyVisibilityEvent)
|
||||||
} else {
|
} else {
|
||||||
initialStates!!.add(historyVisibilityEvent)
|
initialStates!!.add(historyVisibilityEvent)
|
||||||
}
|
}
|
||||||
@ -220,7 +218,7 @@ class CreateRoomParams {
|
|||||||
* @param ids the participant ids to add.
|
* @param ids the participant ids to add.
|
||||||
*/
|
*/
|
||||||
fun addParticipantIds(hsConfig: HomeServerConnectionConfig,
|
fun addParticipantIds(hsConfig: HomeServerConnectionConfig,
|
||||||
credentials: Credentials,
|
userId: String,
|
||||||
ids: List<String>) {
|
ids: List<String>) {
|
||||||
for (id in ids) {
|
for (id in ids) {
|
||||||
if (Patterns.EMAIL_ADDRESS.matcher(id).matches() && hsConfig.identityServerUri != null) {
|
if (Patterns.EMAIL_ADDRESS.matcher(id).matches() && hsConfig.identityServerUri != null) {
|
||||||
@ -234,7 +232,7 @@ class CreateRoomParams {
|
|||||||
invite3pids!!.add(pid)
|
invite3pids!!.add(pid)
|
||||||
} else if (isUserId(id)) {
|
} else if (isUserId(id)) {
|
||||||
// do not invite oneself
|
// do not invite oneself
|
||||||
if (credentials.userId != id) {
|
if (userId != id) {
|
||||||
if (null == invitedUserIds) {
|
if (null == invitedUserIds) {
|
||||||
invitedUserIds = ArrayList()
|
invitedUserIds = ArrayList()
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* 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.api.session.room.send
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
|
||||||
|
interface DraftService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save or update a draft to the room
|
||||||
|
*/
|
||||||
|
fun saveDraft(draft: UserDraft)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the last draft, basically just after sending the message
|
||||||
|
*/
|
||||||
|
fun deleteDraft()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the current drafts if any, as a live data
|
||||||
|
* The draft list can contain one draft for {regular, reply, quote} and an arbitrary number of {edit} drafts
|
||||||
|
*/
|
||||||
|
fun getDraftsLive(): LiveData<List<UserDraft>>
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* 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.api.session.room.send
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes a user draft:
|
||||||
|
* REGULAR: draft of a classical message
|
||||||
|
* QUOTE: draft of a message which quotes another message
|
||||||
|
* EDIT: draft of an edition of a message
|
||||||
|
* REPLY: draft of a reply of another message
|
||||||
|
*/
|
||||||
|
sealed class UserDraft(open val text: String) {
|
||||||
|
data class REGULAR(override val text: String) : UserDraft(text)
|
||||||
|
data class QUOTE(val linkedEventId: String, override val text: String) : UserDraft(text)
|
||||||
|
data class EDIT(val linkedEventId: String, override val text: String) : UserDraft(text)
|
||||||
|
data class REPLY(val linkedEventId: String, override val text: String) : UserDraft(text)
|
||||||
|
|
||||||
|
fun isValid(): Boolean {
|
||||||
|
return when (this) {
|
||||||
|
is REGULAR -> text.isNotBlank()
|
||||||
|
else -> true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -18,6 +18,7 @@ package im.vector.matrix.android.api.session.room.timeline
|
|||||||
|
|
||||||
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.toModel
|
import im.vector.matrix.android.api.session.events.model.toModel
|
||||||
import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary
|
import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary
|
||||||
import im.vector.matrix.android.api.session.room.model.ReadReceipt
|
import im.vector.matrix.android.api.session.room.model.ReadReceipt
|
||||||
@ -92,6 +93,13 @@ data class TimelineEvent(
|
|||||||
*/
|
*/
|
||||||
fun TimelineEvent.hasBeenEdited() = annotations?.editSummary != null
|
fun TimelineEvent.hasBeenEdited() = annotations?.editSummary != null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the eventId which was edited by this event if any
|
||||||
|
*/
|
||||||
|
fun TimelineEvent.getEditedEventId(): String? {
|
||||||
|
return root.getClearContent().toModel<MessageContent>()?.relatesTo?.takeIf { it.type == RelationType.REPLACE }?.eventId
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get last MessageContent, after a possible edition
|
* Get last MessageContent, after a possible edition
|
||||||
*/
|
*/
|
||||||
@ -99,6 +107,20 @@ fun TimelineEvent.getLastMessageContent(): MessageContent? = annotations?.editSu
|
|||||||
?: root.getClearContent().toModel()
|
?: root.getClearContent().toModel()
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get last Message body, after a possible edition
|
||||||
|
*/
|
||||||
|
fun TimelineEvent.getLastMessageBody(): String? {
|
||||||
|
val lastMessageContent = getLastMessageContent()
|
||||||
|
|
||||||
|
if (lastMessageContent != null) {
|
||||||
|
return lastMessageContent.newContent?.toModel<MessageContent>()?.body ?: lastMessageContent.body
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fun TimelineEvent.getTextEditableContent(): String? {
|
fun TimelineEvent.getTextEditableContent(): String? {
|
||||||
val originalContent = root.getClearContent().toModel<MessageContent>() ?: return null
|
val originalContent = root.getClearContent().toModel<MessageContent>() ?: return null
|
||||||
val isReply = originalContent.isReply() || root.content.toModel<EncryptedEventContent>()?.relatesTo?.inReplyTo?.eventId != null
|
val isReply = originalContent.isReply() || root.content.toModel<EncryptedEventContent>()?.relatesTo?.inReplyTo?.eventId != null
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* 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.api.session.securestorage
|
||||||
|
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.io.OutputStream
|
||||||
|
|
||||||
|
interface SecureStorageService {
|
||||||
|
|
||||||
|
fun securelyStoreObject(any: Any, keyAlias: String, outputStream: OutputStream)
|
||||||
|
|
||||||
|
fun <T> loadSecureSecret(inputStream: InputStream, keyAlias: String): T?
|
||||||
|
|
||||||
|
}
|
@ -22,4 +22,5 @@ sealed class SyncState {
|
|||||||
object PAUSED : SyncState()
|
object PAUSED : SyncState()
|
||||||
object KILLING : SyncState()
|
object KILLING : SyncState()
|
||||||
object KILLED : SyncState()
|
object KILLED : SyncState()
|
||||||
|
object NO_NETWORK : SyncState()
|
||||||
}
|
}
|
@ -17,10 +17,12 @@
|
|||||||
package im.vector.matrix.android.internal.auth
|
package im.vector.matrix.android.internal.auth
|
||||||
|
|
||||||
import im.vector.matrix.android.api.auth.data.Credentials
|
import im.vector.matrix.android.api.auth.data.Credentials
|
||||||
|
import im.vector.matrix.android.internal.auth.data.LoginFlowResponse
|
||||||
import im.vector.matrix.android.internal.auth.data.PasswordLoginParams
|
import im.vector.matrix.android.internal.auth.data.PasswordLoginParams
|
||||||
import im.vector.matrix.android.internal.network.NetworkConstants
|
import im.vector.matrix.android.internal.network.NetworkConstants
|
||||||
import retrofit2.Call
|
import retrofit2.Call
|
||||||
import retrofit2.http.Body
|
import retrofit2.http.Body
|
||||||
|
import retrofit2.http.GET
|
||||||
import retrofit2.http.Headers
|
import retrofit2.http.Headers
|
||||||
import retrofit2.http.POST
|
import retrofit2.http.POST
|
||||||
|
|
||||||
@ -29,6 +31,13 @@ import retrofit2.http.POST
|
|||||||
*/
|
*/
|
||||||
internal interface AuthAPI {
|
internal interface AuthAPI {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the supported login flow
|
||||||
|
* Ref: https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-login
|
||||||
|
*/
|
||||||
|
@GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "login")
|
||||||
|
fun getLoginFlows(): Call<LoginFlowResponse>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pass params to the server for the current login phase.
|
* Pass params to the server for the current login phase.
|
||||||
* Set all the timeouts to 1 minute
|
* Set all the timeouts to 1 minute
|
||||||
|
@ -23,7 +23,7 @@ import dagger.Provides
|
|||||||
import im.vector.matrix.android.api.auth.Authenticator
|
import im.vector.matrix.android.api.auth.Authenticator
|
||||||
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.RealmSessionParamsStore
|
import im.vector.matrix.android.internal.auth.db.RealmSessionParamsStore
|
||||||
import im.vector.matrix.android.internal.database.configureEncryption
|
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 io.realm.RealmConfiguration
|
import io.realm.RealmConfiguration
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@ -33,16 +33,21 @@ internal abstract class AuthModule {
|
|||||||
|
|
||||||
@Module
|
@Module
|
||||||
companion object {
|
companion object {
|
||||||
|
private const val DB_ALIAS = "matrix-sdk-auth"
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@Provides
|
@Provides
|
||||||
@AuthDatabase
|
@AuthDatabase
|
||||||
fun providesRealmConfiguration(context: Context): RealmConfiguration {
|
fun providesRealmConfiguration(context: Context, realmKeysUtils: RealmKeysUtils): RealmConfiguration {
|
||||||
val old = File(context.filesDir, "matrix-sdk-auth")
|
val old = File(context.filesDir, "matrix-sdk-auth")
|
||||||
if (old.exists()) {
|
if (old.exists()) {
|
||||||
old.renameTo(File(context.filesDir, "matrix-sdk-auth.realm"))
|
old.renameTo(File(context.filesDir, "matrix-sdk-auth.realm"))
|
||||||
}
|
}
|
||||||
|
|
||||||
return RealmConfiguration.Builder()
|
return RealmConfiguration.Builder()
|
||||||
.configureEncryption("matrix-sdk-auth", context)
|
.apply {
|
||||||
|
realmKeysUtils.configureEncryption(this, DB_ALIAS)
|
||||||
|
}
|
||||||
.name("matrix-sdk-auth.realm")
|
.name("matrix-sdk-auth.realm")
|
||||||
.modules(AuthRealmModule())
|
.modules(AuthRealmModule())
|
||||||
.deleteRealmIfMigrationNeeded()
|
.deleteRealmIfMigrationNeeded()
|
||||||
|
@ -25,6 +25,7 @@ import im.vector.matrix.android.api.auth.data.SessionParams
|
|||||||
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.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.PasswordLoginParams
|
import im.vector.matrix.android.internal.auth.data.PasswordLoginParams
|
||||||
import im.vector.matrix.android.internal.auth.data.ThreePidMedium
|
import im.vector.matrix.android.internal.auth.data.ThreePidMedium
|
||||||
import im.vector.matrix.android.internal.di.Unauthenticated
|
import im.vector.matrix.android.internal.di.Unauthenticated
|
||||||
@ -62,11 +63,20 @@ internal class DefaultAuthenticator @Inject constructor(@Unauthenticated
|
|||||||
return sessionManager.getOrCreateSession(sessionParams)
|
return sessionManager.getOrCreateSession(sessionParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getLoginFlow(homeServerConnectionConfig: HomeServerConnectionConfig, callback: MatrixCallback<LoginFlowResponse>): Cancelable {
|
||||||
|
val job = GlobalScope.launch(coroutineDispatchers.main) {
|
||||||
|
val result = runCatching {
|
||||||
|
getLoginFlowInternal(homeServerConnectionConfig)
|
||||||
|
}
|
||||||
|
result.foldToCallback(callback)
|
||||||
|
}
|
||||||
|
return CancelableCoroutine(job)
|
||||||
|
}
|
||||||
|
|
||||||
override fun authenticate(homeServerConnectionConfig: HomeServerConnectionConfig,
|
override fun authenticate(homeServerConnectionConfig: HomeServerConnectionConfig,
|
||||||
login: String,
|
login: String,
|
||||||
password: String,
|
password: String,
|
||||||
callback: MatrixCallback<Session>): Cancelable {
|
callback: MatrixCallback<Session>): Cancelable {
|
||||||
|
|
||||||
val job = GlobalScope.launch(coroutineDispatchers.main) {
|
val job = GlobalScope.launch(coroutineDispatchers.main) {
|
||||||
val sessionOrFailure = runCatching {
|
val sessionOrFailure = runCatching {
|
||||||
authenticate(homeServerConnectionConfig, login, password)
|
authenticate(homeServerConnectionConfig, login, password)
|
||||||
@ -74,7 +84,14 @@ internal class DefaultAuthenticator @Inject constructor(@Unauthenticated
|
|||||||
sessionOrFailure.foldToCallback(callback)
|
sessionOrFailure.foldToCallback(callback)
|
||||||
}
|
}
|
||||||
return CancelableCoroutine(job)
|
return CancelableCoroutine(job)
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun getLoginFlowInternal(homeServerConnectionConfig: HomeServerConnectionConfig) = withContext(coroutineDispatchers.io) {
|
||||||
|
val authAPI = buildAuthAPI(homeServerConnectionConfig)
|
||||||
|
|
||||||
|
executeRequest<LoginFlowResponse> {
|
||||||
|
apiCall = authAPI.getLoginFlows()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun authenticate(homeServerConnectionConfig: HomeServerConnectionConfig,
|
private suspend fun authenticate(homeServerConnectionConfig: HomeServerConnectionConfig,
|
||||||
@ -95,6 +112,12 @@ internal class DefaultAuthenticator @Inject constructor(@Unauthenticated
|
|||||||
sessionManager.getOrCreateSession(sessionParams)
|
sessionManager.getOrCreateSession(sessionParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun createSessionFromSso(credentials: Credentials, homeServerConnectionConfig: HomeServerConnectionConfig): Session {
|
||||||
|
val sessionParams = SessionParams(credentials, homeServerConnectionConfig)
|
||||||
|
sessionParamsStore.save(sessionParams)
|
||||||
|
return sessionManager.getOrCreateSession(sessionParams)
|
||||||
|
}
|
||||||
|
|
||||||
private fun buildAuthAPI(homeServerConnectionConfig: HomeServerConnectionConfig): AuthAPI {
|
private fun buildAuthAPI(homeServerConnectionConfig: HomeServerConnectionConfig): AuthAPI {
|
||||||
val retrofit = retrofitFactory.create(okHttpClient, homeServerConnectionConfig.homeServerUri.toString())
|
val retrofit = retrofitFactory.create(okHttpClient, homeServerConnectionConfig.homeServerUri.toString())
|
||||||
return retrofit.create(AuthAPI::class.java)
|
return retrofit.create(AuthAPI::class.java)
|
||||||
|
@ -30,4 +30,12 @@ data class InteractiveAuthenticationFlow(
|
|||||||
|
|
||||||
@Json(name = "stages")
|
@Json(name = "stages")
|
||||||
val stages: List<String>? = null
|
val stages: List<String>? = null
|
||||||
)
|
) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
// Possible values for type
|
||||||
|
const val TYPE_LOGIN_SSO = "m.login.sso"
|
||||||
|
const val TYPE_LOGIN_TOKEN = "m.login.token"
|
||||||
|
const val TYPE_LOGIN_PASSWORD = "m.login.password"
|
||||||
|
}
|
||||||
|
}
|
@ -20,7 +20,7 @@ import com.squareup.moshi.Json
|
|||||||
import com.squareup.moshi.JsonClass
|
import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
internal data class LoginFlowResponse(
|
data class LoginFlowResponse(
|
||||||
@Json(name = "flows")
|
@Json(name = "flows")
|
||||||
val flows: List<InteractiveAuthenticationFlow>
|
val flows: List<InteractiveAuthenticationFlow>
|
||||||
)
|
)
|
@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto
|
package im.vector.matrix.android.internal.crypto
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import dagger.Binds
|
import dagger.Binds
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
import dagger.Provides
|
import dagger.Provides
|
||||||
@ -30,12 +29,13 @@ import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStore
|
|||||||
import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStoreMigration
|
import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStoreMigration
|
||||||
import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStoreModule
|
import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStoreModule
|
||||||
import im.vector.matrix.android.internal.crypto.tasks.*
|
import im.vector.matrix.android.internal.crypto.tasks.*
|
||||||
import im.vector.matrix.android.internal.database.configureEncryption
|
import im.vector.matrix.android.internal.database.RealmKeysUtils
|
||||||
import im.vector.matrix.android.internal.di.CryptoDatabase
|
import im.vector.matrix.android.internal.di.CryptoDatabase
|
||||||
|
import im.vector.matrix.android.internal.di.UserCacheDirectory
|
||||||
|
import im.vector.matrix.android.internal.di.UserMd5
|
||||||
import im.vector.matrix.android.internal.session.SessionScope
|
import im.vector.matrix.android.internal.session.SessionScope
|
||||||
import im.vector.matrix.android.internal.session.cache.ClearCacheTask
|
import im.vector.matrix.android.internal.session.cache.ClearCacheTask
|
||||||
import im.vector.matrix.android.internal.session.cache.RealmClearCacheTask
|
import im.vector.matrix.android.internal.session.cache.RealmClearCacheTask
|
||||||
import im.vector.matrix.android.internal.util.md5
|
|
||||||
import io.realm.RealmConfiguration
|
import io.realm.RealmConfiguration
|
||||||
import retrofit2.Retrofit
|
import retrofit2.Retrofit
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@ -45,17 +45,20 @@ internal abstract class CryptoModule {
|
|||||||
|
|
||||||
@Module
|
@Module
|
||||||
companion object {
|
companion object {
|
||||||
|
internal const val DB_ALIAS_PREFIX = "crypto_module_"
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@Provides
|
@Provides
|
||||||
@CryptoDatabase
|
@CryptoDatabase
|
||||||
@SessionScope
|
@SessionScope
|
||||||
fun providesRealmConfiguration(context: Context, credentials: Credentials): RealmConfiguration {
|
fun providesRealmConfiguration(@UserCacheDirectory directory: File,
|
||||||
val userIDHash = credentials.userId.md5()
|
@UserMd5 userMd5: String,
|
||||||
|
realmKeysUtils: RealmKeysUtils): RealmConfiguration {
|
||||||
return RealmConfiguration.Builder()
|
return RealmConfiguration.Builder()
|
||||||
.directory(File(context.filesDir, userIDHash))
|
.directory(directory)
|
||||||
.configureEncryption("crypto_module_$userIDHash", context)
|
.apply {
|
||||||
|
realmKeysUtils.configureEncryption(this, "$DB_ALIAS_PREFIX$userMd5")
|
||||||
|
}
|
||||||
.name("crypto_store.realm")
|
.name("crypto_store.realm")
|
||||||
.modules(RealmCryptoStoreModule())
|
.modules(RealmCryptoStoreModule())
|
||||||
.schemaVersion(RealmCryptoStoreMigration.CRYPTO_STORE_SCHEMA_VERSION)
|
.schemaVersion(RealmCryptoStoreMigration.CRYPTO_STORE_SCHEMA_VERSION)
|
||||||
@ -105,7 +108,7 @@ internal abstract class CryptoModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun bindCryptoService(cryptoManager: CryptoManager): CryptoService
|
abstract fun bindCryptoService(cryptoService: DefaultCryptoService): CryptoService
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun bindDeleteDeviceTask(deleteDeviceTask: DefaultDeleteDeviceTask): DeleteDeviceTask
|
abstract fun bindDeleteDeviceTask(deleteDeviceTask: DefaultDeleteDeviceTask): DeleteDeviceTask
|
||||||
|
@ -62,11 +62,9 @@ import im.vector.matrix.android.internal.crypto.tasks.*
|
|||||||
import im.vector.matrix.android.internal.crypto.verification.DefaultSasVerificationService
|
import im.vector.matrix.android.internal.crypto.verification.DefaultSasVerificationService
|
||||||
import im.vector.matrix.android.internal.database.model.EventEntity
|
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||||
import im.vector.matrix.android.internal.database.query.where
|
import im.vector.matrix.android.internal.database.query.where
|
||||||
import im.vector.matrix.android.internal.di.CryptoDatabase
|
|
||||||
import im.vector.matrix.android.internal.di.MoshiProvider
|
import im.vector.matrix.android.internal.di.MoshiProvider
|
||||||
import im.vector.matrix.android.internal.extensions.foldToCallback
|
import im.vector.matrix.android.internal.extensions.foldToCallback
|
||||||
import im.vector.matrix.android.internal.session.SessionScope
|
import im.vector.matrix.android.internal.session.SessionScope
|
||||||
import im.vector.matrix.android.internal.session.cache.ClearCacheTask
|
|
||||||
import im.vector.matrix.android.internal.session.room.membership.LoadRoomMembersTask
|
import im.vector.matrix.android.internal.session.room.membership.LoadRoomMembersTask
|
||||||
import im.vector.matrix.android.internal.session.room.membership.RoomMembers
|
import im.vector.matrix.android.internal.session.room.membership.RoomMembers
|
||||||
import im.vector.matrix.android.internal.session.sync.model.SyncResponse
|
import im.vector.matrix.android.internal.session.sync.model.SyncResponse
|
||||||
@ -93,7 +91,7 @@ import kotlin.math.max
|
|||||||
* Specially, it tracks all room membership changes events in order to do keys updates.
|
* Specially, it tracks all room membership changes events in order to do keys updates.
|
||||||
*/
|
*/
|
||||||
@SessionScope
|
@SessionScope
|
||||||
internal class CryptoManager @Inject constructor(
|
internal class DefaultCryptoService @Inject constructor(
|
||||||
// Olm Manager
|
// Olm Manager
|
||||||
private val olmManager: OlmManager,
|
private val olmManager: OlmManager,
|
||||||
// The credentials,
|
// The credentials,
|
||||||
@ -135,7 +133,6 @@ internal class CryptoManager @Inject constructor(
|
|||||||
private val setDeviceNameTask: SetDeviceNameTask,
|
private val setDeviceNameTask: SetDeviceNameTask,
|
||||||
private val uploadKeysTask: UploadKeysTask,
|
private val uploadKeysTask: UploadKeysTask,
|
||||||
private val loadRoomMembersTask: LoadRoomMembersTask,
|
private val loadRoomMembersTask: LoadRoomMembersTask,
|
||||||
@CryptoDatabase private val clearCryptoDataTask: ClearCacheTask,
|
|
||||||
private val monarchy: Monarchy,
|
private val monarchy: Monarchy,
|
||||||
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||||
private val taskExecutor: TaskExecutor
|
private val taskExecutor: TaskExecutor
|
||||||
@ -1047,14 +1044,6 @@ internal class CryptoManager @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun clearCryptoCache(callback: MatrixCallback<Unit>) {
|
|
||||||
clearCryptoDataTask
|
|
||||||
.configureWith {
|
|
||||||
this.callback = callback
|
|
||||||
}
|
|
||||||
.executeBy(taskExecutor)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun addNewSessionListener(newSessionListener: NewSessionListener) {
|
override fun addNewSessionListener(newSessionListener: NewSessionListener) {
|
||||||
roomDecryptorProvider.addNewSessionListener(newSessionListener)
|
roomDecryptorProvider.addNewSessionListener(newSessionListener)
|
||||||
}
|
}
|
||||||
@ -1067,6 +1056,6 @@ internal class CryptoManager @Inject constructor(
|
|||||||
* ========================================================================================== */
|
* ========================================================================================== */
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "CryptoManager of " + credentials.userId + " (" + credentials.deviceId + ")"
|
return "DefaultCryptoService of " + credentials.userId + " (" + credentials.deviceId + ")"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -17,11 +17,10 @@
|
|||||||
package im.vector.matrix.android.internal.crypto
|
package im.vector.matrix.android.internal.crypto
|
||||||
|
|
||||||
import im.vector.matrix.android.api.auth.data.Credentials
|
import im.vector.matrix.android.api.auth.data.Credentials
|
||||||
import java.util.*
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class ObjectSigner @Inject constructor(private val credentials: Credentials,
|
internal class ObjectSigner @Inject constructor(private val credentials: Credentials,
|
||||||
private val olmDevice: MXOlmDevice) {
|
private val olmDevice: MXOlmDevice) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sign Object
|
* Sign Object
|
||||||
|
@ -27,14 +27,14 @@ import java.util.*
|
|||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class EnsureOlmSessionsForUsersAction @Inject constructor(private val olmDevice: MXOlmDevice,
|
internal class EnsureOlmSessionsForUsersAction @Inject constructor(private val olmDevice: MXOlmDevice,
|
||||||
private val cryptoStore: IMXCryptoStore,
|
private val cryptoStore: IMXCryptoStore,
|
||||||
private val ensureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction) {
|
private val ensureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Try to make sure we have established olm sessions for the given users.
|
* Try to make sure we have established olm sessions for the given users.
|
||||||
* @param users a list of user ids.
|
* @param users a list of user ids.
|
||||||
*/
|
*/
|
||||||
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 = HashMap<String /* userId */, MutableList<MXDeviceInfo>>()
|
val devicesByUser = HashMap<String /* userId */, MutableList<MXDeviceInfo>>()
|
||||||
|
|
||||||
|
@ -16,15 +16,15 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto.actions
|
package im.vector.matrix.android.internal.crypto.actions
|
||||||
|
|
||||||
import im.vector.matrix.android.api.auth.data.Credentials
|
|
||||||
import im.vector.matrix.android.internal.crypto.keysbackup.KeysBackup
|
import im.vector.matrix.android.internal.crypto.keysbackup.KeysBackup
|
||||||
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
||||||
|
import im.vector.matrix.android.internal.di.UserId
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class SetDeviceVerificationAction @Inject constructor(private val cryptoStore: IMXCryptoStore,
|
internal class SetDeviceVerificationAction @Inject constructor(private val cryptoStore: IMXCryptoStore,
|
||||||
private val credentials: Credentials,
|
@UserId private val userId: String,
|
||||||
private val keysBackup: KeysBackup) {
|
private val keysBackup: KeysBackup) {
|
||||||
|
|
||||||
fun handle(verificationStatus: Int, deviceId: String, userId: String) {
|
fun handle(verificationStatus: Int, deviceId: String, userId: String) {
|
||||||
val device = cryptoStore.getUserDevice(deviceId, userId)
|
val device = cryptoStore.getUserDevice(deviceId, userId)
|
||||||
@ -39,7 +39,7 @@ internal class SetDeviceVerificationAction @Inject constructor(private val crypt
|
|||||||
device.verified = verificationStatus
|
device.verified = verificationStatus
|
||||||
cryptoStore.storeUserDevice(userId, device)
|
cryptoStore.storeUserDevice(userId, device)
|
||||||
|
|
||||||
if (userId == credentials.userId) {
|
if (userId == this.userId) {
|
||||||
// If one of the user's own devices is being marked as verified / unverified,
|
// If one of the user's own devices is being marked as verified / unverified,
|
||||||
// check the key backup status, since whether or not we use this depends on
|
// check the key backup status, since whether or not we use this depends on
|
||||||
// whether it has a signature from a verified device
|
// whether it has a signature from a verified device
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
package im.vector.matrix.android.internal.crypto.algorithms.megolm
|
package im.vector.matrix.android.internal.crypto.algorithms.megolm
|
||||||
|
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import im.vector.matrix.android.api.auth.data.Credentials
|
|
||||||
import im.vector.matrix.android.api.session.crypto.MXCryptoError
|
import im.vector.matrix.android.api.session.crypto.MXCryptoError
|
||||||
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
|
||||||
@ -40,7 +39,7 @@ import kotlinx.coroutines.GlobalScope
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
internal class MXMegolmDecryption(private val credentials: Credentials,
|
internal class MXMegolmDecryption(private val userId: String,
|
||||||
private val olmDevice: MXOlmDevice,
|
private val olmDevice: MXOlmDevice,
|
||||||
private val deviceListManager: DeviceListManager,
|
private val deviceListManager: DeviceListManager,
|
||||||
private val outgoingRoomKeyRequestManager: OutgoingRoomKeyRequestManager,
|
private val outgoingRoomKeyRequestManager: OutgoingRoomKeyRequestManager,
|
||||||
@ -146,11 +145,11 @@ internal class MXMegolmDecryption(private val credentials: Credentials,
|
|||||||
|
|
||||||
val selfMap = HashMap<String, String>()
|
val selfMap = HashMap<String, String>()
|
||||||
// TODO Replace this hard coded keys (see OutgoingRoomKeyRequestManager)
|
// TODO Replace this hard coded keys (see OutgoingRoomKeyRequestManager)
|
||||||
selfMap["userId"] = credentials.userId
|
selfMap["userId"] = userId
|
||||||
selfMap["deviceId"] = "*"
|
selfMap["deviceId"] = "*"
|
||||||
recipients.add(selfMap)
|
recipients.add(selfMap)
|
||||||
|
|
||||||
if (!TextUtils.equals(sender, credentials.userId)) {
|
if (!TextUtils.equals(sender, userId)) {
|
||||||
val senderMap = HashMap<String, String>()
|
val senderMap = HashMap<String, String>()
|
||||||
senderMap["userId"] = sender
|
senderMap["userId"] = sender
|
||||||
senderMap["deviceId"] = encryptedEventContent.deviceId!!
|
senderMap["deviceId"] = encryptedEventContent.deviceId!!
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto.algorithms.megolm
|
package im.vector.matrix.android.internal.crypto.algorithms.megolm
|
||||||
|
|
||||||
import im.vector.matrix.android.api.auth.data.Credentials
|
|
||||||
import im.vector.matrix.android.internal.crypto.DeviceListManager
|
import im.vector.matrix.android.internal.crypto.DeviceListManager
|
||||||
import im.vector.matrix.android.internal.crypto.MXOlmDevice
|
import im.vector.matrix.android.internal.crypto.MXOlmDevice
|
||||||
import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequestManager
|
import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequestManager
|
||||||
@ -24,22 +23,23 @@ import im.vector.matrix.android.internal.crypto.actions.EnsureOlmSessionsForDevi
|
|||||||
import im.vector.matrix.android.internal.crypto.actions.MessageEncrypter
|
import im.vector.matrix.android.internal.crypto.actions.MessageEncrypter
|
||||||
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
||||||
import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask
|
import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask
|
||||||
|
import im.vector.matrix.android.internal.di.UserId
|
||||||
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class MXMegolmDecryptionFactory @Inject constructor(private val credentials: Credentials,
|
internal class MXMegolmDecryptionFactory @Inject constructor(@UserId private val userId: String,
|
||||||
private val olmDevice: MXOlmDevice,
|
private val olmDevice: MXOlmDevice,
|
||||||
private val deviceListManager: DeviceListManager,
|
private val deviceListManager: DeviceListManager,
|
||||||
private val outgoingRoomKeyRequestManager: OutgoingRoomKeyRequestManager,
|
private val outgoingRoomKeyRequestManager: OutgoingRoomKeyRequestManager,
|
||||||
private val messageEncrypter: MessageEncrypter,
|
private val messageEncrypter: MessageEncrypter,
|
||||||
private val ensureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction,
|
private val ensureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction,
|
||||||
private val cryptoStore: IMXCryptoStore,
|
private val cryptoStore: IMXCryptoStore,
|
||||||
private val sendToDeviceTask: SendToDeviceTask,
|
private val sendToDeviceTask: SendToDeviceTask,
|
||||||
private val coroutineDispatchers: MatrixCoroutineDispatchers) {
|
private val coroutineDispatchers: MatrixCoroutineDispatchers) {
|
||||||
|
|
||||||
fun create(): MXMegolmDecryption {
|
fun create(): MXMegolmDecryption {
|
||||||
return MXMegolmDecryption(
|
return MXMegolmDecryption(
|
||||||
credentials,
|
userId,
|
||||||
olmDevice,
|
olmDevice,
|
||||||
deviceListManager,
|
deviceListManager,
|
||||||
outgoingRoomKeyRequestManager,
|
outgoingRoomKeyRequestManager,
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto.algorithms.olm
|
package im.vector.matrix.android.internal.crypto.algorithms.olm
|
||||||
|
|
||||||
import im.vector.matrix.android.api.auth.data.Credentials
|
|
||||||
import im.vector.matrix.android.api.session.crypto.MXCryptoError
|
import im.vector.matrix.android.api.session.crypto.MXCryptoError
|
||||||
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.toModel
|
import im.vector.matrix.android.api.session.events.model.toModel
|
||||||
@ -31,13 +30,12 @@ import im.vector.matrix.android.internal.crypto.model.event.OlmPayloadContent
|
|||||||
import im.vector.matrix.android.internal.di.MoshiProvider
|
import im.vector.matrix.android.internal.di.MoshiProvider
|
||||||
import im.vector.matrix.android.internal.util.convertFromUTF8
|
import im.vector.matrix.android.internal.util.convertFromUTF8
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
internal class MXOlmDecryption(
|
internal class MXOlmDecryption(
|
||||||
// The olm device interface
|
// The olm device interface
|
||||||
private val olmDevice: MXOlmDevice,
|
private val olmDevice: MXOlmDevice,
|
||||||
// the matrix credentials
|
// the matrix userId
|
||||||
private val credentials: Credentials)
|
private val userId: String)
|
||||||
: IMXDecrypting {
|
: IMXDecrypting {
|
||||||
|
|
||||||
override suspend fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult {
|
override suspend fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult {
|
||||||
@ -98,9 +96,9 @@ internal class MXOlmDecryption(
|
|||||||
throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_PROPERTY, reason)
|
throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_PROPERTY, reason)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (olmPayloadContent.recipient != credentials.userId) {
|
if (olmPayloadContent.recipient != userId) {
|
||||||
Timber.e("## decryptEvent() : Event ${event.eventId}:" +
|
Timber.e("## decryptEvent() : Event ${event.eventId}:" +
|
||||||
" Intended recipient ${olmPayloadContent.recipient} does not match our id ${credentials.userId}")
|
" Intended recipient ${olmPayloadContent.recipient} does not match our id $userId")
|
||||||
throw MXCryptoError.Base(MXCryptoError.ErrorType.BAD_RECIPIENT,
|
throw MXCryptoError.Base(MXCryptoError.ErrorType.BAD_RECIPIENT,
|
||||||
String.format(MXCryptoError.BAD_RECIPIENT_REASON, olmPayloadContent.recipient))
|
String.format(MXCryptoError.BAD_RECIPIENT_REASON, olmPayloadContent.recipient))
|
||||||
}
|
}
|
||||||
@ -158,33 +156,14 @@ internal class MXOlmDecryption(
|
|||||||
* @return payload, if decrypted successfully.
|
* @return payload, if decrypted successfully.
|
||||||
*/
|
*/
|
||||||
private fun decryptMessage(message: JsonDict, theirDeviceIdentityKey: String): String? {
|
private fun decryptMessage(message: JsonDict, theirDeviceIdentityKey: String): String? {
|
||||||
val sessionIdsSet = olmDevice.getSessionIds(theirDeviceIdentityKey)
|
val sessionIds = olmDevice.getSessionIds(theirDeviceIdentityKey) ?: emptySet()
|
||||||
|
|
||||||
val sessionIds: List<String>
|
val messageBody = message["body"] as? String ?: return null
|
||||||
|
val messageType = when (val typeAsVoid = message["type"]) {
|
||||||
if (null == sessionIdsSet) {
|
is Double -> typeAsVoid.toInt()
|
||||||
sessionIds = ArrayList()
|
is Int -> typeAsVoid
|
||||||
} else {
|
is Long -> typeAsVoid.toInt()
|
||||||
sessionIds = ArrayList(sessionIdsSet)
|
else -> return null
|
||||||
}
|
|
||||||
|
|
||||||
val messageBody = message["body"] as? String
|
|
||||||
var messageType: Int? = null
|
|
||||||
|
|
||||||
val typeAsVoid = message["type"]
|
|
||||||
|
|
||||||
if (null != typeAsVoid) {
|
|
||||||
if (typeAsVoid is Double) {
|
|
||||||
messageType = typeAsVoid.toInt()
|
|
||||||
} else if (typeAsVoid is Int) {
|
|
||||||
messageType = typeAsVoid
|
|
||||||
} else if (typeAsVoid is Long) {
|
|
||||||
messageType = typeAsVoid.toInt()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (null == messageBody || null == messageType) {
|
|
||||||
return null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try each session in turn
|
// Try each session in turn
|
||||||
|
@ -16,16 +16,16 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto.algorithms.olm
|
package im.vector.matrix.android.internal.crypto.algorithms.olm
|
||||||
|
|
||||||
import im.vector.matrix.android.api.auth.data.Credentials
|
|
||||||
import im.vector.matrix.android.internal.crypto.MXOlmDevice
|
import im.vector.matrix.android.internal.crypto.MXOlmDevice
|
||||||
|
import im.vector.matrix.android.internal.di.UserId
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class MXOlmDecryptionFactory @Inject constructor(private val olmDevice: MXOlmDevice,
|
internal class MXOlmDecryptionFactory @Inject constructor(private val olmDevice: MXOlmDevice,
|
||||||
private val credentials: Credentials) {
|
@UserId private val userId: String) {
|
||||||
|
|
||||||
fun create(): MXOlmDecryption {
|
fun create(): MXOlmDecryption {
|
||||||
return MXOlmDecryption(
|
return MXOlmDecryption(
|
||||||
olmDevice,
|
olmDevice,
|
||||||
credentials)
|
userId)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -26,7 +26,6 @@ import java.io.ByteArrayOutputStream
|
|||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
import java.util.*
|
|
||||||
import javax.crypto.Cipher
|
import javax.crypto.Cipher
|
||||||
import javax.crypto.spec.IvParameterSpec
|
import javax.crypto.spec.IvParameterSpec
|
||||||
import javax.crypto.spec.SecretKeySpec
|
import javax.crypto.spec.SecretKeySpec
|
||||||
@ -59,8 +58,7 @@ object MXEncryptedAttachments {
|
|||||||
// Half of the IV is random, the lower order bits are zeroed
|
// Half of the IV is random, the lower order bits are zeroed
|
||||||
// such that the counter never wraps.
|
// such that the counter never wraps.
|
||||||
// See https://github.com/matrix-org/matrix-ios-kit/blob/3dc0d8e46b4deb6669ed44f72ad79be56471354c/MatrixKit/Models/Room/MXEncryptedAttachments.m#L75
|
// See https://github.com/matrix-org/matrix-ios-kit/blob/3dc0d8e46b4deb6669ed44f72ad79be56471354c/MatrixKit/Models/Room/MXEncryptedAttachments.m#L75
|
||||||
val initVectorBytes = ByteArray(16)
|
val initVectorBytes = ByteArray(16) { 0.toByte() }
|
||||||
Arrays.fill(initVectorBytes, 0.toByte())
|
|
||||||
|
|
||||||
val ivRandomPart = ByteArray(8)
|
val ivRandomPart = ByteArray(8)
|
||||||
secureRandom.nextBytes(ivRandomPart)
|
secureRandom.nextBytes(ivRandomPart)
|
||||||
@ -115,7 +113,7 @@ object MXEncryptedAttachments {
|
|||||||
encryptedByteArray = outStream.toByteArray()
|
encryptedByteArray = outStream.toByteArray()
|
||||||
)
|
)
|
||||||
|
|
||||||
Timber.v("Encrypt in " + (System.currentTimeMillis() - t0) + " ms")
|
Timber.v("Encrypt in ${System.currentTimeMillis() - t0} ms")
|
||||||
return Try.just(result)
|
return Try.just(result)
|
||||||
} catch (oom: OutOfMemoryError) {
|
} catch (oom: OutOfMemoryError) {
|
||||||
Timber.e(oom, "## encryptAttachment failed")
|
Timber.e(oom, "## encryptAttachment failed")
|
||||||
@ -206,13 +204,13 @@ object MXEncryptedAttachments {
|
|||||||
val decryptedStream = ByteArrayInputStream(outStream.toByteArray())
|
val decryptedStream = ByteArrayInputStream(outStream.toByteArray())
|
||||||
outStream.close()
|
outStream.close()
|
||||||
|
|
||||||
Timber.v("Decrypt in " + (System.currentTimeMillis() - t0) + " ms")
|
Timber.v("Decrypt in ${System.currentTimeMillis() - t0} ms")
|
||||||
|
|
||||||
return decryptedStream
|
return decryptedStream
|
||||||
} catch (oom: OutOfMemoryError) {
|
} catch (oom: OutOfMemoryError) {
|
||||||
Timber.e(oom, "## decryptAttachment() : failed " + oom.message)
|
Timber.e(oom, "## decryptAttachment() : failed ${oom.message}")
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e, "## decryptAttachment() : failed " + e.message)
|
Timber.e(e, "## decryptAttachment() : failed ${e.message}")
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -228,34 +226,20 @@ object MXEncryptedAttachments {
|
|||||||
* Base64 URL conversion methods
|
* Base64 URL conversion methods
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private fun base64UrlToBase64(base64Url: String?): String? {
|
private fun base64UrlToBase64(base64Url: String): String {
|
||||||
var result = base64Url
|
return base64Url.replace('-', '+')
|
||||||
if (null != result) {
|
.replace('_', '/')
|
||||||
result = result.replace("-".toRegex(), "+")
|
|
||||||
result = result.replace("_".toRegex(), "/")
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun base64ToBase64Url(base64: String?): String? {
|
private fun base64ToBase64Url(base64: String): String {
|
||||||
var result = base64
|
return base64.replace("\n".toRegex(), "")
|
||||||
if (null != result) {
|
.replace("\\+".toRegex(), "-")
|
||||||
result = result.replace("\n".toRegex(), "")
|
.replace('/', '_')
|
||||||
result = result.replace("\\+".toRegex(), "-")
|
.replace("=", "")
|
||||||
result = result.replace("/".toRegex(), "_")
|
|
||||||
result = result.replace("=".toRegex(), "")
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun base64ToUnpaddedBase64(base64: String?): String? {
|
private fun base64ToUnpaddedBase64(base64: String): String {
|
||||||
var result = base64
|
return base64.replace("\n".toRegex(), "")
|
||||||
if (null != result) {
|
.replace("=", "")
|
||||||
result = result.replace("\n".toRegex(), "")
|
|
||||||
result = result.replace("=".toRegex(), "")
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,9 +66,8 @@ import org.matrix.olm.OlmPkEncryption
|
|||||||
import org.matrix.olm.OlmPkMessage
|
import org.matrix.olm.OlmPkMessage
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.security.InvalidParameterException
|
import java.security.InvalidParameterException
|
||||||
import java.util.*
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.collections.HashMap
|
import kotlin.random.Random
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A KeysBackup class instance manage incremental backup of e2e keys (megolm keys)
|
* A KeysBackup class instance manage incremental backup of e2e keys (megolm keys)
|
||||||
@ -114,8 +113,6 @@ internal class KeysBackup @Inject constructor(
|
|||||||
// The backup key being used.
|
// The backup key being used.
|
||||||
private var backupOlmPkEncryption: OlmPkEncryption? = null
|
private var backupOlmPkEncryption: OlmPkEncryption? = null
|
||||||
|
|
||||||
private val random = Random()
|
|
||||||
|
|
||||||
private var backupAllGroupSessionsCallback: MatrixCallback<Unit>? = null
|
private var backupAllGroupSessionsCallback: MatrixCallback<Unit>? = null
|
||||||
|
|
||||||
private var keysBackupStateListener: KeysBackupStateListener? = null
|
private var keysBackupStateListener: KeysBackupStateListener? = null
|
||||||
@ -848,7 +845,7 @@ internal class KeysBackup @Inject constructor(
|
|||||||
// Wait between 0 and 10 seconds, to avoid backup requests from
|
// Wait between 0 and 10 seconds, to avoid backup requests from
|
||||||
// different clients hitting the server all at the same time when a
|
// different clients hitting the server all at the same time when a
|
||||||
// new key is sent
|
// new key is sent
|
||||||
val delayInMs = random.nextInt(KEY_BACKUP_WAITING_TIME_TO_SEND_KEY_BACKUP_MILLIS).toLong()
|
val delayInMs = Random.nextLong(KEY_BACKUP_WAITING_TIME_TO_SEND_KEY_BACKUP_MILLIS)
|
||||||
|
|
||||||
uiHandler.postDelayed({ backupKeys() }, delayInMs)
|
uiHandler.postDelayed({ backupKeys() }, delayInMs)
|
||||||
}
|
}
|
||||||
@ -1307,7 +1304,7 @@ internal class KeysBackup @Inject constructor(
|
|||||||
|
|
||||||
// Make the request
|
// Make the request
|
||||||
storeSessionDataTask
|
storeSessionDataTask
|
||||||
.configureWith(StoreSessionsDataTask.Params(keysBackupVersion!!.version!!, keysBackupData)){
|
.configureWith(StoreSessionsDataTask.Params(keysBackupVersion!!.version!!, keysBackupData)) {
|
||||||
this.callback = sendingRequestCallback
|
this.callback = sendingRequestCallback
|
||||||
}
|
}
|
||||||
.executeBy(taskExecutor)
|
.executeBy(taskExecutor)
|
||||||
@ -1405,7 +1402,7 @@ internal class KeysBackup @Inject constructor(
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
// Maximum delay in ms in {@link maybeBackupKeys}
|
// Maximum delay in ms in {@link maybeBackupKeys}
|
||||||
private const val KEY_BACKUP_WAITING_TIME_TO_SEND_KEY_BACKUP_MILLIS = 10000
|
private const val KEY_BACKUP_WAITING_TIME_TO_SEND_KEY_BACKUP_MILLIS = 10_000L
|
||||||
|
|
||||||
// Maximum number of keys to send at a time to the homeserver.
|
// Maximum number of keys to send at a time to the homeserver.
|
||||||
private const val KEY_BACKUP_SEND_KEYS_MAX_COUNT = 100
|
private const val KEY_BACKUP_SEND_KEYS_MAX_COUNT = 100
|
||||||
|
@ -22,7 +22,7 @@ package im.vector.matrix.android.internal.crypto.keysbackup
|
|||||||
import androidx.annotation.WorkerThread
|
import androidx.annotation.WorkerThread
|
||||||
import im.vector.matrix.android.api.listeners.ProgressListener
|
import im.vector.matrix.android.api.listeners.ProgressListener
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.*
|
import java.util.UUID
|
||||||
import javax.crypto.Mac
|
import javax.crypto.Mac
|
||||||
import javax.crypto.spec.SecretKeySpec
|
import javax.crypto.spec.SecretKeySpec
|
||||||
import kotlin.experimental.xor
|
import kotlin.experimental.xor
|
||||||
|
@ -20,7 +20,6 @@ import android.os.Handler
|
|||||||
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupState
|
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupState
|
||||||
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupStateListener
|
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupStateListener
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
internal class KeysBackupStateManager(private val uiHandler: Handler) {
|
internal class KeysBackupStateManager(private val uiHandler: Handler) {
|
||||||
|
|
||||||
|
@ -23,7 +23,6 @@ import com.squareup.moshi.JsonClass
|
|||||||
import im.vector.matrix.android.api.util.JsonDict
|
import im.vector.matrix.android.api.util.JsonDict
|
||||||
import im.vector.matrix.android.internal.crypto.model.rest.DeviceKeys
|
import im.vector.matrix.android.internal.crypto.model.rest.DeviceKeys
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
data class MXDeviceInfo(
|
data class MXDeviceInfo(
|
||||||
|
@ -18,7 +18,6 @@ package im.vector.matrix.android.internal.crypto.model
|
|||||||
|
|
||||||
import im.vector.matrix.android.api.util.JsonDict
|
import im.vector.matrix.android.api.util.JsonDict
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
data class MXKey(
|
data class MXKey(
|
||||||
/**
|
/**
|
||||||
@ -46,11 +45,7 @@ data class MXKey(
|
|||||||
* @return the signed data map
|
* @return the signed data map
|
||||||
*/
|
*/
|
||||||
fun signalableJSONDictionary(): Map<String, Any> {
|
fun signalableJSONDictionary(): Map<String, Any> {
|
||||||
val map = HashMap<String, Any>()
|
return mapOf("key" to value)
|
||||||
|
|
||||||
map["key"] = value
|
|
||||||
|
|
||||||
return map
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto.model
|
package im.vector.matrix.android.internal.crypto.model
|
||||||
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
class MXUsersDevicesMap<E> {
|
class MXUsersDevicesMap<E> {
|
||||||
|
|
||||||
@ -27,7 +26,7 @@ class MXUsersDevicesMap<E> {
|
|||||||
* @return the user Ids
|
* @return the user Ids
|
||||||
*/
|
*/
|
||||||
val userIds: List<String>
|
val userIds: List<String>
|
||||||
get() = ArrayList(map.keys)
|
get() = map.keys.toList()
|
||||||
|
|
||||||
val isEmpty: Boolean
|
val isEmpty: Boolean
|
||||||
get() = map.isEmpty()
|
get() = map.isEmpty()
|
||||||
@ -40,7 +39,7 @@ class MXUsersDevicesMap<E> {
|
|||||||
* @return the device ids list
|
* @return the device ids list
|
||||||
*/
|
*/
|
||||||
fun getUserDeviceIds(userId: String?): List<String>? {
|
fun getUserDeviceIds(userId: String?): List<String>? {
|
||||||
return if (userId?.isNotBlank() == true && map.containsKey(userId)) {
|
return if (!userId.isNullOrBlank() && map.containsKey(userId)) {
|
||||||
map[userId]!!.keys.toList()
|
map[userId]!!.keys.toList()
|
||||||
} else null
|
} else null
|
||||||
}
|
}
|
||||||
@ -53,7 +52,7 @@ class MXUsersDevicesMap<E> {
|
|||||||
* @return the object
|
* @return the object
|
||||||
*/
|
*/
|
||||||
fun getObject(userId: String?, deviceId: String?): E? {
|
fun getObject(userId: String?, deviceId: String?): E? {
|
||||||
return if (userId?.isNotBlank() == true && deviceId?.isNotBlank() == true && map.containsKey(userId)) {
|
return if (!userId.isNullOrBlank() && !deviceId.isNullOrBlank()) {
|
||||||
map[userId]?.get(deviceId)
|
map[userId]?.get(deviceId)
|
||||||
} else null
|
} else null
|
||||||
}
|
}
|
||||||
@ -67,11 +66,8 @@ class MXUsersDevicesMap<E> {
|
|||||||
*/
|
*/
|
||||||
fun setObject(userId: String?, deviceId: String?, o: E?) {
|
fun setObject(userId: String?, deviceId: String?, o: E?) {
|
||||||
if (null != o && userId?.isNotBlank() == true && deviceId?.isNotBlank() == true) {
|
if (null != o && userId?.isNotBlank() == true && deviceId?.isNotBlank() == true) {
|
||||||
if (map[userId] == null) {
|
val devices = map.getOrPut(userId) { HashMap() }
|
||||||
map[userId] = HashMap()
|
devices[deviceId] = o
|
||||||
}
|
|
||||||
|
|
||||||
map[userId]?.put(deviceId, o)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,7 +78,7 @@ class MXUsersDevicesMap<E> {
|
|||||||
* @param userId the user id
|
* @param userId the user id
|
||||||
*/
|
*/
|
||||||
fun setObjects(userId: String?, objectsPerDevices: Map<String, E>?) {
|
fun setObjects(userId: String?, objectsPerDevices: Map<String, E>?) {
|
||||||
if (userId?.isNotBlank() == true) {
|
if (!userId.isNullOrBlank()) {
|
||||||
if (null == objectsPerDevices) {
|
if (null == objectsPerDevices) {
|
||||||
map.remove(userId)
|
map.remove(userId)
|
||||||
} else {
|
} else {
|
||||||
@ -97,7 +93,7 @@ class MXUsersDevicesMap<E> {
|
|||||||
* @param userId the user id.
|
* @param userId the user id.
|
||||||
*/
|
*/
|
||||||
fun removeUserObjects(userId: String?) {
|
fun removeUserObjects(userId: String?) {
|
||||||
if (userId?.isNotBlank() == true) {
|
if (!userId.isNullOrBlank()) {
|
||||||
map.remove(userId)
|
map.remove(userId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,11 +16,11 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto.tasks
|
package im.vector.matrix.android.internal.crypto.tasks
|
||||||
|
|
||||||
import im.vector.matrix.android.api.auth.data.Credentials
|
|
||||||
import im.vector.matrix.android.internal.auth.data.LoginFlowTypes
|
import im.vector.matrix.android.internal.auth.data.LoginFlowTypes
|
||||||
import im.vector.matrix.android.internal.crypto.api.CryptoApi
|
import im.vector.matrix.android.internal.crypto.api.CryptoApi
|
||||||
import im.vector.matrix.android.internal.crypto.model.rest.DeleteDeviceAuth
|
import im.vector.matrix.android.internal.crypto.model.rest.DeleteDeviceAuth
|
||||||
import im.vector.matrix.android.internal.crypto.model.rest.DeleteDeviceParams
|
import im.vector.matrix.android.internal.crypto.model.rest.DeleteDeviceParams
|
||||||
|
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.task.Task
|
import im.vector.matrix.android.internal.task.Task
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -34,7 +34,7 @@ internal interface DeleteDeviceWithUserPasswordTask : Task<DeleteDeviceWithUserP
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal class DefaultDeleteDeviceWithUserPasswordTask @Inject constructor(private val cryptoApi: CryptoApi,
|
internal class DefaultDeleteDeviceWithUserPasswordTask @Inject constructor(private val cryptoApi: CryptoApi,
|
||||||
private val credentials: Credentials)
|
@UserId private val userId: String)
|
||||||
: DeleteDeviceWithUserPasswordTask {
|
: DeleteDeviceWithUserPasswordTask {
|
||||||
|
|
||||||
override suspend fun execute(params: DeleteDeviceWithUserPasswordTask.Params) {
|
override suspend fun execute(params: DeleteDeviceWithUserPasswordTask.Params) {
|
||||||
@ -45,7 +45,7 @@ internal class DefaultDeleteDeviceWithUserPasswordTask @Inject constructor(priva
|
|||||||
.apply {
|
.apply {
|
||||||
type = LoginFlowTypes.PASSWORD
|
type = LoginFlowTypes.PASSWORD
|
||||||
session = params.authSession
|
session = params.authSession
|
||||||
user = credentials.userId
|
user = userId
|
||||||
password = params.password
|
password = params.password
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -21,8 +21,8 @@ import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
|
|||||||
import im.vector.matrix.android.internal.crypto.model.rest.SendToDeviceBody
|
import im.vector.matrix.android.internal.crypto.model.rest.SendToDeviceBody
|
||||||
import im.vector.matrix.android.internal.network.executeRequest
|
import im.vector.matrix.android.internal.network.executeRequest
|
||||||
import im.vector.matrix.android.internal.task.Task
|
import im.vector.matrix.android.internal.task.Task
|
||||||
import java.util.*
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
internal interface SendToDeviceTask : Task<SendToDeviceTask.Params, Unit> {
|
internal interface SendToDeviceTask : Task<SendToDeviceTask.Params, Unit> {
|
||||||
data class Params(
|
data class Params(
|
||||||
@ -45,7 +45,7 @@ internal class DefaultSendToDeviceTask @Inject constructor(private val cryptoApi
|
|||||||
return executeRequest {
|
return executeRequest {
|
||||||
apiCall = cryptoApi.sendToDevice(
|
apiCall = cryptoApi.sendToDevice(
|
||||||
params.eventType,
|
params.eventType,
|
||||||
params.transactionId ?: Random().nextInt(Integer.MAX_VALUE).toString(),
|
params.transactionId ?: Random.nextInt(Integer.MAX_VALUE).toString(),
|
||||||
sendToDeviceBody
|
sendToDeviceBody
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
|||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.*
|
import java.util.UUID
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.collections.HashMap
|
import kotlin.collections.HashMap
|
||||||
|
|
||||||
@ -161,7 +161,7 @@ internal class DefaultSasVerificationService @Inject constructor(private val cre
|
|||||||
cancelTransaction(
|
cancelTransaction(
|
||||||
startReq.transactionID!!,
|
startReq.transactionID!!,
|
||||||
otherUserId!!,
|
otherUserId!!,
|
||||||
startReq?.fromDevice ?: event.getSenderKey()!!,
|
startReq.fromDevice ?: event.getSenderKey()!!,
|
||||||
CancelCode.UnknownMethod
|
CancelCode.UnknownMethod
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -388,14 +388,13 @@ internal class DefaultSasVerificationService @Inject constructor(private val cre
|
|||||||
* This string must be unique for the pair of users performing verification for the duration that the transaction is valid
|
* This string must be unique for the pair of users performing verification for the duration that the transaction is valid
|
||||||
*/
|
*/
|
||||||
private fun createUniqueIDForTransaction(userId: String, deviceID: String): String {
|
private fun createUniqueIDForTransaction(userId: String, deviceID: String): String {
|
||||||
val buff = StringBuffer()
|
return buildString {
|
||||||
buff
|
append(credentials.userId).append("|")
|
||||||
.append(credentials.userId).append("|")
|
append(credentials.deviceId).append("|")
|
||||||
.append(credentials.deviceId).append("|")
|
append(userId).append("|")
|
||||||
.append(userId).append("|")
|
append(deviceID).append("|")
|
||||||
.append(deviceID).append("|")
|
append(UUID.randomUUID().toString())
|
||||||
.append(UUID.randomUUID().toString())
|
}
|
||||||
return buff.toString()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,10 +17,12 @@ package im.vector.matrix.android.internal.database
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.Base64
|
import android.util.Base64
|
||||||
import im.vector.matrix.android.api.util.SecretStoringUtils
|
import im.vector.matrix.android.BuildConfig
|
||||||
|
import im.vector.matrix.android.internal.session.securestorage.SecretStoringUtils
|
||||||
import io.realm.RealmConfiguration
|
import io.realm.RealmConfiguration
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* On creation a random key is generated, this key is then encrypted using the system KeyStore.
|
* On creation a random key is generated, this key is then encrypted using the system KeyStore.
|
||||||
@ -34,12 +36,13 @@ import java.security.SecureRandom
|
|||||||
* then we generate a random secret key. The database key is encrypted with the secret key; The secret
|
* then we generate a random secret key. The database key is encrypted with the secret key; The secret
|
||||||
* key is encrypted with the public RSA key and stored with the encrypted key in the shared pref
|
* key is encrypted with the public RSA key and stored with the encrypted key in the shared pref
|
||||||
*/
|
*/
|
||||||
private object RealmKeysUtils {
|
internal class RealmKeysUtils @Inject constructor(context: Context,
|
||||||
|
private val secretStoringUtils: SecretStoringUtils) {
|
||||||
private const val ENCRYPTED_KEY_PREFIX = "REALM_ENCRYPTED_KEY"
|
|
||||||
|
|
||||||
private val rng = SecureRandom()
|
private val rng = SecureRandom()
|
||||||
|
|
||||||
|
private val sharedPreferences = context.getSharedPreferences("im.vector.matrix.android.keys", Context.MODE_PRIVATE)
|
||||||
|
|
||||||
private fun generateKeyForRealm(): ByteArray {
|
private fun generateKeyForRealm(): ByteArray {
|
||||||
val keyForRealm = ByteArray(RealmConfiguration.KEY_LENGTH)
|
val keyForRealm = ByteArray(RealmConfiguration.KEY_LENGTH)
|
||||||
rng.nextBytes(keyForRealm)
|
rng.nextBytes(keyForRealm)
|
||||||
@ -49,8 +52,7 @@ private object RealmKeysUtils {
|
|||||||
/**
|
/**
|
||||||
* Check if there is already a key for this alias
|
* Check if there is already a key for this alias
|
||||||
*/
|
*/
|
||||||
fun hasKeyForDatabase(alias: String, context: Context): Boolean {
|
private fun hasKeyForDatabase(alias: String): Boolean {
|
||||||
val sharedPreferences = getSharedPreferences(context)
|
|
||||||
return sharedPreferences.contains("${ENCRYPTED_KEY_PREFIX}_$alias")
|
return sharedPreferences.contains("${ENCRYPTED_KEY_PREFIX}_$alias")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,13 +61,12 @@ private object RealmKeysUtils {
|
|||||||
* The random key is then encrypted by the keystore, and the encrypted key is stored
|
* The random key is then encrypted by the keystore, and the encrypted key is stored
|
||||||
* in shared preferences.
|
* in shared preferences.
|
||||||
*
|
*
|
||||||
* @return the generate key (can be passed to Realm Configuration)
|
* @return the generated key (can be passed to Realm Configuration)
|
||||||
*/
|
*/
|
||||||
fun createAndSaveKeyForDatabase(alias: String, context: Context): ByteArray {
|
private fun createAndSaveKeyForDatabase(alias: String): ByteArray {
|
||||||
val key = generateKeyForRealm()
|
val key = generateKeyForRealm()
|
||||||
val encodedKey = Base64.encodeToString(key, Base64.NO_PADDING)
|
val encodedKey = Base64.encodeToString(key, Base64.NO_PADDING)
|
||||||
val toStore = SecretStoringUtils.securelyStoreString(encodedKey, alias, context)
|
val toStore = secretStoringUtils.securelyStoreString(encodedKey, alias)
|
||||||
val sharedPreferences = getSharedPreferences(context)
|
|
||||||
sharedPreferences
|
sharedPreferences
|
||||||
.edit()
|
.edit()
|
||||||
.putString("${ENCRYPTED_KEY_PREFIX}_$alias", Base64.encodeToString(toStore!!, Base64.NO_PADDING))
|
.putString("${ENCRYPTED_KEY_PREFIX}_$alias", Base64.encodeToString(toStore!!, Base64.NO_PADDING))
|
||||||
@ -77,30 +78,43 @@ private object RealmKeysUtils {
|
|||||||
* Retrieves the key for this database
|
* Retrieves the key for this database
|
||||||
* throws if something goes wrong
|
* throws if something goes wrong
|
||||||
*/
|
*/
|
||||||
fun extractKeyForDatabase(alias: String, context: Context): ByteArray {
|
private fun extractKeyForDatabase(alias: String): ByteArray {
|
||||||
val sharedPreferences = getSharedPreferences(context)
|
|
||||||
val encryptedB64 = sharedPreferences.getString("${ENCRYPTED_KEY_PREFIX}_$alias", null)
|
val encryptedB64 = sharedPreferences.getString("${ENCRYPTED_KEY_PREFIX}_$alias", null)
|
||||||
val encryptedKey = Base64.decode(encryptedB64, Base64.NO_PADDING)
|
val encryptedKey = Base64.decode(encryptedB64, Base64.NO_PADDING)
|
||||||
val b64 = SecretStoringUtils.loadSecureSecret(encryptedKey, alias, context)
|
val b64 = secretStoringUtils.loadSecureSecret(encryptedKey, alias)
|
||||||
return Base64.decode(b64!!, Base64.NO_PADDING)
|
return Base64.decode(b64!!, Base64.NO_PADDING)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getSharedPreferences(context: Context) =
|
fun configureEncryption(realmConfigurationBuilder: RealmConfiguration.Builder, alias: String) {
|
||||||
context.getSharedPreferences("im.vector.matrix.android.keys", Context.MODE_PRIVATE)
|
val key = if (hasKeyForDatabase(alias)) {
|
||||||
}
|
Timber.i("Found key for alias:$alias")
|
||||||
|
extractKeyForDatabase(alias)
|
||||||
|
} else {
|
||||||
fun RealmConfiguration.Builder.configureEncryption(alias: String, context: Context): RealmConfiguration.Builder {
|
Timber.i("Create key for DB alias:$alias")
|
||||||
if (RealmKeysUtils.hasKeyForDatabase(alias, context)) {
|
createAndSaveKeyForDatabase(alias)
|
||||||
Timber.i("Found key for alias:$alias")
|
|
||||||
RealmKeysUtils.extractKeyForDatabase(alias, context).also {
|
|
||||||
this.encryptionKey(it)
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
Timber.i("Create key for DB alias:$alias")
|
if (BuildConfig.LOG_PRIVATE_DATA) {
|
||||||
RealmKeysUtils.createAndSaveKeyForDatabase(alias, context).also {
|
val log = key.joinToString("") { "%02x".format(it) }
|
||||||
this.encryptionKey(it)
|
Timber.w("Database key for alias `$alias`: $log")
|
||||||
|
}
|
||||||
|
|
||||||
|
realmConfigurationBuilder.encryptionKey(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete elements related to the alias
|
||||||
|
fun clear(alias: String) {
|
||||||
|
if (hasKeyForDatabase(alias)) {
|
||||||
|
secretStoringUtils.safeDeleteKey(alias)
|
||||||
|
|
||||||
|
sharedPreferences
|
||||||
|
.edit()
|
||||||
|
.remove("${ENCRYPTED_KEY_PREFIX}_$alias")
|
||||||
|
.apply()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this
|
|
||||||
|
companion object {
|
||||||
|
private const val ENCRYPTED_KEY_PREFIX = "REALM_ENCRYPTED_KEY"
|
||||||
|
}
|
||||||
}
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* 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.mapper
|
||||||
|
|
||||||
|
import im.vector.matrix.android.api.session.room.send.UserDraft
|
||||||
|
import im.vector.matrix.android.internal.database.model.DraftEntity
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DraftEntity <-> UserDraft
|
||||||
|
*/
|
||||||
|
internal object DraftMapper {
|
||||||
|
|
||||||
|
fun map(entity: DraftEntity): UserDraft {
|
||||||
|
return when (entity.draftMode) {
|
||||||
|
DraftEntity.MODE_REGULAR -> UserDraft.REGULAR(entity.content)
|
||||||
|
DraftEntity.MODE_EDIT -> UserDraft.EDIT(entity.linkedEventId, entity.content)
|
||||||
|
DraftEntity.MODE_QUOTE -> UserDraft.QUOTE(entity.linkedEventId, entity.content)
|
||||||
|
DraftEntity.MODE_REPLY -> UserDraft.REPLY(entity.linkedEventId, entity.content)
|
||||||
|
else -> null
|
||||||
|
} ?: UserDraft.REGULAR("")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun map(domain: UserDraft): DraftEntity {
|
||||||
|
return when (domain) {
|
||||||
|
is UserDraft.REGULAR -> DraftEntity(content = domain.text, draftMode = DraftEntity.MODE_REGULAR, linkedEventId = "")
|
||||||
|
is UserDraft.EDIT -> DraftEntity(content = domain.text, draftMode = DraftEntity.MODE_EDIT, linkedEventId = domain.linkedEventId)
|
||||||
|
is UserDraft.QUOTE -> DraftEntity(content = domain.text, draftMode = DraftEntity.MODE_QUOTE, linkedEventId = domain.linkedEventId)
|
||||||
|
is UserDraft.REPLY -> DraftEntity(content = domain.text, draftMode = DraftEntity.MODE_REPLY, linkedEventId = domain.linkedEventId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -22,14 +22,15 @@ import im.vector.matrix.android.internal.database.model.GroupSummaryEntity
|
|||||||
|
|
||||||
internal object GroupSummaryMapper {
|
internal object GroupSummaryMapper {
|
||||||
|
|
||||||
fun map(roomSummaryEntity: GroupSummaryEntity): GroupSummary {
|
fun map(groupSummaryEntity: GroupSummaryEntity): GroupSummary {
|
||||||
return GroupSummary(
|
return GroupSummary(
|
||||||
roomSummaryEntity.groupId,
|
groupSummaryEntity.groupId,
|
||||||
roomSummaryEntity.displayName,
|
groupSummaryEntity.membership,
|
||||||
roomSummaryEntity.shortDescription,
|
groupSummaryEntity.displayName,
|
||||||
roomSummaryEntity.avatarUrl,
|
groupSummaryEntity.shortDescription,
|
||||||
roomSummaryEntity.roomIds.toList(),
|
groupSummaryEntity.avatarUrl,
|
||||||
roomSummaryEntity.userIds.toList()
|
groupSummaryEntity.roomIds.toList(),
|
||||||
|
groupSummaryEntity.userIds.toList()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,6 @@ internal object PushersMapper {
|
|||||||
fun map(pushEntity: PusherEntity): Pusher {
|
fun map(pushEntity: PusherEntity): Pusher {
|
||||||
|
|
||||||
return Pusher(
|
return Pusher(
|
||||||
userId = pushEntity.userId,
|
|
||||||
pushKey = pushEntity.pushKey,
|
pushKey = pushEntity.pushKey,
|
||||||
kind = pushEntity.kind ?: "",
|
kind = pushEntity.kind ?: "",
|
||||||
appId = pushEntity.appId,
|
appId = pushEntity.appId,
|
||||||
@ -39,9 +38,8 @@ internal object PushersMapper {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun map(pusher: JsonPusher, userId: String): PusherEntity {
|
fun map(pusher: JsonPusher): PusherEntity {
|
||||||
return PusherEntity(
|
return PusherEntity(
|
||||||
userId = userId,
|
|
||||||
pushKey = pusher.pushKey,
|
pushKey = pusher.pushKey,
|
||||||
kind = pusher.kind,
|
kind = pusher.kind,
|
||||||
appId = pusher.appId,
|
appId = pusher.appId,
|
||||||
@ -58,6 +56,6 @@ internal fun PusherEntity.asDomain(): Pusher {
|
|||||||
return PushersMapper.map(this)
|
return PushersMapper.map(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun JsonPusher.toEntity(userId: String): PusherEntity {
|
internal fun JsonPusher.toEntity(): PusherEntity {
|
||||||
return PushersMapper.map(this, userId)
|
return PushersMapper.map(this)
|
||||||
}
|
}
|
@ -26,8 +26,8 @@ import java.util.*
|
|||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class RoomSummaryMapper @Inject constructor(
|
internal class RoomSummaryMapper @Inject constructor(
|
||||||
val cryptoService: CryptoService,
|
private val cryptoService: CryptoService,
|
||||||
val timelineEventMapper: TimelineEventMapper
|
private val timelineEventMapper: TimelineEventMapper
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun map(roomSummaryEntity: RoomSummaryEntity): RoomSummary {
|
fun map(roomSummaryEntity: RoomSummaryEntity): RoomSummary {
|
||||||
@ -35,7 +35,7 @@ internal class RoomSummaryMapper @Inject constructor(
|
|||||||
RoomTag(it.tagName, it.tagOrder)
|
RoomTag(it.tagName, it.tagOrder)
|
||||||
}
|
}
|
||||||
|
|
||||||
val latestEvent = roomSummaryEntity.latestEvent?.let {
|
val latestEvent = roomSummaryEntity.latestPreviewableEvent?.let {
|
||||||
timelineEventMapper.map(it)
|
timelineEventMapper.map(it)
|
||||||
}
|
}
|
||||||
if (latestEvent?.root?.isEncrypted() == true && latestEvent.root.mxDecryptionResult == null) {
|
if (latestEvent?.root?.isEncrypted() == true && latestEvent.root.mxDecryptionResult == null) {
|
||||||
@ -43,29 +43,32 @@ internal class RoomSummaryMapper @Inject constructor(
|
|||||||
//for now decrypt sync
|
//for now decrypt sync
|
||||||
try {
|
try {
|
||||||
val result = cryptoService.decryptEvent(latestEvent.root, latestEvent.root.roomId + UUID.randomUUID().toString())
|
val result = cryptoService.decryptEvent(latestEvent.root, latestEvent.root.roomId + UUID.randomUUID().toString())
|
||||||
latestEvent.root.mxDecryptionResult = OlmDecryptionResult(
|
latestEvent.root.mxDecryptionResult = OlmDecryptionResult(
|
||||||
payload = result.clearEvent,
|
payload = result.clearEvent,
|
||||||
senderKey = result.senderCurve25519Key,
|
senderKey = result.senderCurve25519Key,
|
||||||
keysClaimed = result.claimedEd25519Key?.let { mapOf("ed25519" to it) },
|
keysClaimed = result.claimedEd25519Key?.let { mapOf("ed25519" to it) },
|
||||||
forwardingCurve25519KeyChain = result.forwardingCurve25519KeyChain
|
forwardingCurve25519KeyChain = result.forwardingCurve25519KeyChain
|
||||||
)
|
)
|
||||||
} catch (e: MXCryptoError) {
|
} catch (e: MXCryptoError) {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return RoomSummary(
|
return RoomSummary(
|
||||||
roomId = roomSummaryEntity.roomId,
|
roomId = roomSummaryEntity.roomId,
|
||||||
displayName = roomSummaryEntity.displayName ?: "",
|
displayName = roomSummaryEntity.displayName ?: "",
|
||||||
topic = roomSummaryEntity.topic ?: "",
|
topic = roomSummaryEntity.topic ?: "",
|
||||||
avatarUrl = roomSummaryEntity.avatarUrl ?: "",
|
avatarUrl = roomSummaryEntity.avatarUrl ?: "",
|
||||||
isDirect = roomSummaryEntity.isDirect,
|
isDirect = roomSummaryEntity.isDirect,
|
||||||
latestEvent = latestEvent,
|
latestPreviewableEvent = latestEvent,
|
||||||
otherMemberIds = roomSummaryEntity.otherMemberIds.toList(),
|
otherMemberIds = roomSummaryEntity.otherMemberIds.toList(),
|
||||||
highlightCount = roomSummaryEntity.highlightCount,
|
highlightCount = roomSummaryEntity.highlightCount,
|
||||||
notificationCount = roomSummaryEntity.notificationCount,
|
notificationCount = roomSummaryEntity.notificationCount,
|
||||||
|
hasUnreadMessages = roomSummaryEntity.hasUnreadMessages,
|
||||||
tags = tags,
|
tags = tags,
|
||||||
membership = roomSummaryEntity.membership,
|
membership = roomSummaryEntity.membership,
|
||||||
versioningState = roomSummaryEntity.versioningState
|
versioningState = roomSummaryEntity.versioningState,
|
||||||
|
userDrafts = roomSummaryEntity.userDrafts?.userDrafts?.map { DraftMapper.map(it) } ?: emptyList()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.matrix.android.internal.database.model
|
||||||
|
|
||||||
|
import io.realm.RealmObject
|
||||||
|
|
||||||
|
internal open class DraftEntity(var content: String = "",
|
||||||
|
var draftMode: String = MODE_REGULAR,
|
||||||
|
var linkedEventId: String = ""
|
||||||
|
|
||||||
|
) : RealmObject() {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val MODE_REGULAR = "REGULAR"
|
||||||
|
const val MODE_EDIT = "EDIT"
|
||||||
|
const val MODE_REPLY = "REPLY"
|
||||||
|
const val MODE_QUOTE = "QUOTE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -20,9 +20,13 @@ import im.vector.matrix.android.api.session.room.model.Membership
|
|||||||
import io.realm.RealmObject
|
import io.realm.RealmObject
|
||||||
import io.realm.annotations.PrimaryKey
|
import io.realm.annotations.PrimaryKey
|
||||||
|
|
||||||
internal open class GroupEntity(@PrimaryKey var groupId: String = ""
|
/**
|
||||||
|
* This class is used to store group info (groupId and membership) from the sync response.
|
||||||
) : RealmObject() {
|
* Then [im.vector.matrix.android.internal.session.group.GroupSummaryUpdater] observes change and
|
||||||
|
* makes requests to fetch group information from the homeserver
|
||||||
|
*/
|
||||||
|
internal open class GroupEntity(@PrimaryKey var groupId: String = "")
|
||||||
|
: RealmObject() {
|
||||||
|
|
||||||
private var membershipStr: String = Membership.NONE.name
|
private var membershipStr: String = Membership.NONE.name
|
||||||
var membership: Membership
|
var membership: Membership
|
||||||
|
@ -16,18 +16,28 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.database.model
|
package im.vector.matrix.android.internal.database.model
|
||||||
|
|
||||||
|
import im.vector.matrix.android.api.session.room.model.Membership
|
||||||
import io.realm.RealmList
|
import io.realm.RealmList
|
||||||
import io.realm.RealmObject
|
import io.realm.RealmObject
|
||||||
import io.realm.annotations.PrimaryKey
|
import io.realm.annotations.PrimaryKey
|
||||||
|
|
||||||
internal open class GroupSummaryEntity(@PrimaryKey var groupId: String = "",
|
internal open class GroupSummaryEntity(@PrimaryKey var groupId: String = "",
|
||||||
var displayName: String = "",
|
var displayName: String = "",
|
||||||
var shortDescription: String = "",
|
var shortDescription: String = "",
|
||||||
var avatarUrl: String = "",
|
var avatarUrl: String = "",
|
||||||
var roomIds: RealmList<String> = RealmList(),
|
var roomIds: RealmList<String> = RealmList(),
|
||||||
var userIds: RealmList<String> = RealmList()
|
var userIds: RealmList<String> = RealmList()
|
||||||
) : RealmObject() {
|
) : RealmObject() {
|
||||||
|
|
||||||
|
private var membershipStr: String = Membership.NONE.name
|
||||||
|
var membership: Membership
|
||||||
|
get() {
|
||||||
|
return Membership.valueOf(membershipStr)
|
||||||
|
}
|
||||||
|
set(value) {
|
||||||
|
membershipStr = value.name
|
||||||
|
}
|
||||||
|
|
||||||
companion object
|
companion object
|
||||||
|
|
||||||
}
|
}
|
@ -15,17 +15,24 @@
|
|||||||
*/
|
*/
|
||||||
package im.vector.matrix.android.internal.database.model
|
package im.vector.matrix.android.internal.database.model
|
||||||
|
|
||||||
|
import im.vector.matrix.android.api.pushrules.RuleKind
|
||||||
import io.realm.RealmList
|
import io.realm.RealmList
|
||||||
import io.realm.RealmObject
|
import io.realm.RealmObject
|
||||||
import io.realm.annotations.Index
|
|
||||||
|
|
||||||
|
|
||||||
internal open class PushRulesEntity(
|
internal open class PushRulesEntity(
|
||||||
@Index var userId: String = "",
|
|
||||||
var scope: String = "",
|
var scope: String = "",
|
||||||
// "content", etc.
|
|
||||||
var rulesetKey: String = "",
|
|
||||||
var pushRules: RealmList<PushRuleEntity> = RealmList()
|
var pushRules: RealmList<PushRuleEntity> = RealmList()
|
||||||
) : RealmObject() {
|
) : RealmObject() {
|
||||||
|
|
||||||
|
private var kindStr: String = RuleKind.CONTENT.name
|
||||||
|
var kind: RuleKind
|
||||||
|
get() {
|
||||||
|
return RuleKind.valueOf(kindStr)
|
||||||
|
}
|
||||||
|
set(value) {
|
||||||
|
kindStr = value.name
|
||||||
|
}
|
||||||
|
|
||||||
companion object
|
companion object
|
||||||
}
|
}
|
@ -17,7 +17,6 @@ package im.vector.matrix.android.internal.database.model
|
|||||||
|
|
||||||
import im.vector.matrix.android.api.session.pushers.PusherState
|
import im.vector.matrix.android.api.session.pushers.PusherState
|
||||||
import io.realm.RealmObject
|
import io.realm.RealmObject
|
||||||
import io.realm.annotations.Index
|
|
||||||
|
|
||||||
//TODO
|
//TODO
|
||||||
// at java.lang.Thread.run(Thread.java:764)
|
// at java.lang.Thread.run(Thread.java:764)
|
||||||
@ -29,7 +28,6 @@ import io.realm.annotations.Index
|
|||||||
// at im.vector.matrix.android.internal.session.pushers.AddHttpPusherWorker$doWork$$inlined$fold$lambda$2.execute(AddHttpPusherWorker.kt:70)
|
// at im.vector.matrix.android.internal.session.pushers.AddHttpPusherWorker$doWork$$inlined$fold$lambda$2.execute(AddHttpPusherWorker.kt:70)
|
||||||
// at io.realm.Realm.executeTransaction(Realm.java:1493)
|
// at io.realm.Realm.executeTransaction(Realm.java:1493)
|
||||||
internal open class PusherEntity(
|
internal open class PusherEntity(
|
||||||
@Index var userId: String = "",
|
|
||||||
var pushKey: String = "",
|
var pushKey: String = "",
|
||||||
var kind: String? = null,
|
var kind: String? = null,
|
||||||
var appId: String = "",
|
var appId: String = "",
|
||||||
|
@ -26,7 +26,7 @@ internal open class RoomSummaryEntity(@PrimaryKey var roomId: String = "",
|
|||||||
var displayName: String? = "",
|
var displayName: String? = "",
|
||||||
var avatarUrl: String? = "",
|
var avatarUrl: String? = "",
|
||||||
var topic: String? = "",
|
var topic: String? = "",
|
||||||
var latestEvent: TimelineEventEntity? = null,
|
var latestPreviewableEvent: TimelineEventEntity? = null,
|
||||||
var heroes: RealmList<String> = RealmList(),
|
var heroes: RealmList<String> = RealmList(),
|
||||||
var joinedMembersCount: Int? = 0,
|
var joinedMembersCount: Int? = 0,
|
||||||
var invitedMembersCount: Int? = 0,
|
var invitedMembersCount: Int? = 0,
|
||||||
@ -35,7 +35,9 @@ internal open class RoomSummaryEntity(@PrimaryKey var roomId: String = "",
|
|||||||
var otherMemberIds: RealmList<String> = RealmList(),
|
var otherMemberIds: RealmList<String> = RealmList(),
|
||||||
var notificationCount: Int = 0,
|
var notificationCount: Int = 0,
|
||||||
var highlightCount: Int = 0,
|
var highlightCount: Int = 0,
|
||||||
var tags: RealmList<RoomTagEntity> = RealmList()
|
var hasUnreadMessages: Boolean = false,
|
||||||
|
var tags: RealmList<RoomTagEntity> = RealmList(),
|
||||||
|
var userDrafts: UserDraftsEntity? = null
|
||||||
) : RealmObject() {
|
) : RealmObject() {
|
||||||
|
|
||||||
private var membershipStr: String = Membership.NONE.name
|
private var membershipStr: String = Membership.NONE.name
|
||||||
|
@ -43,6 +43,8 @@ import io.realm.annotations.RealmModule
|
|||||||
PushConditionEntity::class,
|
PushConditionEntity::class,
|
||||||
PusherEntity::class,
|
PusherEntity::class,
|
||||||
PusherDataEntity::class,
|
PusherDataEntity::class,
|
||||||
ReadReceiptsSummaryEntity::class
|
ReadReceiptsSummaryEntity::class,
|
||||||
|
UserDraftsEntity::class,
|
||||||
|
DraftEntity::class
|
||||||
])
|
])
|
||||||
internal class SessionRealmModule
|
internal class SessionRealmModule
|
||||||
|
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* 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.model
|
||||||
|
|
||||||
|
import io.realm.RealmList
|
||||||
|
import io.realm.RealmObject
|
||||||
|
import io.realm.RealmResults
|
||||||
|
import io.realm.annotations.LinkingObjects
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a specific table to be able to do direct query on it and keep the draft ordered
|
||||||
|
*/
|
||||||
|
internal open class UserDraftsEntity(var userDrafts: RealmList<DraftEntity> = RealmList()
|
||||||
|
) : RealmObject() {
|
||||||
|
|
||||||
|
// Link to RoomSummaryEntity
|
||||||
|
@LinkingObjects("userDrafts")
|
||||||
|
val roomSummaryEntity: RealmResults<RoomSummaryEntity>? = null
|
||||||
|
|
||||||
|
companion object
|
||||||
|
|
||||||
|
}
|
@ -23,9 +23,9 @@ import io.realm.Realm
|
|||||||
import io.realm.RealmQuery
|
import io.realm.RealmQuery
|
||||||
import io.realm.kotlin.where
|
import io.realm.kotlin.where
|
||||||
|
|
||||||
internal fun GroupEntity.Companion.where(realm: Realm, roomId: String): RealmQuery<GroupEntity> {
|
internal fun GroupEntity.Companion.where(realm: Realm, groupId: String): RealmQuery<GroupEntity> {
|
||||||
return realm.where<GroupEntity>()
|
return realm.where<GroupEntity>()
|
||||||
.equalTo(GroupEntityFields.GROUP_ID, roomId)
|
.equalTo(GroupEntityFields.GROUP_ID, groupId)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun GroupEntity.Companion.where(realm: Realm, membership: Membership? = null): RealmQuery<GroupEntity> {
|
internal fun GroupEntity.Companion.where(realm: Realm, membership: Membership? = null): RealmQuery<GroupEntity> {
|
||||||
|
@ -30,3 +30,7 @@ internal fun GroupSummaryEntity.Companion.where(realm: Realm, groupId: String? =
|
|||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun GroupSummaryEntity.Companion.where(realm: Realm, groupIds: List<String>): RealmQuery<GroupSummaryEntity> {
|
||||||
|
return realm.where<GroupSummaryEntity>()
|
||||||
|
.`in`(GroupSummaryEntityFields.GROUP_ID, groupIds.toTypedArray())
|
||||||
|
}
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package im.vector.matrix.android.internal.database.query
|
package im.vector.matrix.android.internal.database.query
|
||||||
|
|
||||||
|
import im.vector.matrix.android.api.pushrules.RuleKind
|
||||||
import im.vector.matrix.android.internal.database.model.PushRulesEntity
|
import im.vector.matrix.android.internal.database.model.PushRulesEntity
|
||||||
import im.vector.matrix.android.internal.database.model.PushRulesEntityFields
|
import im.vector.matrix.android.internal.database.model.PushRulesEntityFields
|
||||||
import im.vector.matrix.android.internal.database.model.PusherEntity
|
import im.vector.matrix.android.internal.database.model.PusherEntity
|
||||||
@ -24,10 +25,8 @@ import io.realm.RealmQuery
|
|||||||
import io.realm.kotlin.where
|
import io.realm.kotlin.where
|
||||||
|
|
||||||
internal fun PusherEntity.Companion.where(realm: Realm,
|
internal fun PusherEntity.Companion.where(realm: Realm,
|
||||||
userId: String,
|
|
||||||
pushKey: String? = null): RealmQuery<PusherEntity> {
|
pushKey: String? = null): RealmQuery<PusherEntity> {
|
||||||
return realm.where<PusherEntity>()
|
return realm.where<PusherEntity>()
|
||||||
.equalTo(PusherEntityFields.USER_ID, userId)
|
|
||||||
.apply {
|
.apply {
|
||||||
if (pushKey != null) {
|
if (pushKey != null) {
|
||||||
equalTo(PusherEntityFields.PUSH_KEY, pushKey)
|
equalTo(PusherEntityFields.PUSH_KEY, pushKey)
|
||||||
@ -36,11 +35,9 @@ internal fun PusherEntity.Companion.where(realm: Realm,
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun PushRulesEntity.Companion.where(realm: Realm,
|
internal fun PushRulesEntity.Companion.where(realm: Realm,
|
||||||
userId: String,
|
|
||||||
scope: String,
|
scope: String,
|
||||||
ruleSetKey: String): RealmQuery<PushRulesEntity> {
|
kind: RuleKind): RealmQuery<PushRulesEntity> {
|
||||||
return realm.where<PushRulesEntity>()
|
return realm.where<PushRulesEntity>()
|
||||||
.equalTo(PushRulesEntityFields.USER_ID, userId)
|
|
||||||
.equalTo(PushRulesEntityFields.SCOPE, scope)
|
.equalTo(PushRulesEntityFields.SCOPE, scope)
|
||||||
.equalTo(PushRulesEntityFields.RULESET_KEY, ruleSetKey)
|
.equalTo(PushRulesEntityFields.KIND_STR, kind.name)
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
import com.zhuinden.monarchy.Monarchy
|
||||||
|
import im.vector.matrix.android.internal.database.model.ChunkEntity
|
||||||
|
import im.vector.matrix.android.internal.database.model.ReadReceiptEntity
|
||||||
|
|
||||||
|
internal fun isEventRead(monarchy: Monarchy,
|
||||||
|
userId: String?,
|
||||||
|
roomId: String?,
|
||||||
|
eventId: String?): Boolean {
|
||||||
|
if (userId.isNullOrBlank() || roomId.isNullOrBlank() || eventId.isNullOrBlank()) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var isEventRead = false
|
||||||
|
|
||||||
|
monarchy.doWithRealm { realm ->
|
||||||
|
val liveChunk = ChunkEntity.findLastLiveChunkFromRoom(realm, roomId) ?: return@doWithRealm
|
||||||
|
val eventToCheck = liveChunk.timelineEvents.find(eventId)?.root
|
||||||
|
|
||||||
|
isEventRead = if (eventToCheck?.sender == userId) {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
val readReceipt = ReadReceiptEntity.where(realm, roomId, userId).findFirst() ?: return@doWithRealm
|
||||||
|
val readReceiptIndex = liveChunk.timelineEvents.find(readReceipt.eventId)?.root?.displayIndex ?: Int.MIN_VALUE
|
||||||
|
val eventToCheckIndex = eventToCheck?.displayIndex ?: Int.MAX_VALUE
|
||||||
|
|
||||||
|
eventToCheckIndex <= readReceiptIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return isEventRead
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
import im.vector.matrix.android.internal.database.model.RoomSummaryEntityFields
|
||||||
|
import im.vector.matrix.android.internal.database.model.UserDraftsEntity
|
||||||
|
import im.vector.matrix.android.internal.database.model.UserDraftsEntityFields
|
||||||
|
import io.realm.Realm
|
||||||
|
import io.realm.RealmQuery
|
||||||
|
import io.realm.kotlin.where
|
||||||
|
|
||||||
|
internal fun UserDraftsEntity.Companion.where(realm: Realm, roomId: String? = null): RealmQuery<UserDraftsEntity> {
|
||||||
|
val query = realm.where<UserDraftsEntity>()
|
||||||
|
if (roomId != null) {
|
||||||
|
query.equalTo(UserDraftsEntityFields.ROOM_SUMMARY_ENTITY + "." + RoomSummaryEntityFields.ROOM_ID, roomId)
|
||||||
|
}
|
||||||
|
return query
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* 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.di
|
||||||
|
|
||||||
|
import javax.inject.Qualifier
|
||||||
|
|
||||||
|
@Qualifier
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
annotation class UserCacheDirectory
|
@ -18,6 +18,9 @@ package im.vector.matrix.android.internal.di
|
|||||||
|
|
||||||
import javax.inject.Scope
|
import javax.inject.Scope
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use the annotation @MatrixScope to annotate classes we want the SDK to instantiate only once
|
||||||
|
*/
|
||||||
@Scope
|
@Scope
|
||||||
@MustBeDocumented
|
@MustBeDocumented
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
@ -23,6 +23,7 @@ import im.vector.matrix.android.internal.network.parsing.UriMoshiAdapter
|
|||||||
import im.vector.matrix.android.internal.session.sync.model.UserAccountData
|
import im.vector.matrix.android.internal.session.sync.model.UserAccountData
|
||||||
import im.vector.matrix.android.internal.session.sync.model.UserAccountDataDirectMessages
|
import im.vector.matrix.android.internal.session.sync.model.UserAccountDataDirectMessages
|
||||||
import im.vector.matrix.android.internal.session.sync.model.UserAccountDataFallback
|
import im.vector.matrix.android.internal.session.sync.model.UserAccountDataFallback
|
||||||
|
import im.vector.matrix.android.internal.session.sync.model.UserAccountDataPushRules
|
||||||
|
|
||||||
|
|
||||||
object MoshiProvider {
|
object MoshiProvider {
|
||||||
@ -31,6 +32,7 @@ object MoshiProvider {
|
|||||||
.add(UriMoshiAdapter())
|
.add(UriMoshiAdapter())
|
||||||
.add(RuntimeJsonAdapterFactory.of(UserAccountData::class.java, "type", UserAccountDataFallback::class.java)
|
.add(RuntimeJsonAdapterFactory.of(UserAccountData::class.java, "type", UserAccountDataFallback::class.java)
|
||||||
.registerSubtype(UserAccountDataDirectMessages::class.java, UserAccountData.TYPE_DIRECT_MESSAGES)
|
.registerSubtype(UserAccountDataDirectMessages::class.java, UserAccountData.TYPE_DIRECT_MESSAGES)
|
||||||
|
.registerSubtype(UserAccountDataPushRules::class.java, UserAccountData.TYPE_PUSH_RULES)
|
||||||
)
|
)
|
||||||
.add(RuntimeJsonAdapterFactory.of(MessageContent::class.java, "msgtype", MessageDefaultContent::class.java)
|
.add(RuntimeJsonAdapterFactory.of(MessageContent::class.java, "msgtype", MessageDefaultContent::class.java)
|
||||||
.registerSubtype(MessageTextContent::class.java, MessageType.MSGTYPE_TEXT)
|
.registerSubtype(MessageTextContent::class.java, MessageType.MSGTYPE_TEXT)
|
||||||
|
@ -35,7 +35,7 @@ internal object NetworkModule {
|
|||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun providesHttpLogingInterceptor(): HttpLoggingInterceptor {
|
fun providesHttpLoggingInterceptor(): HttpLoggingInterceptor {
|
||||||
val logger = FormattedJsonHttpLogger()
|
val logger = FormattedJsonHttpLogger()
|
||||||
val interceptor = HttpLoggingInterceptor(logger)
|
val interceptor = HttpLoggingInterceptor(logger)
|
||||||
interceptor.level = BuildConfig.OKHTTP_LOGGING_LEVEL
|
interceptor.level = BuildConfig.OKHTTP_LOGGING_LEVEL
|
||||||
|
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* 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.di
|
||||||
|
|
||||||
|
import javax.inject.Qualifier
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to inject the userId
|
||||||
|
*/
|
||||||
|
@Qualifier
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
internal annotation class UserId
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to inject the md5 of the userId
|
||||||
|
*/
|
||||||
|
@Qualifier
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
internal annotation class UserMd5
|
@ -20,44 +20,64 @@ import android.content.Context
|
|||||||
import com.novoda.merlin.Merlin
|
import com.novoda.merlin.Merlin
|
||||||
import com.novoda.merlin.MerlinsBeard
|
import com.novoda.merlin.MerlinsBeard
|
||||||
import im.vector.matrix.android.internal.di.MatrixScope
|
import im.vector.matrix.android.internal.di.MatrixScope
|
||||||
|
import im.vector.matrix.android.internal.util.BackgroundDetectionObserver
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.collections.ArrayList
|
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
import kotlin.coroutines.suspendCoroutine
|
import kotlin.coroutines.suspendCoroutine
|
||||||
|
|
||||||
@MatrixScope
|
@MatrixScope
|
||||||
internal class NetworkConnectivityChecker @Inject constructor(context: Context) {
|
internal class NetworkConnectivityChecker @Inject constructor(context: Context,
|
||||||
|
backgroundDetectionObserver: BackgroundDetectionObserver)
|
||||||
|
: BackgroundDetectionObserver.Listener {
|
||||||
|
|
||||||
private val merlin = Merlin.Builder()
|
private val merlin = Merlin.Builder()
|
||||||
.withConnectableCallbacks()
|
.withConnectableCallbacks()
|
||||||
.withDisconnectableCallbacks()
|
.withDisconnectableCallbacks()
|
||||||
.build(context)
|
.build(context)
|
||||||
|
|
||||||
private val merlinsBeard = MerlinsBeard.Builder().build(context)
|
private val listeners = Collections.synchronizedSet(LinkedHashSet<Listener>())
|
||||||
private val listeners = Collections.synchronizedList(ArrayList<Listener>())
|
|
||||||
|
// True when internet is available
|
||||||
|
var hasInternetAccess = MerlinsBeard.Builder().build(context).isConnected
|
||||||
|
private set
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
backgroundDetectionObserver.register(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMoveToForeground() {
|
||||||
merlin.bind()
|
merlin.bind()
|
||||||
|
|
||||||
merlin.registerDisconnectable {
|
merlin.registerDisconnectable {
|
||||||
Timber.v("On Disconnect")
|
if (hasInternetAccess) {
|
||||||
val localListeners = listeners.toList()
|
Timber.v("On Disconnect")
|
||||||
localListeners.forEach {
|
hasInternetAccess = false
|
||||||
it.onDisconnect()
|
val localListeners = listeners.toList()
|
||||||
|
localListeners.forEach {
|
||||||
|
it.onDisconnect()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
merlin.registerConnectable {
|
merlin.registerConnectable {
|
||||||
Timber.v("On Connect")
|
if (!hasInternetAccess) {
|
||||||
val localListeners = listeners.toList()
|
Timber.v("On Connect")
|
||||||
localListeners.forEach {
|
hasInternetAccess = true
|
||||||
it.onConnect()
|
val localListeners = listeners.toList()
|
||||||
|
localListeners.forEach {
|
||||||
|
it.onConnect()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onMoveToBackground() {
|
||||||
|
merlin.unbind()
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun waitUntilConnected() {
|
suspend fun waitUntilConnected() {
|
||||||
if (isConnected()) {
|
if (hasInternetAccess) {
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
suspendCoroutine<Unit> { continuation ->
|
suspendCoroutine<Unit> { continuation ->
|
||||||
@ -79,10 +99,6 @@ internal class NetworkConnectivityChecker @Inject constructor(context: Context)
|
|||||||
listeners.remove(listener)
|
listeners.remove(listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isConnected(): Boolean {
|
|
||||||
return merlinsBeard.isConnected
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Listener {
|
interface Listener {
|
||||||
fun onConnect() {
|
fun onConnect() {
|
||||||
|
|
||||||
|
@ -18,10 +18,13 @@ package im.vector.matrix.android.internal.network
|
|||||||
|
|
||||||
import com.squareup.moshi.JsonDataException
|
import com.squareup.moshi.JsonDataException
|
||||||
import com.squareup.moshi.Moshi
|
import com.squareup.moshi.Moshi
|
||||||
|
import im.vector.matrix.android.api.failure.ConsentNotGivenError
|
||||||
import im.vector.matrix.android.api.failure.Failure
|
import im.vector.matrix.android.api.failure.Failure
|
||||||
import im.vector.matrix.android.api.failure.MatrixError
|
import im.vector.matrix.android.api.failure.MatrixError
|
||||||
import im.vector.matrix.android.internal.di.MoshiProvider
|
import im.vector.matrix.android.internal.di.MoshiProvider
|
||||||
|
import kotlinx.coroutines.CancellationException
|
||||||
import okhttp3.ResponseBody
|
import okhttp3.ResponseBody
|
||||||
|
import org.greenrobot.eventbus.EventBus
|
||||||
import retrofit2.Call
|
import retrofit2.Call
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
@ -47,6 +50,7 @@ internal class Request<DATA> {
|
|||||||
is IOException -> Failure.NetworkConnection(exception)
|
is IOException -> Failure.NetworkConnection(exception)
|
||||||
is Failure.ServerError,
|
is Failure.ServerError,
|
||||||
is Failure.OtherServerError -> exception
|
is Failure.OtherServerError -> exception
|
||||||
|
is CancellationException -> Failure.Cancelled(exception)
|
||||||
else -> Failure.Unknown(exception)
|
else -> Failure.Unknown(exception)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -65,6 +69,11 @@ internal class Request<DATA> {
|
|||||||
val matrixError = matrixErrorAdapter.fromJson(errorBodyStr)
|
val matrixError = matrixErrorAdapter.fromJson(errorBodyStr)
|
||||||
|
|
||||||
if (matrixError != null) {
|
if (matrixError != null) {
|
||||||
|
if (matrixError.code == MatrixError.M_CONSENT_NOT_GIVEN && !matrixError.consentUri.isNullOrBlank()) {
|
||||||
|
// Also send this error to the bus, for a global management
|
||||||
|
EventBus.getDefault().post(ConsentNotGivenError(matrixError.consentUri))
|
||||||
|
}
|
||||||
|
|
||||||
return Failure.ServerError(matrixError, httpCode)
|
return Failure.ServerError(matrixError, httpCode)
|
||||||
}
|
}
|
||||||
} catch (ex: JsonDataException) {
|
} catch (ex: JsonDataException) {
|
||||||
|
@ -24,7 +24,7 @@ import timber.log.Timber
|
|||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@MatrixScope
|
@MatrixScope
|
||||||
internal class UserAgentHolder @Inject constructor(val context: Context) {
|
internal class UserAgentHolder @Inject constructor(private val context: Context) {
|
||||||
|
|
||||||
var userAgent: String = ""
|
var userAgent: String = ""
|
||||||
private set
|
private set
|
||||||
|
@ -24,7 +24,6 @@ import java.security.KeyStore
|
|||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
import java.security.cert.CertificateException
|
import java.security.cert.CertificateException
|
||||||
import java.security.cert.X509Certificate
|
import java.security.cert.X509Certificate
|
||||||
import java.util.*
|
|
||||||
import javax.net.ssl.*
|
import javax.net.ssl.*
|
||||||
import kotlin.experimental.and
|
import kotlin.experimental.and
|
||||||
|
|
||||||
|
@ -25,7 +25,6 @@ import java.net.UnknownHostException
|
|||||||
import java.security.KeyManagementException
|
import java.security.KeyManagementException
|
||||||
import java.security.NoSuchAlgorithmException
|
import java.security.NoSuchAlgorithmException
|
||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
import java.util.*
|
|
||||||
import javax.net.ssl.SSLContext
|
import javax.net.ssl.SSLContext
|
||||||
import javax.net.ssl.SSLSocket
|
import javax.net.ssl.SSLSocket
|
||||||
import javax.net.ssl.SSLSocketFactory
|
import javax.net.ssl.SSLSocketFactory
|
||||||
@ -101,25 +100,16 @@ constructor(trustPinned: Array<TrustManager>, acceptedTlsVersions: List<TlsVersi
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun enableTLSOnSocket(socket: Socket?): Socket? {
|
private fun enableTLSOnSocket(socket: Socket?): Socket? {
|
||||||
if (socket != null && socket is SSLSocket) {
|
if (socket is SSLSocket) {
|
||||||
val sslSocket = socket as SSLSocket?
|
val supportedProtocols = socket.supportedProtocols.toSet()
|
||||||
|
val filteredEnabledProtocols = enabledProtocols.filter { it in supportedProtocols }
|
||||||
|
|
||||||
val supportedProtocols = Arrays.asList(*sslSocket!!.supportedProtocols)
|
if (filteredEnabledProtocols.isNotEmpty()) {
|
||||||
val filteredEnabledProtocols = ArrayList<String>()
|
|
||||||
|
|
||||||
for (protocol in enabledProtocols) {
|
|
||||||
if (supportedProtocols.contains(protocol)) {
|
|
||||||
filteredEnabledProtocols.add(protocol)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!filteredEnabledProtocols.isEmpty()) {
|
|
||||||
try {
|
try {
|
||||||
sslSocket.enabledProtocols = filteredEnabledProtocols.toTypedArray()
|
socket.enabledProtocols = filteredEnabledProtocols.toTypedArray()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e)
|
Timber.e(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return socket
|
return socket
|
||||||
|
@ -20,11 +20,11 @@ import android.content.Context
|
|||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import arrow.core.Try
|
import arrow.core.Try
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.auth.data.SessionParams
|
|
||||||
import im.vector.matrix.android.api.session.content.ContentUrlResolver
|
import im.vector.matrix.android.api.session.content.ContentUrlResolver
|
||||||
import im.vector.matrix.android.api.session.file.FileService
|
import im.vector.matrix.android.api.session.file.FileService
|
||||||
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
|
||||||
|
import im.vector.matrix.android.internal.di.UserMd5
|
||||||
import im.vector.matrix.android.internal.extensions.foldToCallback
|
import im.vector.matrix.android.internal.extensions.foldToCallback
|
||||||
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
||||||
import im.vector.matrix.android.internal.util.md5
|
import im.vector.matrix.android.internal.util.md5
|
||||||
@ -40,7 +40,7 @@ import java.io.IOException
|
|||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class DefaultFileService @Inject constructor(private val context: Context,
|
internal class DefaultFileService @Inject constructor(private val context: Context,
|
||||||
private val sessionParams: SessionParams,
|
@UserMd5 private val userMd5: String,
|
||||||
private val contentUrlResolver: ContentUrlResolver,
|
private val contentUrlResolver: ContentUrlResolver,
|
||||||
private val coroutineDispatchers: MatrixCoroutineDispatchers) : FileService {
|
private val coroutineDispatchers: MatrixCoroutineDispatchers) : FileService {
|
||||||
|
|
||||||
@ -105,7 +105,7 @@ internal class DefaultFileService @Inject constructor(private val context: Conte
|
|||||||
// Create dir tree (MF stands for Matrix File):
|
// Create dir tree (MF stands for Matrix File):
|
||||||
// <cache>/MF/<md5(userId)>/<md5(id)>/
|
// <cache>/MF/<md5(userId)>/<md5(id)>/
|
||||||
val tmpFolderRoot = File(context.cacheDir, "MF")
|
val tmpFolderRoot = File(context.cacheDir, "MF")
|
||||||
val tmpFolderUser = File(tmpFolderRoot, sessionParams.credentials.userId.md5())
|
val tmpFolderUser = File(tmpFolderRoot, userMd5)
|
||||||
File(tmpFolderUser, id.md5())
|
File(tmpFolderUser, id.md5())
|
||||||
}
|
}
|
||||||
FileService.DownloadMode.TO_EXPORT -> {
|
FileService.DownloadMode.TO_EXPORT -> {
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package im.vector.matrix.android.internal.session
|
package im.vector.matrix.android.internal.session
|
||||||
|
|
||||||
|
import androidx.annotation.StringRes
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import im.vector.matrix.android.api.session.InitialSyncProgressService
|
import im.vector.matrix.android.api.session.InitialSyncProgressService
|
||||||
@ -25,31 +26,33 @@ import javax.inject.Inject
|
|||||||
@SessionScope
|
@SessionScope
|
||||||
class DefaultInitialSyncProgressService @Inject constructor() : InitialSyncProgressService {
|
class DefaultInitialSyncProgressService @Inject constructor() : InitialSyncProgressService {
|
||||||
|
|
||||||
var status = MutableLiveData<InitialSyncProgressService.Status>()
|
private var status = MutableLiveData<InitialSyncProgressService.Status>()
|
||||||
|
|
||||||
var rootTask: TaskInfo? = null
|
private var rootTask: TaskInfo? = null
|
||||||
|
|
||||||
override fun getLiveStatus(): LiveData<InitialSyncProgressService.Status?> {
|
override fun getInitialSyncProgressStatus(): LiveData<InitialSyncProgressService.Status?> {
|
||||||
return status
|
return status
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun startTask(@StringRes nameRes: Int, totalProgress: Int, parentWeight: Float = 1f) {
|
||||||
fun startTask(nameRes: Int, totalProgress: Int, parentWeight: Float = 1f) {
|
// Create a rootTask, or add a child to the leaf
|
||||||
if (rootTask == null) {
|
if (rootTask == null) {
|
||||||
rootTask = TaskInfo(nameRes, totalProgress)
|
rootTask = TaskInfo(nameRes, totalProgress)
|
||||||
} else {
|
} else {
|
||||||
val currentLeaf = rootTask!!.leaf()
|
val currentLeaf = rootTask!!.leaf()
|
||||||
val newTask = TaskInfo(nameRes, totalProgress)
|
|
||||||
newTask.parent = currentLeaf
|
val newTask = TaskInfo(nameRes,
|
||||||
newTask.offset = currentLeaf.currentProgress
|
totalProgress,
|
||||||
|
currentLeaf,
|
||||||
|
parentWeight)
|
||||||
|
|
||||||
currentLeaf.child = newTask
|
currentLeaf.child = newTask
|
||||||
newTask.parentWeight = parentWeight
|
|
||||||
}
|
}
|
||||||
reportProgress(0)
|
reportProgress(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun reportProgress(progress: Int) {
|
fun reportProgress(progress: Int) {
|
||||||
rootTask?.leaf()?.incrementProgress(progress)
|
rootTask?.leaf()?.setProgress(progress)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun endTask(nameRes: Int) {
|
fun endTask(nameRes: Int) {
|
||||||
@ -58,7 +61,7 @@ class DefaultInitialSyncProgressService @Inject constructor() : InitialSyncProgr
|
|||||||
//close it
|
//close it
|
||||||
val parent = endedTask.parent
|
val parent = endedTask.parent
|
||||||
parent?.child = null
|
parent?.child = null
|
||||||
parent?.incrementProgress(endedTask.offset + (endedTask.totalProgress * endedTask.parentWeight).toInt())
|
parent?.setProgress(endedTask.offset + (endedTask.totalProgress * endedTask.parentWeight).toInt())
|
||||||
}
|
}
|
||||||
if (endedTask?.parent == null) {
|
if (endedTask?.parent == null) {
|
||||||
status.postValue(null)
|
status.postValue(null)
|
||||||
@ -71,14 +74,17 @@ class DefaultInitialSyncProgressService @Inject constructor() : InitialSyncProgr
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inner class TaskInfo(var nameRes: Int,
|
private inner class TaskInfo(@StringRes var nameRes: Int,
|
||||||
var totalProgress: Int) {
|
var totalProgress: Int,
|
||||||
var parent: TaskInfo? = null
|
var parent: TaskInfo? = null,
|
||||||
|
var parentWeight: Float = 1f,
|
||||||
|
var offset: Int = parent?.currentProgress ?: 0) {
|
||||||
var child: TaskInfo? = null
|
var child: TaskInfo? = null
|
||||||
var parentWeight: Float = 1f
|
|
||||||
var currentProgress: Int = 0
|
var currentProgress: Int = 0
|
||||||
var offset: Int = 0
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the further child
|
||||||
|
*/
|
||||||
fun leaf(): TaskInfo {
|
fun leaf(): TaskInfo {
|
||||||
var last = this
|
var last = this
|
||||||
while (last.child != null) {
|
while (last.child != null) {
|
||||||
@ -87,26 +93,27 @@ class DefaultInitialSyncProgressService @Inject constructor() : InitialSyncProgr
|
|||||||
return last
|
return last
|
||||||
}
|
}
|
||||||
|
|
||||||
fun incrementProgress(progress: Int) {
|
/**
|
||||||
|
* Set progress of the parent if any (which will post value), or post the value
|
||||||
|
*/
|
||||||
|
fun setProgress(progress: Int) {
|
||||||
currentProgress = progress
|
currentProgress = progress
|
||||||
// val newProgress = Math.min(currentProgress + progress, totalProgress)
|
// val newProgress = Math.min(currentProgress + progress, totalProgress)
|
||||||
parent?.let {
|
parent?.let {
|
||||||
val parentProgress = (currentProgress * parentWeight).toInt()
|
val parentProgress = (currentProgress * parentWeight).toInt()
|
||||||
it.incrementProgress(offset + parentProgress)
|
it.setProgress(offset + parentProgress)
|
||||||
}
|
} ?: run {
|
||||||
if (parent == null) {
|
Timber.e("--- ${leaf().nameRes}: $currentProgress")
|
||||||
Timber.e("--- ${leaf().nameRes}: ${currentProgress}")
|
|
||||||
status.postValue(
|
status.postValue(
|
||||||
InitialSyncProgressService.Status(leaf().nameRes, currentProgress)
|
InitialSyncProgressService.Status(leaf().nameRes, currentProgress)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun <T> reportSubtask(reporter: DefaultInitialSyncProgressService?,
|
inline fun <T> reportSubtask(reporter: DefaultInitialSyncProgressService?,
|
||||||
nameRes: Int,
|
@StringRes nameRes: Int,
|
||||||
totalProgress: Int,
|
totalProgress: Int,
|
||||||
parentWeight: Float = 1f,
|
parentWeight: Float = 1f,
|
||||||
block: () -> T): T {
|
block: () -> T): T {
|
||||||
@ -121,11 +128,11 @@ inline fun <K, V, R> Map<out K, V>.mapWithProgress(reporter: DefaultInitialSyncP
|
|||||||
taskId: Int,
|
taskId: Int,
|
||||||
weight: Float,
|
weight: Float,
|
||||||
transform: (Map.Entry<K, V>) -> R): List<R> {
|
transform: (Map.Entry<K, V>) -> R): List<R> {
|
||||||
val total = count()
|
val total = count().toFloat()
|
||||||
var current = 0
|
var current = 0
|
||||||
reporter?.startTask(taskId, 100, weight)
|
reporter?.startTask(taskId, 100, weight)
|
||||||
return this.map {
|
return map {
|
||||||
reporter?.reportProgress((current / total.toFloat() * 100).toInt())
|
reporter?.reportProgress((current / total * 100).toInt())
|
||||||
current++
|
current++
|
||||||
transform.invoke(it)
|
transform.invoke(it)
|
||||||
}.also {
|
}.also {
|
||||||
|
@ -35,16 +35,15 @@ import im.vector.matrix.android.api.session.group.GroupService
|
|||||||
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
|
||||||
import im.vector.matrix.android.api.session.room.RoomService
|
import im.vector.matrix.android.api.session.room.RoomService
|
||||||
|
import im.vector.matrix.android.api.session.securestorage.SecureStorageService
|
||||||
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.user.UserService
|
import im.vector.matrix.android.api.session.user.UserService
|
||||||
import im.vector.matrix.android.api.util.MatrixCallbackDelegate
|
import im.vector.matrix.android.internal.crypto.DefaultCryptoService
|
||||||
import im.vector.matrix.android.internal.crypto.CryptoManager
|
|
||||||
import im.vector.matrix.android.internal.database.LiveEntityObserver
|
import im.vector.matrix.android.internal.database.LiveEntityObserver
|
||||||
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.worker.WorkManagerUtil
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Provider
|
import javax.inject.Provider
|
||||||
@ -63,8 +62,9 @@ internal class DefaultSession @Inject constructor(override val sessionParams: Se
|
|||||||
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 cryptoService: Lazy<CryptoManager>,
|
private val cryptoService: Lazy<DefaultCryptoService>,
|
||||||
private val fileService: Lazy<FileService>,
|
private val fileService: Lazy<FileService>,
|
||||||
|
private val secureStorageService: Lazy<SecureStorageService>,
|
||||||
private val syncThreadProvider: Provider<SyncThread>,
|
private val syncThreadProvider: Provider<SyncThread>,
|
||||||
private val contentUrlResolver: ContentUrlResolver,
|
private val contentUrlResolver: ContentUrlResolver,
|
||||||
private val contentUploadProgressTracker: ContentUploadStateTracker,
|
private val contentUploadProgressTracker: ContentUploadStateTracker,
|
||||||
@ -75,13 +75,13 @@ internal class DefaultSession @Inject constructor(override val sessionParams: Se
|
|||||||
GroupService by groupService.get(),
|
GroupService by groupService.get(),
|
||||||
UserService by userService.get(),
|
UserService by userService.get(),
|
||||||
CryptoService by cryptoService.get(),
|
CryptoService by cryptoService.get(),
|
||||||
CacheService by cacheService.get(),
|
|
||||||
SignOutService by signOutService.get(),
|
SignOutService by signOutService.get(),
|
||||||
FilterService by filterService.get(),
|
FilterService by filterService.get(),
|
||||||
PushRuleService by pushRuleService.get(),
|
PushRuleService by pushRuleService.get(),
|
||||||
PushersService by pushersService.get(),
|
PushersService by pushersService.get(),
|
||||||
FileService by fileService.get(),
|
FileService by fileService.get(),
|
||||||
InitialSyncProgressService by initialSyncProgressService.get() {
|
InitialSyncProgressService by initialSyncProgressService.get(),
|
||||||
|
SecureStorageService by secureStorageService.get() {
|
||||||
|
|
||||||
private var isOpen = false
|
private var isOpen = false
|
||||||
|
|
||||||
@ -144,43 +144,6 @@ internal class DefaultSession @Inject constructor(override val sessionParams: Se
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainThread
|
|
||||||
override fun signOut(callback: MatrixCallback<Unit>) {
|
|
||||||
Timber.w("SIGN_OUT: start")
|
|
||||||
|
|
||||||
assert(isOpen)
|
|
||||||
|
|
||||||
Timber.w("SIGN_OUT: call webservice")
|
|
||||||
return signOutService.get().signOut(object : MatrixCallback<Unit> {
|
|
||||||
override fun onSuccess(data: Unit) {
|
|
||||||
Timber.w("SIGN_OUT: call webservice -> SUCCESS: clear cache")
|
|
||||||
stopSync()
|
|
||||||
stopAnyBackgroundSync()
|
|
||||||
// Clear the cache
|
|
||||||
cacheService.get().clearCache(object : MatrixCallback<Unit> {
|
|
||||||
override fun onSuccess(data: Unit) {
|
|
||||||
Timber.w("SIGN_OUT: clear cache -> SUCCESS: clear crypto cache")
|
|
||||||
cryptoService.get().clearCryptoCache(MatrixCallbackDelegate(callback))
|
|
||||||
WorkManagerUtil.cancelAllWorks(context)
|
|
||||||
callback.onSuccess(Unit)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onFailure(failure: Throwable) {
|
|
||||||
// ignore error
|
|
||||||
Timber.e("SIGN_OUT: clear cache -> ERROR: ignoring")
|
|
||||||
onSuccess(Unit)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onFailure(failure: Throwable) {
|
|
||||||
// Ignore failure
|
|
||||||
Timber.e("SIGN_OUT: call webservice -> ERROR: ignoring")
|
|
||||||
onSuccess(Unit)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun clearCache(callback: MatrixCallback<Unit>) {
|
override fun clearCache(callback: MatrixCallback<Unit>) {
|
||||||
stopSync()
|
stopSync()
|
||||||
stopAnyBackgroundSync()
|
stopAnyBackgroundSync()
|
||||||
|
@ -27,21 +27,20 @@ import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
|
|||||||
import im.vector.matrix.android.api.auth.data.SessionParams
|
import im.vector.matrix.android.api.auth.data.SessionParams
|
||||||
import im.vector.matrix.android.api.session.InitialSyncProgressService
|
import im.vector.matrix.android.api.session.InitialSyncProgressService
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.session.Session
|
||||||
|
import im.vector.matrix.android.api.session.securestorage.SecureStorageService
|
||||||
import im.vector.matrix.android.internal.database.LiveEntityObserver
|
import im.vector.matrix.android.internal.database.LiveEntityObserver
|
||||||
import im.vector.matrix.android.internal.database.configureEncryption
|
import im.vector.matrix.android.internal.database.RealmKeysUtils
|
||||||
import im.vector.matrix.android.internal.database.model.SessionRealmModule
|
import im.vector.matrix.android.internal.database.model.SessionRealmModule
|
||||||
import im.vector.matrix.android.internal.di.Authenticated
|
import im.vector.matrix.android.internal.di.*
|
||||||
import im.vector.matrix.android.internal.di.SessionDatabase
|
|
||||||
import im.vector.matrix.android.internal.di.Unauthenticated
|
|
||||||
import im.vector.matrix.android.internal.network.AccessTokenInterceptor
|
import im.vector.matrix.android.internal.network.AccessTokenInterceptor
|
||||||
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.session.group.GroupSummaryUpdater
|
import im.vector.matrix.android.internal.session.group.GroupSummaryUpdater
|
||||||
import im.vector.matrix.android.internal.session.room.DefaultRoomFactory
|
|
||||||
import im.vector.matrix.android.internal.session.room.EventRelationsAggregationUpdater
|
import im.vector.matrix.android.internal.session.room.EventRelationsAggregationUpdater
|
||||||
import im.vector.matrix.android.internal.session.room.RoomFactory
|
|
||||||
import im.vector.matrix.android.internal.session.room.create.RoomCreateEventLiveObserver
|
import im.vector.matrix.android.internal.session.room.create.RoomCreateEventLiveObserver
|
||||||
import im.vector.matrix.android.internal.session.room.prune.EventsPruner
|
import im.vector.matrix.android.internal.session.room.prune.EventsPruner
|
||||||
import im.vector.matrix.android.internal.session.room.tombstone.RoomTombstoneEventLiveObserver
|
import im.vector.matrix.android.internal.session.room.tombstone.RoomTombstoneEventLiveObserver
|
||||||
|
import im.vector.matrix.android.internal.session.securestorage.DefaultSecureStorageService
|
||||||
import im.vector.matrix.android.internal.util.md5
|
import im.vector.matrix.android.internal.util.md5
|
||||||
import io.realm.RealmConfiguration
|
import io.realm.RealmConfiguration
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
@ -53,6 +52,7 @@ internal abstract class SessionModule {
|
|||||||
|
|
||||||
@Module
|
@Module
|
||||||
companion object {
|
companion object {
|
||||||
|
internal const val DB_ALIAS_PREFIX = "session_db_"
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@Provides
|
@Provides
|
||||||
@ -67,18 +67,40 @@ internal abstract class SessionModule {
|
|||||||
return sessionParams.credentials
|
return sessionParams.credentials
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
@UserId
|
||||||
|
@Provides
|
||||||
|
fun providesUserId(credentials: Credentials): String {
|
||||||
|
return credentials.userId
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
@UserMd5
|
||||||
|
@Provides
|
||||||
|
fun providesUserMd5(@UserId userId: String): String {
|
||||||
|
return userId.md5()
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
@Provides
|
||||||
|
@UserCacheDirectory
|
||||||
|
fun providesFilesDir(@UserMd5 userMd5: String, context: Context): File {
|
||||||
|
return File(context.filesDir, userMd5)
|
||||||
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@Provides
|
@Provides
|
||||||
@SessionDatabase
|
@SessionDatabase
|
||||||
@SessionScope
|
@SessionScope
|
||||||
fun providesRealmConfiguration(sessionParams: SessionParams, context: Context): RealmConfiguration {
|
fun providesRealmConfiguration(realmKeysUtils: RealmKeysUtils,
|
||||||
val childPath = sessionParams.credentials.userId.md5()
|
@UserCacheDirectory directory: File,
|
||||||
val directory = File(context.filesDir, childPath)
|
@UserMd5 userMd5: String): RealmConfiguration {
|
||||||
|
|
||||||
return RealmConfiguration.Builder()
|
return RealmConfiguration.Builder()
|
||||||
.directory(directory)
|
.directory(directory)
|
||||||
.name("disk_store.realm")
|
.name("disk_store.realm")
|
||||||
.configureEncryption("session_db_$childPath", context)
|
.apply {
|
||||||
|
realmKeysUtils.configureEncryption(this, "$DB_ALIAS_PREFIX$userMd5")
|
||||||
|
}
|
||||||
.modules(SessionRealmModule())
|
.modules(SessionRealmModule())
|
||||||
.deleteRealmIfMigrationNeeded()
|
.deleteRealmIfMigrationNeeded()
|
||||||
.build()
|
.build()
|
||||||
@ -101,7 +123,18 @@ internal abstract class SessionModule {
|
|||||||
fun providesOkHttpClient(@Unauthenticated okHttpClient: OkHttpClient,
|
fun providesOkHttpClient(@Unauthenticated okHttpClient: OkHttpClient,
|
||||||
accessTokenInterceptor: AccessTokenInterceptor): OkHttpClient {
|
accessTokenInterceptor: AccessTokenInterceptor): OkHttpClient {
|
||||||
return okHttpClient.newBuilder()
|
return okHttpClient.newBuilder()
|
||||||
.addInterceptor(accessTokenInterceptor)
|
.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()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,4 +175,7 @@ internal abstract class SessionModule {
|
|||||||
@Binds
|
@Binds
|
||||||
abstract fun bindInitialSyncProgressService(initialSyncProgressService: DefaultInitialSyncProgressService): InitialSyncProgressService
|
abstract fun bindInitialSyncProgressService(initialSyncProgressService: DefaultInitialSyncProgressService): InitialSyncProgressService
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
abstract fun bindSecureStorageService(secureStorageService: DefaultSecureStorageService): SecureStorageService
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -30,9 +30,8 @@ internal class DefaultContentUploadStateTracker @Inject constructor() : ContentU
|
|||||||
private val listeners = mutableMapOf<String, MutableList<ContentUploadStateTracker.UpdateListener>>()
|
private val listeners = mutableMapOf<String, MutableList<ContentUploadStateTracker.UpdateListener>>()
|
||||||
|
|
||||||
override fun track(key: String, updateListener: ContentUploadStateTracker.UpdateListener) {
|
override fun track(key: String, updateListener: ContentUploadStateTracker.UpdateListener) {
|
||||||
val listeners = listeners[key] ?: ArrayList()
|
val listeners = listeners.getOrPut(key) { ArrayList() }
|
||||||
listeners.add(updateListener)
|
listeners.add(updateListener)
|
||||||
this.listeners[key] = listeners
|
|
||||||
val currentState = states[key] ?: ContentUploadStateTracker.State.Idle
|
val currentState = states[key] ?: ContentUploadStateTracker.State.Idle
|
||||||
mainHandler.post { updateListener.onUpdate(currentState) }
|
mainHandler.post { updateListener.onUpdate(currentState) }
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.session.filter
|
package im.vector.matrix.android.internal.session.filter
|
||||||
|
|
||||||
import im.vector.matrix.android.api.auth.data.SessionParams
|
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.task.Task
|
import im.vector.matrix.android.internal.task.Task
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -33,7 +33,7 @@ internal interface SaveFilterTask : Task<SaveFilterTask.Params, Unit> {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class DefaultSaveFilterTask @Inject constructor(private val sessionParams: SessionParams,
|
internal class DefaultSaveFilterTask @Inject constructor(@UserId private val userId: String,
|
||||||
private val filterAPI: FilterApi,
|
private val filterAPI: FilterApi,
|
||||||
private val filterRepository: FilterRepository
|
private val filterRepository: FilterRepository
|
||||||
) : SaveFilterTask {
|
) : SaveFilterTask {
|
||||||
@ -41,7 +41,7 @@ internal class DefaultSaveFilterTask @Inject constructor(private val sessionPara
|
|||||||
override suspend fun execute(params: SaveFilterTask.Params) {
|
override suspend fun execute(params: SaveFilterTask.Params) {
|
||||||
val filterResponse = executeRequest<FilterResponse> {
|
val filterResponse = executeRequest<FilterResponse> {
|
||||||
// TODO auto retry
|
// TODO auto retry
|
||||||
apiCall = filterAPI.uploadFilter(sessionParams.credentials.userId, params.filter)
|
apiCall = filterAPI.uploadFilter(userId, params.filter)
|
||||||
}
|
}
|
||||||
filterRepository.storeFilterId(params.filter, filterResponse.filterId)
|
filterRepository.storeFilterId(params.filter, filterResponse.filterId)
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package im.vector.matrix.android.internal.session.group
|
package im.vector.matrix.android.internal.session.group
|
||||||
|
|
||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
|
import im.vector.matrix.android.api.session.room.model.Membership
|
||||||
import im.vector.matrix.android.internal.database.model.GroupSummaryEntity
|
import im.vector.matrix.android.internal.database.model.GroupSummaryEntity
|
||||||
import im.vector.matrix.android.internal.database.query.where
|
import im.vector.matrix.android.internal.database.query.where
|
||||||
import im.vector.matrix.android.internal.network.executeRequest
|
import im.vector.matrix.android.internal.network.executeRequest
|
||||||
@ -64,8 +65,7 @@ internal class DefaultGetGroupDataTask @Inject constructor(
|
|||||||
groupSummaryEntity.avatarUrl = groupSummary.profile?.avatarUrl ?: ""
|
groupSummaryEntity.avatarUrl = groupSummary.profile?.avatarUrl ?: ""
|
||||||
val name = groupSummary.profile?.name
|
val name = groupSummary.profile?.name
|
||||||
groupSummaryEntity.displayName = if (name.isNullOrEmpty()) groupId else name
|
groupSummaryEntity.displayName = if (name.isNullOrEmpty()) groupId else name
|
||||||
groupSummaryEntity.shortDescription = groupSummary.profile?.shortDescription
|
groupSummaryEntity.shortDescription = groupSummary.profile?.shortDescription ?: ""
|
||||||
?: ""
|
|
||||||
|
|
||||||
val roomIds = groupRooms.rooms.map { it.roomId }
|
val roomIds = groupRooms.rooms.map { it.roomId }
|
||||||
groupSummaryEntity.roomIds.clear()
|
groupSummaryEntity.roomIds.clear()
|
||||||
@ -74,8 +74,12 @@ internal class DefaultGetGroupDataTask @Inject constructor(
|
|||||||
val userIds = groupUsers.users.map { it.userId }
|
val userIds = groupUsers.users.map { it.userId }
|
||||||
groupSummaryEntity.userIds.clear()
|
groupSummaryEntity.userIds.clear()
|
||||||
groupSummaryEntity.userIds.addAll(userIds)
|
groupSummaryEntity.userIds.addAll(userIds)
|
||||||
|
|
||||||
|
groupSummaryEntity.membership = when (groupSummary.user?.membership) {
|
||||||
|
Membership.JOIN.value -> Membership.JOIN
|
||||||
|
Membership.INVITE.value -> Membership.INVITE
|
||||||
|
else -> Membership.LEAVE
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -20,35 +20,48 @@ import android.content.Context
|
|||||||
import androidx.work.ExistingWorkPolicy
|
import androidx.work.ExistingWorkPolicy
|
||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
import im.vector.matrix.android.api.auth.data.Credentials
|
import im.vector.matrix.android.api.session.room.model.Membership
|
||||||
import im.vector.matrix.android.internal.database.RealmLiveEntityObserver
|
import im.vector.matrix.android.internal.database.RealmLiveEntityObserver
|
||||||
import im.vector.matrix.android.internal.database.model.GroupEntity
|
import im.vector.matrix.android.internal.database.model.GroupEntity
|
||||||
|
import im.vector.matrix.android.internal.database.model.GroupSummaryEntity
|
||||||
import im.vector.matrix.android.internal.database.query.where
|
import im.vector.matrix.android.internal.database.query.where
|
||||||
import im.vector.matrix.android.internal.di.SessionDatabase
|
import im.vector.matrix.android.internal.di.UserId
|
||||||
import im.vector.matrix.android.internal.worker.WorkManagerUtil
|
import im.vector.matrix.android.internal.worker.WorkManagerUtil
|
||||||
import im.vector.matrix.android.internal.worker.WorkManagerUtil.matrixOneTimeWorkRequestBuilder
|
import im.vector.matrix.android.internal.worker.WorkManagerUtil.matrixOneTimeWorkRequestBuilder
|
||||||
import im.vector.matrix.android.internal.worker.WorkerParamsFactory
|
import im.vector.matrix.android.internal.worker.WorkerParamsFactory
|
||||||
import io.realm.OrderedCollectionChangeSet
|
import io.realm.OrderedCollectionChangeSet
|
||||||
import io.realm.RealmConfiguration
|
|
||||||
import io.realm.RealmResults
|
import io.realm.RealmResults
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
private const val GET_GROUP_DATA_WORKER = "GET_GROUP_DATA_WORKER"
|
private const val GET_GROUP_DATA_WORKER = "GET_GROUP_DATA_WORKER"
|
||||||
|
|
||||||
internal class GroupSummaryUpdater @Inject constructor(private val context: Context,
|
internal class GroupSummaryUpdater @Inject constructor(private val context: Context,
|
||||||
private val credentials: Credentials,
|
@UserId private val userId: String,
|
||||||
@SessionDatabase realmConfiguration: RealmConfiguration)
|
private val monarchy: Monarchy)
|
||||||
: RealmLiveEntityObserver<GroupEntity>(realmConfiguration) {
|
: RealmLiveEntityObserver<GroupEntity>(monarchy.realmConfiguration) {
|
||||||
|
|
||||||
override val query = Monarchy.Query<GroupEntity> { GroupEntity.where(it) }
|
override val query = Monarchy.Query { GroupEntity.where(it) }
|
||||||
|
|
||||||
override fun onChange(results: RealmResults<GroupEntity>, changeSet: OrderedCollectionChangeSet) {
|
override fun onChange(results: RealmResults<GroupEntity>, changeSet: OrderedCollectionChangeSet) {
|
||||||
val newGroupIds = changeSet.insertions
|
// `insertions` for new groups and `changes` to handle left groups
|
||||||
|
val modifiedGroupEntity = (changeSet.insertions + changeSet.changes)
|
||||||
.asSequence()
|
.asSequence()
|
||||||
.mapNotNull { results[it]?.groupId}
|
.mapNotNull { results[it] }
|
||||||
.toList()
|
|
||||||
|
fetchGroupsData(modifiedGroupEntity
|
||||||
|
.filter { it.membership == Membership.JOIN || it.membership == Membership.INVITE }
|
||||||
|
.map { it.groupId }
|
||||||
|
.toList())
|
||||||
|
|
||||||
|
deleteGroups(modifiedGroupEntity
|
||||||
|
.filter { it.membership == Membership.LEAVE }
|
||||||
|
.map { it.groupId }
|
||||||
|
.toList())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fetchGroupsData(groupIds: List<String>) {
|
||||||
|
val getGroupDataWorkerParams = GetGroupDataWorker.Params(userId, groupIds)
|
||||||
|
|
||||||
val getGroupDataWorkerParams = GetGroupDataWorker.Params(credentials.userId, newGroupIds)
|
|
||||||
val workData = WorkerParamsFactory.toData(getGroupDataWorkerParams)
|
val workData = WorkerParamsFactory.toData(getGroupDataWorkerParams)
|
||||||
|
|
||||||
val sendWork = matrixOneTimeWorkRequestBuilder<GetGroupDataWorker>()
|
val sendWork = matrixOneTimeWorkRequestBuilder<GetGroupDataWorker>()
|
||||||
@ -61,4 +74,15 @@ internal class GroupSummaryUpdater @Inject constructor(private val context: Cont
|
|||||||
.enqueue()
|
.enqueue()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the GroupSummaryEntity of left groups
|
||||||
|
*/
|
||||||
|
private fun deleteGroups(groupIds: List<String>) {
|
||||||
|
monarchy
|
||||||
|
.writeAsync { realm ->
|
||||||
|
GroupSummaryEntity.where(realm, groupIds)
|
||||||
|
.findAll()
|
||||||
|
.deleteAllFromRealm()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -17,9 +17,10 @@ package im.vector.matrix.android.internal.session.notification
|
|||||||
|
|
||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.auth.data.SessionParams
|
|
||||||
import im.vector.matrix.android.api.pushrules.Action
|
|
||||||
import im.vector.matrix.android.api.pushrules.PushRuleService
|
import im.vector.matrix.android.api.pushrules.PushRuleService
|
||||||
|
import im.vector.matrix.android.api.pushrules.RuleKind
|
||||||
|
import im.vector.matrix.android.api.pushrules.RuleSetKey
|
||||||
|
import im.vector.matrix.android.api.pushrules.getActions
|
||||||
import im.vector.matrix.android.api.pushrules.rest.PushRule
|
import im.vector.matrix.android.api.pushrules.rest.PushRule
|
||||||
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.util.Cancelable
|
import im.vector.matrix.android.api.util.Cancelable
|
||||||
@ -35,53 +36,60 @@ import timber.log.Timber
|
|||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@SessionScope
|
@SessionScope
|
||||||
internal class DefaultPushRuleService @Inject constructor(
|
internal class DefaultPushRuleService @Inject constructor(private val getPushRulesTask: GetPushRulesTask,
|
||||||
private val sessionParams: SessionParams,
|
private val updatePushRuleEnableStatusTask: UpdatePushRuleEnableStatusTask,
|
||||||
private val pushRulesTask: GetPushRulesTask,
|
private val taskExecutor: TaskExecutor,
|
||||||
private val updatePushRuleEnableStatusTask: UpdatePushRuleEnableStatusTask,
|
private val monarchy: Monarchy
|
||||||
private val taskExecutor: TaskExecutor,
|
|
||||||
private val monarchy: Monarchy
|
|
||||||
) : PushRuleService {
|
) : PushRuleService {
|
||||||
|
|
||||||
private var listeners = ArrayList<PushRuleService.PushRuleListener>()
|
private var listeners = ArrayList<PushRuleService.PushRuleListener>()
|
||||||
|
|
||||||
override fun fetchPushRules(scope: String) {
|
override fun fetchPushRules(scope: String) {
|
||||||
pushRulesTask
|
getPushRulesTask
|
||||||
.configureWith(GetPushRulesTask.Params(scope))
|
.configureWith(GetPushRulesTask.Params(scope))
|
||||||
.executeBy(taskExecutor)
|
.executeBy(taskExecutor)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getPushRules(scope: String): List<PushRule> {
|
override fun getPushRules(scope: String): List<PushRule> {
|
||||||
|
|
||||||
var contentRules: List<PushRule> = emptyList()
|
var contentRules: List<PushRule> = emptyList()
|
||||||
var overrideRules: List<PushRule> = emptyList()
|
var overrideRules: List<PushRule> = emptyList()
|
||||||
var roomRules: List<PushRule> = emptyList()
|
var roomRules: List<PushRule> = emptyList()
|
||||||
var senderRules: List<PushRule> = emptyList()
|
var senderRules: List<PushRule> = emptyList()
|
||||||
var underrideRules: List<PushRule> = emptyList()
|
var underrideRules: List<PushRule> = emptyList()
|
||||||
|
|
||||||
// TODO Create const for ruleSetKey
|
|
||||||
monarchy.doWithRealm { realm ->
|
monarchy.doWithRealm { realm ->
|
||||||
PushRulesEntity.where(realm, sessionParams.credentials.userId, scope, "content").findFirst()?.let { re ->
|
PushRulesEntity.where(realm, scope, RuleSetKey.CONTENT)
|
||||||
contentRules = re.pushRules.map { PushRulesMapper.mapContentRule(it) }
|
.findFirst()
|
||||||
}
|
?.let { pushRulesEntity ->
|
||||||
PushRulesEntity.where(realm, sessionParams.credentials.userId, scope, "override").findFirst()?.let { re ->
|
contentRules = pushRulesEntity.pushRules.map { PushRulesMapper.mapContentRule(it) }
|
||||||
overrideRules = re.pushRules.map { PushRulesMapper.map(it) }
|
}
|
||||||
}
|
PushRulesEntity.where(realm, scope, RuleSetKey.OVERRIDE)
|
||||||
PushRulesEntity.where(realm, sessionParams.credentials.userId, scope, "room").findFirst()?.let { re ->
|
.findFirst()
|
||||||
roomRules = re.pushRules.map { PushRulesMapper.mapRoomRule(it) }
|
?.let { pushRulesEntity ->
|
||||||
}
|
overrideRules = pushRulesEntity.pushRules.map { PushRulesMapper.map(it) }
|
||||||
PushRulesEntity.where(realm, sessionParams.credentials.userId, scope, "sender").findFirst()?.let { re ->
|
}
|
||||||
senderRules = re.pushRules.map { PushRulesMapper.mapSenderRule(it) }
|
PushRulesEntity.where(realm, scope, RuleSetKey.ROOM)
|
||||||
}
|
.findFirst()
|
||||||
PushRulesEntity.where(realm, sessionParams.credentials.userId, scope, "underride").findFirst()?.let { re ->
|
?.let { pushRulesEntity ->
|
||||||
underrideRules = re.pushRules.map { PushRulesMapper.map(it) }
|
roomRules = pushRulesEntity.pushRules.map { PushRulesMapper.mapRoomRule(it) }
|
||||||
}
|
}
|
||||||
|
PushRulesEntity.where(realm, scope, RuleSetKey.SENDER)
|
||||||
|
.findFirst()
|
||||||
|
?.let { pushRulesEntity ->
|
||||||
|
senderRules = pushRulesEntity.pushRules.map { PushRulesMapper.mapSenderRule(it) }
|
||||||
|
}
|
||||||
|
PushRulesEntity.where(realm, scope, RuleSetKey.UNDERRIDE)
|
||||||
|
.findFirst()
|
||||||
|
?.let { pushRulesEntity ->
|
||||||
|
underrideRules = pushRulesEntity.pushRules.map { PushRulesMapper.map(it) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return contentRules + overrideRules + roomRules + senderRules + underrideRules
|
// Ref. for the order: https://matrix.org/docs/spec/client_server/latest#push-rules
|
||||||
|
return overrideRules + contentRules + roomRules + senderRules + underrideRules
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updatePushRuleEnableStatus(kind: String, pushRule: PushRule, enabled: Boolean, callback: MatrixCallback<Unit>): Cancelable {
|
override fun updatePushRuleEnableStatus(kind: RuleKind, pushRule: PushRule, enabled: Boolean, callback: MatrixCallback<Unit>): Cancelable {
|
||||||
return updatePushRuleEnableStatusTask
|
return updatePushRuleEnableStatusTask
|
||||||
.configureWith(UpdatePushRuleEnableStatusTask.Params(kind, pushRule, enabled)) {
|
.configureWith(UpdatePushRuleEnableStatusTask.Params(kind, pushRule, enabled)) {
|
||||||
this.callback = callback
|
this.callback = callback
|
||||||
@ -114,8 +122,9 @@ internal class DefaultPushRuleService @Inject constructor(
|
|||||||
|
|
||||||
fun dispatchBing(event: Event, rule: PushRule) {
|
fun dispatchBing(event: Event, rule: PushRule) {
|
||||||
try {
|
try {
|
||||||
|
val actionsList = rule.getActions()
|
||||||
listeners.forEach {
|
listeners.forEach {
|
||||||
it.onMatchRule(event, Action.mapFrom(rule) ?: emptyList())
|
it.onMatchRule(event, actionsList)
|
||||||
}
|
}
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
Timber.e(e, "Error while dispatching bing")
|
Timber.e(e, "Error while dispatching bing")
|
||||||
@ -132,6 +141,16 @@ internal class DefaultPushRuleService @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun dispatchRedactedEventId(redactedEventId: String) {
|
||||||
|
try {
|
||||||
|
listeners.forEach {
|
||||||
|
it.onEventRedacted(redactedEventId)
|
||||||
|
}
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
Timber.e(e, "Error while dispatching room left")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun dispatchFinish() {
|
fun dispatchFinish() {
|
||||||
try {
|
try {
|
||||||
listeners.forEach {
|
listeners.forEach {
|
||||||
|
@ -16,11 +16,11 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.session.notification
|
package im.vector.matrix.android.internal.session.notification
|
||||||
|
|
||||||
import im.vector.matrix.android.api.auth.data.SessionParams
|
|
||||||
import im.vector.matrix.android.api.pushrules.rest.PushRule
|
import im.vector.matrix.android.api.pushrules.rest.PushRule
|
||||||
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.room.RoomService
|
import im.vector.matrix.android.api.session.room.RoomService
|
||||||
|
import im.vector.matrix.android.internal.di.UserId
|
||||||
import im.vector.matrix.android.internal.session.pushers.DefaultConditionResolver
|
import im.vector.matrix.android.internal.session.pushers.DefaultConditionResolver
|
||||||
import im.vector.matrix.android.internal.session.sync.model.RoomsSyncResponse
|
import im.vector.matrix.android.internal.session.sync.model.RoomsSyncResponse
|
||||||
import im.vector.matrix.android.internal.task.Task
|
import im.vector.matrix.android.internal.task.Task
|
||||||
@ -37,7 +37,7 @@ internal interface ProcessEventForPushTask : Task<ProcessEventForPushTask.Params
|
|||||||
internal class DefaultProcessEventForPushTask @Inject constructor(
|
internal class DefaultProcessEventForPushTask @Inject constructor(
|
||||||
private val defaultPushRuleService: DefaultPushRuleService,
|
private val defaultPushRuleService: DefaultPushRuleService,
|
||||||
private val roomService: RoomService,
|
private val roomService: RoomService,
|
||||||
private val sessionParams: SessionParams
|
@UserId private val userId: String
|
||||||
) : ProcessEventForPushTask {
|
) : ProcessEventForPushTask {
|
||||||
|
|
||||||
override suspend fun execute(params: ProcessEventForPushTask.Params) {
|
override suspend fun execute(params: ProcessEventForPushTask.Params) {
|
||||||
@ -68,7 +68,7 @@ internal class DefaultProcessEventForPushTask @Inject constructor(
|
|||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}.filter {
|
}.filter {
|
||||||
it.senderId != sessionParams.credentials.userId
|
it.senderId != userId
|
||||||
}
|
}
|
||||||
Timber.v("[PushRules] Found ${allEvents.size} out of ${(newJoinEvents + inviteEvents).size}" +
|
Timber.v("[PushRules] Found ${allEvents.size} out of ${(newJoinEvents + inviteEvents).size}" +
|
||||||
" to check for push rules with ${params.rules.size} rules")
|
" to check for push rules with ${params.rules.size} rules")
|
||||||
@ -78,11 +78,31 @@ internal class DefaultProcessEventForPushTask @Inject constructor(
|
|||||||
defaultPushRuleService.dispatchBing(event, it)
|
defaultPushRuleService.dispatchBing(event, it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val allRedactedEvents = params.syncResponse.join
|
||||||
|
.map { entries ->
|
||||||
|
entries.value.timeline?.events?.filter {
|
||||||
|
it.type == EventType.REDACTION
|
||||||
|
}
|
||||||
|
.orEmpty()
|
||||||
|
.mapNotNull { it.redacts }
|
||||||
|
}
|
||||||
|
.fold(emptyList<String>(), { acc, next ->
|
||||||
|
acc + next
|
||||||
|
})
|
||||||
|
|
||||||
|
Timber.v("[PushRules] Found ${allRedactedEvents.size} redacted events")
|
||||||
|
|
||||||
|
allRedactedEvents.forEach { redactedEventId ->
|
||||||
|
defaultPushRuleService.dispatchRedactedEventId(redactedEventId)
|
||||||
|
}
|
||||||
|
|
||||||
defaultPushRuleService.dispatchFinish()
|
defaultPushRuleService.dispatchFinish()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun fulfilledBingRule(event: Event, rules: List<PushRule>): PushRule? {
|
private fun fulfilledBingRule(event: Event, rules: List<PushRule>): PushRule? {
|
||||||
val conditionResolver = DefaultConditionResolver(event, roomService, sessionParams)
|
// TODO This should be injected
|
||||||
|
val conditionResolver = DefaultConditionResolver(event, roomService, userId)
|
||||||
rules.filter { it.enabled }.forEach { rule ->
|
rules.filter { it.enabled }.forEach { rule ->
|
||||||
val isFullfilled = rule.conditions?.map {
|
val isFullfilled = rule.conditions?.map {
|
||||||
it.asExecutableCondition()?.isSatisfied(conditionResolver) ?: false
|
it.asExecutableCondition()?.isSatisfied(conditionResolver) ?: false
|
||||||
|
@ -57,14 +57,14 @@ internal class AddHttpPusherWorker(context: Context, params: WorkerParameters)
|
|||||||
return Result.failure()
|
return Result.failure()
|
||||||
}
|
}
|
||||||
return try {
|
return try {
|
||||||
setPusher(pusher, params.userId)
|
setPusher(pusher)
|
||||||
Result.success()
|
Result.success()
|
||||||
} catch (exception: Throwable) {
|
} catch (exception: Throwable) {
|
||||||
when (exception) {
|
when (exception) {
|
||||||
is Failure.NetworkConnection -> Result.retry()
|
is Failure.NetworkConnection -> Result.retry()
|
||||||
else -> {
|
else -> {
|
||||||
monarchy.awaitTransaction { realm ->
|
monarchy.awaitTransaction { realm ->
|
||||||
PusherEntity.where(realm, params.userId, pusher.pushKey).findFirst()?.let {
|
PusherEntity.where(realm, pusher.pushKey).findFirst()?.let {
|
||||||
//update it
|
//update it
|
||||||
it.state = PusherState.FAILED_TO_REGISTER
|
it.state = PusherState.FAILED_TO_REGISTER
|
||||||
}
|
}
|
||||||
@ -76,12 +76,12 @@ internal class AddHttpPusherWorker(context: Context, params: WorkerParameters)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun setPusher(pusher: JsonPusher, userId: String) {
|
private suspend fun setPusher(pusher: JsonPusher) {
|
||||||
executeRequest<Unit> {
|
executeRequest<Unit> {
|
||||||
apiCall = pushersAPI.setPusher(pusher)
|
apiCall = pushersAPI.setPusher(pusher)
|
||||||
}
|
}
|
||||||
monarchy.awaitTransaction { realm ->
|
monarchy.awaitTransaction { realm ->
|
||||||
val echo = PusherEntity.where(realm, userId, pusher.pushKey).findFirst()
|
val echo = PusherEntity.where(realm, pusher.pushKey).findFirst()
|
||||||
if (echo != null) {
|
if (echo != null) {
|
||||||
//update it
|
//update it
|
||||||
echo.appDisplayName = pusher.appDisplayName
|
echo.appDisplayName = pusher.appDisplayName
|
||||||
@ -93,7 +93,7 @@ internal class AddHttpPusherWorker(context: Context, params: WorkerParameters)
|
|||||||
echo.data?.url = pusher.data?.url
|
echo.data?.url = pusher.data?.url
|
||||||
echo.state = PusherState.REGISTERED
|
echo.state = PusherState.REGISTERED
|
||||||
} else {
|
} else {
|
||||||
pusher.toEntity(userId).also {
|
pusher.toEntity().also {
|
||||||
it.state = PusherState.REGISTERED
|
it.state = PusherState.REGISTERED
|
||||||
realm.insertOrUpdate(it)
|
realm.insertOrUpdate(it)
|
||||||
}
|
}
|
||||||
|
@ -15,15 +15,16 @@
|
|||||||
*/
|
*/
|
||||||
package im.vector.matrix.android.internal.session.pushers
|
package im.vector.matrix.android.internal.session.pushers
|
||||||
|
|
||||||
import im.vector.matrix.android.api.auth.data.SessionParams
|
|
||||||
import im.vector.matrix.android.api.pushrules.*
|
import im.vector.matrix.android.api.pushrules.*
|
||||||
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.RoomService
|
import im.vector.matrix.android.api.session.room.RoomService
|
||||||
|
import im.vector.matrix.android.internal.di.UserId
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
|
// TODO Inject constructor
|
||||||
internal class DefaultConditionResolver(private val event: Event,
|
internal class DefaultConditionResolver(private val event: Event,
|
||||||
private val roomService: RoomService,
|
private val roomService: RoomService,
|
||||||
private val sessionParams: SessionParams) : ConditionResolver {
|
@UserId private val userId: String) : ConditionResolver {
|
||||||
|
|
||||||
|
|
||||||
override fun resolveEventMatchCondition(eventMatchCondition: EventMatchCondition): Boolean {
|
override fun resolveEventMatchCondition(eventMatchCondition: EventMatchCondition): Boolean {
|
||||||
@ -45,8 +46,7 @@ internal class DefaultConditionResolver(private val event: Event,
|
|||||||
override fun resolveContainsDisplayNameCondition(containsDisplayNameCondition: ContainsDisplayNameCondition): Boolean {
|
override fun resolveContainsDisplayNameCondition(containsDisplayNameCondition: ContainsDisplayNameCondition): Boolean {
|
||||||
val roomId = event.roomId ?: return false
|
val roomId = event.roomId ?: return false
|
||||||
val room = roomService.getRoom(roomId) ?: return false
|
val room = roomService.getRoom(roomId) ?: return false
|
||||||
val myDisplayName = room.getRoomMember(sessionParams.credentials.userId)?.displayName
|
val myDisplayName = room.getRoomMember(userId)?.displayName ?: return false
|
||||||
?: return false
|
|
||||||
return containsDisplayNameCondition.isSatisfied(event, myDisplayName)
|
return containsDisplayNameCondition.isSatisfied(event, myDisplayName)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -21,12 +21,12 @@ import androidx.work.BackoffPolicy
|
|||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.auth.data.SessionParams
|
|
||||||
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.pushers.PushersService
|
import im.vector.matrix.android.api.session.pushers.PushersService
|
||||||
import im.vector.matrix.android.internal.database.mapper.asDomain
|
import im.vector.matrix.android.internal.database.mapper.asDomain
|
||||||
import im.vector.matrix.android.internal.database.model.PusherEntity
|
import im.vector.matrix.android.internal.database.model.PusherEntity
|
||||||
import im.vector.matrix.android.internal.database.query.where
|
import im.vector.matrix.android.internal.database.query.where
|
||||||
|
import im.vector.matrix.android.internal.di.UserId
|
||||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||||
import im.vector.matrix.android.internal.task.configureWith
|
import im.vector.matrix.android.internal.task.configureWith
|
||||||
import im.vector.matrix.android.internal.worker.WorkManagerUtil
|
import im.vector.matrix.android.internal.worker.WorkManagerUtil
|
||||||
@ -37,13 +37,12 @@ import java.util.concurrent.TimeUnit
|
|||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
|
||||||
internal class DefaultPusherService @Inject constructor(
|
internal class DefaultPusherService @Inject constructor(private val context: Context,
|
||||||
private val context: Context,
|
private val monarchy: Monarchy,
|
||||||
private val monarchy: Monarchy,
|
@UserId private val userId: String,
|
||||||
private val sessionParam: SessionParams,
|
private val getPusherTask: GetPushersTask,
|
||||||
private val getPusherTask: GetPushersTask,
|
private val removePusherTask: RemovePusherTask,
|
||||||
private val removePusherTask: RemovePusherTask,
|
private val taskExecutor: TaskExecutor
|
||||||
private val taskExecutor: TaskExecutor
|
|
||||||
) : PushersService {
|
) : PushersService {
|
||||||
|
|
||||||
|
|
||||||
@ -70,7 +69,7 @@ internal class DefaultPusherService @Inject constructor(
|
|||||||
append = append)
|
append = append)
|
||||||
|
|
||||||
|
|
||||||
val params = AddHttpPusherWorker.Params(pusher, sessionParam.credentials.userId)
|
val params = AddHttpPusherWorker.Params(pusher, userId)
|
||||||
|
|
||||||
val request = matrixOneTimeWorkRequestBuilder<AddHttpPusherWorker>()
|
val request = matrixOneTimeWorkRequestBuilder<AddHttpPusherWorker>()
|
||||||
.setConstraints(WorkManagerUtil.workConstraints)
|
.setConstraints(WorkManagerUtil.workConstraints)
|
||||||
@ -82,7 +81,7 @@ internal class DefaultPusherService @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun removeHttpPusher(pushkey: String, appId: String, callback: MatrixCallback<Unit>) {
|
override fun removeHttpPusher(pushkey: String, appId: String, callback: MatrixCallback<Unit>) {
|
||||||
val params = RemovePusherTask.Params(sessionParam.credentials.userId, pushkey, appId)
|
val params = RemovePusherTask.Params(pushkey, appId)
|
||||||
removePusherTask
|
removePusherTask
|
||||||
.configureWith(params) {
|
.configureWith(params) {
|
||||||
this.callback = callback
|
this.callback = callback
|
||||||
@ -93,12 +92,12 @@ internal class DefaultPusherService @Inject constructor(
|
|||||||
|
|
||||||
override fun livePushers(): LiveData<List<Pusher>> {
|
override fun livePushers(): LiveData<List<Pusher>> {
|
||||||
return monarchy.findAllMappedWithChanges(
|
return monarchy.findAllMappedWithChanges(
|
||||||
{ realm -> PusherEntity.where(realm, sessionParam.credentials.userId) },
|
{ realm -> PusherEntity.where(realm) },
|
||||||
{ it.asDomain() }
|
{ it.asDomain() }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun pushers(): List<Pusher> {
|
override fun pushers(): List<Pusher> {
|
||||||
return monarchy.fetchAllCopiedSync { PusherEntity.where(it, sessionParam.credentials.userId) }.map { it.asDomain() }
|
return monarchy.fetchAllCopiedSync { PusherEntity.where(it) }.map { it.asDomain() }
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -15,80 +15,27 @@
|
|||||||
*/
|
*/
|
||||||
package im.vector.matrix.android.internal.session.pushers
|
package im.vector.matrix.android.internal.session.pushers
|
||||||
|
|
||||||
import com.zhuinden.monarchy.Monarchy
|
|
||||||
import im.vector.matrix.android.api.auth.data.SessionParams
|
|
||||||
import im.vector.matrix.android.api.pushrules.rest.GetPushRulesResponse
|
import im.vector.matrix.android.api.pushrules.rest.GetPushRulesResponse
|
||||||
import im.vector.matrix.android.internal.database.mapper.PushRulesMapper
|
|
||||||
import im.vector.matrix.android.internal.database.model.PushRulesEntity
|
|
||||||
import im.vector.matrix.android.internal.database.model.PusherEntityFields
|
|
||||||
import im.vector.matrix.android.internal.network.executeRequest
|
import im.vector.matrix.android.internal.network.executeRequest
|
||||||
import im.vector.matrix.android.internal.task.Task
|
import im.vector.matrix.android.internal.task.Task
|
||||||
import im.vector.matrix.android.internal.util.awaitTransaction
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
|
||||||
internal interface GetPushRulesTask : Task<GetPushRulesTask.Params, Unit> {
|
internal interface GetPushRulesTask : Task<GetPushRulesTask.Params, Unit> {
|
||||||
|
|
||||||
data class Params(val scope: String)
|
data class Params(val scope: String)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We keep this task, but it should not be used anymore, the push rules comes from the sync response
|
||||||
|
*/
|
||||||
internal class DefaultGetPushRulesTask @Inject constructor(private val pushRulesApi: PushRulesApi,
|
internal class DefaultGetPushRulesTask @Inject constructor(private val pushRulesApi: PushRulesApi,
|
||||||
private val monarchy: Monarchy,
|
private val savePushRulesTask: SavePushRulesTask) : GetPushRulesTask {
|
||||||
private val sessionParams: SessionParams) : GetPushRulesTask {
|
|
||||||
|
|
||||||
override suspend fun execute(params: GetPushRulesTask.Params) {
|
override suspend fun execute(params: GetPushRulesTask.Params) {
|
||||||
val response = executeRequest<GetPushRulesResponse> {
|
val response = executeRequest<GetPushRulesResponse> {
|
||||||
apiCall = pushRulesApi.getAllRules()
|
apiCall = pushRulesApi.getAllRules()
|
||||||
}
|
}
|
||||||
val scope = params.scope
|
|
||||||
monarchy.awaitTransaction { realm ->
|
|
||||||
//clear existings?
|
|
||||||
//TODO
|
|
||||||
realm.where(PushRulesEntity::class.java)
|
|
||||||
.equalTo(PusherEntityFields.USER_ID, sessionParams.credentials.userId)
|
|
||||||
.findAll().deleteAllFromRealm()
|
|
||||||
|
|
||||||
val content = PushRulesEntity(sessionParams.credentials.userId, scope, "content")
|
savePushRulesTask.execute(SavePushRulesTask.Params(response))
|
||||||
response.global.content?.forEach { rule ->
|
|
||||||
PushRulesMapper.map(rule).also {
|
|
||||||
content.pushRules.add(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
realm.insertOrUpdate(content)
|
|
||||||
|
|
||||||
val override = PushRulesEntity(sessionParams.credentials.userId, scope, "override")
|
|
||||||
response.global.override?.forEach { rule ->
|
|
||||||
PushRulesMapper.map(rule).also {
|
|
||||||
override.pushRules.add(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
realm.insertOrUpdate(override)
|
|
||||||
|
|
||||||
val rooms = PushRulesEntity(sessionParams.credentials.userId, scope, "room")
|
|
||||||
response.global.room?.forEach { rule ->
|
|
||||||
PushRulesMapper.map(rule).also {
|
|
||||||
rooms.pushRules.add(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
realm.insertOrUpdate(rooms)
|
|
||||||
|
|
||||||
val senders = PushRulesEntity(sessionParams.credentials.userId, scope, "sender")
|
|
||||||
response.global.sender?.forEach { rule ->
|
|
||||||
PushRulesMapper.map(rule).also {
|
|
||||||
senders.pushRules.add(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
realm.insertOrUpdate(senders)
|
|
||||||
|
|
||||||
val underrides = PushRulesEntity(sessionParams.credentials.userId, scope, "underride")
|
|
||||||
response.global.underride?.forEach { rule ->
|
|
||||||
PushRulesMapper.map(rule).also {
|
|
||||||
underrides.pushRules.add(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
realm.insertOrUpdate(underrides)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -16,11 +16,9 @@
|
|||||||
package im.vector.matrix.android.internal.session.pushers
|
package im.vector.matrix.android.internal.session.pushers
|
||||||
|
|
||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
import im.vector.matrix.android.api.auth.data.SessionParams
|
|
||||||
import im.vector.matrix.android.api.session.pushers.PusherState
|
import im.vector.matrix.android.api.session.pushers.PusherState
|
||||||
import im.vector.matrix.android.internal.database.mapper.toEntity
|
import im.vector.matrix.android.internal.database.mapper.toEntity
|
||||||
import im.vector.matrix.android.internal.database.model.PusherEntity
|
import im.vector.matrix.android.internal.database.model.PusherEntity
|
||||||
import im.vector.matrix.android.internal.database.model.PusherEntityFields
|
|
||||||
import im.vector.matrix.android.internal.network.executeRequest
|
import im.vector.matrix.android.internal.network.executeRequest
|
||||||
import im.vector.matrix.android.internal.task.Task
|
import im.vector.matrix.android.internal.task.Task
|
||||||
import im.vector.matrix.android.internal.util.awaitTransaction
|
import im.vector.matrix.android.internal.util.awaitTransaction
|
||||||
@ -29,8 +27,7 @@ import javax.inject.Inject
|
|||||||
internal interface GetPushersTask : Task<Unit, Unit>
|
internal interface GetPushersTask : Task<Unit, Unit>
|
||||||
|
|
||||||
internal class DefaultGetPusherTask @Inject constructor(private val pushersAPI: PushersAPI,
|
internal class DefaultGetPusherTask @Inject constructor(private val pushersAPI: PushersAPI,
|
||||||
private val monarchy: Monarchy,
|
private val monarchy: Monarchy) : GetPushersTask {
|
||||||
private val sessionParams: SessionParams) : GetPushersTask {
|
|
||||||
|
|
||||||
override suspend fun execute(params: Unit) {
|
override suspend fun execute(params: Unit) {
|
||||||
val response = executeRequest<GetPushersResponse> {
|
val response = executeRequest<GetPushersResponse> {
|
||||||
@ -39,10 +36,9 @@ internal class DefaultGetPusherTask @Inject constructor(private val pushersAPI:
|
|||||||
monarchy.awaitTransaction { realm ->
|
monarchy.awaitTransaction { realm ->
|
||||||
//clear existings?
|
//clear existings?
|
||||||
realm.where(PusherEntity::class.java)
|
realm.where(PusherEntity::class.java)
|
||||||
.equalTo(PusherEntityFields.USER_ID, sessionParams.credentials.userId)
|
|
||||||
.findAll().deleteAllFromRealm()
|
.findAll().deleteAllFromRealm()
|
||||||
response.pushers?.forEach { jsonPusher ->
|
response.pushers?.forEach { jsonPusher ->
|
||||||
jsonPusher.toEntity(sessionParams.credentials.userId).also {
|
jsonPusher.toEntity().also {
|
||||||
it.state = PusherState.REGISTERED
|
it.state = PusherState.REGISTERED
|
||||||
realm.insertOrUpdate(it)
|
realm.insertOrUpdate(it)
|
||||||
}
|
}
|
||||||
|
@ -59,6 +59,9 @@ internal abstract class PushersModule {
|
|||||||
@Binds
|
@Binds
|
||||||
abstract fun bindGetPushRulesTask(getPushRulesTask: DefaultGetPushRulesTask): GetPushRulesTask
|
abstract fun bindGetPushRulesTask(getPushRulesTask: DefaultGetPushRulesTask): GetPushRulesTask
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
abstract fun bindSavePushRulesTask(savePushRulesTask: DefaultSavePushRulesTask): SavePushRulesTask
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun bindRemovePusherTask(removePusherTask: DefaultRemovePusherTask): RemovePusherTask
|
abstract fun bindRemovePusherTask(removePusherTask: DefaultRemovePusherTask): RemovePusherTask
|
||||||
|
|
||||||
|
@ -28,8 +28,7 @@ import io.realm.Realm
|
|||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal interface RemovePusherTask : Task<RemovePusherTask.Params, Unit> {
|
internal interface RemovePusherTask : Task<RemovePusherTask.Params, Unit> {
|
||||||
data class Params(val userId: String,
|
data class Params(val pushKey: String,
|
||||||
val pushKey: String,
|
|
||||||
val pushAppId: String)
|
val pushAppId: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,12 +39,12 @@ internal class DefaultRemovePusherTask @Inject constructor(
|
|||||||
|
|
||||||
override suspend fun execute(params: RemovePusherTask.Params) {
|
override suspend fun execute(params: RemovePusherTask.Params) {
|
||||||
monarchy.awaitTransaction { realm ->
|
monarchy.awaitTransaction { realm ->
|
||||||
val existingEntity = PusherEntity.where(realm, params.userId, params.pushKey).findFirst()
|
val existingEntity = PusherEntity.where(realm, params.pushKey).findFirst()
|
||||||
existingEntity?.state = PusherState.UNREGISTERING
|
existingEntity?.state = PusherState.UNREGISTERING
|
||||||
}
|
}
|
||||||
|
|
||||||
val existing = Realm.getInstance(monarchy.realmConfiguration).use { realm ->
|
val existing = Realm.getInstance(monarchy.realmConfiguration).use { realm ->
|
||||||
PusherEntity.where(realm, params.userId, params.pushKey).findFirst()?.asDomain()
|
PusherEntity.where(realm, params.pushKey).findFirst()?.asDomain()
|
||||||
} ?: throw Exception("No existing pusher")
|
} ?: throw Exception("No existing pusher")
|
||||||
|
|
||||||
val deleteBody = JsonPusher(
|
val deleteBody = JsonPusher(
|
||||||
@ -64,7 +63,7 @@ internal class DefaultRemovePusherTask @Inject constructor(
|
|||||||
apiCall = pushersAPI.setPusher(deleteBody)
|
apiCall = pushersAPI.setPusher(deleteBody)
|
||||||
}
|
}
|
||||||
monarchy.awaitTransaction {
|
monarchy.awaitTransaction {
|
||||||
PusherEntity.where(it, params.userId, params.pushKey).findFirst()?.deleteFromRealm()
|
PusherEntity.where(it, params.pushKey).findFirst()?.deleteFromRealm()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user