mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-01-07 16:57:39 +03:00
Merge branch 'release/0.8.0'
This commit is contained in:
commit
eb32c5455f
373 changed files with 7180 additions and 3234 deletions
|
@ -3,14 +3,36 @@
|
|||
# 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
|
||||
|
||||
steps:
|
||||
- label: "Assemble GPlay Debug version"
|
||||
- label: "Compile and run Unit tests"
|
||||
agents:
|
||||
# We use a medium sized instance instead of the normal small ones because
|
||||
# gradle build is long
|
||||
# gradle build can be memory hungry
|
||||
queue: "medium"
|
||||
commands:
|
||||
- "./gradlew clean test --stacktrace"
|
||||
plugins:
|
||||
- docker#v3.1.0:
|
||||
image: "runmymind/docker-android-sdk"
|
||||
propagate-environment: true
|
||||
|
||||
- label: "Compile Android tests"
|
||||
agents:
|
||||
# We use a medium sized instance instead of the normal small ones because
|
||||
# gradle build can be memory hungry
|
||||
queue: "medium"
|
||||
commands:
|
||||
- "./gradlew clean assembleAndroidTest --stacktrace"
|
||||
plugins:
|
||||
- docker#v3.1.0:
|
||||
image: "runmymind/docker-android-sdk"
|
||||
propagate-environment: true
|
||||
|
||||
- label: "Assemble GPlay Debug version"
|
||||
agents:
|
||||
# We use a xlarge sized instance instead of the normal small ones because
|
||||
# gradle build can be memory hungry
|
||||
queue: "xlarge"
|
||||
commands:
|
||||
- "./gradlew clean lintGplayRelease assembleGplayDebug --stacktrace"
|
||||
artifact_paths:
|
||||
|
@ -23,9 +45,9 @@ steps:
|
|||
|
||||
- label: "Assemble FDroid Debug version"
|
||||
agents:
|
||||
# We use a medium sized instance instead of the normal small ones because
|
||||
# gradle build is long
|
||||
queue: "medium"
|
||||
# We use a xlarge sized instance instead of the normal small ones because
|
||||
# gradle build can be memory hungry
|
||||
queue: "xlarge"
|
||||
commands:
|
||||
- "./gradlew clean lintFdroidRelease assembleFdroidDebug --stacktrace"
|
||||
artifact_paths:
|
||||
|
@ -38,9 +60,9 @@ steps:
|
|||
|
||||
- label: "Build Google Play unsigned APK"
|
||||
agents:
|
||||
# We use a medium sized instance instead of the normal small ones because
|
||||
# gradle build is long
|
||||
queue: "medium"
|
||||
# We use a xlarge sized instance instead of the normal small ones because
|
||||
# gradle build can be memory hungry
|
||||
queue: "xlarge"
|
||||
commands:
|
||||
- "./gradlew clean assembleGplayRelease --stacktrace"
|
||||
artifact_paths:
|
||||
|
|
24
CHANGES.md
24
CHANGES.md
|
@ -1,3 +1,26 @@
|
|||
Changes in RiotX 0.8.0 (2019-11-19)
|
||||
===================================================
|
||||
|
||||
Features ✨:
|
||||
- Handle long click on room in the room list (#395)
|
||||
- Ignore/UnIgnore users, and display list of ignored users (#542, #617)
|
||||
|
||||
Improvements 🙌:
|
||||
- Search reaction by name or keyword in emoji picker
|
||||
- Handle code tags (#567)
|
||||
- Support spoiler messages
|
||||
- Support m.sticker and m.room.join_rules events in timeline
|
||||
|
||||
Other changes:
|
||||
- Markdown set to off by default (#412)
|
||||
- Accessibility improvements to the attachment file type chooser
|
||||
|
||||
Bugfix 🐛:
|
||||
- Fix issues with some member events rendering (#498)
|
||||
- Passphrase does not match (Export room keys) (#644)
|
||||
- Ask for permission to write external storage when uri comes from the keyboard (#658)
|
||||
- Fix issue with english US/GB translation (#671)
|
||||
|
||||
Changes in RiotX 0.7.0 (2019-10-24)
|
||||
===================================================
|
||||
|
||||
|
@ -12,6 +35,7 @@ Improvements:
|
|||
- Attachments: start using system pickers (#52)
|
||||
- Mark all messages as read (#396)
|
||||
|
||||
|
||||
Other changes:
|
||||
- Accessibility improvements to read receipts in the room timeline and reactions emoji chooser
|
||||
|
||||
|
|
|
@ -86,6 +86,10 @@ Also, if possible, please test your change on a real device. Testing on Android
|
|||
When adding new string resources, please only add new entries in file `value/strings.xml`. Translations will be added later by the community of translators with a specific tool named [Weblate](https://translate.riot.im/projects/riot-android/).
|
||||
Do not hesitate to use plurals when appropriate.
|
||||
|
||||
### Accessibility
|
||||
|
||||
Please consider accessibility as an important point. As a minimum requirement, in layout XML files please use attributes such as `android:contentDescription` and `android:importantForAccessibility`, and test with a screen reader if it's working well. You can add new string resources, dedicated to accessibility, in this case, please prefix theirs id with `a11y_`.
|
||||
|
||||
### Layout
|
||||
|
||||
When adding or editing layouts, make sure the layout will render correctly if device uses a RTL (Right To Left) language.
|
||||
|
|
|
@ -11,6 +11,8 @@ android {
|
|||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
||||
// Multidex is useful for tests
|
||||
multiDexEnabled true
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* 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.rx;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ExampleInstrumentedTest {
|
||||
@Test
|
||||
public void useAppContext() {
|
||||
// Context of the app under test.
|
||||
Context appContext = InstrumentationRegistry.getTargetContext();
|
||||
|
||||
assertEquals("im.vector.matrix.rx.test", appContext.getPackageName());
|
||||
}
|
||||
}
|
|
@ -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.ReadReceipt
|
||||
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||
import im.vector.matrix.android.api.session.room.notification.RoomNotificationState
|
||||
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.util.Optional
|
||||
|
@ -67,6 +68,10 @@ class RxRoom(private val room: Room) {
|
|||
fun liveDrafts(): Observable<List<UserDraft>> {
|
||||
return room.getDraftsLive().asObservable()
|
||||
}
|
||||
|
||||
fun liveNotificationState(): Observable<RoomNotificationState> {
|
||||
return room.getLiveRoomNotificationState().asObservable()
|
||||
}
|
||||
}
|
||||
|
||||
fun Room.rx(): RxRoom {
|
||||
|
|
|
@ -54,6 +54,10 @@ class RxSession(private val session: Session) {
|
|||
return session.liveUsers().asObservable()
|
||||
}
|
||||
|
||||
fun liveIgnoredUsers(): Observable<List<User>> {
|
||||
return session.liveIgnoredUsers().asObservable()
|
||||
}
|
||||
|
||||
fun livePagedUsers(filter: String? = null): Observable<PagedList<User>> {
|
||||
return session.livePagedUsers(filter).asObservable()
|
||||
}
|
||||
|
|
|
@ -155,7 +155,8 @@ dependencies {
|
|||
testImplementation 'junit:junit:4.12'
|
||||
testImplementation 'org.robolectric:robolectric:4.3'
|
||||
//testImplementation 'org.robolectric:shadows-support-v4:3.0'
|
||||
testImplementation 'io.mockk:mockk:1.9.3.kotlin12'
|
||||
// Note: version sticks to 1.9.2 due to https://github.com/mockk/mockk/issues/281
|
||||
testImplementation 'io.mockk:mockk:1.9.2.kotlin12'
|
||||
testImplementation 'org.amshove.kluent:kluent-android:1.44'
|
||||
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
|
||||
|
||||
|
@ -165,7 +166,8 @@ dependencies {
|
|||
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||
androidTestImplementation 'org.amshove.kluent:kluent-android:1.44'
|
||||
androidTestImplementation 'io.mockk:mockk-android:1.9.3.kotlin12'
|
||||
// Note: version sticks to 1.9.2 due to https://github.com/mockk/mockk/issues/281
|
||||
androidTestImplementation 'io.mockk:mockk-android:1.9.2.kotlin12'
|
||||
androidTestImplementation "androidx.arch.core:core-testing:$lifecycle_version"
|
||||
androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
|
||||
|
||||
|
|
|
@ -17,12 +17,12 @@
|
|||
package im.vector.matrix.android
|
||||
|
||||
import android.content.Context
|
||||
import androidx.test.InstrumentationRegistry
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import java.io.File
|
||||
|
||||
interface InstrumentedTest {
|
||||
fun context(): Context {
|
||||
return InstrumentationRegistry.getTargetContext()
|
||||
return ApplicationProvider.getApplicationContext()
|
||||
}
|
||||
|
||||
fun cacheDir(): File {
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
package im.vector.matrix.android.auth
|
||||
|
||||
import androidx.test.annotation.UiThreadTest
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.rule.GrantPermissionRule
|
||||
import androidx.test.runner.AndroidJUnit4
|
||||
import im.vector.matrix.android.InstrumentedTest
|
||||
import im.vector.matrix.android.OkReplayRuleChainNoActivity
|
||||
import im.vector.matrix.android.api.auth.Authenticator
|
||||
|
|
|
@ -67,5 +67,5 @@ interface Authenticator {
|
|||
/**
|
||||
* Create a session after a SSO successful login
|
||||
*/
|
||||
fun createSessionFromSso(credentials: Credentials, homeServerConnectionConfig: HomeServerConnectionConfig): Session
|
||||
fun createSessionFromSso(credentials: Credentials, homeServerConnectionConfig: HomeServerConnectionConfig, callback: MatrixCallback<Session>): Cancelable
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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.crypto
|
||||
|
||||
import im.vector.matrix.android.api.session.crypto.sas.EmojiRepresentation
|
||||
import im.vector.matrix.android.internal.crypto.verification.getEmojiForCode
|
||||
|
||||
/**
|
||||
* Provide all the emojis used for SAS verification (for debug purpose)
|
||||
*/
|
||||
fun getAllVerificationEmojis(): List<EmojiRepresentation> {
|
||||
return (0..63).map { getEmojiForCode(it) }
|
||||
}
|
|
@ -21,7 +21,7 @@ import timber.log.Timber
|
|||
sealed class Action {
|
||||
object Notify : Action()
|
||||
object DoNotNotify : Action()
|
||||
data class Sound(val sound: String) : Action()
|
||||
data class Sound(val sound: String = ACTION_OBJECT_VALUE_VALUE_DEFAULT) : Action()
|
||||
data class Highlight(val highlight: Boolean) : Action()
|
||||
}
|
||||
|
||||
|
@ -63,6 +63,29 @@ private const val ACTION_OBJECT_VALUE_VALUE_DEFAULT = "default"
|
|||
*
|
||||
* </pre>
|
||||
*/
|
||||
|
||||
@Suppress("IMPLICIT_CAST_TO_ANY")
|
||||
fun List<Action>.toJson(): List<Any> {
|
||||
return map { action ->
|
||||
when (action) {
|
||||
is Action.Notify -> ACTION_NOTIFY
|
||||
is Action.DoNotNotify -> ACTION_DONT_NOTIFY
|
||||
is Action.Sound -> {
|
||||
mapOf(
|
||||
ACTION_OBJECT_SET_TWEAK_KEY to ACTION_OBJECT_SET_TWEAK_VALUE_SOUND,
|
||||
ACTION_OBJECT_VALUE_KEY to action.sound
|
||||
)
|
||||
}
|
||||
is Action.Highlight -> {
|
||||
mapOf(
|
||||
ACTION_OBJECT_SET_TWEAK_KEY to ACTION_OBJECT_SET_TWEAK_VALUE_HIGHLIGHT,
|
||||
ACTION_OBJECT_VALUE_KEY to action.highlight
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun PushRule.getActions(): List<Action> {
|
||||
val result = ArrayList<Action>()
|
||||
|
||||
|
|
|
@ -34,6 +34,10 @@ interface PushRuleService {
|
|||
|
||||
fun updatePushRuleEnableStatus(kind: RuleKind, pushRule: PushRule, enabled: Boolean, callback: MatrixCallback<Unit>): Cancelable
|
||||
|
||||
fun addPushRule(kind: RuleKind, pushRule: PushRule, callback: MatrixCallback<Unit>): Cancelable
|
||||
|
||||
fun removePushRule(kind: RuleKind, pushRule: PushRule, callback: MatrixCallback<Unit>): Cancelable
|
||||
|
||||
fun addPushRuleListener(listener: PushRuleListener)
|
||||
|
||||
fun removePushRuleListener(listener: PushRuleListener)
|
||||
|
|
|
@ -19,7 +19,7 @@ package im.vector.matrix.android.api.session.cache
|
|||
import im.vector.matrix.android.api.MatrixCallback
|
||||
|
||||
/**
|
||||
* This interface defines a method to sign out. It's implemented at the session level.
|
||||
* This interface defines a method to clear the cache. It's implemented at the session level.
|
||||
*/
|
||||
interface CacheService {
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
package im.vector.matrix.android.api.session.events.model
|
||||
|
||||
import java.util.*
|
||||
import java.util.UUID
|
||||
|
||||
object LocalEcho {
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import im.vector.matrix.android.api.session.room.crypto.RoomCryptoService
|
|||
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.relation.RelationService
|
||||
import im.vector.matrix.android.api.session.room.notification.RoomPushRuleService
|
||||
import im.vector.matrix.android.api.session.room.reporting.ReportingService
|
||||
import im.vector.matrix.android.api.session.room.read.ReadService
|
||||
import im.vector.matrix.android.api.session.room.send.DraftService
|
||||
|
@ -41,7 +42,8 @@ interface Room :
|
|||
StateService,
|
||||
ReportingService,
|
||||
RelationService,
|
||||
RoomCryptoService {
|
||||
RoomCryptoService,
|
||||
RoomPushRuleService {
|
||||
|
||||
/**
|
||||
* The roomId of this room
|
||||
|
|
|
@ -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.model
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
|
||||
/**
|
||||
* Enum for [RoomJoinRulesContent] : https://matrix.org/docs/spec/client_server/r0.4.0#m-room-join-rules
|
||||
*/
|
||||
enum class RoomJoinRules(val value: String) {
|
||||
|
||||
@Json(name = "public")
|
||||
PUBLIC("public"),
|
||||
|
||||
@Json(name = "invite")
|
||||
INVITE("invite"),
|
||||
|
||||
@Json(name = "knock")
|
||||
KNOCK("knock"),
|
||||
|
||||
@Json(name = "private")
|
||||
PRIVATE("private")
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* 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.model
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
/**
|
||||
* Class representing the EventType.STATE_ROOM_JOIN_RULES state event content
|
||||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class RoomJoinRulesContent(
|
||||
@Json(name = "join_rule") val joinRules: RoomJoinRules? = null
|
||||
)
|
|
@ -38,7 +38,7 @@ data class MessageImageContent(
|
|||
/**
|
||||
* Metadata about the image referred to in url.
|
||||
*/
|
||||
@Json(name = "info") val info: ImageInfo? = null,
|
||||
@Json(name = "info") override val info: ImageInfo? = null,
|
||||
|
||||
/**
|
||||
* Required. Required if the file is unencrypted. The URL (typically MXC URI) to the image.
|
||||
|
@ -52,4 +52,4 @@ data class MessageImageContent(
|
|||
* Required if the file is encrypted. Information on the encrypted file, as specified in End-to-end encryption.
|
||||
*/
|
||||
@Json(name = "file") override val encryptedFileInfo: EncryptedFileInfo? = null
|
||||
) : MessageEncryptedContent
|
||||
) : MessageImageInfoContent
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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.model.message
|
||||
|
||||
/**
|
||||
* A content with image information
|
||||
*/
|
||||
interface MessageImageInfoContent : MessageEncryptedContent {
|
||||
val info: ImageInfo?
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* 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.model.message
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import im.vector.matrix.android.api.session.events.model.Content
|
||||
import im.vector.matrix.android.api.session.room.model.relation.RelationDefaultContent
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.EncryptedFileInfo
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class MessageStickerContent(
|
||||
/**
|
||||
* Set in local, not from server
|
||||
*/
|
||||
override val type: String = MessageType.MSGTYPE_STICKER_LOCAL,
|
||||
|
||||
/**
|
||||
* Required. A textual representation of the image. This could be the alt text of the image, the filename of the image,
|
||||
* or some kind of content description for accessibility e.g. 'image attachment'.
|
||||
*/
|
||||
@Json(name = "body") override val body: String,
|
||||
|
||||
/**
|
||||
* Metadata about the image referred to in url.
|
||||
*/
|
||||
@Json(name = "info") override val info: ImageInfo? = null,
|
||||
|
||||
/**
|
||||
* Required. Required if the file is unencrypted. The URL (typically MXC URI) to the image.
|
||||
*/
|
||||
@Json(name = "url") override val url: String? = null,
|
||||
|
||||
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
|
||||
@Json(name = "m.new_content") override val newContent: Content? = null,
|
||||
|
||||
/**
|
||||
* Required if the file is encrypted. Information on the encrypted file, as specified in End-to-end encryption.
|
||||
*/
|
||||
@Json(name = "file") override val encryptedFileInfo: EncryptedFileInfo? = null
|
||||
) : MessageImageInfoContent
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* 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.notification
|
||||
|
||||
/**
|
||||
* Defines the room notification state
|
||||
*/
|
||||
enum class RoomNotificationState {
|
||||
/**
|
||||
* All the messages will trigger a noisy notification
|
||||
*/
|
||||
ALL_MESSAGES_NOISY,
|
||||
|
||||
/**
|
||||
* All the messages will trigger a notification
|
||||
*/
|
||||
ALL_MESSAGES,
|
||||
|
||||
/**
|
||||
* Only the messages with user display name / user name will trigger notifications
|
||||
*/
|
||||
MENTIONS_ONLY,
|
||||
|
||||
/**
|
||||
* No notifications
|
||||
*/
|
||||
MUTE
|
||||
}
|
|
@ -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.room.notification
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.util.Cancelable
|
||||
|
||||
interface RoomPushRuleService {
|
||||
|
||||
fun getLiveRoomNotificationState(): LiveData<RoomNotificationState>
|
||||
|
||||
fun setRoomNotificationState(roomNotificationState: RoomNotificationState, matrixCallback: MatrixCallback<Unit>): Cancelable
|
||||
}
|
|
@ -23,6 +23,7 @@ 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.ReadReceipt
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageContent
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageStickerContent
|
||||
import im.vector.matrix.android.api.session.room.model.message.isReply
|
||||
import im.vector.matrix.android.api.util.ContentUtils.extractUsefulTextFromReply
|
||||
import im.vector.matrix.android.internal.crypto.model.event.EncryptedEventContent
|
||||
|
@ -62,15 +63,11 @@ data class TimelineEvent(
|
|||
}
|
||||
|
||||
fun getDisambiguatedDisplayName(): String {
|
||||
return if (isUniqueDisplayName) {
|
||||
senderName
|
||||
} else {
|
||||
senderName?.let { name ->
|
||||
"$name (${root.senderId})"
|
||||
}
|
||||
return when {
|
||||
senderName.isNullOrBlank() -> root.senderId ?: ""
|
||||
isUniqueDisplayName -> senderName
|
||||
else -> "$senderName (${root.senderId})"
|
||||
}
|
||||
?: root.senderId
|
||||
?: ""
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -103,8 +100,14 @@ fun TimelineEvent.getEditedEventId(): String? {
|
|||
/**
|
||||
* Get last MessageContent, after a possible edition
|
||||
*/
|
||||
fun TimelineEvent.getLastMessageContent(): MessageContent? = annotations?.editSummary?.aggregatedContent?.toModel()
|
||||
?: root.getClearContent().toModel()
|
||||
fun TimelineEvent.getLastMessageContent(): MessageContent? {
|
||||
return if (root.getClearType() == EventType.STICKER) {
|
||||
root.getClearContent().toModel<MessageStickerContent>()
|
||||
} else {
|
||||
annotations?.editSummary?.aggregatedContent?.toModel()
|
||||
?: root.getClearContent().toModel()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get last Message body, after a possible edition
|
||||
|
@ -113,7 +116,8 @@ fun TimelineEvent.getLastMessageBody(): String? {
|
|||
val lastMessageContent = getLastMessageContent()
|
||||
|
||||
if (lastMessageContent != null) {
|
||||
return lastMessageContent.newContent?.toModel<MessageContent>()?.body ?: lastMessageContent.body
|
||||
return lastMessageContent.newContent?.toModel<MessageContent>()?.body
|
||||
?: lastMessageContent.body
|
||||
}
|
||||
|
||||
return null
|
||||
|
|
|
@ -64,4 +64,19 @@ interface UserService {
|
|||
* @return a Livedata of users
|
||||
*/
|
||||
fun livePagedUsers(filter: String? = null): LiveData<PagedList<User>>
|
||||
|
||||
/**
|
||||
* Get list of ignored users
|
||||
*/
|
||||
fun liveIgnoredUsers(): LiveData<List<User>>
|
||||
|
||||
/**
|
||||
* Ignore users
|
||||
*/
|
||||
fun ignoreUserIds(userIds: List<String>, callback: MatrixCallback<Unit>): Cancelable
|
||||
|
||||
/**
|
||||
* Un-ignore some users
|
||||
*/
|
||||
fun unIgnoreUserIds(userIds: List<String>, callback: MatrixCallback<Unit>): Cancelable
|
||||
}
|
||||
|
|
|
@ -21,4 +21,6 @@ import java.lang.reflect.ParameterizedType
|
|||
|
||||
typealias JsonDict = Map<String, @JvmSuppressWildcards Any>
|
||||
|
||||
val emptyJsonDict = emptyMap<String, Any>()
|
||||
|
||||
internal val JSON_DICT_PARAMETERIZED_TYPE: ParameterizedType = Types.newParameterizedType(Map::class.java, String::class.java, Any::class.java)
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package im.vector.matrix.android.internal.auth
|
||||
|
||||
import android.util.Patterns
|
||||
import dagger.Lazy
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.auth.Authenticator
|
||||
import im.vector.matrix.android.api.auth.data.Credentials
|
||||
|
@ -39,10 +40,9 @@ import kotlinx.coroutines.launch
|
|||
import kotlinx.coroutines.withContext
|
||||
import okhttp3.OkHttpClient
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Provider
|
||||
|
||||
internal class DefaultAuthenticator @Inject constructor(@Unauthenticated
|
||||
private val okHttpClient: Provider<OkHttpClient>,
|
||||
private val okHttpClient: Lazy<OkHttpClient>,
|
||||
private val retrofitFactory: RetrofitFactory,
|
||||
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||
private val sessionParamsStore: SessionParamsStore,
|
||||
|
@ -112,14 +112,27 @@ internal class DefaultAuthenticator @Inject constructor(@Unauthenticated
|
|||
sessionManager.getOrCreateSession(sessionParams)
|
||||
}
|
||||
|
||||
override fun createSessionFromSso(credentials: Credentials, homeServerConnectionConfig: HomeServerConnectionConfig): Session {
|
||||
override fun createSessionFromSso(credentials: Credentials,
|
||||
homeServerConnectionConfig: HomeServerConnectionConfig,
|
||||
callback: MatrixCallback<Session>): Cancelable {
|
||||
val job = GlobalScope.launch(coroutineDispatchers.main) {
|
||||
val sessionOrFailure = runCatching {
|
||||
createSessionFromSso(credentials, homeServerConnectionConfig)
|
||||
}
|
||||
sessionOrFailure.foldToCallback(callback)
|
||||
}
|
||||
return CancelableCoroutine(job)
|
||||
}
|
||||
|
||||
private suspend fun createSessionFromSso(credentials: Credentials,
|
||||
homeServerConnectionConfig: HomeServerConnectionConfig): Session = withContext(coroutineDispatchers.computation) {
|
||||
val sessionParams = SessionParams(credentials, homeServerConnectionConfig)
|
||||
sessionParamsStore.save(sessionParams)
|
||||
return sessionManager.getOrCreateSession(sessionParams)
|
||||
sessionManager.getOrCreateSession(sessionParams)
|
||||
}
|
||||
|
||||
private fun buildAuthAPI(homeServerConnectionConfig: HomeServerConnectionConfig): AuthAPI {
|
||||
val retrofit = retrofitFactory.create(okHttpClient.get(), homeServerConnectionConfig.homeServerUri.toString())
|
||||
val retrofit = retrofitFactory.create(okHttpClient, homeServerConnectionConfig.homeServerUri.toString())
|
||||
return retrofit.create(AuthAPI::class.java)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
package im.vector.matrix.android.internal.auth
|
||||
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.api.auth.data.SessionParams
|
||||
|
||||
internal interface SessionParamsStore {
|
||||
|
@ -27,9 +26,9 @@ internal interface SessionParamsStore {
|
|||
|
||||
fun getAll(): List<SessionParams>
|
||||
|
||||
fun save(sessionParams: SessionParams): Try<Unit>
|
||||
suspend fun save(sessionParams: SessionParams)
|
||||
|
||||
fun delete(userId: String): Try<Unit>
|
||||
suspend fun delete(userId: String)
|
||||
|
||||
fun deleteAll(): Try<Unit>
|
||||
suspend fun deleteAll()
|
||||
}
|
||||
|
|
|
@ -16,9 +16,9 @@
|
|||
|
||||
package im.vector.matrix.android.internal.auth.db
|
||||
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.api.auth.data.SessionParams
|
||||
import im.vector.matrix.android.internal.auth.SessionParamsStore
|
||||
import im.vector.matrix.android.internal.database.awaitTransaction
|
||||
import im.vector.matrix.android.internal.di.AuthDatabase
|
||||
import io.realm.Realm
|
||||
import io.realm.RealmConfiguration
|
||||
|
@ -62,41 +62,29 @@ internal class RealmSessionParamsStore @Inject constructor(private val mapper: S
|
|||
return sessionParams
|
||||
}
|
||||
|
||||
override fun save(sessionParams: SessionParams): Try<Unit> {
|
||||
return Try {
|
||||
override suspend fun save(sessionParams: SessionParams) {
|
||||
awaitTransaction(realmConfiguration) {
|
||||
val entity = mapper.map(sessionParams)
|
||||
if (entity != null) {
|
||||
val realm = Realm.getInstance(realmConfiguration)
|
||||
realm.executeTransaction {
|
||||
it.insert(entity)
|
||||
}
|
||||
realm.close()
|
||||
it.insert(entity)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun delete(userId: String): Try<Unit> {
|
||||
return Try {
|
||||
val realm = Realm.getInstance(realmConfiguration)
|
||||
realm.executeTransaction {
|
||||
it.where(SessionParamsEntity::class.java)
|
||||
.equalTo(SessionParamsEntityFields.USER_ID, userId)
|
||||
.findAll()
|
||||
.deleteAllFromRealm()
|
||||
}
|
||||
realm.close()
|
||||
override suspend fun delete(userId: String) {
|
||||
awaitTransaction(realmConfiguration) {
|
||||
it.where(SessionParamsEntity::class.java)
|
||||
.equalTo(SessionParamsEntityFields.USER_ID, userId)
|
||||
.findAll()
|
||||
.deleteAllFromRealm()
|
||||
}
|
||||
}
|
||||
|
||||
override fun deleteAll(): Try<Unit> {
|
||||
return Try {
|
||||
val realm = Realm.getInstance(realmConfiguration)
|
||||
realm.executeTransaction {
|
||||
it.where(SessionParamsEntity::class.java)
|
||||
.findAll()
|
||||
.deleteAllFromRealm()
|
||||
}
|
||||
realm.close()
|
||||
override suspend fun deleteAll() {
|
||||
awaitTransaction(realmConfiguration) {
|
||||
it.where(SessionParamsEntity::class.java)
|
||||
.findAll()
|
||||
.deleteAllFromRealm()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
|
|||
if (':' in userId) {
|
||||
try {
|
||||
synchronized(notReadyToRetryHS) {
|
||||
res = !notReadyToRetryHS.contains(userId.substring(userId.lastIndexOf(":") + 1))
|
||||
res = !notReadyToRetryHS.contains(userId.substringAfterLast(':'))
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "## canRetryKeysDownload() failed")
|
||||
|
|
|
@ -216,7 +216,7 @@ internal class OutgoingRoomKeyRequestManager @Inject constructor(
|
|||
sendMessageToDevices(requestMessage, request.recipients, request.requestId, object : MatrixCallback<Unit> {
|
||||
private fun onDone(state: OutgoingRoomKeyRequest.RequestState) {
|
||||
if (request.state !== OutgoingRoomKeyRequest.RequestState.UNSENT) {
|
||||
Timber.v("## sendOutgoingRoomKeyRequest() : Cannot update room key request from UNSENT as it was already updated to " + request.state)
|
||||
Timber.v("## sendOutgoingRoomKeyRequest() : Cannot update room key request from UNSENT as it was already updated to ${request.state}")
|
||||
} else {
|
||||
request.state = state
|
||||
cryptoStore.updateOutgoingRoomKeyRequest(request)
|
||||
|
|
|
@ -41,8 +41,7 @@ fun <T> doWithRealm(realmConfiguration: RealmConfiguration, action: (Realm) -> T
|
|||
*/
|
||||
fun <T : RealmObject> doRealmQueryAndCopy(realmConfiguration: RealmConfiguration, action: (Realm) -> T?): T? {
|
||||
return Realm.getInstance(realmConfiguration).use { realm ->
|
||||
val result = action.invoke(realm)
|
||||
result?.let { realm.copyFromRealm(it) }
|
||||
action.invoke(realm)?.let { realm.copyFromRealm(it) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,8 +50,7 @@ fun <T : RealmObject> doRealmQueryAndCopy(realmConfiguration: RealmConfiguration
|
|||
*/
|
||||
fun <T : RealmObject> doRealmQueryAndCopyList(realmConfiguration: RealmConfiguration, action: (Realm) -> Iterable<T>): Iterable<T> {
|
||||
return Realm.getInstance(realmConfiguration).use { realm ->
|
||||
val result = action.invoke(realm)
|
||||
realm.copyFromRealm(result)
|
||||
action.invoke(realm).let { realm.copyFromRealm(it) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -91,7 +91,7 @@ internal class RealmCryptoStore(private val realmConfiguration: RealmConfigurati
|
|||
realmLocker = Realm.getInstance(realmConfiguration)
|
||||
|
||||
// Ensure CryptoMetadataEntity is inserted in DB
|
||||
doWithRealm(realmConfiguration) { realm ->
|
||||
doRealmTransaction(realmConfiguration) { realm ->
|
||||
var currentMetadata = realm.where<CryptoMetadataEntity>().findFirst()
|
||||
|
||||
var deleteAll = false
|
||||
|
@ -109,15 +109,13 @@ internal class RealmCryptoStore(private val realmConfiguration: RealmConfigurati
|
|||
}
|
||||
|
||||
if (currentMetadata == null) {
|
||||
realm.executeTransaction {
|
||||
if (deleteAll) {
|
||||
it.deleteAll()
|
||||
}
|
||||
if (deleteAll) {
|
||||
realm.deleteAll()
|
||||
}
|
||||
|
||||
// Metadata not found, or database cleaned, create it
|
||||
it.createObject(CryptoMetadataEntity::class.java, credentials.userId).apply {
|
||||
deviceId = credentials.deviceId
|
||||
}
|
||||
// Metadata not found, or database cleaned, create it
|
||||
realm.createObject(CryptoMetadataEntity::class.java, credentials.userId).apply {
|
||||
deviceId = credentials.deviceId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
|||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import java.lang.Exception
|
||||
import java.util.UUID
|
||||
import javax.inject.Inject
|
||||
import kotlin.collections.HashMap
|
||||
|
@ -166,72 +167,59 @@ internal class DefaultSasVerificationService @Inject constructor(private val cre
|
|||
return
|
||||
}
|
||||
// Download device keys prior to everything
|
||||
checkKeysAreDownloaded(
|
||||
otherUserId!!,
|
||||
startReq,
|
||||
success = {
|
||||
Timber.v("## SAS onStartRequestReceived ${startReq.transactionID!!}")
|
||||
val tid = startReq.transactionID!!
|
||||
val existing = getExistingTransaction(otherUserId, tid)
|
||||
val existingTxs = getExistingTransactionsForUser(otherUserId)
|
||||
if (existing != null) {
|
||||
// should cancel both!
|
||||
Timber.v("## SAS onStartRequestReceived - Request exist with same if ${startReq.transactionID!!}")
|
||||
existing.cancel(CancelCode.UnexpectedMessage)
|
||||
cancelTransaction(tid, otherUserId, startReq.fromDevice!!, CancelCode.UnexpectedMessage)
|
||||
} else if (existingTxs?.isEmpty() == false) {
|
||||
Timber.v("## SAS onStartRequestReceived - There is already a transaction with this user ${startReq.transactionID!!}")
|
||||
// Multiple keyshares between two devices: any two devices may only have at most one key verification in flight at a time.
|
||||
existingTxs.forEach {
|
||||
it.cancel(CancelCode.UnexpectedMessage)
|
||||
}
|
||||
cancelTransaction(tid, otherUserId, startReq.fromDevice!!, CancelCode.UnexpectedMessage)
|
||||
} else {
|
||||
// Ok we can create
|
||||
if (KeyVerificationStart.VERIF_METHOD_SAS == startReq.method) {
|
||||
Timber.v("## SAS onStartRequestReceived - request accepted ${startReq.transactionID!!}")
|
||||
val tx = IncomingSASVerificationTransaction(
|
||||
this,
|
||||
setDeviceVerificationAction,
|
||||
credentials,
|
||||
cryptoStore,
|
||||
sendToDeviceTask,
|
||||
taskExecutor,
|
||||
myDeviceInfoHolder.get().myDevice.fingerprint()!!,
|
||||
startReq.transactionID!!,
|
||||
otherUserId)
|
||||
addTransaction(tx)
|
||||
tx.acceptToDeviceEvent(otherUserId, startReq)
|
||||
} else {
|
||||
Timber.e("## SAS onStartRequestReceived - unknown method ${startReq.method}")
|
||||
cancelTransaction(tid, otherUserId, startReq.fromDevice
|
||||
?: event.getSenderKey()!!, CancelCode.UnknownMethod)
|
||||
}
|
||||
}
|
||||
},
|
||||
error = {
|
||||
cancelTransaction(startReq.transactionID!!, otherUserId, startReq.fromDevice!!, CancelCode.UnexpectedMessage)
|
||||
})
|
||||
if (checkKeysAreDownloaded(otherUserId!!, startReq) != null) {
|
||||
Timber.v("## SAS onStartRequestReceived ${startReq.transactionID!!}")
|
||||
val tid = startReq.transactionID!!
|
||||
val existing = getExistingTransaction(otherUserId, tid)
|
||||
val existingTxs = getExistingTransactionsForUser(otherUserId)
|
||||
if (existing != null) {
|
||||
// should cancel both!
|
||||
Timber.v("## SAS onStartRequestReceived - Request exist with same if ${startReq.transactionID!!}")
|
||||
existing.cancel(CancelCode.UnexpectedMessage)
|
||||
cancelTransaction(tid, otherUserId, startReq.fromDevice!!, CancelCode.UnexpectedMessage)
|
||||
} else if (existingTxs?.isEmpty() == false) {
|
||||
Timber.v("## SAS onStartRequestReceived - There is already a transaction with this user ${startReq.transactionID!!}")
|
||||
// Multiple keyshares between two devices: any two devices may only have at most one key verification in flight at a time.
|
||||
existingTxs.forEach {
|
||||
it.cancel(CancelCode.UnexpectedMessage)
|
||||
}
|
||||
cancelTransaction(tid, otherUserId, startReq.fromDevice!!, CancelCode.UnexpectedMessage)
|
||||
} else {
|
||||
// Ok we can create
|
||||
if (KeyVerificationStart.VERIF_METHOD_SAS == startReq.method) {
|
||||
Timber.v("## SAS onStartRequestReceived - request accepted ${startReq.transactionID!!}")
|
||||
val tx = IncomingSASVerificationTransaction(
|
||||
this,
|
||||
setDeviceVerificationAction,
|
||||
credentials,
|
||||
cryptoStore,
|
||||
sendToDeviceTask,
|
||||
taskExecutor,
|
||||
myDeviceInfoHolder.get().myDevice.fingerprint()!!,
|
||||
startReq.transactionID!!,
|
||||
otherUserId)
|
||||
addTransaction(tx)
|
||||
tx.acceptToDeviceEvent(otherUserId, startReq)
|
||||
} else {
|
||||
Timber.e("## SAS onStartRequestReceived - unknown method ${startReq.method}")
|
||||
cancelTransaction(tid, otherUserId, startReq.fromDevice
|
||||
?: event.getSenderKey()!!, CancelCode.UnknownMethod)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cancelTransaction(startReq.transactionID!!, otherUserId, startReq.fromDevice!!, CancelCode.UnexpectedMessage)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun checkKeysAreDownloaded(otherUserId: String,
|
||||
startReq: KeyVerificationStart,
|
||||
success: (MXUsersDevicesMap<MXDeviceInfo>) -> Unit,
|
||||
error: () -> Unit) {
|
||||
runCatching {
|
||||
deviceListManager.downloadKeys(listOf(otherUserId), true)
|
||||
}.fold(
|
||||
{
|
||||
if (it.getUserDeviceIds(otherUserId)?.contains(startReq.fromDevice) == true) {
|
||||
success(it)
|
||||
} else {
|
||||
error()
|
||||
}
|
||||
},
|
||||
{
|
||||
error()
|
||||
}
|
||||
)
|
||||
startReq: KeyVerificationStart): MXUsersDevicesMap<MXDeviceInfo>? {
|
||||
return try {
|
||||
val keys = deviceListManager.downloadKeys(listOf(otherUserId), true)
|
||||
val deviceIds = keys.getUserDeviceIds(otherUserId) ?: return null
|
||||
keys.takeIf { deviceIds.contains(startReq.fromDevice) }
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun onCancelReceived(event: Event) {
|
||||
|
@ -342,10 +330,8 @@ internal class DefaultSasVerificationService @Inject constructor(private val cre
|
|||
private fun addTransaction(tx: VerificationTransaction) {
|
||||
tx.otherUserId.let { otherUserId ->
|
||||
synchronized(txMap) {
|
||||
if (txMap[otherUserId] == null) {
|
||||
txMap[otherUserId] = HashMap()
|
||||
}
|
||||
txMap[otherUserId]?.set(tx.transactionId, tx)
|
||||
val txInnerMap = txMap.getOrPut(otherUserId) { HashMap() }
|
||||
txInnerMap[tx.transactionId] = tx
|
||||
dispatchTxAdded(tx)
|
||||
tx.addListener(this)
|
||||
}
|
||||
|
|
|
@ -20,14 +20,19 @@ import io.realm.RealmConfiguration
|
|||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
|
||||
suspend fun awaitTransaction(config: RealmConfiguration, transaction: suspend (realm: Realm) -> Unit) = withContext(Dispatchers.IO) {
|
||||
suspend fun awaitTransaction(config: RealmConfiguration, transaction: suspend (realm: Realm) -> Unit) = withContext(Dispatchers.Default) {
|
||||
Realm.getInstance(config).use { bgRealm ->
|
||||
bgRealm.beginTransaction()
|
||||
try {
|
||||
val start = System.currentTimeMillis()
|
||||
transaction(bgRealm)
|
||||
if (isActive) {
|
||||
bgRealm.commitTransaction()
|
||||
val end = System.currentTimeMillis()
|
||||
val time = end - start
|
||||
Timber.v("Execute transaction in $time millis")
|
||||
}
|
||||
} finally {
|
||||
if (bgRealm.isInTransaction) {
|
||||
|
|
|
@ -39,14 +39,17 @@ internal fun TimelineEventEntity.updateSenderData() {
|
|||
val isUnlinked = chunkEntity.isUnlinked()
|
||||
var senderMembershipEvent: EventEntity?
|
||||
var senderRoomMemberContent: String?
|
||||
var senderRoomMemberPrevContent: String?
|
||||
when {
|
||||
stateIndex <= 0 -> {
|
||||
senderMembershipEvent = chunkEntity.timelineEvents.buildQuery(senderId, isUnlinked).next(from = stateIndex)?.root
|
||||
senderRoomMemberContent = senderMembershipEvent?.prevContent
|
||||
senderRoomMemberPrevContent = senderMembershipEvent?.content
|
||||
}
|
||||
else -> {
|
||||
senderMembershipEvent = chunkEntity.timelineEvents.buildQuery(senderId, isUnlinked).prev(since = stateIndex)?.root
|
||||
senderRoomMemberContent = senderMembershipEvent?.content
|
||||
senderRoomMemberPrevContent = senderMembershipEvent?.prevContent
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,11 +61,27 @@ internal fun TimelineEventEntity.updateSenderData() {
|
|||
.equalTo(EventEntityFields.TYPE, EventType.STATE_ROOM_MEMBER)
|
||||
.prev(since = stateIndex)
|
||||
senderRoomMemberContent = senderMembershipEvent?.content
|
||||
senderRoomMemberPrevContent = senderMembershipEvent?.prevContent
|
||||
}
|
||||
|
||||
ContentMapper.map(senderRoomMemberContent).toModel<RoomMember>()?.also {
|
||||
this.senderAvatar = it.avatarUrl
|
||||
this.senderName = it.displayName
|
||||
this.isUniqueDisplayName = RoomMembers(realm, roomId).isUniqueDisplayName(it.displayName)
|
||||
}
|
||||
|
||||
// We try to fallback on prev content if we got a room member state events with null fields
|
||||
if (root?.type == EventType.STATE_ROOM_MEMBER) {
|
||||
ContentMapper.map(senderRoomMemberPrevContent).toModel<RoomMember>()?.also {
|
||||
if (this.senderAvatar == null && it.avatarUrl != null) {
|
||||
this.senderAvatar = it.avatarUrl
|
||||
}
|
||||
if (this.senderName == null && it.displayName != null) {
|
||||
this.senderName = it.displayName
|
||||
this.isUniqueDisplayName = RoomMembers(realm, roomId).isUniqueDisplayName(it.displayName)
|
||||
}
|
||||
}
|
||||
}
|
||||
val senderRoomMember: RoomMember? = ContentMapper.map(senderRoomMemberContent).toModel()
|
||||
this.senderAvatar = senderRoomMember?.avatarUrl
|
||||
this.senderName = senderRoomMember?.displayName
|
||||
this.isUniqueDisplayName = RoomMembers(realm, roomId).isUniqueDisplayName(senderRoomMember?.displayName)
|
||||
this.senderMembershipEvent = senderMembershipEvent
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ import im.vector.matrix.android.api.session.room.model.RoomSummary
|
|||
import im.vector.matrix.android.api.session.room.model.tag.RoomTag
|
||||
import im.vector.matrix.android.internal.crypto.algorithms.olm.OlmDecryptionResult
|
||||
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
|
||||
import java.util.*
|
||||
import java.util.UUID
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class RoomSummaryMapper @Inject constructor(
|
||||
|
@ -36,7 +36,7 @@ internal class RoomSummaryMapper @Inject constructor(
|
|||
}
|
||||
|
||||
val latestEvent = roomSummaryEntity.latestPreviewableEvent?.let {
|
||||
timelineEventMapper.map(it)
|
||||
timelineEventMapper.map(it, buildReadReceipts = false)
|
||||
}
|
||||
if (latestEvent?.root?.isEncrypted() == true && latestEvent.root.mxDecryptionResult == null) {
|
||||
// TODO use a global event decryptor? attache to session and that listen to new sessionId?
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* 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 IgnoredUserEntity(var userId: String = "") : RealmObject() {
|
||||
|
||||
companion object
|
||||
}
|
|
@ -17,6 +17,8 @@ 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
|
||||
|
||||
internal open class PushRuleEntity(
|
||||
// Required. The actions to perform when this rule is matched.
|
||||
|
@ -33,5 +35,8 @@ internal open class PushRuleEntity(
|
|||
var pattern: String? = null
|
||||
) : RealmObject() {
|
||||
|
||||
@LinkingObjects("pushRules")
|
||||
val parent: RealmResults<PushRulesEntity>? = null
|
||||
|
||||
companion object
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ import io.realm.annotations.RealmModule
|
|||
RoomTagEntity::class,
|
||||
SyncEntity::class,
|
||||
UserEntity::class,
|
||||
IgnoredUserEntity::class,
|
||||
EventAnnotationsSummaryEntity::class,
|
||||
ReactionAggregatedSummaryEntity::class,
|
||||
EditAggregatedSummaryEntity::class,
|
||||
|
|
|
@ -16,27 +16,26 @@
|
|||
|
||||
package im.vector.matrix.android.internal.database.query
|
||||
|
||||
import im.vector.matrix.android.internal.database.awaitTransaction
|
||||
import im.vector.matrix.android.internal.database.model.FilterEntity
|
||||
import im.vector.matrix.android.internal.session.filter.FilterFactory
|
||||
import io.realm.Realm
|
||||
import io.realm.kotlin.createObject
|
||||
import io.realm.kotlin.where
|
||||
|
||||
/**
|
||||
* Get the current filter, create one if it does not exist
|
||||
*/
|
||||
internal fun FilterEntity.Companion.getFilter(realm: Realm): FilterEntity {
|
||||
internal suspend fun FilterEntity.Companion.getFilter(realm: Realm): FilterEntity {
|
||||
var filter = realm.where<FilterEntity>().findFirst()
|
||||
if (filter == null) {
|
||||
realm.executeTransaction {
|
||||
realm.createObject<FilterEntity>().apply {
|
||||
filterBodyJson = FilterFactory.createDefaultFilterBody().toJSONString()
|
||||
roomEventFilterJson = FilterFactory.createDefaultRoomFilter().toJSONString()
|
||||
filterId = ""
|
||||
}
|
||||
filter = FilterEntity().apply {
|
||||
filterBodyJson = FilterFactory.createDefaultFilterBody().toJSONString()
|
||||
roomEventFilterJson = FilterFactory.createDefaultRoomFilter().toJSONString()
|
||||
filterId = ""
|
||||
}
|
||||
awaitTransaction(realm.configuration) {
|
||||
it.insert(filter)
|
||||
}
|
||||
filter = realm.where<FilterEntity>().findFirst()!!
|
||||
}
|
||||
|
||||
return filter
|
||||
}
|
||||
|
|
|
@ -16,10 +16,10 @@
|
|||
package im.vector.matrix.android.internal.database.query
|
||||
|
||||
import im.vector.matrix.android.api.pushrules.RuleKind
|
||||
import im.vector.matrix.android.internal.database.model.*
|
||||
import im.vector.matrix.android.internal.database.model.PushRuleEntity
|
||||
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.PusherEntity
|
||||
import im.vector.matrix.android.internal.database.model.PusherEntityFields
|
||||
import io.realm.Realm
|
||||
import io.realm.RealmQuery
|
||||
import io.realm.kotlin.where
|
||||
|
@ -41,3 +41,11 @@ internal fun PushRulesEntity.Companion.where(realm: Realm,
|
|||
.equalTo(PushRulesEntityFields.SCOPE, scope)
|
||||
.equalTo(PushRulesEntityFields.KIND_STR, kind.name)
|
||||
}
|
||||
|
||||
internal fun PushRuleEntity.Companion.where(realm: Realm,
|
||||
scope: String,
|
||||
ruleId: String): RealmQuery<PushRuleEntity> {
|
||||
return realm.where<PushRuleEntity>()
|
||||
.equalTo("${PushRuleEntityFields.PARENT}.${PushRulesEntityFields.SCOPE}", scope)
|
||||
.equalTo(PushRuleEntityFields.RULE_ID, ruleId)
|
||||
}
|
||||
|
|
|
@ -24,8 +24,7 @@ import io.realm.kotlin.where
|
|||
|
||||
internal fun ReadReceiptEntity.Companion.where(realm: Realm, roomId: String, userId: String): RealmQuery<ReadReceiptEntity> {
|
||||
return realm.where<ReadReceiptEntity>()
|
||||
.equalTo(ReadReceiptEntityFields.ROOM_ID, roomId)
|
||||
.equalTo(ReadReceiptEntityFields.USER_ID, userId)
|
||||
.equalTo(ReadReceiptEntityFields.PRIMARY_KEY, buildPrimaryKey(roomId, userId))
|
||||
}
|
||||
|
||||
internal fun ReadReceiptEntity.Companion.whereUserId(realm: Realm, userId: String): RealmQuery<ReadReceiptEntity> {
|
||||
|
@ -45,8 +44,10 @@ internal fun ReadReceiptEntity.Companion.createUnmanaged(roomId: String, eventId
|
|||
|
||||
internal fun ReadReceiptEntity.Companion.getOrCreate(realm: Realm, roomId: String, userId: String): ReadReceiptEntity {
|
||||
return ReadReceiptEntity.where(realm, roomId, userId).findFirst()
|
||||
?: realm.createObject(ReadReceiptEntity::class.java, "${roomId}_$userId").apply {
|
||||
?: realm.createObject(ReadReceiptEntity::class.java, buildPrimaryKey(roomId, userId)).apply {
|
||||
this.roomId = roomId
|
||||
this.userId = userId
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildPrimaryKey(roomId: String, userId: String) = "${roomId}_$userId"
|
||||
|
|
|
@ -111,7 +111,7 @@ internal fun RealmQuery<TimelineEventEntity>.prev(since: Int? = null, strict: Bo
|
|||
|
||||
internal fun RealmList<TimelineEventEntity>.find(eventId: String): TimelineEventEntity? {
|
||||
return this.where()
|
||||
.equalTo(TimelineEventEntityFields.ROOT.EVENT_ID, eventId)
|
||||
.equalTo(TimelineEventEntityFields.EVENT_ID, eventId)
|
||||
.findFirst()
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ internal object MatrixModule {
|
|||
@MatrixScope
|
||||
fun providesMatrixCoroutineDispatchers(): MatrixCoroutineDispatchers {
|
||||
return MatrixCoroutineDispatchers(io = Dispatchers.IO,
|
||||
computation = Dispatchers.IO,
|
||||
computation = Dispatchers.Default,
|
||||
main = Dispatchers.Main,
|
||||
crypto = createBackgroundHandler("Crypto_Thread").asCoroutineDispatcher(),
|
||||
sync = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
|
||||
|
|
|
@ -20,10 +20,11 @@ import com.squareup.moshi.Moshi
|
|||
import im.vector.matrix.android.api.session.room.model.message.*
|
||||
import im.vector.matrix.android.internal.network.parsing.RuntimeJsonAdapterFactory
|
||||
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.UserAccountDataDirectMessages
|
||||
import im.vector.matrix.android.internal.session.sync.model.UserAccountDataFallback
|
||||
import im.vector.matrix.android.internal.session.sync.model.UserAccountDataPushRules
|
||||
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountData
|
||||
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataDirectMessages
|
||||
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataFallback
|
||||
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataIgnoredUsers
|
||||
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataPushRules
|
||||
|
||||
object MoshiProvider {
|
||||
|
||||
|
@ -31,6 +32,7 @@ object MoshiProvider {
|
|||
.add(UriMoshiAdapter())
|
||||
.add(RuntimeJsonAdapterFactory.of(UserAccountData::class.java, "type", UserAccountDataFallback::class.java)
|
||||
.registerSubtype(UserAccountDataDirectMessages::class.java, UserAccountData.TYPE_DIRECT_MESSAGES)
|
||||
.registerSubtype(UserAccountDataIgnoredUsers::class.java, UserAccountData.TYPE_IGNORED_USER_LIST)
|
||||
.registerSubtype(UserAccountDataPushRules::class.java, UserAccountData.TYPE_PUSH_RULES)
|
||||
)
|
||||
.add(RuntimeJsonAdapterFactory.of(MessageContent::class.java, "msgtype", MessageDefaultContent::class.java)
|
||||
|
|
|
@ -22,7 +22,7 @@ import com.novoda.merlin.MerlinsBeard
|
|||
import im.vector.matrix.android.internal.di.MatrixScope
|
||||
import im.vector.matrix.android.internal.util.BackgroundDetectionObserver
|
||||
import timber.log.Timber
|
||||
import java.util.*
|
||||
import java.util.Collections
|
||||
import javax.inject.Inject
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
|
|
@ -17,17 +17,24 @@
|
|||
package im.vector.matrix.android.internal.network
|
||||
|
||||
import com.squareup.moshi.Moshi
|
||||
import dagger.Lazy
|
||||
import okhttp3.Call
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.moshi.MoshiConverterFactory
|
||||
import javax.inject.Inject
|
||||
|
||||
class RetrofitFactory @Inject constructor(private val moshi: Moshi) {
|
||||
|
||||
fun create(okHttpClient: OkHttpClient, baseUrl: String): Retrofit {
|
||||
fun create(okHttpClient: Lazy<OkHttpClient>, baseUrl: String): Retrofit {
|
||||
return Retrofit.Builder()
|
||||
.baseUrl(baseUrl)
|
||||
.client(okHttpClient)
|
||||
.callFactory(object : Call.Factory {
|
||||
override fun newCall(request: Request): Call {
|
||||
return okHttpClient.get().newCall(request)
|
||||
}
|
||||
})
|
||||
.addConverterFactory(UnitConverterFactory)
|
||||
.addConverterFactory(MoshiConverterFactory.create(moshi))
|
||||
.build()
|
||||
|
|
|
@ -19,6 +19,7 @@ package im.vector.matrix.android.internal.session
|
|||
import android.content.Context
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import dagger.Binds
|
||||
import dagger.Lazy
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.multibindings.IntoSet
|
||||
|
@ -132,7 +133,7 @@ internal abstract class SessionModule {
|
|||
@JvmStatic
|
||||
@Provides
|
||||
@SessionScope
|
||||
fun providesRetrofit(@Authenticated okHttpClient: OkHttpClient,
|
||||
fun providesRetrofit(@Authenticated okHttpClient: Lazy<OkHttpClient>,
|
||||
sessionParams: SessionParams,
|
||||
retrofitFactory: RetrofitFactory): Retrofit {
|
||||
return retrofitFactory
|
||||
|
|
|
@ -16,54 +16,44 @@
|
|||
|
||||
package im.vector.matrix.android.internal.session.filter
|
||||
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import im.vector.matrix.android.internal.database.model.FilterEntity
|
||||
import im.vector.matrix.android.internal.database.model.FilterEntityFields
|
||||
import im.vector.matrix.android.internal.database.query.getFilter
|
||||
import im.vector.matrix.android.internal.di.SessionDatabase
|
||||
import im.vector.matrix.android.internal.util.awaitTransaction
|
||||
import io.realm.Realm
|
||||
import io.realm.RealmConfiguration
|
||||
import io.realm.kotlin.where
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class DefaultFilterRepository @Inject constructor(
|
||||
@SessionDatabase private val realmConfiguration: RealmConfiguration
|
||||
) : FilterRepository {
|
||||
internal class DefaultFilterRepository @Inject constructor(private val monarchy: Monarchy) : FilterRepository {
|
||||
|
||||
override fun storeFilter(filterBody: FilterBody, roomEventFilter: RoomEventFilter): Boolean {
|
||||
val result: Boolean
|
||||
override suspend fun storeFilter(filterBody: FilterBody, roomEventFilter: RoomEventFilter): Boolean {
|
||||
return Realm.getInstance(monarchy.realmConfiguration).use { realm ->
|
||||
val filter = FilterEntity.getFilter(realm)
|
||||
val result = if (filter.filterBodyJson != filterBody.toJSONString()) {
|
||||
// Filter has changed, store it and reset the filter Id
|
||||
monarchy.awaitTransaction {
|
||||
// We manage only one filter for now
|
||||
val filterBodyJson = filterBody.toJSONString()
|
||||
val roomEventFilterJson = roomEventFilter.toJSONString()
|
||||
|
||||
val realm = Realm.getInstance(realmConfiguration)
|
||||
val filterEntity = FilterEntity.getFilter(it)
|
||||
|
||||
val filter = FilterEntity.getFilter(realm)
|
||||
|
||||
if (filter.filterBodyJson != filterBody.toJSONString()) {
|
||||
// Filter has changed, store it and reset the filter Id
|
||||
realm.executeTransaction {
|
||||
// We manage only one filter for now
|
||||
val filterBodyJson = filterBody.toJSONString()
|
||||
val roomEventFilterJson = roomEventFilter.toJSONString()
|
||||
|
||||
val filterEntity = FilterEntity.getFilter(it)
|
||||
|
||||
filterEntity.filterBodyJson = filterBodyJson
|
||||
filterEntity.roomEventFilterJson = roomEventFilterJson
|
||||
// Reset filterId
|
||||
filterEntity.filterId = ""
|
||||
filterEntity.filterBodyJson = filterBodyJson
|
||||
filterEntity.roomEventFilterJson = roomEventFilterJson
|
||||
// Reset filterId
|
||||
filterEntity.filterId = ""
|
||||
}
|
||||
true
|
||||
} else {
|
||||
filter.filterId.isBlank()
|
||||
}
|
||||
result = true
|
||||
} else {
|
||||
result = filter.filterId.isBlank()
|
||||
result
|
||||
}
|
||||
|
||||
realm.close()
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
override fun storeFilterId(filterBody: FilterBody, filterId: String) {
|
||||
val realm = Realm.getInstance(realmConfiguration)
|
||||
|
||||
realm.executeTransaction {
|
||||
override suspend fun storeFilterId(filterBody: FilterBody, filterId: String) {
|
||||
monarchy.awaitTransaction {
|
||||
// We manage only one filter for now
|
||||
val filterBodyJson = filterBody.toJSONString()
|
||||
|
||||
|
@ -73,39 +63,24 @@ internal class DefaultFilterRepository @Inject constructor(
|
|||
?.findFirst()
|
||||
?.filterId = filterId
|
||||
}
|
||||
|
||||
realm.close()
|
||||
}
|
||||
|
||||
override fun getFilter(): String {
|
||||
val result: String
|
||||
|
||||
val realm = Realm.getInstance(realmConfiguration)
|
||||
|
||||
val filter = FilterEntity.getFilter(realm)
|
||||
|
||||
result = if (filter.filterId.isBlank()) {
|
||||
// Use the Json format
|
||||
filter.filterBodyJson
|
||||
} else {
|
||||
// Use FilterId
|
||||
filter.filterId
|
||||
override suspend fun getFilter(): String {
|
||||
return Realm.getInstance(monarchy.realmConfiguration).use {
|
||||
val filter = FilterEntity.getFilter(it)
|
||||
if (filter.filterId.isBlank()) {
|
||||
// Use the Json format
|
||||
filter.filterBodyJson
|
||||
} else {
|
||||
// Use FilterId
|
||||
filter.filterId
|
||||
}
|
||||
}
|
||||
|
||||
realm.close()
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
override fun getRoomFilter(): String {
|
||||
val realm = Realm.getInstance(realmConfiguration)
|
||||
|
||||
val filter = FilterEntity.getFilter(realm)
|
||||
|
||||
val result = filter.roomEventFilterJson
|
||||
|
||||
realm.close()
|
||||
|
||||
return result
|
||||
override suspend fun getRoomFilter(): String {
|
||||
return Realm.getInstance(monarchy.realmConfiguration).use {
|
||||
FilterEntity.getFilter(it).roomEventFilterJson
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,36 +21,13 @@ import im.vector.matrix.android.internal.task.TaskExecutor
|
|||
import im.vector.matrix.android.internal.task.configureWith
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class DefaultFilterService @Inject constructor(private val filterRepository: FilterRepository,
|
||||
private val saveFilterTask: SaveFilterTask,
|
||||
internal class DefaultFilterService @Inject constructor(private val saveFilterTask: SaveFilterTask,
|
||||
private val taskExecutor: TaskExecutor) : FilterService {
|
||||
|
||||
// TODO Pass a list of support events instead
|
||||
override fun setFilter(filterPreset: FilterService.FilterPreset) {
|
||||
val filterBody = when (filterPreset) {
|
||||
FilterService.FilterPreset.RiotFilter -> {
|
||||
FilterFactory.createRiotFilterBody()
|
||||
}
|
||||
FilterService.FilterPreset.NoFilter -> {
|
||||
FilterFactory.createDefaultFilterBody()
|
||||
}
|
||||
}
|
||||
|
||||
val roomFilter = when (filterPreset) {
|
||||
FilterService.FilterPreset.RiotFilter -> {
|
||||
FilterFactory.createRiotRoomFilter()
|
||||
}
|
||||
FilterService.FilterPreset.NoFilter -> {
|
||||
FilterFactory.createDefaultRoomFilter()
|
||||
}
|
||||
}
|
||||
|
||||
val updated = filterRepository.storeFilter(filterBody, roomFilter)
|
||||
|
||||
if (updated) {
|
||||
saveFilterTask
|
||||
.configureWith(SaveFilterTask.Params(filterBody))
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
saveFilterTask
|
||||
.configureWith(SaveFilterTask.Params(filterPreset))
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,18 +16,19 @@
|
|||
|
||||
package im.vector.matrix.android.internal.session.filter
|
||||
|
||||
import im.vector.matrix.android.api.session.sync.FilterService
|
||||
import im.vector.matrix.android.internal.di.UserId
|
||||
import im.vector.matrix.android.internal.network.executeRequest
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Save a filter to the server
|
||||
* Save a filter, in db and if any changes, upload to the server
|
||||
*/
|
||||
internal interface SaveFilterTask : Task<SaveFilterTask.Params, Unit> {
|
||||
|
||||
data class Params(
|
||||
val filter: FilterBody
|
||||
val filterPreset: FilterService.FilterPreset
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -37,10 +38,29 @@ internal class DefaultSaveFilterTask @Inject constructor(@UserId private val use
|
|||
) : SaveFilterTask {
|
||||
|
||||
override suspend fun execute(params: SaveFilterTask.Params) {
|
||||
val filterResponse = executeRequest<FilterResponse> {
|
||||
// TODO auto retry
|
||||
apiCall = filterAPI.uploadFilter(userId, params.filter)
|
||||
val filterBody = when (params.filterPreset) {
|
||||
FilterService.FilterPreset.RiotFilter -> {
|
||||
FilterFactory.createRiotFilterBody()
|
||||
}
|
||||
FilterService.FilterPreset.NoFilter -> {
|
||||
FilterFactory.createDefaultFilterBody()
|
||||
}
|
||||
}
|
||||
val roomFilter = when (params.filterPreset) {
|
||||
FilterService.FilterPreset.RiotFilter -> {
|
||||
FilterFactory.createRiotRoomFilter()
|
||||
}
|
||||
FilterService.FilterPreset.NoFilter -> {
|
||||
FilterFactory.createDefaultRoomFilter()
|
||||
}
|
||||
}
|
||||
val updated = filterRepository.storeFilter(filterBody, roomFilter)
|
||||
if (updated) {
|
||||
val filterResponse = executeRequest<FilterResponse> {
|
||||
// TODO auto retry
|
||||
apiCall = filterAPI.uploadFilter(userId, filterBody)
|
||||
}
|
||||
filterRepository.storeFilterId(filterBody, filterResponse.filterId)
|
||||
}
|
||||
filterRepository.storeFilterId(params.filter, filterResponse.filterId)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,20 +21,20 @@ internal interface FilterRepository {
|
|||
/**
|
||||
* Return true if the filterBody has changed, or need to be sent to the server
|
||||
*/
|
||||
fun storeFilter(filterBody: FilterBody, roomEventFilter: RoomEventFilter): Boolean
|
||||
suspend fun storeFilter(filterBody: FilterBody, roomEventFilter: RoomEventFilter): Boolean
|
||||
|
||||
/**
|
||||
* Set the filterId of this filter
|
||||
*/
|
||||
fun storeFilterId(filterBody: FilterBody, filterId: String)
|
||||
suspend fun storeFilterId(filterBody: FilterBody, filterId: String)
|
||||
|
||||
/**
|
||||
* Return filter json or filter id
|
||||
*/
|
||||
fun getFilter(): String
|
||||
suspend fun getFilter(): String
|
||||
|
||||
/**
|
||||
* Return the room filter
|
||||
*/
|
||||
fun getRoomFilter(): String
|
||||
suspend fun getRoomFilter(): String
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ import im.vector.matrix.android.internal.database.query.getOrCreate
|
|||
import im.vector.matrix.android.internal.network.executeRequest
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
import im.vector.matrix.android.internal.util.awaitTransaction
|
||||
import java.util.*
|
||||
import java.util.Date
|
||||
import javax.inject.Inject
|
||||
|
||||
internal interface GetHomeServerCapabilitiesTask : Task<Unit, Unit>
|
||||
|
|
|
@ -28,7 +28,9 @@ 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.query.where
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
import im.vector.matrix.android.internal.session.pushers.AddPushRuleTask
|
||||
import im.vector.matrix.android.internal.session.pushers.GetPushRulesTask
|
||||
import im.vector.matrix.android.internal.session.pushers.RemovePushRuleTask
|
||||
import im.vector.matrix.android.internal.session.pushers.UpdatePushRuleEnableStatusTask
|
||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||
import im.vector.matrix.android.internal.task.configureWith
|
||||
|
@ -38,6 +40,8 @@ import javax.inject.Inject
|
|||
@SessionScope
|
||||
internal class DefaultPushRuleService @Inject constructor(private val getPushRulesTask: GetPushRulesTask,
|
||||
private val updatePushRuleEnableStatusTask: UpdatePushRuleEnableStatusTask,
|
||||
private val addPushRuleTask: AddPushRuleTask,
|
||||
private val removePushRuleTask: RemovePushRuleTask,
|
||||
private val taskExecutor: TaskExecutor,
|
||||
private val monarchy: Monarchy
|
||||
) : PushRuleService {
|
||||
|
@ -98,6 +102,22 @@ internal class DefaultPushRuleService @Inject constructor(private val getPushRul
|
|||
.executeBy(taskExecutor)
|
||||
}
|
||||
|
||||
override fun addPushRule(kind: RuleKind, pushRule: PushRule, callback: MatrixCallback<Unit>): Cancelable {
|
||||
return addPushRuleTask
|
||||
.configureWith(AddPushRuleTask.Params(kind, pushRule)) {
|
||||
this.callback = callback
|
||||
}
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
|
||||
override fun removePushRule(kind: RuleKind, pushRule: PushRule, callback: MatrixCallback<Unit>): Cancelable {
|
||||
return removePushRuleTask
|
||||
.configureWith(RemovePushRuleTask.Params(kind, pushRule)) {
|
||||
this.callback = callback
|
||||
}
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
|
||||
override fun removePushRuleListener(listener: PushRuleService.PushRuleListener) {
|
||||
synchronized(listeners) {
|
||||
listeners.remove(listener)
|
||||
|
|
|
@ -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.internal.session.pushers
|
||||
|
||||
import im.vector.matrix.android.api.pushrules.RuleKind
|
||||
import im.vector.matrix.android.api.pushrules.rest.PushRule
|
||||
import im.vector.matrix.android.internal.network.executeRequest
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
import javax.inject.Inject
|
||||
|
||||
internal interface AddPushRuleTask : Task<AddPushRuleTask.Params, Unit> {
|
||||
data class Params(
|
||||
val kind: RuleKind,
|
||||
val pushRule: PushRule
|
||||
)
|
||||
}
|
||||
|
||||
internal class DefaultAddPushRuleTask @Inject constructor(private val pushRulesApi: PushRulesApi)
|
||||
: AddPushRuleTask {
|
||||
|
||||
override suspend fun execute(params: AddPushRuleTask.Params) {
|
||||
return executeRequest {
|
||||
apiCall = pushRulesApi.addRule(params.kind.value, params.pushRule.ruleId, params.pushRule)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -32,7 +32,7 @@ import im.vector.matrix.android.internal.task.configureWith
|
|||
import im.vector.matrix.android.internal.worker.WorkManagerUtil
|
||||
import im.vector.matrix.android.internal.worker.WorkManagerUtil.matrixOneTimeWorkRequestBuilder
|
||||
import im.vector.matrix.android.internal.worker.WorkerParamsFactory
|
||||
import java.util.*
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
|
@ -25,6 +25,8 @@ import im.vector.matrix.android.api.session.pushers.PushersService
|
|||
import im.vector.matrix.android.internal.session.notification.DefaultProcessEventForPushTask
|
||||
import im.vector.matrix.android.internal.session.notification.DefaultPushRuleService
|
||||
import im.vector.matrix.android.internal.session.notification.ProcessEventForPushTask
|
||||
import im.vector.matrix.android.internal.session.room.notification.DefaultSetRoomNotificationStateTask
|
||||
import im.vector.matrix.android.internal.session.room.notification.SetRoomNotificationStateTask
|
||||
import retrofit2.Retrofit
|
||||
|
||||
@Module
|
||||
|
@ -67,6 +69,15 @@ internal abstract class PushersModule {
|
|||
@Binds
|
||||
abstract fun bindUpdatePushRuleEnableStatusTask(updatePushRuleEnableStatusTask: DefaultUpdatePushRuleEnableStatusTask): UpdatePushRuleEnableStatusTask
|
||||
|
||||
@Binds
|
||||
abstract fun bindAddPushRuleTask(addPushRuleTask: DefaultAddPushRuleTask): AddPushRuleTask
|
||||
|
||||
@Binds
|
||||
abstract fun bindRemovePushRuleTask(removePushRuleTask: DefaultRemovePushRuleTask): RemovePushRuleTask
|
||||
|
||||
@Binds
|
||||
abstract fun bindSetRoomNotificationStateTask(setRoomNotificationStateTask: DefaultSetRoomNotificationStateTask): SetRoomNotificationStateTask
|
||||
|
||||
@Binds
|
||||
abstract fun bindPushRuleService(pushRuleService: DefaultPushRuleService): PushRuleService
|
||||
|
||||
|
|
|
@ -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.internal.session.pushers
|
||||
|
||||
import im.vector.matrix.android.api.pushrules.RuleKind
|
||||
import im.vector.matrix.android.api.pushrules.rest.PushRule
|
||||
import im.vector.matrix.android.internal.network.executeRequest
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
import javax.inject.Inject
|
||||
|
||||
internal interface RemovePushRuleTask : Task<RemovePushRuleTask.Params, Unit> {
|
||||
data class Params(
|
||||
val kind: RuleKind,
|
||||
val pushRule: PushRule
|
||||
)
|
||||
}
|
||||
|
||||
internal class DefaultRemovePushRuleTask @Inject constructor(private val pushRulesApi: PushRulesApi)
|
||||
: RemovePushRuleTask {
|
||||
|
||||
override suspend fun execute(params: RemovePushRuleTask.Params) {
|
||||
return executeRequest {
|
||||
apiCall = pushRulesApi.deleteRule(params.kind.value, params.pushRule.ruleId)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,6 +24,7 @@ import im.vector.matrix.android.api.session.room.Room
|
|||
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.relation.RelationService
|
||||
import im.vector.matrix.android.api.session.room.notification.RoomPushRuleService
|
||||
import im.vector.matrix.android.api.session.room.reporting.ReportingService
|
||||
import im.vector.matrix.android.api.session.room.read.ReadService
|
||||
import im.vector.matrix.android.api.session.room.send.DraftService
|
||||
|
@ -49,7 +50,8 @@ internal class DefaultRoom @Inject constructor(override val roomId: String,
|
|||
private val readService: ReadService,
|
||||
private val cryptoService: CryptoService,
|
||||
private val relationService: RelationService,
|
||||
private val roomMembersService: MembershipService) :
|
||||
private val roomMembersService: MembershipService,
|
||||
private val roomPushRuleService: RoomPushRuleService) :
|
||||
Room,
|
||||
TimelineService by timelineService,
|
||||
SendService by sendService,
|
||||
|
@ -58,7 +60,8 @@ internal class DefaultRoom @Inject constructor(override val roomId: String,
|
|||
ReportingService by reportingService,
|
||||
ReadService by readService,
|
||||
RelationService by relationService,
|
||||
MembershipService by roomMembersService {
|
||||
MembershipService by roomMembersService,
|
||||
RoomPushRuleService by roomPushRuleService {
|
||||
|
||||
override fun getRoomSummaryLive(): LiveData<Optional<RoomSummary>> {
|
||||
val liveData = monarchy.findAllMappedWithChanges(
|
||||
|
|
|
@ -21,7 +21,7 @@ import im.vector.matrix.android.api.session.events.model.EventType
|
|||
import im.vector.matrix.android.api.session.events.model.toModel
|
||||
import im.vector.matrix.android.api.session.room.model.RoomAvatarContent
|
||||
import im.vector.matrix.android.api.session.room.model.RoomMember
|
||||
import im.vector.matrix.android.internal.database.mapper.asDomain
|
||||
import im.vector.matrix.android.internal.database.mapper.ContentMapper
|
||||
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||
import im.vector.matrix.android.internal.database.model.EventEntityFields
|
||||
import im.vector.matrix.android.internal.database.query.prev
|
||||
|
@ -41,8 +41,8 @@ internal class RoomAvatarResolver @Inject constructor(private val monarchy: Mona
|
|||
fun resolve(roomId: String): String? {
|
||||
var res: String? = null
|
||||
monarchy.doWithRealm { realm ->
|
||||
val roomName = EventEntity.where(realm, roomId, EventType.STATE_ROOM_AVATAR).prev()?.asDomain()
|
||||
res = roomName?.content.toModel<RoomAvatarContent>()?.avatarUrl
|
||||
val roomName = EventEntity.where(realm, roomId, EventType.STATE_ROOM_AVATAR).prev()
|
||||
res = ContentMapper.map(roomName?.content).toModel<RoomAvatarContent>()?.avatarUrl
|
||||
if (!res.isNullOrEmpty()) {
|
||||
return@doWithRealm
|
||||
}
|
||||
|
@ -60,6 +60,6 @@ internal class RoomAvatarResolver @Inject constructor(private val monarchy: Mona
|
|||
}
|
||||
|
||||
private fun EventEntity?.toRoomMember(): RoomMember? {
|
||||
return this?.asDomain()?.content?.toModel<RoomMember>()
|
||||
return ContentMapper.map(this?.content).toModel<RoomMember>()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import im.vector.matrix.android.api.session.room.Room
|
|||
import im.vector.matrix.android.internal.database.mapper.RoomSummaryMapper
|
||||
import im.vector.matrix.android.internal.session.room.draft.DefaultDraftService
|
||||
import im.vector.matrix.android.internal.session.room.membership.DefaultMembershipService
|
||||
import im.vector.matrix.android.internal.session.room.notification.DefaultRoomPushRuleService
|
||||
import im.vector.matrix.android.internal.session.room.read.DefaultReadService
|
||||
import im.vector.matrix.android.internal.session.room.relation.DefaultRelationService
|
||||
import im.vector.matrix.android.internal.session.room.reporting.DefaultReportingService
|
||||
|
@ -44,7 +45,8 @@ internal class DefaultRoomFactory @Inject constructor(private val monarchy: Mona
|
|||
private val reportingServiceFactory: DefaultReportingService.Factory,
|
||||
private val readServiceFactory: DefaultReadService.Factory,
|
||||
private val relationServiceFactory: DefaultRelationService.Factory,
|
||||
private val membershipServiceFactory: DefaultMembershipService.Factory) :
|
||||
private val membershipServiceFactory: DefaultMembershipService.Factory,
|
||||
private val roomPushRuleServiceFactory: DefaultRoomPushRuleService.Factory) :
|
||||
RoomFactory {
|
||||
|
||||
override fun create(roomId: String): Room {
|
||||
|
@ -60,7 +62,8 @@ internal class DefaultRoomFactory @Inject constructor(private val monarchy: Mona
|
|||
readServiceFactory.create(roomId),
|
||||
cryptoService,
|
||||
relationServiceFactory.create(roomId),
|
||||
membershipServiceFactory.create(roomId)
|
||||
membershipServiceFactory.create(roomId),
|
||||
roomPushRuleServiceFactory.create(roomId)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ import im.vector.matrix.android.api.session.events.model.EventType
|
|||
import im.vector.matrix.android.api.session.events.model.toModel
|
||||
import im.vector.matrix.android.api.session.room.model.Membership
|
||||
import im.vector.matrix.android.api.session.room.model.RoomTopicContent
|
||||
import im.vector.matrix.android.internal.database.mapper.asDomain
|
||||
import im.vector.matrix.android.internal.database.mapper.ContentMapper
|
||||
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||
import im.vector.matrix.android.internal.database.model.EventEntityFields
|
||||
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
|
||||
|
@ -65,8 +65,10 @@ internal class RoomSummaryUpdater @Inject constructor(@UserId private val userId
|
|||
roomId: String,
|
||||
membership: Membership? = null,
|
||||
roomSummary: RoomSyncSummary? = null,
|
||||
unreadNotifications: RoomSyncUnreadNotifications? = null) {
|
||||
val roomSummaryEntity = RoomSummaryEntity.where(realm, roomId).findFirst() ?: realm.createObject(roomId)
|
||||
unreadNotifications: RoomSyncUnreadNotifications? = null,
|
||||
updateMembers: Boolean = false) {
|
||||
val roomSummaryEntity = RoomSummaryEntity.where(realm, roomId).findFirst()
|
||||
?: realm.createObject(roomId)
|
||||
|
||||
if (roomSummary != null) {
|
||||
if (roomSummary.heroes.isNotEmpty()) {
|
||||
|
@ -88,24 +90,27 @@ internal class RoomSummaryUpdater @Inject constructor(@UserId private val userId
|
|||
}
|
||||
|
||||
val latestPreviewableEvent = TimelineEventEntity.latestEvent(realm, roomId, includesSending = true, filterTypes = PREVIEWABLE_TYPES)
|
||||
val lastTopicEvent = EventEntity.where(realm, roomId, EventType.STATE_ROOM_TOPIC).prev()?.asDomain()
|
||||
val lastTopicEvent = EventEntity.where(realm, roomId, EventType.STATE_ROOM_TOPIC).prev()
|
||||
|
||||
roomSummaryEntity.hasUnreadMessages = roomSummaryEntity.notificationCount > 0
|
||||
// avoid this call if we are sure there are unread events
|
||||
|| !isEventRead(monarchy, userId, roomId, latestPreviewableEvent?.eventId)
|
||||
|
||||
val otherRoomMembers = RoomMembers(realm, roomId)
|
||||
.queryRoomMembersEvent()
|
||||
.notEqualTo(EventEntityFields.STATE_KEY, userId)
|
||||
.findAll()
|
||||
.asSequence()
|
||||
.map { it.stateKey }
|
||||
// avoid this call if we are sure there are unread events
|
||||
|| !isEventRead(monarchy, userId, roomId, latestPreviewableEvent?.eventId)
|
||||
|
||||
roomSummaryEntity.displayName = roomDisplayNameResolver.resolve(roomId).toString()
|
||||
roomSummaryEntity.avatarUrl = roomAvatarResolver.resolve(roomId)
|
||||
roomSummaryEntity.topic = lastTopicEvent?.content.toModel<RoomTopicContent>()?.topic
|
||||
roomSummaryEntity.topic = ContentMapper.map(lastTopicEvent?.content).toModel<RoomTopicContent>()?.topic
|
||||
roomSummaryEntity.latestPreviewableEvent = latestPreviewableEvent
|
||||
roomSummaryEntity.otherMemberIds.clear()
|
||||
roomSummaryEntity.otherMemberIds.addAll(otherRoomMembers)
|
||||
|
||||
if (updateMembers) {
|
||||
val otherRoomMembers = RoomMembers(realm, roomId)
|
||||
.queryRoomMembersEvent()
|
||||
.notEqualTo(EventEntityFields.STATE_KEY, userId)
|
||||
.findAll()
|
||||
.asSequence()
|
||||
.map { it.stateKey }
|
||||
|
||||
roomSummaryEntity.otherMemberIds.clear()
|
||||
roomSummaryEntity.otherMemberIds.addAll(otherRoomMembers)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ internal class DefaultLoadRoomMembersTask @Inject constructor(private val roomAP
|
|||
it.updateSenderData()
|
||||
}
|
||||
roomEntity.areAllMembersLoaded = true
|
||||
roomSummaryUpdater.update(realm, roomId)
|
||||
roomSummaryUpdater.update(realm, roomId, updateMembers = true)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ import im.vector.matrix.android.R
|
|||
import im.vector.matrix.android.api.session.events.model.EventType
|
||||
import im.vector.matrix.android.api.session.events.model.toModel
|
||||
import im.vector.matrix.android.api.session.room.model.*
|
||||
import im.vector.matrix.android.internal.database.mapper.asDomain
|
||||
import im.vector.matrix.android.internal.database.mapper.ContentMapper
|
||||
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||
import im.vector.matrix.android.internal.database.model.EventEntityFields
|
||||
import im.vector.matrix.android.internal.database.model.RoomEntity
|
||||
|
@ -56,20 +56,20 @@ internal class RoomDisplayNameResolver @Inject constructor(private val context:
|
|||
var name: CharSequence? = null
|
||||
monarchy.doWithRealm { realm ->
|
||||
val roomEntity = RoomEntity.where(realm, roomId = roomId).findFirst()
|
||||
val roomName = EventEntity.where(realm, roomId, EventType.STATE_ROOM_NAME).prev()?.asDomain()
|
||||
name = roomName?.content.toModel<RoomNameContent>()?.name
|
||||
val roomName = EventEntity.where(realm, roomId, EventType.STATE_ROOM_NAME).prev()
|
||||
name = ContentMapper.map(roomName?.content).toModel<RoomNameContent>()?.name
|
||||
if (!name.isNullOrEmpty()) {
|
||||
return@doWithRealm
|
||||
}
|
||||
|
||||
val canonicalAlias = EventEntity.where(realm, roomId, EventType.STATE_CANONICAL_ALIAS).prev()?.asDomain()
|
||||
name = canonicalAlias?.content.toModel<RoomCanonicalAliasContent>()?.canonicalAlias
|
||||
val canonicalAlias = EventEntity.where(realm, roomId, EventType.STATE_CANONICAL_ALIAS).prev()
|
||||
name = ContentMapper.map(canonicalAlias?.content).toModel<RoomCanonicalAliasContent>()?.canonicalAlias
|
||||
if (!name.isNullOrEmpty()) {
|
||||
return@doWithRealm
|
||||
}
|
||||
|
||||
val aliases = EventEntity.where(realm, roomId, EventType.STATE_ROOM_ALIASES).prev()?.asDomain()
|
||||
name = aliases?.content.toModel<RoomAliasesContent>()?.aliases?.firstOrNull()
|
||||
val aliases = EventEntity.where(realm, roomId, EventType.STATE_ROOM_ALIASES).prev()
|
||||
name = ContentMapper.map(aliases?.content).toModel<RoomAliasesContent>()?.aliases?.firstOrNull()
|
||||
if (!name.isNullOrEmpty()) {
|
||||
return@doWithRealm
|
||||
}
|
||||
|
@ -132,6 +132,6 @@ internal class RoomDisplayNameResolver @Inject constructor(private val context:
|
|||
}
|
||||
|
||||
private fun EventEntity?.toRoomMember(): RoomMember? {
|
||||
return this?.asDomain()?.content?.toModel<RoomMember>()
|
||||
return ContentMapper.map(this?.content).toModel<RoomMember>()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,6 +73,7 @@ internal class RoomMembers(private val realm: Realm,
|
|||
return EventEntity
|
||||
.where(realm, roomId, EventType.STATE_ROOM_MEMBER)
|
||||
.sort(EventEntityFields.STATE_INDEX, Sort.DESCENDING)
|
||||
.isNotNull(EventEntityFields.STATE_KEY)
|
||||
.distinct(EventEntityFields.STATE_KEY)
|
||||
.isNotNull(EventEntityFields.CONTENT)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* 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.session.room.notification
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.Transformations
|
||||
import com.squareup.inject.assisted.Assisted
|
||||
import com.squareup.inject.assisted.AssistedInject
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.pushrules.RuleScope
|
||||
import im.vector.matrix.android.api.session.room.notification.RoomNotificationState
|
||||
import im.vector.matrix.android.api.session.room.notification.RoomPushRuleService
|
||||
import im.vector.matrix.android.api.util.Cancelable
|
||||
import im.vector.matrix.android.internal.database.model.PushRuleEntity
|
||||
import im.vector.matrix.android.internal.database.query.where
|
||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||
import im.vector.matrix.android.internal.task.configureWith
|
||||
|
||||
internal class DefaultRoomPushRuleService @AssistedInject constructor(@Assisted private val roomId: String,
|
||||
private val setRoomNotificationStateTask: SetRoomNotificationStateTask,
|
||||
private val monarchy: Monarchy,
|
||||
private val taskExecutor: TaskExecutor)
|
||||
: RoomPushRuleService {
|
||||
|
||||
@AssistedInject.Factory
|
||||
interface Factory {
|
||||
fun create(roomId: String): RoomPushRuleService
|
||||
}
|
||||
|
||||
override fun getLiveRoomNotificationState(): LiveData<RoomNotificationState> {
|
||||
return Transformations.map(getPushRuleForRoom()) {
|
||||
it?.toRoomNotificationState() ?: RoomNotificationState.ALL_MESSAGES
|
||||
}
|
||||
}
|
||||
|
||||
override fun setRoomNotificationState(roomNotificationState: RoomNotificationState, matrixCallback: MatrixCallback<Unit>): Cancelable {
|
||||
return setRoomNotificationStateTask
|
||||
.configureWith(SetRoomNotificationStateTask.Params(roomId, roomNotificationState)) {
|
||||
this.callback = callback
|
||||
}
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
|
||||
private fun getPushRuleForRoom(): LiveData<RoomPushRule?> {
|
||||
val liveData = monarchy.findAllMappedWithChanges(
|
||||
{ realm ->
|
||||
PushRuleEntity.where(realm, scope = RuleScope.GLOBAL, ruleId = roomId)
|
||||
},
|
||||
{ result ->
|
||||
result.toRoomPushRule()
|
||||
}
|
||||
)
|
||||
return Transformations.map(liveData) { results ->
|
||||
results.firstOrNull()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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.session.room.notification
|
||||
|
||||
import im.vector.matrix.android.api.pushrules.RuleKind
|
||||
import im.vector.matrix.android.api.pushrules.rest.PushRule
|
||||
|
||||
internal data class RoomPushRule(
|
||||
val kind: RuleKind,
|
||||
val rule: PushRule
|
||||
)
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* 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.session.room.notification
|
||||
|
||||
import im.vector.matrix.android.api.pushrules.*
|
||||
import im.vector.matrix.android.api.pushrules.rest.PushCondition
|
||||
import im.vector.matrix.android.api.pushrules.rest.PushRule
|
||||
import im.vector.matrix.android.api.session.room.notification.RoomNotificationState
|
||||
import im.vector.matrix.android.internal.database.mapper.PushRulesMapper
|
||||
import im.vector.matrix.android.internal.database.model.PushRuleEntity
|
||||
|
||||
internal fun PushRuleEntity.toRoomPushRule(): RoomPushRule? {
|
||||
val kind = parent?.firstOrNull()?.kind
|
||||
val pushRule = when (kind) {
|
||||
RuleSetKey.OVERRIDE -> {
|
||||
PushRulesMapper.map(this)
|
||||
}
|
||||
RuleSetKey.ROOM -> {
|
||||
PushRulesMapper.mapRoomRule(this)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
return if (pushRule == null || kind == null) {
|
||||
null
|
||||
} else {
|
||||
RoomPushRule(kind, pushRule)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun RoomNotificationState.toRoomPushRule(roomId: String): RoomPushRule? {
|
||||
return when {
|
||||
this == RoomNotificationState.ALL_MESSAGES -> null
|
||||
this == RoomNotificationState.ALL_MESSAGES_NOISY -> {
|
||||
val rule = PushRule(
|
||||
actions = listOf(Action.Notify, Action.Sound()).toJson(),
|
||||
enabled = true,
|
||||
ruleId = roomId
|
||||
)
|
||||
return RoomPushRule(RuleSetKey.ROOM, rule)
|
||||
}
|
||||
else -> {
|
||||
val condition = PushCondition(
|
||||
kind = Condition.Kind.event_match.value,
|
||||
key = "room_id",
|
||||
pattern = roomId
|
||||
)
|
||||
val rule = PushRule(
|
||||
actions = listOf(Action.DoNotNotify).toJson(),
|
||||
enabled = true,
|
||||
ruleId = roomId,
|
||||
conditions = listOf(condition)
|
||||
)
|
||||
val kind = if (this == RoomNotificationState.MUTE) {
|
||||
RuleSetKey.OVERRIDE
|
||||
} else {
|
||||
RuleSetKey.ROOM
|
||||
}
|
||||
return RoomPushRule(kind, rule)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun RoomPushRule.toRoomNotificationState(): RoomNotificationState {
|
||||
return if (rule.enabled) {
|
||||
val actions = rule.getActions()
|
||||
if (actions.contains(Action.DoNotNotify)) {
|
||||
if (kind == RuleSetKey.OVERRIDE) {
|
||||
RoomNotificationState.MUTE
|
||||
} else {
|
||||
RoomNotificationState.MENTIONS_ONLY
|
||||
}
|
||||
} else if (actions.contains(Action.Notify)) {
|
||||
val hasSoundAction = actions.find {
|
||||
it is Action.Sound
|
||||
} != null
|
||||
if (hasSoundAction) {
|
||||
RoomNotificationState.ALL_MESSAGES_NOISY
|
||||
} else {
|
||||
RoomNotificationState.ALL_MESSAGES
|
||||
}
|
||||
} else {
|
||||
RoomNotificationState.ALL_MESSAGES
|
||||
}
|
||||
} else {
|
||||
RoomNotificationState.ALL_MESSAGES
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* 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.session.room.notification
|
||||
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import im.vector.matrix.android.api.pushrules.RuleScope
|
||||
import im.vector.matrix.android.api.session.room.notification.RoomNotificationState
|
||||
import im.vector.matrix.android.internal.database.model.PushRuleEntity
|
||||
import im.vector.matrix.android.internal.database.query.where
|
||||
import im.vector.matrix.android.internal.session.pushers.AddPushRuleTask
|
||||
import im.vector.matrix.android.internal.session.pushers.RemovePushRuleTask
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
import io.realm.Realm
|
||||
import javax.inject.Inject
|
||||
|
||||
internal interface SetRoomNotificationStateTask : Task<SetRoomNotificationStateTask.Params, Unit> {
|
||||
data class Params(
|
||||
val roomId: String,
|
||||
val roomNotificationState: RoomNotificationState
|
||||
)
|
||||
}
|
||||
|
||||
internal class DefaultSetRoomNotificationStateTask @Inject constructor(private val monarchy: Monarchy,
|
||||
private val removePushRuleTask: RemovePushRuleTask,
|
||||
private val addPushRuleTask: AddPushRuleTask)
|
||||
: SetRoomNotificationStateTask {
|
||||
|
||||
override suspend fun execute(params: SetRoomNotificationStateTask.Params) {
|
||||
val currentRoomPushRule = Realm.getInstance(monarchy.realmConfiguration).use {
|
||||
PushRuleEntity.where(it, scope = RuleScope.GLOBAL, ruleId = params.roomId).findFirst()?.toRoomPushRule()
|
||||
}
|
||||
if (currentRoomPushRule != null) {
|
||||
removePushRuleTask.execute(RemovePushRuleTask.Params(currentRoomPushRule.kind, currentRoomPushRule.rule))
|
||||
}
|
||||
val newRoomPushRule = params.roomNotificationState.toRoomPushRule(params.roomId)
|
||||
if (newRoomPushRule != null) {
|
||||
addPushRuleTask.execute(AddPushRuleTask.Params(newRoomPushRule.kind, newRoomPushRule.rule))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -39,7 +39,6 @@ import im.vector.matrix.android.internal.session.room.RoomSummaryUpdater
|
|||
import im.vector.matrix.android.internal.util.StringProvider
|
||||
import org.commonmark.parser.Parser
|
||||
import org.commonmark.renderer.html.HtmlRenderer
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
|
@ -119,7 +118,7 @@ internal class LocalEchoEventFactory @Inject constructor(@UserId private val use
|
|||
permalink,
|
||||
stringProvider.getString(R.string.message_reply_to_prefix),
|
||||
userLink,
|
||||
originalEvent.senderName ?: originalEvent.root.senderId,
|
||||
originalEvent.getDisambiguatedDisplayName(),
|
||||
body.takeFormatted(),
|
||||
createTextContent(newBodyText, newBodyAutoMarkdown).takeFormatted()
|
||||
)
|
||||
|
|
|
@ -52,7 +52,8 @@ import io.realm.RealmQuery
|
|||
import io.realm.RealmResults
|
||||
import io.realm.Sort
|
||||
import timber.log.Timber
|
||||
import java.util.*
|
||||
import java.util.Collections
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
import kotlin.collections.ArrayList
|
||||
|
|
|
@ -31,7 +31,7 @@ import java.security.KeyPairGenerator
|
|||
import java.security.KeyStore
|
||||
import java.security.KeyStoreException
|
||||
import java.security.SecureRandom
|
||||
import java.util.*
|
||||
import java.util.Calendar
|
||||
import javax.crypto.*
|
||||
import javax.crypto.spec.GCMParameterSpec
|
||||
import javax.crypto.spec.IvParameterSpec
|
||||
|
|
|
@ -67,6 +67,7 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch
|
|||
}
|
||||
|
||||
suspend fun handle(roomsSyncResponse: RoomsSyncResponse, isInitialSync: Boolean, reporter: DefaultInitialSyncProgressService? = null) {
|
||||
Timber.v("Execute transaction from $this")
|
||||
monarchy.awaitTransaction { realm ->
|
||||
handleRoomSync(realm, HandlingStrategy.JOINED(roomsSyncResponse.join), isInitialSync, reporter)
|
||||
handleRoomSync(realm, HandlingStrategy.INVITED(roomsSyncResponse.invite), isInitialSync, reporter)
|
||||
|
@ -133,6 +134,7 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch
|
|||
roomEntity.membership = Membership.JOIN
|
||||
|
||||
// State event
|
||||
|
||||
if (roomSync.state != null && roomSync.state.events.isNotEmpty()) {
|
||||
val minStateIndex = roomEntity.untimelinedStateEvents.where().min(EventEntityFields.STATE_INDEX)?.toInt()
|
||||
?: Int.MIN_VALUE
|
||||
|
@ -146,7 +148,6 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (roomSync.timeline != null && roomSync.timeline.events.isNotEmpty()) {
|
||||
val chunkEntity = handleTimelineEvents(
|
||||
realm,
|
||||
|
@ -157,14 +158,19 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch
|
|||
)
|
||||
roomEntity.addOrUpdate(chunkEntity)
|
||||
}
|
||||
roomSummaryUpdater.update(realm, roomId, Membership.JOIN, roomSync.summary, roomSync.unreadNotifications)
|
||||
val hasRoomMember = roomSync.state?.events?.firstOrNull {
|
||||
it.type == EventType.STATE_ROOM_MEMBER
|
||||
} != null || roomSync.timeline?.events?.firstOrNull {
|
||||
it.type == EventType.STATE_ROOM_MEMBER
|
||||
} != null
|
||||
|
||||
roomSummaryUpdater.update(realm, roomId, Membership.JOIN, roomSync.summary, roomSync.unreadNotifications, updateMembers = hasRoomMember)
|
||||
return roomEntity
|
||||
}
|
||||
|
||||
private fun handleInvitedRoom(realm: Realm,
|
||||
roomId: String,
|
||||
roomSync:
|
||||
InvitedRoomSync): RoomEntity {
|
||||
roomSync: InvitedRoomSync): RoomEntity {
|
||||
Timber.v("Handle invited sync for room $roomId")
|
||||
val roomEntity = RoomEntity.where(realm, roomId).findFirst() ?: realm.createObject(roomId)
|
||||
roomEntity.membership = Membership.INVITE
|
||||
|
@ -172,7 +178,10 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch
|
|||
val chunkEntity = handleTimelineEvents(realm, roomEntity, roomSync.inviteState.events)
|
||||
roomEntity.addOrUpdate(chunkEntity)
|
||||
}
|
||||
roomSummaryUpdater.update(realm, roomId, Membership.INVITE)
|
||||
val hasRoomMember = roomSync.inviteState?.events?.firstOrNull {
|
||||
it.type == EventType.STATE_ROOM_MEMBER
|
||||
} != null
|
||||
roomSummaryUpdater.update(realm, roomId, Membership.INVITE, updateMembers = hasRoomMember)
|
||||
return roomEntity
|
||||
}
|
||||
|
||||
|
|
|
@ -16,27 +16,24 @@
|
|||
|
||||
package im.vector.matrix.android.internal.session.sync
|
||||
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import im.vector.matrix.android.internal.database.model.SyncEntity
|
||||
import im.vector.matrix.android.internal.di.SessionDatabase
|
||||
import im.vector.matrix.android.internal.util.awaitTransaction
|
||||
import io.realm.Realm
|
||||
import io.realm.RealmConfiguration
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class SyncTokenStore @Inject constructor(@SessionDatabase private val realmConfiguration: RealmConfiguration) {
|
||||
internal class SyncTokenStore @Inject constructor(private val monarchy: Monarchy) {
|
||||
|
||||
fun getLastToken(): String? {
|
||||
val realm = Realm.getInstance(realmConfiguration)
|
||||
val token = realm.where(SyncEntity::class.java).findFirst()?.nextBatch
|
||||
realm.close()
|
||||
return token
|
||||
return Realm.getInstance(monarchy.realmConfiguration).use {
|
||||
it.where(SyncEntity::class.java).findFirst()?.nextBatch
|
||||
}
|
||||
}
|
||||
|
||||
fun saveToken(token: String?) {
|
||||
val realm = Realm.getInstance(realmConfiguration)
|
||||
realm.executeTransaction {
|
||||
suspend fun saveToken(token: String?) {
|
||||
monarchy.awaitTransaction {
|
||||
val sync = SyncEntity(token)
|
||||
it.insertOrUpdate(sync)
|
||||
}
|
||||
realm.close()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,10 +27,9 @@ import im.vector.matrix.android.internal.di.UserId
|
|||
import im.vector.matrix.android.internal.session.pushers.SavePushRulesTask
|
||||
import im.vector.matrix.android.internal.session.room.membership.RoomMembers
|
||||
import im.vector.matrix.android.internal.session.sync.model.InvitedRoomSync
|
||||
import im.vector.matrix.android.internal.session.sync.model.UserAccountDataDirectMessages
|
||||
import im.vector.matrix.android.internal.session.sync.model.UserAccountDataPushRules
|
||||
import im.vector.matrix.android.internal.session.sync.model.UserAccountDataSync
|
||||
import im.vector.matrix.android.internal.session.sync.model.accountdata.*
|
||||
import im.vector.matrix.android.internal.session.user.accountdata.DirectChatsHelper
|
||||
import im.vector.matrix.android.internal.session.user.accountdata.SaveIgnoredUsersTask
|
||||
import im.vector.matrix.android.internal.session.user.accountdata.UpdateUserAccountDataTask
|
||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||
import im.vector.matrix.android.internal.task.configureWith
|
||||
|
@ -44,6 +43,7 @@ internal class UserAccountDataSyncHandler @Inject constructor(private val monarc
|
|||
private val directChatsHelper: DirectChatsHelper,
|
||||
private val updateUserAccountDataTask: UpdateUserAccountDataTask,
|
||||
private val savePushRulesTask: SavePushRulesTask,
|
||||
private val saveIgnoredUsersTask: SaveIgnoredUsersTask,
|
||||
private val taskExecutor: TaskExecutor) {
|
||||
|
||||
suspend fun handle(accountData: UserAccountDataSync?, invites: Map<String, InvitedRoomSync>?) {
|
||||
|
@ -51,9 +51,18 @@ internal class UserAccountDataSyncHandler @Inject constructor(private val monarc
|
|||
when (it) {
|
||||
is UserAccountDataDirectMessages -> handleDirectChatRooms(it)
|
||||
is UserAccountDataPushRules -> handlePushRules(it)
|
||||
else -> return@forEach
|
||||
is UserAccountDataIgnoredUsers -> handleIgnoredUsers(it)
|
||||
is UserAccountDataFallback -> Timber.d("Receive account data of unhandled type ${it.type}")
|
||||
else -> error("Missing code here!")
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Store all account data, app can be interested of it
|
||||
// accountData?.list?.forEach {
|
||||
// it.toString()
|
||||
// MoshiProvider.providesMoshi()
|
||||
// }
|
||||
|
||||
monarchy.doWithRealm { realm ->
|
||||
synchronizeWithServerIfNeeded(realm, invites)
|
||||
}
|
||||
|
@ -114,4 +123,11 @@ internal class UserAccountDataSyncHandler @Inject constructor(private val monarc
|
|||
updateUserAccountDataTask.configureWith(updateUserAccountParams).executeBy(taskExecutor)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleIgnoredUsers(userAccountDataIgnoredUsers: UserAccountDataIgnoredUsers) {
|
||||
saveIgnoredUsersTask
|
||||
.configureWith(SaveIgnoredUsersTask.Params(userAccountDataIgnoredUsers.content.ignoredUsers.keys.toList()))
|
||||
.executeBy(taskExecutor)
|
||||
// TODO If not initial sync, we should execute a init sync
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ package im.vector.matrix.android.internal.session.sync.model
|
|||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataSync
|
||||
|
||||
// SyncResponse represents the request response for server sync v2.
|
||||
@JsonClass(generateAdapter = true)
|
||||
|
|
|
@ -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.internal.session.sync.model.accountdata
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import im.vector.matrix.android.api.util.JsonDict
|
||||
import im.vector.matrix.android.api.util.emptyJsonDict
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class IgnoredUsersContent(
|
||||
/**
|
||||
* Required. The map of users to ignore. UserId -> empty object for future enhancement
|
||||
*/
|
||||
@Json(name = "ignored_users") val ignoredUsers: Map<String, JsonDict>
|
||||
) {
|
||||
|
||||
companion object {
|
||||
fun createWithUserIds(userIds: List<String>): IgnoredUsersContent {
|
||||
return IgnoredUsersContent(
|
||||
ignoredUsers = userIds.associateWith { emptyJsonDict }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,9 +14,13 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.matrix.android.internal.session.sync.model
|
||||
package im.vector.matrix.android.internal.session.sync.model.accountdata
|
||||
|
||||
internal interface UserAccountData {
|
||||
import com.squareup.moshi.Json
|
||||
|
||||
internal abstract class UserAccountData {
|
||||
|
||||
@Json(name = "type") abstract val type: String
|
||||
|
||||
companion object {
|
||||
const val TYPE_IGNORED_USER_LIST = "m.ignored_user_list"
|
|
@ -14,12 +14,13 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.matrix.android.internal.session.sync.model
|
||||
package im.vector.matrix.android.internal.session.sync.model.accountdata
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class UserAccountDataDirectMessages(
|
||||
@Json(name = "type") override val type: String = TYPE_DIRECT_MESSAGES,
|
||||
@Json(name = "content") val content: Map<String, List<String>>
|
||||
) : UserAccountData
|
||||
) : UserAccountData()
|
|
@ -14,12 +14,13 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.matrix.android.internal.session.sync.model
|
||||
package im.vector.matrix.android.internal.session.sync.model.accountdata
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class UserAccountDataFallback(
|
||||
@Json(name = "type") override val type: String,
|
||||
@Json(name = "content") val content: Map<String, Any>
|
||||
) : UserAccountData
|
||||
) : UserAccountData()
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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.session.sync.model.accountdata
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class UserAccountDataIgnoredUsers(
|
||||
@Json(name = "type") override val type: String = TYPE_IGNORED_USER_LIST,
|
||||
@Json(name = "content") val content: IgnoredUsersContent
|
||||
) : UserAccountData()
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.matrix.android.internal.session.sync.model
|
||||
package im.vector.matrix.android.internal.session.sync.model.accountdata
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
@ -22,5 +22,6 @@ import im.vector.matrix.android.api.pushrules.rest.GetPushRulesResponse
|
|||
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class UserAccountDataPushRules(
|
||||
@Json(name = "type") override val type: String = TYPE_PUSH_RULES,
|
||||
@Json(name = "content") val content: GetPushRulesResponse
|
||||
) : UserAccountData
|
||||
) : UserAccountData()
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.matrix.android.internal.session.sync.model
|
||||
package im.vector.matrix.android.internal.session.sync.model.accountdata
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
|
@ -29,9 +29,12 @@ import im.vector.matrix.android.api.util.Cancelable
|
|||
import im.vector.matrix.android.api.util.Optional
|
||||
import im.vector.matrix.android.api.util.toOptional
|
||||
import im.vector.matrix.android.internal.database.mapper.asDomain
|
||||
import im.vector.matrix.android.internal.database.model.IgnoredUserEntity
|
||||
import im.vector.matrix.android.internal.database.model.IgnoredUserEntityFields
|
||||
import im.vector.matrix.android.internal.database.model.UserEntity
|
||||
import im.vector.matrix.android.internal.database.model.UserEntityFields
|
||||
import im.vector.matrix.android.internal.database.query.where
|
||||
import im.vector.matrix.android.internal.session.user.accountdata.UpdateIgnoredUserIdsTask
|
||||
import im.vector.matrix.android.internal.session.user.model.SearchUserTask
|
||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||
import im.vector.matrix.android.internal.task.configureWith
|
||||
|
@ -40,8 +43,8 @@ import javax.inject.Inject
|
|||
|
||||
internal class DefaultUserService @Inject constructor(private val monarchy: Monarchy,
|
||||
private val searchUserTask: SearchUserTask,
|
||||
private val updateIgnoredUserIdsTask: UpdateIgnoredUserIdsTask,
|
||||
private val taskExecutor: TaskExecutor) : UserService {
|
||||
|
||||
private val realmDataSourceFactory: Monarchy.RealmDataSourceFactory<UserEntity> by lazy {
|
||||
monarchy.createDataSourceFactory { realm ->
|
||||
realm.where(UserEntity::class.java)
|
||||
|
@ -62,7 +65,7 @@ internal class DefaultUserService @Inject constructor(private val monarchy: Mona
|
|||
|
||||
override fun getUser(userId: String): User? {
|
||||
val userEntity = monarchy.fetchCopied { UserEntity.where(it, userId).findFirst() }
|
||||
?: return null
|
||||
?: return null
|
||||
|
||||
return userEntity.asDomain()
|
||||
}
|
||||
|
@ -117,4 +120,33 @@ internal class DefaultUserService @Inject constructor(private val monarchy: Mona
|
|||
}
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
|
||||
override fun liveIgnoredUsers(): LiveData<List<User>> {
|
||||
return monarchy.findAllMappedWithChanges(
|
||||
{ realm ->
|
||||
realm.where(IgnoredUserEntity::class.java)
|
||||
.isNotEmpty(IgnoredUserEntityFields.USER_ID)
|
||||
.sort(IgnoredUserEntityFields.USER_ID)
|
||||
},
|
||||
{ getUser(it.userId) ?: User(userId = it.userId) }
|
||||
)
|
||||
}
|
||||
|
||||
override fun ignoreUserIds(userIds: List<String>, callback: MatrixCallback<Unit>): Cancelable {
|
||||
val params = UpdateIgnoredUserIdsTask.Params(userIdsToIgnore = userIds.toList())
|
||||
return updateIgnoredUserIdsTask
|
||||
.configureWith(params) {
|
||||
this.callback = callback
|
||||
}
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
|
||||
override fun unIgnoreUserIds(userIds: List<String>, callback: MatrixCallback<Unit>): Cancelable {
|
||||
val params = UpdateIgnoredUserIdsTask.Params(userIdsToUnIgnore = userIds.toList())
|
||||
return updateIgnoredUserIdsTask
|
||||
.configureWith(params) {
|
||||
this.callback = callback
|
||||
}
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,10 @@ import dagger.Module
|
|||
import dagger.Provides
|
||||
import im.vector.matrix.android.api.session.user.UserService
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
import im.vector.matrix.android.internal.session.user.accountdata.DefaultSaveIgnoredUsersTask
|
||||
import im.vector.matrix.android.internal.session.user.accountdata.DefaultUpdateIgnoredUserIdsTask
|
||||
import im.vector.matrix.android.internal.session.user.accountdata.SaveIgnoredUsersTask
|
||||
import im.vector.matrix.android.internal.session.user.accountdata.UpdateIgnoredUserIdsTask
|
||||
import im.vector.matrix.android.internal.session.user.model.DefaultSearchUserTask
|
||||
import im.vector.matrix.android.internal.session.user.model.SearchUserTask
|
||||
import retrofit2.Retrofit
|
||||
|
@ -43,4 +47,10 @@ internal abstract class UserModule {
|
|||
|
||||
@Binds
|
||||
abstract fun bindSearchUserTask(searchUserTask: DefaultSearchUserTask): SearchUserTask
|
||||
|
||||
@Binds
|
||||
abstract fun bindSaveIgnoredUsersTask(task: DefaultSaveIgnoredUsersTask): SaveIgnoredUsersTask
|
||||
|
||||
@Binds
|
||||
abstract fun bindUpdateIgnoredUserIdsTask(task: DefaultUpdateIgnoredUserIdsTask): UpdateIgnoredUserIdsTask
|
||||
}
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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.session.user.accountdata
|
||||
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import im.vector.matrix.android.internal.database.model.IgnoredUserEntity
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
import im.vector.matrix.android.internal.util.awaitTransaction
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Save the ignored users list in DB
|
||||
*/
|
||||
internal interface SaveIgnoredUsersTask : Task<SaveIgnoredUsersTask.Params, Unit> {
|
||||
data class Params(
|
||||
val userIds: List<String>
|
||||
)
|
||||
}
|
||||
|
||||
internal class DefaultSaveIgnoredUsersTask @Inject constructor(private val monarchy: Monarchy) : SaveIgnoredUsersTask {
|
||||
|
||||
override suspend fun execute(params: SaveIgnoredUsersTask.Params) {
|
||||
monarchy.awaitTransaction { realm ->
|
||||
// clear current ignored users
|
||||
realm.where(IgnoredUserEntity::class.java)
|
||||
.findAll()
|
||||
.deleteAllFromRealm()
|
||||
|
||||
// And save the new received list
|
||||
params.userIds.forEach { realm.createObject(IgnoredUserEntity::class.java).apply { userId = it } }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* 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.session.user.accountdata
|
||||
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import im.vector.matrix.android.internal.database.model.IgnoredUserEntity
|
||||
import im.vector.matrix.android.internal.di.UserId
|
||||
import im.vector.matrix.android.internal.network.executeRequest
|
||||
import im.vector.matrix.android.internal.session.sync.model.accountdata.IgnoredUsersContent
|
||||
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountData
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
import javax.inject.Inject
|
||||
|
||||
internal interface UpdateIgnoredUserIdsTask : Task<UpdateIgnoredUserIdsTask.Params, Unit> {
|
||||
|
||||
data class Params(
|
||||
val userIdsToIgnore: List<String> = emptyList(),
|
||||
val userIdsToUnIgnore: List<String> = emptyList()
|
||||
)
|
||||
}
|
||||
|
||||
internal class DefaultUpdateIgnoredUserIdsTask @Inject constructor(private val accountDataApi: AccountDataAPI,
|
||||
private val monarchy: Monarchy,
|
||||
private val saveIgnoredUsersTask: SaveIgnoredUsersTask,
|
||||
@UserId private val userId: String) : UpdateIgnoredUserIdsTask {
|
||||
|
||||
override suspend fun execute(params: UpdateIgnoredUserIdsTask.Params) {
|
||||
// Get current list
|
||||
val ignoredUserIds = monarchy.fetchAllMappedSync(
|
||||
{ realm -> realm.where(IgnoredUserEntity::class.java) },
|
||||
{ it.userId }
|
||||
).toMutableSet()
|
||||
|
||||
val original = ignoredUserIds.toList()
|
||||
|
||||
ignoredUserIds.removeAll { it in params.userIdsToUnIgnore }
|
||||
ignoredUserIds.addAll(params.userIdsToIgnore)
|
||||
|
||||
if (original == ignoredUserIds) {
|
||||
// No change
|
||||
return
|
||||
}
|
||||
|
||||
val list = ignoredUserIds.toList()
|
||||
val body = IgnoredUsersContent.createWithUserIds(list)
|
||||
|
||||
executeRequest<Unit> {
|
||||
apiCall = accountDataApi.setAccountData(userId, UserAccountData.TYPE_IGNORED_USER_LIST, body)
|
||||
}
|
||||
|
||||
// Update the DB right now (do not wait for the sync to come back with updated data, for a faster UI update)
|
||||
saveIgnoredUsersTask.execute(SaveIgnoredUsersTask.Params(list))
|
||||
}
|
||||
}
|
|
@ -18,7 +18,7 @@ package im.vector.matrix.android.internal.session.user.accountdata
|
|||
|
||||
import im.vector.matrix.android.internal.di.UserId
|
||||
import im.vector.matrix.android.internal.network.executeRequest
|
||||
import im.vector.matrix.android.internal.session.sync.model.UserAccountData
|
||||
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountData
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -29,6 +29,7 @@ internal interface UpdateUserAccountDataTask : Task<UpdateUserAccountDataTask.Pa
|
|||
fun getData(): Any
|
||||
}
|
||||
|
||||
// TODO Use [UserAccountDataDirectMessages] class?
|
||||
data class DirectChatParams(override val type: String = UserAccountData.TYPE_DIRECT_MESSAGES,
|
||||
private val directMessages: Map<String, List<String>>
|
||||
) : Params {
|
||||
|
|
|
@ -36,7 +36,7 @@ import java.security.*
|
|||
import java.security.cert.CertificateException
|
||||
import java.security.spec.AlgorithmParameterSpec
|
||||
import java.security.spec.RSAKeyGenParameterSpec
|
||||
import java.util.*
|
||||
import java.util.Calendar
|
||||
import java.util.zip.GZIPOutputStream
|
||||
import javax.crypto.*
|
||||
import javax.crypto.spec.GCMParameterSpec
|
||||
|
|
|
@ -18,7 +18,7 @@ package im.vector.matrix.android.internal.util
|
|||
|
||||
import im.vector.matrix.android.api.MatrixPatterns
|
||||
import timber.log.Timber
|
||||
import java.util.*
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* Convert a string to an UTF8 String
|
||||
|
|
|
@ -172,4 +172,5 @@
|
|||
<string name="event_status_sending_message">Изпращане на съобщение…</string>
|
||||
<string name="clear_timeline_send_queue">Изчисти опашката за изпращане</string>
|
||||
|
||||
<string name="notice_room_third_party_revoked_invite">%1$s оттегли поканата за присъединяване на %2$s към стаята</string>
|
||||
</resources>
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources>
|
||||
<string name="verification_emoji_wrench">Spanner</string>
|
||||
<string name="verification_emoji_airplane">Aeroplane</string>
|
||||
</resources>
|
|
@ -1,11 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<!-- Note to translator: please add here only the string which are different than default version (which is en-rGB) -->
|
||||
|
||||
<string name="verification_emoji_wrench">Wrench</string>
|
||||
<string name="verification_emoji_airplane">Airplane</string>
|
||||
|
||||
</resources>
|
||||
|
||||
|
|
@ -12,8 +12,8 @@
|
|||
<string name="notice_room_leave">%1$s님이 떠났습니다</string>
|
||||
<string name="notice_room_reject">%1$s님이 초대를 거부했습니다</string>
|
||||
<string name="notice_room_kick">%1$s님이 %2$s님을 추방했습니다</string>
|
||||
<string name="notice_room_unban">%1$s님이 %2$s님의 차단을 풀었습니다</string>
|
||||
<string name="notice_room_ban">%1$s님이 %2$s님을 차단했습니다</string>
|
||||
<string name="notice_room_unban">%1$s님이 %2$s님의 출입 금지를 풀었습니다</string>
|
||||
<string name="notice_room_ban">%1$s님이 %2$s님을 출입 금지했습니다</string>
|
||||
<string name="notice_room_withdraw">%1$s님이 %2$s님의 초대를 취소했습니다</string>
|
||||
<string name="notice_avatar_url_changed">%1$s님이 아바타를 변경했습니다</string>
|
||||
<string name="notice_display_name_set">%1$s님이 표시 이름을 %2$s(으)로 설정했습니다</string>
|
||||
|
|
|
@ -170,7 +170,7 @@
|
|||
<!-- All translations should be the same across all Riot clients, please use the same translation than RiotWeb -->
|
||||
<string name="verification_emoji_glasses">Glasses</string>
|
||||
<!-- All translations should be the same across all Riot clients, please use the same translation than RiotWeb -->
|
||||
<string name="verification_emoji_wrench">Spanner</string>
|
||||
<string name="verification_emoji_wrench">Wrench</string>
|
||||
<!-- All translations should be the same across all Riot clients, please use the same translation than RiotWeb -->
|
||||
<string name="verification_emoji_santa">Santa</string>
|
||||
<!-- All translations should be the same across all Riot clients, please use the same translation than RiotWeb -->
|
||||
|
@ -208,7 +208,7 @@
|
|||
<!-- All translations should be the same across all Riot clients, please use the same translation than RiotWeb -->
|
||||
<string name="verification_emoji_bicycle">Bicycle</string>
|
||||
<!-- All translations should be the same across all Riot clients, please use the same translation than RiotWeb -->
|
||||
<string name="verification_emoji_airplane">Aeroplane</string>
|
||||
<string name="verification_emoji_airplane">Airplane</string>
|
||||
<!-- All translations should be the same across all Riot clients, please use the same translation than RiotWeb -->
|
||||
<string name="verification_emoji_rocket">Rocket</string>
|
||||
<!-- All translations should be the same across all Riot clients, please use the same translation than RiotWeb -->
|
||||
|
|
|
@ -18,7 +18,7 @@ package im.vector.matrix.android.api.pushrules
|
|||
|
||||
import im.vector.matrix.android.api.pushrules.rest.PushRule
|
||||
import im.vector.matrix.android.internal.di.MoshiProvider
|
||||
import org.junit.Assert
|
||||
import org.junit.Assert.*
|
||||
import org.junit.Test
|
||||
|
||||
class PushRuleActionsTest {
|
||||
|
@ -63,22 +63,17 @@ class PushRuleActionsTest {
|
|||
|
||||
val pushRule = MoshiProvider.providesMoshi().adapter<PushRule>(PushRule::class.java).fromJson(rawPushRule)
|
||||
|
||||
Assert.assertNotNull("Should have parsed the rule", pushRule)
|
||||
Assert.assertNotNull("Failed to parse actions", Action.mapFrom(pushRule!!))
|
||||
assertNotNull("Should have parsed the rule", pushRule)
|
||||
|
||||
val actions = Action.mapFrom(pushRule)
|
||||
Assert.assertEquals(3, actions!!.size)
|
||||
val actions = pushRule!!.getActions()
|
||||
assertEquals(3, actions.size)
|
||||
|
||||
Assert.assertEquals("First action should be notify", Action.Type.NOTIFY, actions[0].type)
|
||||
assertTrue("First action should be notify", actions[0] is Action.Notify)
|
||||
|
||||
Assert.assertEquals("Second action should be tweak", Action.Type.SET_TWEAK, actions[1].type)
|
||||
Assert.assertEquals("Second action tweak key should be sound", "sound", actions[1].tweak_action)
|
||||
Assert.assertEquals("Second action should have default as stringValue", "default", actions[1].stringValue)
|
||||
Assert.assertNull("Second action boolValue should be null", actions[1].boolValue)
|
||||
assertTrue("Second action should be sound", actions[1] is Action.Sound)
|
||||
assertEquals("Second action should have default sound", "default", (actions[1] as Action.Sound).sound)
|
||||
|
||||
Assert.assertEquals("Third action should be tweak", Action.Type.SET_TWEAK, actions[2].type)
|
||||
Assert.assertEquals("Third action tweak key should be highlight", "highlight", actions[2].tweak_action)
|
||||
Assert.assertEquals("Third action tweak param should be false", false, actions[2].boolValue)
|
||||
Assert.assertNull("Third action stringValue should be null", actions[2].stringValue)
|
||||
assertTrue("Third action should be highlight", actions[2] is Action.Highlight)
|
||||
assertEquals("Third action tweak param should be false", false, (actions[2] as Action.Highlight).highlight)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,23 +16,15 @@
|
|||
|
||||
package im.vector.matrix.android.api.pushrules
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.session.content.ContentAttachmentData
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
import im.vector.matrix.android.api.session.events.model.toContent
|
||||
import im.vector.matrix.android.api.session.room.Room
|
||||
import im.vector.matrix.android.api.session.room.RoomService
|
||||
import im.vector.matrix.android.api.session.room.model.*
|
||||
import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams
|
||||
import im.vector.matrix.android.api.session.room.model.Membership
|
||||
import im.vector.matrix.android.api.session.room.model.RoomMember
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageTextContent
|
||||
import im.vector.matrix.android.api.session.room.send.UserDraft
|
||||
import im.vector.matrix.android.api.session.room.timeline.Timeline
|
||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||
import im.vector.matrix.android.api.session.room.timeline.TimelineSettings
|
||||
import im.vector.matrix.android.api.util.Cancelable
|
||||
import im.vector.matrix.android.api.util.Optional
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
|
||||
|
@ -133,17 +125,20 @@ class PushrulesConditionTest {
|
|||
val conditionEqual3Bis = RoomMemberCountCondition("==3")
|
||||
val conditionLessThan3 = RoomMemberCountCondition("<3")
|
||||
|
||||
val session = MockRoomService()
|
||||
val room2JoinedId = "2joined"
|
||||
val room3JoinedId = "3joined"
|
||||
|
||||
Event(
|
||||
type = "m.room.message",
|
||||
eventId = "mx0",
|
||||
content = MessageTextContent("m.text", "A").toContent(),
|
||||
originServerTs = 0,
|
||||
roomId = "2joined").also {
|
||||
Assert.assertFalse("This room does not have 3 members", conditionEqual3.isSatisfied(it, session))
|
||||
Assert.assertFalse("This room does not have 3 members", conditionEqual3Bis.isSatisfied(it, session))
|
||||
Assert.assertTrue("This room has less than 3 members", conditionLessThan3.isSatisfied(it, session))
|
||||
val roomStub2Joined = mockk<Room> {
|
||||
every { getNumberOfJoinedMembers() } returns 2
|
||||
}
|
||||
|
||||
val roomStub3Joined = mockk<Room> {
|
||||
every { getNumberOfJoinedMembers() } returns 3
|
||||
}
|
||||
|
||||
val sessionStub = mockk<RoomService> {
|
||||
every { getRoom(room2JoinedId) } returns roomStub2Joined
|
||||
every { getRoom(room3JoinedId) } returns roomStub3Joined
|
||||
}
|
||||
|
||||
Event(
|
||||
|
@ -151,10 +146,21 @@ class PushrulesConditionTest {
|
|||
eventId = "mx0",
|
||||
content = MessageTextContent("m.text", "A").toContent(),
|
||||
originServerTs = 0,
|
||||
roomId = "3joined").also {
|
||||
Assert.assertTrue("This room has 3 members", conditionEqual3.isSatisfied(it, session))
|
||||
Assert.assertTrue("This room has 3 members", conditionEqual3Bis.isSatisfied(it, session))
|
||||
Assert.assertFalse("This room has more than 3 members", conditionLessThan3.isSatisfied(it, session))
|
||||
roomId = room2JoinedId).also {
|
||||
Assert.assertFalse("This room does not have 3 members", conditionEqual3.isSatisfied(it, sessionStub))
|
||||
Assert.assertFalse("This room does not have 3 members", conditionEqual3Bis.isSatisfied(it, sessionStub))
|
||||
Assert.assertTrue("This room has less than 3 members", conditionLessThan3.isSatisfied(it, sessionStub))
|
||||
}
|
||||
|
||||
Event(
|
||||
type = "m.room.message",
|
||||
eventId = "mx0",
|
||||
content = MessageTextContent("m.text", "A").toContent(),
|
||||
originServerTs = 0,
|
||||
roomId = room3JoinedId).also {
|
||||
Assert.assertTrue("This room has 3 members", conditionEqual3.isSatisfied(it, sessionStub))
|
||||
Assert.assertTrue("This room has 3 members", conditionEqual3Bis.isSatisfied(it, sessionStub))
|
||||
Assert.assertFalse("This room has more than 3 members", conditionLessThan3.isSatisfied(it, sessionStub))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -171,208 +177,4 @@ class PushrulesConditionTest {
|
|||
Assert.assertTrue("Notice", conditionEqual.isSatisfied(it))
|
||||
}
|
||||
}
|
||||
|
||||
class MockRoomService() : RoomService {
|
||||
override fun createRoom(createRoomParams: CreateRoomParams, callback: MatrixCallback<String>): Cancelable {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun joinRoom(roomId: String, viaServers: List<String>, callback: MatrixCallback<Unit>): Cancelable {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun getRoom(roomId: String): Room? {
|
||||
return when (roomId) {
|
||||
"2joined" -> MockRoom(roomId, 2)
|
||||
"3joined" -> MockRoom(roomId, 3)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
override fun liveRoomSummaries(): LiveData<List<RoomSummary>> {
|
||||
return MutableLiveData()
|
||||
}
|
||||
|
||||
override fun markAllAsRead(roomIds: List<String>, callback: MatrixCallback<Unit>): Cancelable {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
}
|
||||
|
||||
class MockRoom(override val roomId: String, val _numberOfJoinedMembers: Int) : Room {
|
||||
override fun getReadMarkerLive(): LiveData<Optional<String>> {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun getMyReadReceiptLive(): LiveData<Optional<String>> {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun resendTextMessage(localEcho: TimelineEvent): Cancelable? {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun resendMediaMessage(localEcho: TimelineEvent): Cancelable? {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun deleteFailedEcho(localEcho: TimelineEvent) {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun clearSendingQueue() {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun resendAllFailedMessages() {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun saveDraft(draft: UserDraft) {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun deleteDraft() {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun getDraftsLive(): LiveData<List<UserDraft>> {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun getEventReadReceiptsLive(eventId: String): LiveData<List<ReadReceipt>> {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun getStateEvent(eventType: String): Event? {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun editReply(replyToEdit: TimelineEvent, originalTimelineEvent: TimelineEvent, newBodyText: String, compatibilityBodyText: String): Cancelable {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun fetchEditHistory(eventId: String, callback: MatrixCallback<List<Event>>) {
|
||||
}
|
||||
|
||||
override fun getTimeLineEventLive(eventId: String): LiveData<Optional<TimelineEvent>> {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun getNumberOfJoinedMembers(): Int {
|
||||
return _numberOfJoinedMembers
|
||||
}
|
||||
|
||||
override fun getRoomSummaryLive(): LiveData<Optional<RoomSummary>> {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun roomSummary(): RoomSummary? {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun createTimeline(eventId: String?, settings: TimelineSettings): Timeline {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun getTimeLineEvent(eventId: String): TimelineEvent? {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun sendTextMessage(text: String, msgType: String, autoMarkdown: Boolean): Cancelable {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun sendFormattedTextMessage(text: String, formattedText: String): Cancelable {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun sendMedia(attachment: ContentAttachmentData): Cancelable {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun sendMedias(attachments: List<ContentAttachmentData>): Cancelable {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun redactEvent(event: Event, reason: String?): Cancelable {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun markAllAsRead(callback: MatrixCallback<Unit>) {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun setReadReceipt(eventId: String, callback: MatrixCallback<Unit>) {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun setReadMarker(fullyReadEventId: String, callback: MatrixCallback<Unit>) {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun isEventRead(eventId: String): Boolean {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun loadRoomMembersIfNeeded(matrixCallback: MatrixCallback<Unit>): Cancelable {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun getRoomMember(userId: String): RoomMember? {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun getRoomMemberIdsLive(): LiveData<List<String>> {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun invite(userId: String, callback: MatrixCallback<Unit>): Cancelable {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun join(viaServers: List<String>, callback: MatrixCallback<Unit>): Cancelable {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun leave(callback: MatrixCallback<Unit>): Cancelable {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun updateTopic(topic: String, callback: MatrixCallback<Unit>) {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun sendReaction(targetEventId: String, reaction: String): Cancelable {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun undoReaction(targetEventId: String, reaction: String): Cancelable {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun editTextMessage(targetEventId: String, msgType: String, newBodyText: String,
|
||||
newBodyAutoMarkdown: Boolean, compatibilityBodyText: String): Cancelable {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun replyToMessage(eventReplied: TimelineEvent, replyText: String, autoMarkdown: Boolean): Cancelable? {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun getEventSummaryLive(eventId: String): LiveData<Optional<EventAnnotationsSummary>> {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun isEncrypted(): Boolean {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun encryptionAlgorithm(): String? {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun shouldEncryptForInvitedMembers(): Boolean {
|
||||
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ cp ../matrix-android-sdk/matrix-sdk/src/main/res/values-da/strings.xml ./mat
|
|||
cp ../matrix-android-sdk/matrix-sdk/src/main/res/values-de/strings.xml ./matrix-sdk-android/src/main/res/values-de/strings.xml
|
||||
cp ../matrix-android-sdk/matrix-sdk/src/main/res/values-el/strings.xml ./matrix-sdk-android/src/main/res/values-el/strings.xml
|
||||
cp ../matrix-android-sdk/matrix-sdk/src/main/res/values-eo/strings.xml ./matrix-sdk-android/src/main/res/values-eo/strings.xml
|
||||
cp ../matrix-android-sdk/matrix-sdk/src/main/res/values-en-rUS/strings.xml ./matrix-sdk-android/src/main/res/values-en-rUS/strings.xml
|
||||
cp ../matrix-android-sdk/matrix-sdk/src/main/res/values-en-rGB/strings.xml ./matrix-sdk-android/src/main/res/values-en-rGB/strings.xml
|
||||
cp ../matrix-android-sdk/matrix-sdk/src/main/res/values-es/strings.xml ./matrix-sdk-android/src/main/res/values-es/strings.xml
|
||||
cp ../matrix-android-sdk/matrix-sdk/src/main/res/values-es-rMX/strings.xml ./matrix-sdk-android/src/main/res/values-es-rMX/strings.xml
|
||||
cp ../matrix-android-sdk/matrix-sdk/src/main/res/values-eu/strings.xml ./matrix-sdk-android/src/main/res/values-eu/strings.xml
|
||||
|
@ -102,6 +102,7 @@ cp ../riot-android/vector/src/main/res/values-ro/strings.xml ./vector/src
|
|||
cp ../riot-android/vector/src/main/res/values-ru/strings.xml ./vector/src/main/res/values-ru/strings.xml
|
||||
cp ../riot-android/vector/src/main/res/values-sk/strings.xml ./vector/src/main/res/values-sk/strings.xml
|
||||
cp ../riot-android/vector/src/main/res/values-sq/strings.xml ./vector/src/main/res/values-sq/strings.xml
|
||||
cp ../riot-android/vector/src/main/res/values-sr/strings.xml ./vector/src/main/res/values-sr/strings.xml
|
||||
cp ../riot-android/vector/src/main/res/values-te/strings.xml ./vector/src/main/res/values-te/strings.xml
|
||||
cp ../riot-android/vector/src/main/res/values-th/strings.xml ./vector/src/main/res/values-th/strings.xml
|
||||
cp ../riot-android/vector/src/main/res/values-tlh/strings.xml ./vector/src/main/res/values-tlh/strings.xml
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue