Merge branch 'vector-im:develop' into develop

This commit is contained in:
emotionalamoeba 2022-05-04 16:22:57 +01:00 committed by GitHub
commit 9fe75eacf1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
342 changed files with 2872 additions and 1462 deletions

View File

@ -106,7 +106,7 @@ body:
https://github.com/matrix-org/matrix-android-sdk2-sample https://github.com/matrix-org/matrix-android-sdk2-sample
- [ ] Update the dependency to the new version of the SDK2. It can take some time for MavenCentral to make the librarie available. You can check status on https://repo1.maven.org/maven2/org/matrix/android/matrix-android-sdk2/ - [ ] Update the dependency to the new version of the SDK2. It can take a few minutes for MavenCentral to make the library available. You can check status on https://repo1.maven.org/maven2/org/matrix/android/matrix-android-sdk2/
- [ ] Build and run the sample, you may have to fix some API break - [ ] Build and run the sample, you may have to fix some API break
- [ ] Commit and push directly on `main` - [ ] Commit and push directly on `main`
validations: validations:

View File

@ -11,7 +11,6 @@ jobs:
if: > if: >
contains(github.event.issue.labels.*.name, 'A-Maths') || contains(github.event.issue.labels.*.name, 'A-Maths') ||
contains(github.event.issue.labels.*.name, 'A-Message-Pinning') || contains(github.event.issue.labels.*.name, 'A-Message-Pinning') ||
contains(github.event.issue.labels.*.name, 'A-Threads') ||
contains(github.event.issue.labels.*.name, 'A-Polls') || contains(github.event.issue.labels.*.name, 'A-Polls') ||
contains(github.event.issue.labels.*.name, 'A-Location-Sharing') || contains(github.event.issue.labels.*.name, 'A-Location-Sharing') ||
contains(github.event.issue.labels.*.name, 'A-Message-Bubbles') || contains(github.event.issue.labels.*.name, 'A-Message-Bubbles') ||
@ -252,3 +251,30 @@ jobs:
env: env:
PROJECT_ID: "PN_kwDOAM0swc4AArk0" PROJECT_ID: "PN_kwDOAM0swc4AArk0"
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }} GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
move_element_x_issues:
name: ElementX issues to ElementX project board
runs-on: ubuntu-latest
# Skip in forks
if: >
github.repository == 'vector-im/element-android' &&
(contains(github.event.issue.labels.*.name, 'Z-ElementX-Alpha') ||
contains(github.event.issue.labels.*.name, 'Z-ElementX-Beta') ||
contains(github.event.issue.labels.*.name, 'Z-ElementX'))
steps:
- uses: octokit/graphql-action@v2.x
with:
headers: '{"GraphQL-Features": "projects_next_graphql"}'
query: |
mutation add_to_project($projectid:ID!,$contentid:ID!) {
addProjectNextItem(input:{projectId:$projectid contentId:$contentid}) {
projectNextItem {
id
}
}
}
projectid: ${{ env.PROJECT_ID }}
contentid: ${{ github.event.issue.node_id }}
env:
PROJECT_ID: "PN_kwDOAM0swc4ABTXY"
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}

View File

@ -7,6 +7,8 @@ jobs:
add_design_pr_to_project: add_design_pr_to_project:
name: Move PRs asking for design review to the design board name: Move PRs asking for design review to the design board
runs-on: ubuntu-latest runs-on: ubuntu-latest
# Skip in forks
if: github.repository == 'vector-im/element-android'
steps: steps:
- uses: octokit/graphql-action@v2.x - uses: octokit/graphql-action@v2.x
id: find_team_members id: find_team_members
@ -74,6 +76,8 @@ jobs:
add_product_pr_to_project: add_product_pr_to_project:
name: Move PRs asking for product review to the product board name: Move PRs asking for product review to the product board
runs-on: ubuntu-latest runs-on: ubuntu-latest
# Skip in forks
if: github.repository == 'vector-im/element-android'
steps: steps:
- uses: octokit/graphql-action@v2.x - uses: octokit/graphql-action@v2.x
id: find_team_members id: find_team_members

View File

@ -13,6 +13,8 @@ jobs:
- name: Update Gradle Wrapper - name: Update Gradle Wrapper
uses: gradle-update/update-gradle-wrapper-action@v1 uses: gradle-update/update-gradle-wrapper-action@v1
# Skip in forks
if: github.repository == 'vector-im/element-android'
with: with:
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
target-branch: develop target-branch: develop

View File

@ -1,3 +1,11 @@
Changes in Element 1.4.13 (2022-04-26)
======================================
Bugfixes 🐛
----------
- Fix UI freeze observed after each incremental sync ([#5835](https://github.com/vector-im/element-android/issues/5835))
Changes in Element v1.4.12 (2022-04-20) Changes in Element v1.4.12 (2022-04-20)
======================================= =======================================

View File

@ -124,7 +124,9 @@ As a general rule, please never edit or add or remove translations to the projec
#### Adding new string #### Adding new string
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 using Weblate. When adding new string resources, please only add new entries in the file `value/strings.xml`. Translations will be added later by the community of translators using Weblate.
The file `value/strings.xml` must only contain American English (U. S. English) values, as this is the default language of the Android operating system. So for instance, please use "color" instead of "colour". Element Android will still use the language set on the system by the user, like any other Android applications which provide translations. The system language can be any other English language variants, or any other languages. Note that this is also possible to override the system language using the Element Android in-app language settings.
New strings can be added anywhere in the file `value/strings.xml`, not necessarily at the end of the file. Generally, it's even better to add the new strings in some dedicated section per feature, and not at the end of the file, to avoid merge conflict between 2 PR adding strings at the end of the same file. New strings can be added anywhere in the file `value/strings.xml`, not necessarily at the end of the file. Generally, it's even better to add the new strings in some dedicated section per feature, and not at the end of the file, to avoid merge conflict between 2 PR adding strings at the end of the same file.

View File

@ -21,8 +21,8 @@ buildscript {
classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.3' classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.3'
classpath 'com.google.android.gms:oss-licenses-plugin:0.10.5' classpath 'com.google.android.gms:oss-licenses-plugin:0.10.5'
classpath "com.likethesalad.android:stem-plugin:2.0.0" classpath "com.likethesalad.android:stem-plugin:2.0.0"
classpath 'org.owasp:dependency-check-gradle:7.0.4.1' classpath 'org.owasp:dependency-check-gradle:7.1.0.1'
classpath "org.jetbrains.dokka:dokka-gradle-plugin:1.6.20" classpath "org.jetbrains.dokka:dokka-gradle-plugin:1.6.21"
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files
} }
@ -30,7 +30,7 @@ buildscript {
// ktlint Plugin // ktlint Plugin
plugins { plugins {
id "org.jlleitschuh.gradle.ktlint" version "10.2.1" id "org.jlleitschuh.gradle.ktlint" version "10.3.0"
} }
// https://github.com/jeremylong/DependencyCheck // https://github.com/jeremylong/DependencyCheck

1
changelog.d/5421.bugfix Normal file
View File

@ -0,0 +1 @@
Fixes crash when accepting or receiving VOIP calls

1
changelog.d/5592.bugfix Normal file
View File

@ -0,0 +1 @@
Improve/fix crashes on messages decryption

1
changelog.d/5721.bugfix Normal file
View File

@ -0,0 +1 @@
Improving deactivation experience along with a crash fix

1
changelog.d/5783.wip Normal file
View File

@ -0,0 +1 @@
Reorders the registration steps to prioritise email, then terms for the FTUE onboarding

1
changelog.d/5812.sdk Normal file
View File

@ -0,0 +1 @@
Move package `org.matrix.android.sdk.api.pushrules` to `org.matrix.android.sdk.api.session.pushrules`

1
changelog.d/5814.feature Normal file
View File

@ -0,0 +1 @@
Live location sharing: updating beacon state event content structure

2
changelog.d/5816.sdk Normal file
View File

@ -0,0 +1,2 @@
Some `Session` apis are now available by requesting the service first. For instance `Session.updateAvatar(...)` is now `Session.profileService().updateAvatar(...)`
The shortcut `Room.search()` has been removed, you have to use `Session.searchService().search()`

1
changelog.d/5826.bugfix Normal file
View File

@ -0,0 +1 @@
Adds missing suggested tag for rooms in Explore Space

1
changelog.d/5832.misc Normal file
View File

@ -0,0 +1 @@
Add a GH workflow to push ElementX issues to the global board.

1
changelog.d/5836.doc Normal file
View File

@ -0,0 +1 @@
Update the PR process doc with 2 reviewers and a new reviewer team.

1
changelog.d/5847.bugfix Normal file
View File

@ -0,0 +1 @@
Fixes missing call icons when threads are enabled

1
changelog.d/5854.doc Normal file
View File

@ -0,0 +1 @@
Improve documentation of the project and of the SDK

1
changelog.d/5855.sdk Normal file
View File

@ -0,0 +1 @@
- Add return type to RoomApi.sendStateEvent() to retrieve the created event id

1
changelog.d/5858.sdk Normal file
View File

@ -0,0 +1 @@
`Room` apis are now available by requesting the service first. For instance `Room.updateAvatar(...)` is now `Room.stateService().updateAvatar(...)`

1
changelog.d/5862.wip Normal file
View File

@ -0,0 +1 @@
[Live location sharing] Improve aggregation process of events

1
changelog.d/5871.bugfix Normal file
View File

@ -0,0 +1 @@
Fix UX freezing when creating secure backup

1
changelog.d/5872.misc Normal file
View File

@ -0,0 +1 @@
Faster Olm decrypt when there is a lot of existing sessions

1
changelog.d/5874.bugfix Normal file
View File

@ -0,0 +1 @@
Fixes sign in via other requiring homeserver registration to be enabled

1
changelog.d/5886.bugfix Normal file
View File

@ -0,0 +1 @@
Fix UISIDetector grace period bug

1
changelog.d/5890.sdk Normal file
View File

@ -0,0 +1 @@
Remove unecessary field `eventId` from `EventAnnotationsSummary` and `ReferencesAggregatedSummary`

1
changelog.d/5924.bugfix Normal file
View File

@ -0,0 +1 @@
Fix a crash with space invitations in the space list, and do not display space invitation twice.

1
changelog.d/5925.bugfix Normal file
View File

@ -0,0 +1 @@
Fixes crash on android api 21/22 devices when opening messages due to Konfetti library

View File

@ -164,6 +164,7 @@ ext.groups = [
'org.codehaus.woodstox', 'org.codehaus.woodstox',
'org.eclipse.ee4j', 'org.eclipse.ee4j',
'org.ec4j.core', 'org.ec4j.core',
'org.freemarker',
'org.glassfish.jaxb', 'org.glassfish.jaxb',
'org.hamcrest', 'org.hamcrest',
'org.jacoco', 'org.jacoco',

View File

@ -32,14 +32,15 @@ Also, draft PR should not stay indefinitely in this state. It may be removed if
##### PR Review Assignment ##### PR Review Assignment
We use automatic assignment for PR reviews. A PR is automatically routed by GitHub to a team member using the round robin algorithm. The process is the following: We use automatic assignment for PR reviews. A PR is automatically routed by GitHub to 2 team members using the round robin algorithm. The process is the following:
- The PR creator assigns the [element-android](https://github.com/orgs/vector-im/teams/element-android) team as a reviewer. They can skip this process and assign directly a specific member if they think they should take a look at it. - The PR creator can assign specific people if they have another Android developer in their team or they think a specific reviewer should take a look at the PR.
- GitHub automatically assigns one reviewer. If the chosen reviewer is not available (holiday, etc.), remove them and set again the team, GitHub will select another reviewer. - If there are missing reviewers, the PR creator assigns the [element-android-reviewers](https://github.com/orgs/vector-im/teams/element-android-reviewers) team as a reviewer.
- The reviewer gets a notification to make the review: they review the code following the good practice (see the rest of this document). - GitHub automatically assigns other reviewers. If one of the chosen reviewers is not available (holiday, etc.), remove them and set again the team, GitHub will select another reviewer.
- Reviewers get a notification to make the review: they review the code following the good practice (see the rest of this document).
- After making their own review, if they feel not confident enough, they can ask another person for a full review, or they can tag someone within a PR comment to check specific lines. - After making their own review, if they feel not confident enough, they can ask another person for a full review, or they can tag someone within a PR comment to check specific lines.
For PRs coming from the community, the issue wrangler can assign either the team [element-android](https://github.com/orgs/vector-im/teams/element-android) or any member directly. For PRs coming from the community, the issue wrangler can assign either the team [element-android-reviewers](https://github.com/orgs/vector-im/teams/element-android-reviewers) or any members directly.
##### PR review time ##### PR review time

View File

@ -0,0 +1,2 @@
Main changes in this version: Allows users to appear offline and adds an audio player for audio attachments
Full changelog: https://github.com/vector-im/element-android/releases

View File

@ -21,6 +21,8 @@ import kotlinx.coroutines.flow.Flow
import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.query.QueryStringValue
import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.room.Room import org.matrix.android.sdk.api.session.room.Room
import org.matrix.android.sdk.api.session.room.getStateEvent
import org.matrix.android.sdk.api.session.room.getTimelineEvent
import org.matrix.android.sdk.api.session.room.members.RoomMemberQueryParams import org.matrix.android.sdk.api.session.room.members.RoomMemberQueryParams
import org.matrix.android.sdk.api.session.room.model.EventAnnotationsSummary import org.matrix.android.sdk.api.session.room.model.EventAnnotationsSummary
import org.matrix.android.sdk.api.session.room.model.ReadReceipt import org.matrix.android.sdk.api.session.room.model.ReadReceipt
@ -45,81 +47,81 @@ class FlowRoom(private val room: Room) {
} }
fun liveRoomMembers(queryParams: RoomMemberQueryParams): Flow<List<RoomMemberSummary>> { fun liveRoomMembers(queryParams: RoomMemberQueryParams): Flow<List<RoomMemberSummary>> {
return room.getRoomMembersLive(queryParams).asFlow() return room.membershipService().getRoomMembersLive(queryParams).asFlow()
.startWith(room.coroutineDispatchers.io) { .startWith(room.coroutineDispatchers.io) {
room.getRoomMembers(queryParams) room.membershipService().getRoomMembers(queryParams)
} }
} }
fun liveAnnotationSummary(eventId: String): Flow<Optional<EventAnnotationsSummary>> { fun liveAnnotationSummary(eventId: String): Flow<Optional<EventAnnotationsSummary>> {
return room.getEventAnnotationsSummaryLive(eventId).asFlow() return room.relationService().getEventAnnotationsSummaryLive(eventId).asFlow()
.startWith(room.coroutineDispatchers.io) { .startWith(room.coroutineDispatchers.io) {
room.getEventAnnotationsSummary(eventId).toOptional() room.relationService().getEventAnnotationsSummary(eventId).toOptional()
} }
} }
fun liveTimelineEvent(eventId: String): Flow<Optional<TimelineEvent>> { fun liveTimelineEvent(eventId: String): Flow<Optional<TimelineEvent>> {
return room.getTimelineEventLive(eventId).asFlow() return room.timelineService().getTimelineEventLive(eventId).asFlow()
.startWith(room.coroutineDispatchers.io) { .startWith(room.coroutineDispatchers.io) {
room.getTimelineEvent(eventId).toOptional() room.getTimelineEvent(eventId).toOptional()
} }
} }
fun liveStateEvent(eventType: String, stateKey: QueryStringValue): Flow<Optional<Event>> { fun liveStateEvent(eventType: String, stateKey: QueryStringValue): Flow<Optional<Event>> {
return room.getStateEventLive(eventType, stateKey).asFlow() return room.stateService().getStateEventLive(eventType, stateKey).asFlow()
.startWith(room.coroutineDispatchers.io) { .startWith(room.coroutineDispatchers.io) {
room.getStateEvent(eventType, stateKey).toOptional() room.getStateEvent(eventType, stateKey).toOptional()
} }
} }
fun liveStateEvents(eventTypes: Set<String>): Flow<List<Event>> { fun liveStateEvents(eventTypes: Set<String>): Flow<List<Event>> {
return room.getStateEventsLive(eventTypes).asFlow() return room.stateService().getStateEventsLive(eventTypes).asFlow()
.startWith(room.coroutineDispatchers.io) { .startWith(room.coroutineDispatchers.io) {
room.getStateEvents(eventTypes) room.stateService().getStateEvents(eventTypes)
} }
} }
fun liveReadMarker(): Flow<Optional<String>> { fun liveReadMarker(): Flow<Optional<String>> {
return room.getReadMarkerLive().asFlow() return room.readService().getReadMarkerLive().asFlow()
} }
fun liveReadReceipt(): Flow<Optional<String>> { fun liveReadReceipt(): Flow<Optional<String>> {
return room.getMyReadReceiptLive().asFlow() return room.readService().getMyReadReceiptLive().asFlow()
} }
fun liveEventReadReceipts(eventId: String): Flow<List<ReadReceipt>> { fun liveEventReadReceipts(eventId: String): Flow<List<ReadReceipt>> {
return room.getEventReadReceiptsLive(eventId).asFlow() return room.readService().getEventReadReceiptsLive(eventId).asFlow()
} }
fun liveDraft(): Flow<Optional<UserDraft>> { fun liveDraft(): Flow<Optional<UserDraft>> {
return room.getDraftLive().asFlow() return room.draftService().getDraftLive().asFlow()
.startWith(room.coroutineDispatchers.io) { .startWith(room.coroutineDispatchers.io) {
room.getDraft().toOptional() room.draftService().getDraft().toOptional()
} }
} }
fun liveNotificationState(): Flow<RoomNotificationState> { fun liveNotificationState(): Flow<RoomNotificationState> {
return room.getLiveRoomNotificationState().asFlow() return room.roomPushRuleService().getLiveRoomNotificationState().asFlow()
} }
fun liveThreadSummaries(): Flow<List<ThreadSummary>> { fun liveThreadSummaries(): Flow<List<ThreadSummary>> {
return room.getAllThreadSummariesLive().asFlow() return room.threadsService().getAllThreadSummariesLive().asFlow()
.startWith(room.coroutineDispatchers.io) { .startWith(room.coroutineDispatchers.io) {
room.getAllThreadSummaries() room.threadsService().getAllThreadSummaries()
} }
} }
fun liveThreadList(): Flow<List<ThreadRootEvent>> { fun liveThreadList(): Flow<List<ThreadRootEvent>> {
return room.getAllThreadsLive().asFlow() return room.threadsLocalService().getAllThreadsLive().asFlow()
.startWith(room.coroutineDispatchers.io) { .startWith(room.coroutineDispatchers.io) {
room.getAllThreads() room.threadsLocalService().getAllThreads()
} }
} }
fun liveLocalUnreadThreadList(): Flow<List<ThreadRootEvent>> { fun liveLocalUnreadThreadList(): Flow<List<ThreadRootEvent>> {
return room.getMarkedThreadNotificationsLive().asFlow() return room.threadsLocalService().getMarkedThreadNotificationsLive().asFlow()
.startWith(room.coroutineDispatchers.io) { .startWith(room.coroutineDispatchers.io) {
room.getMarkedThreadNotifications() room.threadsLocalService().getMarkedThreadNotifications()
} }
} }
} }

View File

@ -46,16 +46,16 @@ import org.matrix.android.sdk.api.util.toOptional
class FlowSession(private val session: Session) { class FlowSession(private val session: Session) {
fun liveRoomSummaries(queryParams: RoomSummaryQueryParams, sortOrder: RoomSortOrder = RoomSortOrder.NONE): Flow<List<RoomSummary>> { fun liveRoomSummaries(queryParams: RoomSummaryQueryParams, sortOrder: RoomSortOrder = RoomSortOrder.NONE): Flow<List<RoomSummary>> {
return session.getRoomSummariesLive(queryParams, sortOrder).asFlow() return session.roomService().getRoomSummariesLive(queryParams, sortOrder).asFlow()
.startWith(session.coroutineDispatchers.io) { .startWith(session.coroutineDispatchers.io) {
session.getRoomSummaries(queryParams, sortOrder) session.roomService().getRoomSummaries(queryParams, sortOrder)
} }
} }
fun liveGroupSummaries(queryParams: GroupSummaryQueryParams): Flow<List<GroupSummary>> { fun liveGroupSummaries(queryParams: GroupSummaryQueryParams): Flow<List<GroupSummary>> {
return session.getGroupSummariesLive(queryParams).asFlow() return session.groupService().getGroupSummariesLive(queryParams).asFlow()
.startWith(session.coroutineDispatchers.io) { .startWith(session.coroutineDispatchers.io) {
session.getGroupSummaries(queryParams) session.groupService().getGroupSummaries(queryParams)
} }
} }
@ -67,9 +67,9 @@ class FlowSession(private val session: Session) {
} }
fun liveBreadcrumbs(queryParams: RoomSummaryQueryParams): Flow<List<RoomSummary>> { fun liveBreadcrumbs(queryParams: RoomSummaryQueryParams): Flow<List<RoomSummary>> {
return session.getBreadcrumbsLive(queryParams).asFlow() return session.roomService().getBreadcrumbsLive(queryParams).asFlow()
.startWith(session.coroutineDispatchers.io) { .startWith(session.coroutineDispatchers.io) {
session.getBreadcrumbs(queryParams) session.roomService().getBreadcrumbs(queryParams)
} }
} }
@ -85,43 +85,47 @@ class FlowSession(private val session: Session) {
} }
fun livePushers(): Flow<List<Pusher>> { fun livePushers(): Flow<List<Pusher>> {
return session.getPushersLive().asFlow() return session.pushersService().getPushersLive().asFlow()
} }
fun liveUser(userId: String): Flow<Optional<User>> { fun liveUser(userId: String): Flow<Optional<User>> {
return session.getUserLive(userId).asFlow() return session.userService().getUserLive(userId).asFlow()
.startWith(session.coroutineDispatchers.io) { .startWith(session.coroutineDispatchers.io) {
session.getUser(userId).toOptional() session.userService().getUser(userId).toOptional()
} }
} }
fun liveRoomMember(userId: String, roomId: String): Flow<Optional<RoomMemberSummary>> { fun liveRoomMember(userId: String, roomId: String): Flow<Optional<RoomMemberSummary>> {
return session.getRoomMemberLive(userId, roomId).asFlow() return session.roomService().getRoomMemberLive(userId, roomId).asFlow()
.startWith(session.coroutineDispatchers.io) { .startWith(session.coroutineDispatchers.io) {
session.getRoomMember(userId, roomId).toOptional() session.roomService().getRoomMember(userId, roomId).toOptional()
} }
} }
fun liveUsers(): Flow<List<User>> { fun liveUsers(): Flow<List<User>> {
return session.getUsersLive().asFlow() return session.userService().getUsersLive().asFlow()
} }
fun liveIgnoredUsers(): Flow<List<User>> { fun liveIgnoredUsers(): Flow<List<User>> {
return session.getIgnoredUsersLive().asFlow() return session.userService().getIgnoredUsersLive().asFlow()
} }
fun livePagedUsers(filter: String? = null, excludedUserIds: Set<String>? = null): Flow<PagedList<User>> { fun livePagedUsers(filter: String? = null, excludedUserIds: Set<String>? = null): Flow<PagedList<User>> {
return session.getPagedUsersLive(filter, excludedUserIds).asFlow() return session.userService().getPagedUsersLive(filter, excludedUserIds).asFlow()
} }
fun liveThreePIds(refreshData: Boolean): Flow<List<ThreePid>> { fun liveThreePIds(refreshData: Boolean): Flow<List<ThreePid>> {
return session.getThreePidsLive(refreshData).asFlow() return session.profileService().getThreePidsLive(refreshData).asFlow()
.startWith(session.coroutineDispatchers.io) { session.getThreePids() } .startWith(session.coroutineDispatchers.io) {
session.profileService().getThreePids()
}
} }
fun livePendingThreePIds(): Flow<List<ThreePid>> { fun livePendingThreePIds(): Flow<List<ThreePid>> {
return session.getPendingThreePidsLive().asFlow() return session.profileService().getPendingThreePidsLive().asFlow()
.startWith(session.coroutineDispatchers.io) { session.getPendingThreePids() } .startWith(session.coroutineDispatchers.io) {
session.profileService().getPendingThreePids()
}
} }
fun liveUserCryptoDevices(userId: String): Flow<List<CryptoDeviceInfo>> { fun liveUserCryptoDevices(userId: String): Flow<List<CryptoDeviceInfo>> {
@ -179,7 +183,7 @@ class FlowSession(private val session: Session) {
} }
fun liveRoomChangeMembershipState(): Flow<Map<String, ChangeMembershipState>> { fun liveRoomChangeMembershipState(): Flow<Map<String, ChangeMembershipState>> {
return session.getChangeMembershipsLive().asFlow() return session.roomService().getChangeMembershipsLive().asFlow()
} }
} }

View File

@ -14,6 +14,27 @@ buildscript {
} }
} }
dokkaHtml {
dokkaSourceSets {
configureEach {
// Emit warnings about not documented members.
reportUndocumented.set(true)
// Suppress legacy Riot's packages.
perPackageOption {
matchingRegex.set("org.matrix.android.sdk.internal.legacy.riot")
suppress.set(true)
}
perPackageOption {
matchingRegex.set("org.matrix.androidsdk.crypto.data")
suppress.set(true)
}
// List of files with module and package documentation
// https://kotlinlang.org/docs/reference/kotlin-doc.html#module-and-package-documentation
includes.from("./docs/modules.md", "./docs/packages.md")
}
}
}
android { android {
testOptions.unitTests.includeAndroidResources = true testOptions.unitTests.includeAndroidResources = true

View File

@ -0,0 +1,18 @@
# Module matrix-sdk-android
## Welcome to the matrix-sdk-android documentation!
This pages list the complete API that this SDK is exposing to a client application.
*We are still building the documentation, so everything is not documented yet.*
A few entry points:
- **Matrix**: The app will have to create and manage a Matrix object.
- From this **Matrix** object, you will be able to get various services, including the **AuthenticationService**.
- With this **AuthenticationService** you will be able to get an existing **Session**, or create one using a **LoginWizard** or a **RegistrationWizard**, which will finally give you a **Session**.
- From the **Session**, you will be able to retrieve many Services, including the **RoomService**.
- From the **RoomService**, you will be able to list the rooms, create a **Room**, and get a specific **Room**.
- And from a **Room**, you will be able to do many things, including get a **Timeline**, send messages, etc.
Please read the whole documentation to learn more!

View File

@ -0,0 +1,3 @@
# Package org.matrix.android.sdk.api
This is the root package of the API exposed by this SDK.

View File

@ -46,7 +46,7 @@ class ChangePasswordTest : InstrumentedTest {
// Change password // Change password
commonTestHelper.runBlockingTest { commonTestHelper.runBlockingTest {
session.changePassword(TestConstants.PASSWORD, NEW_PASSWORD) session.accountService().changePassword(TestConstants.PASSWORD, NEW_PASSWORD)
} }
// Try to login with the previous password, it will fail // Try to login with the previous password, it will fail

View File

@ -47,7 +47,7 @@ class DeactivateAccountTest : InstrumentedTest {
// Deactivate the account // Deactivate the account
commonTestHelper.runBlockingTest { commonTestHelper.runBlockingTest {
session.deactivateAccount( session.accountService().deactivateAccount(
eraseAllData = false, eraseAllData = false,
userInteractiveAuthInterceptor = object : UserInteractiveAuthInterceptor { userInteractiveAuthInterceptor = object : UserInteractiveAuthInterceptor {
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) { override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {

View File

@ -145,7 +145,7 @@ class CommonTestHelper(context: Context) {
* @param nbOfMessages the number of time the message will be sent * @param nbOfMessages the number of time the message will be sent
*/ */
fun sendTextMessage(room: Room, message: String, nbOfMessages: Int, timeout: Long = TestConstants.timeOutMillis): List<TimelineEvent> { fun sendTextMessage(room: Room, message: String, nbOfMessages: Int, timeout: Long = TestConstants.timeOutMillis): List<TimelineEvent> {
val timeline = room.createTimeline(null, TimelineSettings(10)) val timeline = room.timelineService().createTimeline(null, TimelineSettings(10))
timeline.start() timeline.start()
val sentEvents = sendTextMessagesBatched(timeline, room, message, nbOfMessages, timeout) val sentEvents = sendTextMessagesBatched(timeline, room, message, nbOfMessages, timeout)
timeline.dispose() timeline.dispose()
@ -165,11 +165,11 @@ class CommonTestHelper(context: Context) {
.forEach { batchedMessages -> .forEach { batchedMessages ->
batchedMessages.forEach { formattedMessage -> batchedMessages.forEach { formattedMessage ->
if (rootThreadEventId != null) { if (rootThreadEventId != null) {
room.replyInThread( room.relationService().replyInThread(
rootThreadEventId = rootThreadEventId, rootThreadEventId = rootThreadEventId,
replyInThreadText = formattedMessage) replyInThreadText = formattedMessage)
} else { } else {
room.sendTextMessage(formattedMessage) room.sendService().sendTextMessage(formattedMessage)
} }
} }
waitWithLatch(timeout) { latch -> waitWithLatch(timeout) { latch ->
@ -214,7 +214,7 @@ class CommonTestHelper(context: Context) {
numberOfMessages: Int, numberOfMessages: Int,
rootThreadEventId: String, rootThreadEventId: String,
timeout: Long = TestConstants.timeOutMillis): List<TimelineEvent> { timeout: Long = TestConstants.timeOutMillis): List<TimelineEvent> {
val timeline = room.createTimeline(null, TimelineSettings(10)) val timeline = room.timelineService().createTimeline(null, TimelineSettings(10))
timeline.start() timeline.start()
val sentEvents = sendTextMessagesBatched(timeline, room, message, numberOfMessages, timeout, rootThreadEventId) val sentEvents = sendTextMessagesBatched(timeline, room, message, numberOfMessages, timeout, rootThreadEventId)
timeline.dispose() timeline.dispose()
@ -431,7 +431,7 @@ class CommonTestHelper(context: Context) {
fun signOutAndClose(session: Session) { fun signOutAndClose(session: Session) {
runBlockingTest(timeout = 60_000) { runBlockingTest(timeout = 60_000) {
session.signOut(true) session.signOutService().signOut(true)
} }
// no need signout will close // no need signout will close
// session.close() // session.close()

View File

@ -40,6 +40,7 @@ import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxStat
import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.toContent import org.matrix.android.sdk.api.session.events.model.toContent
import org.matrix.android.sdk.api.session.getRoom
import org.matrix.android.sdk.api.session.room.Room import org.matrix.android.sdk.api.session.room.Room
import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary
@ -64,12 +65,12 @@ class CryptoTestHelper(private val testHelper: CommonTestHelper) {
val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, defaultSessionParams) val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, defaultSessionParams)
val roomId = testHelper.runBlockingTest { val roomId = testHelper.runBlockingTest {
aliceSession.createRoom(CreateRoomParams().apply { name = "MyRoom" }) aliceSession.roomService().createRoom(CreateRoomParams().apply { name = "MyRoom" })
} }
if (encryptedRoom) { if (encryptedRoom) {
testHelper.waitWithLatch { latch -> testHelper.waitWithLatch { latch ->
val room = aliceSession.getRoom(roomId)!! val room = aliceSession.getRoom(roomId)!!
room.enableEncryption() room.roomCryptoService().enableEncryption()
val roomSummaryLive = room.getRoomSummaryLive() val roomSummaryLive = room.getRoomSummaryLive()
val roomSummaryObserver = object : Observer<Optional<RoomSummary>> { val roomSummaryObserver = object : Observer<Optional<RoomSummary>> {
override fun onChanged(roomSummary: Optional<RoomSummary>) { override fun onChanged(roomSummary: Optional<RoomSummary>) {
@ -98,7 +99,7 @@ class CryptoTestHelper(private val testHelper: CommonTestHelper) {
val bobSession = testHelper.createAccount(TestConstants.USER_BOB, defaultSessionParams) val bobSession = testHelper.createAccount(TestConstants.USER_BOB, defaultSessionParams)
testHelper.waitWithLatch { latch -> testHelper.waitWithLatch { latch ->
val bobRoomSummariesLive = bobSession.getRoomSummariesLive(roomSummaryQueryParams { }) val bobRoomSummariesLive = bobSession.roomService().getRoomSummariesLive(roomSummaryQueryParams { })
val newRoomObserver = object : Observer<List<RoomSummary>> { val newRoomObserver = object : Observer<List<RoomSummary>> {
override fun onChanged(t: List<RoomSummary>?) { override fun onChanged(t: List<RoomSummary>?) {
if (t?.isNotEmpty() == true) { if (t?.isNotEmpty() == true) {
@ -108,14 +109,15 @@ class CryptoTestHelper(private val testHelper: CommonTestHelper) {
} }
} }
bobRoomSummariesLive.observeForever(newRoomObserver) bobRoomSummariesLive.observeForever(newRoomObserver)
aliceRoom.invite(bobSession.myUserId) aliceRoom.membershipService().invite(bobSession.myUserId)
} }
testHelper.waitWithLatch { latch -> testHelper.waitWithLatch { latch ->
val bobRoomSummariesLive = bobSession.getRoomSummariesLive(roomSummaryQueryParams { }) val bobRoomSummariesLive = bobSession.roomService().getRoomSummariesLive(roomSummaryQueryParams { })
val roomJoinedObserver = object : Observer<List<RoomSummary>> { val roomJoinedObserver = object : Observer<List<RoomSummary>> {
override fun onChanged(t: List<RoomSummary>?) { override fun onChanged(t: List<RoomSummary>?) {
if (bobSession.getRoom(aliceRoomId) if (bobSession.getRoom(aliceRoomId)
?.membershipService()
?.getRoomMember(bobSession.myUserId) ?.getRoomMember(bobSession.myUserId)
?.membership == Membership.JOIN) { ?.membership == Membership.JOIN) {
bobRoomSummariesLive.removeObserver(this) bobRoomSummariesLive.removeObserver(this)
@ -124,7 +126,7 @@ class CryptoTestHelper(private val testHelper: CommonTestHelper) {
} }
} }
bobRoomSummariesLive.observeForever(roomJoinedObserver) bobRoomSummariesLive.observeForever(roomJoinedObserver)
bobSession.joinRoom(aliceRoomId) bobSession.roomService().joinRoom(aliceRoomId)
} }
// Ensure bob can send messages to the room // Ensure bob can send messages to the room
// val roomFromBobPOV = bobSession.getRoom(aliceRoomId)!! // val roomFromBobPOV = bobSession.getRoom(aliceRoomId)!!
@ -160,11 +162,11 @@ class CryptoTestHelper(private val testHelper: CommonTestHelper) {
val samSession = testHelper.createAccount(TestConstants.USER_SAM, defaultSessionParams) val samSession = testHelper.createAccount(TestConstants.USER_SAM, defaultSessionParams)
testHelper.runBlockingTest { testHelper.runBlockingTest {
room.invite(samSession.myUserId, null) room.membershipService().invite(samSession.myUserId, null)
} }
testHelper.runBlockingTest { testHelper.runBlockingTest {
samSession.joinRoom(room.roomId, null, emptyList()) samSession.roomService().joinRoom(room.roomId, null, emptyList())
} }
return samSession return samSession
@ -242,8 +244,8 @@ class CryptoTestHelper(private val testHelper: CommonTestHelper) {
fun createDM(alice: Session, bob: Session): String { fun createDM(alice: Session, bob: Session): String {
var roomId: String = "" var roomId: String = ""
testHelper.waitWithLatch { latch -> testHelper.waitWithLatch { latch ->
roomId = alice.createDirectRoom(bob.myUserId) roomId = alice.roomService().createDirectRoom(bob.myUserId)
val bobRoomSummariesLive = bob.getRoomSummariesLive(roomSummaryQueryParams { }) val bobRoomSummariesLive = bob.roomService().getRoomSummariesLive(roomSummaryQueryParams { })
val newRoomObserver = object : Observer<List<RoomSummary>> { val newRoomObserver = object : Observer<List<RoomSummary>> {
override fun onChanged(t: List<RoomSummary>?) { override fun onChanged(t: List<RoomSummary>?) {
if (t?.any { it.roomId == roomId }.orFalse()) { if (t?.any { it.roomId == roomId }.orFalse()) {
@ -256,10 +258,11 @@ class CryptoTestHelper(private val testHelper: CommonTestHelper) {
} }
testHelper.waitWithLatch { latch -> testHelper.waitWithLatch { latch ->
val bobRoomSummariesLive = bob.getRoomSummariesLive(roomSummaryQueryParams { }) val bobRoomSummariesLive = bob.roomService().getRoomSummariesLive(roomSummaryQueryParams { })
val newRoomObserver = object : Observer<List<RoomSummary>> { val newRoomObserver = object : Observer<List<RoomSummary>> {
override fun onChanged(t: List<RoomSummary>?) { override fun onChanged(t: List<RoomSummary>?) {
if (bob.getRoom(roomId) if (bob.getRoom(roomId)
?.membershipService()
?.getRoomMember(bob.myUserId) ?.getRoomMember(bob.myUserId)
?.membership == Membership.JOIN) { ?.membership == Membership.JOIN) {
bobRoomSummariesLive.removeObserver(this) bobRoomSummariesLive.removeObserver(this)
@ -268,7 +271,7 @@ class CryptoTestHelper(private val testHelper: CommonTestHelper) {
} }
} }
bobRoomSummariesLive.observeForever(newRoomObserver) bobRoomSummariesLive.observeForever(newRoomObserver)
bob.joinRoom(roomId) bob.roomService().joinRoom(roomId)
} }
return roomId return roomId
@ -367,20 +370,20 @@ class CryptoTestHelper(private val testHelper: CommonTestHelper) {
aliceSession.cryptoService().setWarnOnUnknownDevices(false) aliceSession.cryptoService().setWarnOnUnknownDevices(false)
val roomId = testHelper.runBlockingTest { val roomId = testHelper.runBlockingTest {
aliceSession.createRoom(CreateRoomParams().apply { name = "MyRoom" }) aliceSession.roomService().createRoom(CreateRoomParams().apply { name = "MyRoom" })
} }
val room = aliceSession.getRoom(roomId)!! val room = aliceSession.getRoom(roomId)!!
testHelper.runBlockingTest { testHelper.runBlockingTest {
room.enableEncryption() room.roomCryptoService().enableEncryption()
} }
val sessions = mutableListOf(aliceSession) val sessions = mutableListOf(aliceSession)
for (index in 1 until numberOfMembers) { for (index in 1 until numberOfMembers) {
val session = testHelper.createAccount("User_$index", defaultSessionParams) val session = testHelper.createAccount("User_$index", defaultSessionParams)
testHelper.runBlockingTest(timeout = 600_000) { room.invite(session.myUserId, null) } testHelper.runBlockingTest(timeout = 600_000) { room.membershipService().invite(session.myUserId, null) }
println("TEST -> " + session.myUserId + " invited") println("TEST -> " + session.myUserId + " invited")
testHelper.runBlockingTest { session.joinRoom(room.roomId, null, emptyList()) } testHelper.runBlockingTest { session.roomService().joinRoom(room.roomId, null, emptyList()) }
println("TEST -> " + session.myUserId + " joined") println("TEST -> " + session.myUserId + " joined")
sessions.add(session) sessions.add(session)
} }

View File

@ -38,8 +38,11 @@ import org.matrix.android.sdk.api.session.crypto.model.OlmDecryptionResult
import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.content.EncryptedEventContent import org.matrix.android.sdk.api.session.events.model.content.EncryptedEventContent
import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.getRoom
import org.matrix.android.sdk.api.session.getRoomSummary
import org.matrix.android.sdk.api.session.room.Room import org.matrix.android.sdk.api.session.room.Room
import org.matrix.android.sdk.api.session.room.failure.JoinRoomFailure import org.matrix.android.sdk.api.session.room.failure.JoinRoomFailure
import org.matrix.android.sdk.api.session.room.getTimelineEvent
import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.message.MessageContent import org.matrix.android.sdk.api.session.room.model.message.MessageContent
import org.matrix.android.sdk.api.session.room.send.SendState import org.matrix.android.sdk.api.session.room.send.SendState
@ -86,7 +89,7 @@ class E2eeSanityTests : InstrumentedTest {
otherAccounts.forEach { otherAccounts.forEach {
testHelper.runBlockingTest { testHelper.runBlockingTest {
Log.v("#E2E TEST", "Alice invites ${it.myUserId}") Log.v("#E2E TEST", "Alice invites ${it.myUserId}")
aliceRoomPOV.invite(it.myUserId) aliceRoomPOV.membershipService().invite(it.myUserId)
} }
} }
@ -128,7 +131,7 @@ class E2eeSanityTests : InstrumentedTest {
newAccount.forEach { newAccount.forEach {
testHelper.runBlockingTest { testHelper.runBlockingTest {
Log.v("#E2E TEST", "Alice invites ${it.myUserId}") Log.v("#E2E TEST", "Alice invites ${it.myUserId}")
aliceRoomPOV.invite(it.myUserId) aliceRoomPOV.membershipService().invite(it.myUserId)
} }
} }
@ -523,10 +526,10 @@ class E2eeSanityTests : InstrumentedTest {
} }
private fun sendMessageInRoom(aliceRoomPOV: Room, text: String): String? { private fun sendMessageInRoom(aliceRoomPOV: Room, text: String): String? {
aliceRoomPOV.sendTextMessage(text) aliceRoomPOV.sendService().sendTextMessage(text)
var sentEventId: String? = null var sentEventId: String? = null
testHelper.waitWithLatch(4 * TestConstants.timeOutMillis) { latch -> testHelper.waitWithLatch(4 * TestConstants.timeOutMillis) { latch ->
val timeline = aliceRoomPOV.createTimeline(null, TimelineSettings(60)) val timeline = aliceRoomPOV.timelineService().createTimeline(null, TimelineSettings(60))
timeline.start() timeline.start()
testHelper.retryPeriodicallyWithLatch(latch) { testHelper.retryPeriodicallyWithLatch(latch) {
@ -551,7 +554,7 @@ class E2eeSanityTests : InstrumentedTest {
testHelper.waitWithLatch { latch -> testHelper.waitWithLatch { latch ->
testHelper.retryPeriodicallyWithLatch(latch) { testHelper.retryPeriodicallyWithLatch(latch) {
otherAccounts.map { otherAccounts.map {
aliceSession.getRoomMember(it.myUserId, e2eRoomID)?.membership aliceSession.roomService().getRoomMember(it.myUserId, e2eRoomID)?.membership
}.all { }.all {
it == Membership.JOIN it == Membership.JOIN
} }
@ -574,7 +577,7 @@ class E2eeSanityTests : InstrumentedTest {
testHelper.runBlockingTest(60_000) { testHelper.runBlockingTest(60_000) {
Log.v("#E2E TEST", "${otherSession.myUserId} tries to join room $e2eRoomID") Log.v("#E2E TEST", "${otherSession.myUserId} tries to join room $e2eRoomID")
try { try {
otherSession.joinRoom(e2eRoomID) otherSession.roomService().joinRoom(e2eRoomID)
} catch (ex: JoinRoomFailure.JoinedWithTimeout) { } catch (ex: JoinRoomFailure.JoinedWithTimeout) {
// it's ok we will wait after // it's ok we will wait after
} }

View File

@ -29,6 +29,8 @@ import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.content.EncryptedEventContent import org.matrix.android.sdk.api.session.events.model.content.EncryptedEventContent
import org.matrix.android.sdk.api.session.events.model.content.RoomKeyContent import org.matrix.android.sdk.api.session.events.model.content.RoomKeyContent
import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.getRoom
import org.matrix.android.sdk.api.session.room.getTimelineEvent
import org.matrix.android.sdk.common.CommonTestHelper import org.matrix.android.sdk.common.CommonTestHelper
import org.matrix.android.sdk.common.CryptoTestHelper import org.matrix.android.sdk.common.CryptoTestHelper

View File

@ -34,6 +34,7 @@ import org.matrix.android.sdk.api.session.crypto.MXCryptoError
import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.content.EncryptedEventContent import org.matrix.android.sdk.api.session.events.model.content.EncryptedEventContent
import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.getRoom
import org.matrix.android.sdk.api.session.room.timeline.Timeline import org.matrix.android.sdk.api.session.room.timeline.Timeline
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
@ -97,7 +98,7 @@ class UnwedgingTest : InstrumentedTest {
val roomFromBobPOV = bobSession.getRoom(aliceRoomId)!! val roomFromBobPOV = bobSession.getRoom(aliceRoomId)!!
val roomFromAlicePOV = aliceSession.getRoom(aliceRoomId)!! val roomFromAlicePOV = aliceSession.getRoom(aliceRoomId)!!
val bobTimeline = roomFromBobPOV.createTimeline(null, TimelineSettings(20)) val bobTimeline = roomFromBobPOV.timelineService().createTimeline(null, TimelineSettings(20))
bobTimeline.start() bobTimeline.start()
val bobFinalLatch = CountDownLatch(1) val bobFinalLatch = CountDownLatch(1)
@ -128,7 +129,7 @@ class UnwedgingTest : InstrumentedTest {
messagesReceivedByBob = emptyList() messagesReceivedByBob = emptyList()
// - Alice sends a 1st message with a 1st megolm session // - Alice sends a 1st message with a 1st megolm session
roomFromAlicePOV.sendTextMessage("First message") roomFromAlicePOV.sendService().sendTextMessage("First message")
// Wait for the message to be received by Bob // Wait for the message to be received by Bob
testHelper.await(latch) testHelper.await(latch)
@ -156,7 +157,7 @@ class UnwedgingTest : InstrumentedTest {
Timber.i("## CRYPTO | testUnwedging: Alice sends a 2nd message with a 2nd megolm session") Timber.i("## CRYPTO | testUnwedging: Alice sends a 2nd message with a 2nd megolm session")
// - Alice sends a 2nd message with a 2nd megolm session // - Alice sends a 2nd message with a 2nd megolm session
roomFromAlicePOV.sendTextMessage("Second message") roomFromAlicePOV.sendService().sendTextMessage("Second message")
// Wait for the message to be received by Bob // Wait for the message to be received by Bob
testHelper.await(latch) testHelper.await(latch)
@ -185,7 +186,7 @@ class UnwedgingTest : InstrumentedTest {
Timber.i("## CRYPTO | testUnwedging: Alice sends a 3rd message with a 3rd megolm session but a wedged olm session") Timber.i("## CRYPTO | testUnwedging: Alice sends a 3rd message with a 3rd megolm session but a wedged olm session")
// - Alice sends a 3rd message with a 3rd megolm session but a wedged olm session // - Alice sends a 3rd message with a 3rd megolm session but a wedged olm session
roomFromAlicePOV.sendTextMessage("Third message") roomFromAlicePOV.sendService().sendTextMessage("Third message")
// Bob should not be able to decrypt, because the session key could not be sent // Bob should not be able to decrypt, because the session key could not be sent
} }
bobTimeline.removeListener(bobEventsListener) bobTimeline.removeListener(bobEventsListener)

View File

@ -28,6 +28,7 @@ import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_MEGOLM
import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.content.EncryptionEventContent import org.matrix.android.sdk.api.session.events.model.content.EncryptionEventContent
import org.matrix.android.sdk.api.session.events.model.toContent import org.matrix.android.sdk.api.session.events.model.toContent
import org.matrix.android.sdk.api.session.getRoom
import org.matrix.android.sdk.api.session.room.Room import org.matrix.android.sdk.api.session.room.Room
import org.matrix.android.sdk.api.session.room.send.SendState import org.matrix.android.sdk.api.session.room.send.SendState
import org.matrix.android.sdk.api.session.room.timeline.Timeline import org.matrix.android.sdk.api.session.room.timeline.Timeline
@ -48,7 +49,7 @@ class EncryptionTest : InstrumentedTest {
fun test_EncryptionEvent() { fun test_EncryptionEvent() {
performTest(roomShouldBeEncrypted = false) { room -> performTest(roomShouldBeEncrypted = false) { room ->
// Send an encryption Event as an Event (and not as a state event) // Send an encryption Event as an Event (and not as a state event)
room.sendEvent( room.sendService().sendEvent(
eventType = EventType.STATE_ROOM_ENCRYPTION, eventType = EventType.STATE_ROOM_ENCRYPTION,
content = EncryptionEventContent(algorithm = MXCRYPTO_ALGORITHM_MEGOLM).toContent() content = EncryptionEventContent(algorithm = MXCRYPTO_ALGORITHM_MEGOLM).toContent()
) )
@ -60,7 +61,7 @@ class EncryptionTest : InstrumentedTest {
performTest(roomShouldBeEncrypted = true) { room -> performTest(roomShouldBeEncrypted = true) { room ->
runBlocking { runBlocking {
// Send an encryption Event as a State Event // Send an encryption Event as a State Event
room.sendStateEvent( room.stateService().sendStateEvent(
eventType = EventType.STATE_ROOM_ENCRYPTION, eventType = EventType.STATE_ROOM_ENCRYPTION,
stateKey = "", stateKey = "",
body = EncryptionEventContent(algorithm = MXCRYPTO_ALGORITHM_MEGOLM).toContent() body = EncryptionEventContent(algorithm = MXCRYPTO_ALGORITHM_MEGOLM).toContent()
@ -75,9 +76,9 @@ class EncryptionTest : InstrumentedTest {
val aliceSession = cryptoTestData.firstSession val aliceSession = cryptoTestData.firstSession
val room = aliceSession.getRoom(cryptoTestData.roomId)!! val room = aliceSession.getRoom(cryptoTestData.roomId)!!
room.isEncrypted() shouldBe false room.roomCryptoService().isEncrypted() shouldBe false
val timeline = room.createTimeline(null, TimelineSettings(10)) val timeline = room.timelineService().createTimeline(null, TimelineSettings(10))
val latch = CountDownLatch(1) val latch = CountDownLatch(1)
val timelineListener = object : Timeline.Listener { val timelineListener = object : Timeline.Listener {
override fun onTimelineFailure(throwable: Throwable) { override fun onTimelineFailure(throwable: Throwable) {
@ -105,7 +106,7 @@ class EncryptionTest : InstrumentedTest {
testHelper.await(latch) testHelper.await(latch)
timeline.dispose() timeline.dispose()
testHelper.waitWithLatch { testHelper.waitWithLatch {
room.isEncrypted() shouldBe roomShouldBeEncrypted room.roomCryptoService().isEncrypted() shouldBe roomShouldBeEncrypted
it.countDown() it.countDown()
} }
cryptoTestData.cleanUp(testHelper) cryptoTestData.cleanUp(testHelper)

View File

@ -50,6 +50,8 @@ import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransa
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState
import org.matrix.android.sdk.api.session.events.model.content.EncryptedEventContent import org.matrix.android.sdk.api.session.events.model.content.EncryptedEventContent
import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.getRoom
import org.matrix.android.sdk.api.session.room.getTimelineEvent
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
import org.matrix.android.sdk.api.session.room.model.message.MessageContent import org.matrix.android.sdk.api.session.room.model.message.MessageContent
@ -73,7 +75,7 @@ class KeyShareTests : InstrumentedTest {
// Create an encrypted room and add a message // Create an encrypted room and add a message
val roomId = commonTestHelper.runBlockingTest { val roomId = commonTestHelper.runBlockingTest {
aliceSession.createRoom( aliceSession.roomService().createRoom(
CreateRoomParams().apply { CreateRoomParams().apply {
visibility = RoomDirectoryVisibility.PRIVATE visibility = RoomDirectoryVisibility.PRIVATE
enableEncryption() enableEncryption()
@ -83,7 +85,7 @@ class KeyShareTests : InstrumentedTest {
val room = aliceSession.getRoom(roomId) val room = aliceSession.getRoom(roomId)
assertNotNull(room) assertNotNull(room)
Thread.sleep(4_000) Thread.sleep(4_000)
assertTrue(room?.isEncrypted() == true) assertTrue(room?.roomCryptoService()?.isEncrypted() == true)
val sentEventId = commonTestHelper.sendTextMessage(room!!, "My Message", 1).first().eventId val sentEventId = commonTestHelper.sendTextMessage(room!!, "My Message", 1).first().eventId
// Open a new sessionx // Open a new sessionx
@ -340,7 +342,7 @@ class KeyShareTests : InstrumentedTest {
// Create an encrypted room and send a couple of messages // Create an encrypted room and send a couple of messages
val roomId = commonTestHelper.runBlockingTest { val roomId = commonTestHelper.runBlockingTest {
aliceSession.createRoom( aliceSession.roomService().createRoom(
CreateRoomParams().apply { CreateRoomParams().apply {
visibility = RoomDirectoryVisibility.PRIVATE visibility = RoomDirectoryVisibility.PRIVATE
enableEncryption() enableEncryption()
@ -350,7 +352,7 @@ class KeyShareTests : InstrumentedTest {
val roomAlicePov = aliceSession.getRoom(roomId) val roomAlicePov = aliceSession.getRoom(roomId)
assertNotNull(roomAlicePov) assertNotNull(roomAlicePov)
Thread.sleep(1_000) Thread.sleep(1_000)
assertTrue(roomAlicePov?.isEncrypted() == true) assertTrue(roomAlicePov?.roomCryptoService()?.isEncrypted() == true)
val secondEventId = commonTestHelper.sendTextMessage(roomAlicePov!!, "Message", 3)[1].eventId val secondEventId = commonTestHelper.sendTextMessage(roomAlicePov!!, "Message", 3)[1].eventId
// Create bob session // Create bob session
@ -374,11 +376,11 @@ class KeyShareTests : InstrumentedTest {
// Let alice invite bob // Let alice invite bob
commonTestHelper.runBlockingTest { commonTestHelper.runBlockingTest {
roomAlicePov.invite(bobSession.myUserId, null) roomAlicePov.membershipService().invite(bobSession.myUserId, null)
} }
commonTestHelper.runBlockingTest { commonTestHelper.runBlockingTest {
bobSession.joinRoom(roomAlicePov.roomId, null, emptyList()) bobSession.roomService().joinRoom(roomAlicePov.roomId, null, emptyList())
} }
// we want to discard alice outbound session // we want to discard alice outbound session

View File

@ -33,6 +33,8 @@ import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.content.EncryptedEventContent import org.matrix.android.sdk.api.session.events.model.content.EncryptedEventContent
import org.matrix.android.sdk.api.session.events.model.content.WithHeldCode import org.matrix.android.sdk.api.session.events.model.content.WithHeldCode
import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.getRoom
import org.matrix.android.sdk.api.session.room.getTimelineEvent
import org.matrix.android.sdk.common.CommonTestHelper import org.matrix.android.sdk.common.CommonTestHelper
import org.matrix.android.sdk.common.CryptoTestHelper import org.matrix.android.sdk.common.CryptoTestHelper
import org.matrix.android.sdk.common.MockOkHttpInterceptor import org.matrix.android.sdk.common.MockOkHttpInterceptor

View File

@ -41,6 +41,7 @@ import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysVersion
import org.matrix.android.sdk.api.session.crypto.keysbackup.MegolmBackupCreationInfo import org.matrix.android.sdk.api.session.crypto.keysbackup.MegolmBackupCreationInfo
import org.matrix.android.sdk.api.session.crypto.keysbackup.toKeysVersionResult import org.matrix.android.sdk.api.session.crypto.keysbackup.toKeysVersionResult
import org.matrix.android.sdk.api.session.crypto.model.ImportRoomKeysResult import org.matrix.android.sdk.api.session.crypto.model.ImportRoomKeysResult
import org.matrix.android.sdk.api.session.getRoom
import org.matrix.android.sdk.common.CommonTestHelper import org.matrix.android.sdk.common.CommonTestHelper
import org.matrix.android.sdk.common.CryptoTestHelper import org.matrix.android.sdk.common.CryptoTestHelper
import org.matrix.android.sdk.common.TestConstants import org.matrix.android.sdk.common.TestConstants

View File

@ -60,7 +60,7 @@ class QuadSTests : InstrumentedTest {
val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
val quadS = aliceSession.sharedSecretStorageService val quadS = aliceSession.sharedSecretStorageService()
val TEST_KEY_ID = "my.test.Key" val TEST_KEY_ID = "my.test.Key"
@ -120,7 +120,7 @@ class QuadSTests : InstrumentedTest {
// Store a secret // Store a secret
val clearSecret = "42".toByteArray().toBase64NoPadding() val clearSecret = "42".toByteArray().toBase64NoPadding()
testHelper.runBlockingTest { testHelper.runBlockingTest {
aliceSession.sharedSecretStorageService.storeSecret( aliceSession.sharedSecretStorageService().storeSecret(
"secret.of.life", "secret.of.life",
clearSecret, clearSecret,
listOf(SharedSecretStorageService.KeyRef(null, keySpec)) // default key listOf(SharedSecretStorageService.KeyRef(null, keySpec)) // default key
@ -141,7 +141,7 @@ class QuadSTests : InstrumentedTest {
// Try to decrypt?? // Try to decrypt??
val decryptedSecret = testHelper.runBlockingTest { val decryptedSecret = testHelper.runBlockingTest {
aliceSession.sharedSecretStorageService.getSecret( aliceSession.sharedSecretStorageService().getSecret(
"secret.of.life", "secret.of.life",
null, // default key null, // default key
keySpec!! keySpec!!
@ -158,7 +158,7 @@ class QuadSTests : InstrumentedTest {
val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
val quadS = aliceSession.sharedSecretStorageService val quadS = aliceSession.sharedSecretStorageService()
val TEST_KEY_ID = "my.test.Key" val TEST_KEY_ID = "my.test.Key"
@ -187,7 +187,7 @@ class QuadSTests : InstrumentedTest {
val mySecretText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit" val mySecretText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit"
testHelper.runBlockingTest { testHelper.runBlockingTest {
aliceSession.sharedSecretStorageService.storeSecret( aliceSession.sharedSecretStorageService().storeSecret(
"my.secret", "my.secret",
mySecretText.toByteArray().toBase64NoPadding(), mySecretText.toByteArray().toBase64NoPadding(),
listOf( listOf(
@ -207,14 +207,14 @@ class QuadSTests : InstrumentedTest {
// Assert that can decrypt with both keys // Assert that can decrypt with both keys
testHelper.runBlockingTest { testHelper.runBlockingTest {
aliceSession.sharedSecretStorageService.getSecret("my.secret", aliceSession.sharedSecretStorageService().getSecret("my.secret",
keyId1, keyId1,
RawBytesKeySpec.fromRecoveryKey(key1Info.recoveryKey)!! RawBytesKeySpec.fromRecoveryKey(key1Info.recoveryKey)!!
) )
} }
testHelper.runBlockingTest { testHelper.runBlockingTest {
aliceSession.sharedSecretStorageService.getSecret("my.secret", aliceSession.sharedSecretStorageService().getSecret("my.secret",
keyId2, keyId2,
RawBytesKeySpec.fromRecoveryKey(key2Info.recoveryKey)!! RawBytesKeySpec.fromRecoveryKey(key2Info.recoveryKey)!!
) )
@ -236,7 +236,7 @@ class QuadSTests : InstrumentedTest {
val mySecretText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit" val mySecretText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit"
testHelper.runBlockingTest { testHelper.runBlockingTest {
aliceSession.sharedSecretStorageService.storeSecret( aliceSession.sharedSecretStorageService().storeSecret(
"my.secret", "my.secret",
mySecretText.toByteArray().toBase64NoPadding(), mySecretText.toByteArray().toBase64NoPadding(),
listOf(SharedSecretStorageService.KeyRef(keyId1, RawBytesKeySpec.fromRecoveryKey(key1Info.recoveryKey))) listOf(SharedSecretStorageService.KeyRef(keyId1, RawBytesKeySpec.fromRecoveryKey(key1Info.recoveryKey)))
@ -245,7 +245,7 @@ class QuadSTests : InstrumentedTest {
testHelper.runBlockingTest { testHelper.runBlockingTest {
try { try {
aliceSession.sharedSecretStorageService.getSecret("my.secret", aliceSession.sharedSecretStorageService().getSecret("my.secret",
keyId1, keyId1,
RawBytesKeySpec.fromPassphrase( RawBytesKeySpec.fromPassphrase(
"A bad passphrase", "A bad passphrase",
@ -260,7 +260,7 @@ class QuadSTests : InstrumentedTest {
// Now try with correct key // Now try with correct key
testHelper.runBlockingTest { testHelper.runBlockingTest {
aliceSession.sharedSecretStorageService.getSecret("my.secret", aliceSession.sharedSecretStorageService().getSecret("my.secret",
keyId1, keyId1,
RawBytesKeySpec.fromPassphrase( RawBytesKeySpec.fromPassphrase(
passphrase, passphrase,
@ -292,7 +292,7 @@ class QuadSTests : InstrumentedTest {
} }
private fun generatedSecret(session: Session, keyId: String, asDefault: Boolean = true): SsssKeyCreationInfo { private fun generatedSecret(session: Session, keyId: String, asDefault: Boolean = true): SsssKeyCreationInfo {
val quadS = session.sharedSecretStorageService val quadS = session.sharedSecretStorageService()
val testHelper = CommonTestHelper(context()) val testHelper = CommonTestHelper(context())
val creationInfo = testHelper.runBlockingTest { val creationInfo = testHelper.runBlockingTest {
@ -312,7 +312,7 @@ class QuadSTests : InstrumentedTest {
} }
private fun generatedSecretFromPassphrase(session: Session, passphrase: String, keyId: String, asDefault: Boolean = true): SsssKeyCreationInfo { private fun generatedSecretFromPassphrase(session: Session, passphrase: String, keyId: String, asDefault: Boolean = true): SsssKeyCreationInfo {
val quadS = session.sharedSecretStorageService val quadS = session.sharedSecretStorageService()
val testHelper = CommonTestHelper(context()) val testHelper = CommonTestHelper(context())
val creationInfo = testHelper.runBlockingTest { val creationInfo = testHelper.runBlockingTest {

View File

@ -31,6 +31,7 @@ import org.matrix.android.sdk.InstrumentedTest
import org.matrix.android.sdk.api.session.events.model.getRootThreadEventId import org.matrix.android.sdk.api.session.events.model.getRootThreadEventId
import org.matrix.android.sdk.api.session.events.model.isTextMessage import org.matrix.android.sdk.api.session.events.model.isTextMessage
import org.matrix.android.sdk.api.session.events.model.isThread import org.matrix.android.sdk.api.session.events.model.isThread
import org.matrix.android.sdk.api.session.getRoom
import org.matrix.android.sdk.api.session.room.timeline.Timeline import org.matrix.android.sdk.api.session.room.timeline.Timeline
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
import org.matrix.android.sdk.common.CommonTestHelper import org.matrix.android.sdk.common.CommonTestHelper
@ -80,7 +81,7 @@ class ThreadMessagingTest : InstrumentedTest {
replyInThread.root.getRootThreadEventId().shouldBeEqualTo(initMessage.root.eventId) replyInThread.root.getRootThreadEventId().shouldBeEqualTo(initMessage.root.eventId)
// The init normal message should now be a root thread event // The init normal message should now be a root thread event
val timeline = aliceRoom.createTimeline(null, TimelineSettings(30)) val timeline = aliceRoom.timelineService().createTimeline(null, TimelineSettings(30))
timeline.start() timeline.start()
aliceSession.startSync(true) aliceSession.startSync(true)
@ -141,7 +142,7 @@ class ThreadMessagingTest : InstrumentedTest {
replyInThread.root.getRootThreadEventId().shouldBeEqualTo(initMessage.root.eventId) replyInThread.root.getRootThreadEventId().shouldBeEqualTo(initMessage.root.eventId)
// The init normal message should now be a root thread event // The init normal message should now be a root thread event
val timeline = aliceRoom.createTimeline(null, TimelineSettings(30)) val timeline = aliceRoom.timelineService().createTimeline(null, TimelineSettings(30))
timeline.start() timeline.start()
aliceSession.startSync(true) aliceSession.startSync(true)
@ -214,7 +215,7 @@ class ThreadMessagingTest : InstrumentedTest {
} }
// The init normal message should now be a root thread event // The init normal message should now be a root thread event
val timeline = aliceRoom.createTimeline(null, TimelineSettings(30)) val timeline = aliceRoom.timelineService().createTimeline(null, TimelineSettings(30))
timeline.start() timeline.start()
aliceSession.startSync(true) aliceSession.startSync(true)
@ -309,7 +310,7 @@ class ThreadMessagingTest : InstrumentedTest {
} }
// The init normal message should now be a root thread event // The init normal message should now be a root thread event
val timeline = aliceRoom.createTimeline(null, TimelineSettings(30)) val timeline = aliceRoom.timelineService().createTimeline(null, TimelineSettings(30))
timeline.start() timeline.start()
aliceSession.startSync(true) aliceSession.startSync(true)

View File

@ -30,6 +30,7 @@ import org.junit.runners.MethodSorters
import org.matrix.android.sdk.InstrumentedTest import org.matrix.android.sdk.InstrumentedTest
import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.getRoom
import org.matrix.android.sdk.api.session.room.model.PollResponseAggregatedSummary import org.matrix.android.sdk.api.session.room.model.PollResponseAggregatedSummary
import org.matrix.android.sdk.api.session.room.model.PollSummaryContent import org.matrix.android.sdk.api.session.room.model.PollSummaryContent
import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent
@ -57,10 +58,10 @@ class PollAggregationTest : InstrumentedTest {
val roomFromBobPOV = cryptoTestData.secondSession!!.getRoom(cryptoTestData.roomId)!! val roomFromBobPOV = cryptoTestData.secondSession!!.getRoom(cryptoTestData.roomId)!!
// Bob creates a poll // Bob creates a poll
roomFromBobPOV.sendPoll(PollType.DISCLOSED, pollQuestion, pollOptions) roomFromBobPOV.sendService().sendPoll(PollType.DISCLOSED, pollQuestion, pollOptions)
aliceSession.startSync(true) aliceSession.startSync(true)
val aliceTimeline = roomFromAlicePOV.createTimeline(null, TimelineSettings(30)) val aliceTimeline = roomFromAlicePOV.timelineService().createTimeline(null, TimelineSettings(30))
aliceTimeline.start() aliceTimeline.start()
val TOTAL_TEST_COUNT = 7 val TOTAL_TEST_COUNT = 7
@ -83,37 +84,37 @@ class PollAggregationTest : InstrumentedTest {
// Poll has just been created. // Poll has just been created.
testInitialPollConditions(pollContent, pollSummary) testInitialPollConditions(pollContent, pollSummary)
lock.countDown() lock.countDown()
roomFromBobPOV.voteToPoll(pollEventId, pollContent.getBestPollCreationInfo()?.answers?.firstOrNull()?.id ?: "") roomFromBobPOV.sendService().voteToPoll(pollEventId, pollContent.getBestPollCreationInfo()?.answers?.firstOrNull()?.id ?: "")
} }
TOTAL_TEST_COUNT - 1 -> { TOTAL_TEST_COUNT - 1 -> {
// Bob: Option 1 // Bob: Option 1
testBobVotesOption1(pollContent, pollSummary) testBobVotesOption1(pollContent, pollSummary)
lock.countDown() lock.countDown()
roomFromBobPOV.voteToPoll(pollEventId, pollContent.getBestPollCreationInfo()?.answers?.get(1)?.id ?: "") roomFromBobPOV.sendService().voteToPoll(pollEventId, pollContent.getBestPollCreationInfo()?.answers?.get(1)?.id ?: "")
} }
TOTAL_TEST_COUNT - 2 -> { TOTAL_TEST_COUNT - 2 -> {
// Bob: Option 2 // Bob: Option 2
testBobChangesVoteToOption2(pollContent, pollSummary) testBobChangesVoteToOption2(pollContent, pollSummary)
lock.countDown() lock.countDown()
roomFromAlicePOV.voteToPoll(pollEventId, pollContent.getBestPollCreationInfo()?.answers?.get(1)?.id ?: "") roomFromAlicePOV.sendService().voteToPoll(pollEventId, pollContent.getBestPollCreationInfo()?.answers?.get(1)?.id ?: "")
} }
TOTAL_TEST_COUNT - 3 -> { TOTAL_TEST_COUNT - 3 -> {
// Alice: Option 2, Bob: Option 2 // Alice: Option 2, Bob: Option 2
testAliceAndBobVoteToOption2(pollContent, pollSummary) testAliceAndBobVoteToOption2(pollContent, pollSummary)
lock.countDown() lock.countDown()
roomFromAlicePOV.voteToPoll(pollEventId, pollContent.getBestPollCreationInfo()?.answers?.firstOrNull()?.id ?: "") roomFromAlicePOV.sendService().voteToPoll(pollEventId, pollContent.getBestPollCreationInfo()?.answers?.firstOrNull()?.id ?: "")
} }
TOTAL_TEST_COUNT - 4 -> { TOTAL_TEST_COUNT - 4 -> {
// Alice: Option 1, Bob: Option 2 // Alice: Option 1, Bob: Option 2
testAliceVotesOption1AndBobVotesOption2(pollContent, pollSummary) testAliceVotesOption1AndBobVotesOption2(pollContent, pollSummary)
lock.countDown() lock.countDown()
roomFromBobPOV.endPoll(pollEventId) roomFromBobPOV.sendService().endPoll(pollEventId)
} }
TOTAL_TEST_COUNT - 5 -> { TOTAL_TEST_COUNT - 5 -> {
// Alice: Option 1, Bob: Option 2 [poll is ended] // Alice: Option 1, Bob: Option 2 [poll is ended]
testEndedPoll(pollSummary) testEndedPoll(pollSummary)
lock.countDown() lock.countDown()
roomFromAlicePOV.voteToPoll(pollEventId, pollContent.getBestPollCreationInfo()?.answers?.get(1)?.id ?: "") roomFromAlicePOV.sendService().voteToPoll(pollEventId, pollContent.getBestPollCreationInfo()?.answers?.get(1)?.id ?: "")
} }
TOTAL_TEST_COUNT - 6 -> { TOTAL_TEST_COUNT - 6 -> {
// Alice: Option 1 (ignore change), Bob: Option 2 [poll is ended] // Alice: Option 1 (ignore change), Bob: Option 2 [poll is ended]

View File

@ -30,6 +30,7 @@ import org.matrix.android.sdk.InstrumentedTest
import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.getRoom
import org.matrix.android.sdk.api.session.room.model.message.MessageContent import org.matrix.android.sdk.api.session.room.model.message.MessageContent
import org.matrix.android.sdk.api.session.room.timeline.Timeline import org.matrix.android.sdk.api.session.room.timeline.Timeline
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
@ -74,7 +75,7 @@ class TimelineForwardPaginationTest : InstrumentedTest {
// Alice clear the cache and restart the sync // Alice clear the cache and restart the sync
commonTestHelper.clearCacheAndSync(aliceSession) commonTestHelper.clearCacheAndSync(aliceSession)
val aliceTimeline = roomFromAlicePOV.createTimeline(null, TimelineSettings(30)) val aliceTimeline = roomFromAlicePOV.timelineService().createTimeline(null, TimelineSettings(30))
aliceTimeline.start() aliceTimeline.start()
// Alice sees the 10 last message of the room, and can only navigate BACKWARD // Alice sees the 10 last message of the room, and can only navigate BACKWARD

View File

@ -28,6 +28,7 @@ import org.matrix.android.sdk.InstrumentedTest
import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.getRoom
import org.matrix.android.sdk.api.session.room.model.message.MessageContent import org.matrix.android.sdk.api.session.room.model.message.MessageContent
import org.matrix.android.sdk.api.session.room.timeline.Timeline import org.matrix.android.sdk.api.session.room.timeline.Timeline
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
@ -62,7 +63,7 @@ class TimelinePreviousLastForwardTest : InstrumentedTest {
val roomFromAlicePOV = aliceSession.getRoom(aliceRoomId)!! val roomFromAlicePOV = aliceSession.getRoom(aliceRoomId)!!
val roomFromBobPOV = bobSession.getRoom(aliceRoomId)!! val roomFromBobPOV = bobSession.getRoom(aliceRoomId)!!
val bobTimeline = roomFromBobPOV.createTimeline(null, TimelineSettings(30)) val bobTimeline = roomFromBobPOV.timelineService().createTimeline(null, TimelineSettings(30))
bobTimeline.start() bobTimeline.start()
run { run {

View File

@ -28,6 +28,7 @@ import org.matrix.android.sdk.InstrumentedTest
import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.events.model.isTextMessage import org.matrix.android.sdk.api.session.events.model.isTextMessage
import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.getRoom
import org.matrix.android.sdk.api.session.room.model.message.MessageTextContent import org.matrix.android.sdk.api.session.room.model.message.MessageTextContent
import org.matrix.android.sdk.api.session.room.timeline.Timeline import org.matrix.android.sdk.api.session.room.timeline.Timeline
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
@ -65,7 +66,7 @@ class TimelineSimpleBackPaginationTest : InstrumentedTest {
message, message,
numberOfMessagesToSent) numberOfMessagesToSent)
val bobTimeline = roomFromBobPOV.createTimeline(null, TimelineSettings(30)) val bobTimeline = roomFromBobPOV.timelineService().createTimeline(null, TimelineSettings(30))
bobTimeline.start() bobTimeline.start()
commonTestHelper.waitWithLatch(timeout = TestConstants.timeOutMillis * 10) { commonTestHelper.waitWithLatch(timeout = TestConstants.timeOutMillis * 10) {

View File

@ -27,6 +27,7 @@ import org.junit.runners.MethodSorters
import org.matrix.android.sdk.InstrumentedTest import org.matrix.android.sdk.InstrumentedTest
import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.getRoom
import org.matrix.android.sdk.api.session.room.model.message.MessageContent import org.matrix.android.sdk.api.session.room.model.message.MessageContent
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
import org.matrix.android.sdk.common.CommonTestHelper import org.matrix.android.sdk.common.CommonTestHelper
@ -71,7 +72,7 @@ class TimelineWithManyMembersTest : InstrumentedTest {
for (index in 1 until cryptoTestData.sessions.size) { for (index in 1 until cryptoTestData.sessions.size) {
val session = cryptoTestData.sessions[index] val session = cryptoTestData.sessions[index]
val roomForCurrentMember = session.getRoom(cryptoTestData.roomId)!! val roomForCurrentMember = session.getRoom(cryptoTestData.roomId)!!
val timelineForCurrentMember = roomForCurrentMember.createTimeline(null, TimelineSettings(30)) val timelineForCurrentMember = roomForCurrentMember.timelineService().createTimeline(null, TimelineSettings(30))
timelineForCurrentMember.start() timelineForCurrentMember.start()
session.startSync(true) session.startSync(true)

View File

@ -24,6 +24,7 @@ import org.junit.runners.JUnit4
import org.junit.runners.MethodSorters import org.junit.runners.MethodSorters
import org.matrix.android.sdk.InstrumentedTest import org.matrix.android.sdk.InstrumentedTest
import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.getRoom
import org.matrix.android.sdk.api.session.search.SearchResult import org.matrix.android.sdk.api.session.search.SearchResult
import org.matrix.android.sdk.common.CommonTestHelper import org.matrix.android.sdk.common.CommonTestHelper
import org.matrix.android.sdk.common.CryptoTestData import org.matrix.android.sdk.common.CryptoTestData
@ -59,9 +60,10 @@ class SearchMessagesTest : InstrumentedTest {
fun sendTextMessageAndSearchPartOfItUsingRoom() { fun sendTextMessageAndSearchPartOfItUsingRoom() {
doTest { cryptoTestData -> doTest { cryptoTestData ->
cryptoTestData.firstSession cryptoTestData.firstSession
.getRoom(cryptoTestData.roomId)!! .searchService()
.search( .search(
searchTerm = "lore", searchTerm = "lore",
roomId = cryptoTestData.roomId,
limit = 10, limit = 10,
includeProfile = true, includeProfile = true,
afterLimit = 0, afterLimit = 0,

View File

@ -29,6 +29,7 @@ import org.junit.runners.MethodSorters
import org.matrix.android.sdk.InstrumentedTest import org.matrix.android.sdk.InstrumentedTest
import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.getStateEvent
import org.matrix.android.sdk.api.session.room.model.GuestAccess import org.matrix.android.sdk.api.session.room.model.GuestAccess
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
import org.matrix.android.sdk.api.session.room.model.RoomGuestAccessContent import org.matrix.android.sdk.api.session.room.model.RoomGuestAccessContent
@ -147,7 +148,7 @@ class SpaceCreationTest : InstrumentedTest {
// create a room // create a room
var firstChild: String? = null var firstChild: String? = null
commonTestHelper.waitWithLatch { commonTestHelper.waitWithLatch {
firstChild = aliceSession.createRoom(CreateRoomParams().apply { firstChild = aliceSession.roomService().createRoom(CreateRoomParams().apply {
this.name = "FirstRoom" this.name = "FirstRoom"
this.topic = "Description of first room" this.topic = "Description of first room"
this.preset = CreateRoomPreset.PRESET_PUBLIC_CHAT this.preset = CreateRoomPreset.PRESET_PUBLIC_CHAT
@ -162,7 +163,7 @@ class SpaceCreationTest : InstrumentedTest {
var secondChild: String? = null var secondChild: String? = null
commonTestHelper.waitWithLatch { commonTestHelper.waitWithLatch {
secondChild = aliceSession.createRoom(CreateRoomParams().apply { secondChild = aliceSession.roomService().createRoom(CreateRoomParams().apply {
this.name = "SecondRoom" this.name = "SecondRoom"
this.topic = "Description of second room" this.topic = "Description of second room"
this.preset = CreateRoomPreset.PRESET_PUBLIC_CHAT this.preset = CreateRoomPreset.PRESET_PUBLIC_CHAT

View File

@ -35,6 +35,9 @@ import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.toContent import org.matrix.android.sdk.api.session.events.model.toContent
import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.getRoom
import org.matrix.android.sdk.api.session.getRoomSummary
import org.matrix.android.sdk.api.session.room.getStateEvent
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesAllowEntry import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesAllowEntry
import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary
@ -68,7 +71,7 @@ class SpaceHierarchyTest : InstrumentedTest {
var roomId = "" var roomId = ""
commonTestHelper.waitWithLatch { commonTestHelper.waitWithLatch {
roomId = session.createRoom(CreateRoomParams().apply { name = "General" }) roomId = session.roomService().createRoom(CreateRoomParams().apply { name = "General" })
it.countDown() it.countDown()
} }
@ -203,27 +206,27 @@ class SpaceHierarchyTest : InstrumentedTest {
var orphan1 = "" var orphan1 = ""
commonTestHelper.waitWithLatch { commonTestHelper.waitWithLatch {
orphan1 = session.createRoom(CreateRoomParams().apply { name = "O1" }) orphan1 = session.roomService().createRoom(CreateRoomParams().apply { name = "O1" })
it.countDown() it.countDown()
} }
var orphan2 = "" var orphan2 = ""
commonTestHelper.waitWithLatch { commonTestHelper.waitWithLatch {
orphan2 = session.createRoom(CreateRoomParams().apply { name = "O2" }) orphan2 = session.roomService().createRoom(CreateRoomParams().apply { name = "O2" })
it.countDown() it.countDown()
} }
val allRooms = session.getRoomSummaries(roomSummaryQueryParams { excludeType = listOf(RoomType.SPACE) }) val allRooms = session.roomService().getRoomSummaries(roomSummaryQueryParams { excludeType = listOf(RoomType.SPACE) })
assertEquals("Unexpected number of rooms", 9, allRooms.size) assertEquals("Unexpected number of rooms", 9, allRooms.size)
val orphans = session.getFlattenRoomSummaryChildrenOf(null) val orphans = session.roomService().getFlattenRoomSummaryChildrenOf(null)
assertEquals("Unexpected number of orphan rooms", 2, orphans.size) assertEquals("Unexpected number of orphan rooms", 2, orphans.size)
assertTrue("O1 should be an orphan", orphans.any { it.roomId == orphan1 }) assertTrue("O1 should be an orphan", orphans.any { it.roomId == orphan1 })
assertTrue("O2 should be an orphan ${orphans.map { it.name }}", orphans.any { it.roomId == orphan2 }) assertTrue("O2 should be an orphan ${orphans.map { it.name }}", orphans.any { it.roomId == orphan2 })
val aChildren = session.getFlattenRoomSummaryChildrenOf(spaceAInfo.spaceId) val aChildren = session.roomService().getFlattenRoomSummaryChildrenOf(spaceAInfo.spaceId)
assertEquals("Unexpected number of flatten child rooms", 4, aChildren.size) assertEquals("Unexpected number of flatten child rooms", 4, aChildren.size)
assertTrue("A1 should be a child of A", aChildren.any { it.name == "A1" }) assertTrue("A1 should be a child of A", aChildren.any { it.name == "A1" })
@ -233,13 +236,13 @@ class SpaceHierarchyTest : InstrumentedTest {
// Add a non canonical child and check that it does not appear as orphan // Add a non canonical child and check that it does not appear as orphan
commonTestHelper.waitWithLatch { commonTestHelper.waitWithLatch {
val a3 = session.createRoom(CreateRoomParams().apply { name = "A3" }) val a3 = session.roomService().createRoom(CreateRoomParams().apply { name = "A3" })
spaceA!!.addChildren(a3, viaServers, null, false) spaceA!!.addChildren(a3, viaServers, null, false)
it.countDown() it.countDown()
} }
Thread.sleep(6_000) Thread.sleep(6_000)
val orphansUpdate = session.getRoomSummaries(roomSummaryQueryParams { val orphansUpdate = session.roomService().getRoomSummaries(roomSummaryQueryParams {
activeSpaceFilter = ActiveSpaceFilter.ActiveSpace(null) activeSpaceFilter = ActiveSpaceFilter.ActiveSpace(null)
}) })
assertEquals("Unexpected number of orphan rooms ${orphansUpdate.map { it.name }}", 2, orphansUpdate.size) assertEquals("Unexpected number of orphan rooms ${orphansUpdate.map { it.name }}", 2, orphansUpdate.size)
@ -279,7 +282,7 @@ class SpaceHierarchyTest : InstrumentedTest {
// A -> C -> A // A -> C -> A
val aChildren = session.getFlattenRoomSummaryChildrenOf(spaceAInfo.spaceId) val aChildren = session.roomService().getFlattenRoomSummaryChildrenOf(spaceAInfo.spaceId)
assertEquals("Unexpected number of flatten child rooms ${aChildren.map { it.name }}", 4, aChildren.size) assertEquals("Unexpected number of flatten child rooms ${aChildren.map { it.name }}", 4, aChildren.size)
assertTrue("A1 should be a child of A", aChildren.any { it.name == "A1" }) assertTrue("A1 should be a child of A", aChildren.any { it.name == "A1" })
@ -319,7 +322,7 @@ class SpaceHierarchyTest : InstrumentedTest {
commonTestHelper.waitWithLatch { latch -> commonTestHelper.waitWithLatch { latch ->
val flatAChildren = session.getFlattenRoomSummaryChildrenOfLive(spaceAInfo.spaceId) val flatAChildren = session.roomService().getFlattenRoomSummaryChildrenOfLive(spaceAInfo.spaceId)
val childObserver = object : Observer<List<RoomSummary>> { val childObserver = object : Observer<List<RoomSummary>> {
override fun onChanged(children: List<RoomSummary>?) { override fun onChanged(children: List<RoomSummary>?) {
// Log.d("## TEST", "Space A flat children update : ${children?.map { it.name }}") // Log.d("## TEST", "Space A flat children update : ${children?.map { it.name }}")
@ -346,7 +349,7 @@ class SpaceHierarchyTest : InstrumentedTest {
val bRoomId = spaceBInfo.roomIds.first() val bRoomId = spaceBInfo.roomIds.first()
commonTestHelper.waitWithLatch { latch -> commonTestHelper.waitWithLatch { latch ->
val flatAChildren = session.getFlattenRoomSummaryChildrenOfLive(spaceAInfo.spaceId) val flatAChildren = session.roomService().getFlattenRoomSummaryChildrenOfLive(spaceAInfo.spaceId)
val childObserver = object : Observer<List<RoomSummary>> { val childObserver = object : Observer<List<RoomSummary>> {
override fun onChanged(children: List<RoomSummary>?) { override fun onChanged(children: List<RoomSummary>?) {
System.out.println("## TEST | Space A flat children update : ${children?.map { it.name }}") System.out.println("## TEST | Space A flat children update : ${children?.map { it.name }}")
@ -359,7 +362,7 @@ class SpaceHierarchyTest : InstrumentedTest {
} }
// part from b room // part from b room
session.leaveRoom(bRoomId) session.roomService().leaveRoom(bRoomId)
// The room should have disapear from flat children // The room should have disapear from flat children
flatAChildren.observeForever(childObserver) flatAChildren.observeForever(childObserver)
} }
@ -385,7 +388,7 @@ class SpaceHierarchyTest : InstrumentedTest {
val viaServers = listOf(session.sessionParams.homeServerHost ?: "") val viaServers = listOf(session.sessionParams.homeServerHost ?: "")
roomIds = childInfo.map { entry -> roomIds = childInfo.map { entry ->
session.createRoom(CreateRoomParams().apply { name = entry.first }) session.roomService().createRoom(CreateRoomParams().apply { name = entry.first })
} }
roomIds.forEachIndexed { index, roomId -> roomIds.forEachIndexed { index, roomId ->
syncedSpace!!.addChildren(roomId, viaServers, null, childInfo[index].second) syncedSpace!!.addChildren(roomId, viaServers, null, childInfo[index].second)
@ -414,8 +417,9 @@ class SpaceHierarchyTest : InstrumentedTest {
roomIds = roomIds =
childInfo.map { entry -> childInfo.map { entry ->
val homeServerCapabilities = session val homeServerCapabilities = session
.homeServerCapabilitiesService()
.getHomeServerCapabilities() .getHomeServerCapabilities()
session.createRoom(CreateRoomParams().apply { session.roomService().createRoom(CreateRoomParams().apply {
name = entry.first name = entry.first
this.featurePreset = RestrictedRoomPreset( this.featurePreset = RestrictedRoomPreset(
homeServerCapabilities, homeServerCapabilities,
@ -475,7 +479,9 @@ class SpaceHierarchyTest : InstrumentedTest {
// + C // + C
// + c1, c2 // + c1, c2
val rootSpaces = session.spaceService().getRootSpaceSummaries() val rootSpaces = commonTestHelper.runBlockingTest {
session.spaceService().getRootSpaceSummaries()
}
assertEquals("Unexpected number of root spaces ${rootSpaces.map { it.name }}", 2, rootSpaces.size) assertEquals("Unexpected number of root spaces ${rootSpaces.map { it.name }}", 2, rootSpaces.size)
@ -494,22 +500,22 @@ class SpaceHierarchyTest : InstrumentedTest {
)) ))
commonTestHelper.runBlockingTest { commonTestHelper.runBlockingTest {
aliceSession.getRoom(spaceAInfo.spaceId)!!.invite(bobSession.myUserId, null) aliceSession.getRoom(spaceAInfo.spaceId)!!.membershipService().invite(bobSession.myUserId, null)
} }
commonTestHelper.runBlockingTest { commonTestHelper.runBlockingTest {
bobSession.joinRoom(spaceAInfo.spaceId, null, emptyList()) bobSession.roomService().joinRoom(spaceAInfo.spaceId, null, emptyList())
} }
var bobRoomId = "" var bobRoomId = ""
commonTestHelper.waitWithLatch { commonTestHelper.waitWithLatch {
bobRoomId = bobSession.createRoom(CreateRoomParams().apply { name = "A Bob Room" }) bobRoomId = bobSession.roomService().createRoom(CreateRoomParams().apply { name = "A Bob Room" })
bobSession.getRoom(bobRoomId)!!.invite(aliceSession.myUserId) bobSession.getRoom(bobRoomId)!!.membershipService().invite(aliceSession.myUserId)
it.countDown() it.countDown()
} }
commonTestHelper.runBlockingTest { commonTestHelper.runBlockingTest {
aliceSession.joinRoom(bobRoomId) aliceSession.roomService().joinRoom(bobRoomId)
} }
commonTestHelper.waitWithLatch { latch -> commonTestHelper.waitWithLatch { latch ->
@ -549,7 +555,7 @@ class SpaceHierarchyTest : InstrumentedTest {
?.setUserPowerLevel(aliceSession.myUserId, Role.Admin.value) ?.setUserPowerLevel(aliceSession.myUserId, Role.Admin.value)
?.toContent() ?.toContent()
room.sendStateEvent(EventType.STATE_ROOM_POWER_LEVELS, stateKey = "", newPowerLevelsContent!!) room.stateService().sendStateEvent(EventType.STATE_ROOM_POWER_LEVELS, stateKey = "", newPowerLevelsContent!!)
it.countDown() it.countDown()
} }

View File

@ -43,7 +43,8 @@ import javax.inject.Inject
/** /**
* This is the main entry point to the matrix sdk. * This is the main entry point to the matrix sdk.
* To get the singleton instance, use getInstance static method. * <br/>
* See [Companion.createInstance] to create an instance. The app should create and manage the instance itself.
*/ */
class Matrix private constructor(context: Context, matrixConfiguration: MatrixConfiguration) { class Matrix private constructor(context: Context, matrixConfiguration: MatrixConfiguration) {
@ -74,9 +75,7 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
fun getUserAgent() = userAgentHolder.userAgent fun getUserAgent() = userAgentHolder.userAgent
fun authenticationService(): AuthenticationService { fun authenticationService() = authenticationService
return authenticationService
}
fun rawService() = rawService fun rawService() = rawService
@ -84,9 +83,7 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
fun homeServerHistoryService() = homeServerHistoryService fun homeServerHistoryService() = homeServerHistoryService
fun legacySessionImporter(): LegacySessionImporter { fun legacySessionImporter() = legacySessionImporter
return legacySessionImporter
}
fun workerFactory(): WorkerFactory = matrixWorkerFactory fun workerFactory(): WorkerFactory = matrixWorkerFactory
@ -143,6 +140,9 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
return instance return instance
} }
/**
* @return a String with details about the Matrix SDK version
*/
fun getSdkVersion(): String { fun getSdkVersion(): String {
return BuildConfig.SDK_VERSION + " (" + BuildConfig.GIT_SDK_REVISION + ")" return BuildConfig.SDK_VERSION + " (" + BuildConfig.GIT_SDK_REVISION + ")"
} }

View File

@ -24,10 +24,8 @@ import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
import org.matrix.android.sdk.api.auth.data.SessionParams import org.matrix.android.sdk.api.auth.data.SessionParams
import org.matrix.android.sdk.api.failure.GlobalError import org.matrix.android.sdk.api.failure.GlobalError
import org.matrix.android.sdk.api.federation.FederationService import org.matrix.android.sdk.api.federation.FederationService
import org.matrix.android.sdk.api.pushrules.PushRuleService
import org.matrix.android.sdk.api.session.account.AccountService import org.matrix.android.sdk.api.session.account.AccountService
import org.matrix.android.sdk.api.session.accountdata.SessionAccountDataService import org.matrix.android.sdk.api.session.accountdata.SessionAccountDataService
import org.matrix.android.sdk.api.session.cache.CacheService
import org.matrix.android.sdk.api.session.call.CallSignalingService import org.matrix.android.sdk.api.session.call.CallSignalingService
import org.matrix.android.sdk.api.session.content.ContentUploadStateTracker import org.matrix.android.sdk.api.session.content.ContentUploadStateTracker
import org.matrix.android.sdk.api.session.content.ContentUrlResolver import org.matrix.android.sdk.api.session.content.ContentUrlResolver
@ -47,6 +45,7 @@ import org.matrix.android.sdk.api.session.permalinks.PermalinkService
import org.matrix.android.sdk.api.session.presence.PresenceService import org.matrix.android.sdk.api.session.presence.PresenceService
import org.matrix.android.sdk.api.session.profile.ProfileService import org.matrix.android.sdk.api.session.profile.ProfileService
import org.matrix.android.sdk.api.session.pushers.PushersService import org.matrix.android.sdk.api.session.pushers.PushersService
import org.matrix.android.sdk.api.session.pushrules.PushRuleService
import org.matrix.android.sdk.api.session.room.RoomDirectoryService import org.matrix.android.sdk.api.session.room.RoomDirectoryService
import org.matrix.android.sdk.api.session.room.RoomService import org.matrix.android.sdk.api.session.room.RoomService
import org.matrix.android.sdk.api.session.search.SearchService import org.matrix.android.sdk.api.session.search.SearchService
@ -68,26 +67,7 @@ import org.matrix.android.sdk.api.session.widgets.WidgetService
* This interface defines interactions with a session. * This interface defines interactions with a session.
* An instance of a session will be provided by the SDK. * An instance of a session will be provided by the SDK.
*/ */
interface Session : interface Session {
RoomService,
RoomDirectoryService,
GroupService,
UserService,
CacheService,
SignOutService,
FilterService,
TermsService,
EventService,
ProfileService,
PresenceService,
PushRuleService,
PushersService,
SyncStatusService,
HomeServerCapabilitiesService,
SecureStorageService,
AccountService,
ToDeviceService,
EventStreamService {
val coroutineDispatchers: MatrixCoroutineDispatchers val coroutineDispatchers: MatrixCoroutineDispatchers
@ -144,6 +124,11 @@ interface Session :
*/ */
fun stopSync() fun stopSync()
/**
* Clear cache of the session
*/
suspend fun clearCache()
/** /**
* This method allows to listen the sync state. * This method allows to listen the sync state.
* @return a [LiveData] of [SyncState]. * @return a [LiveData] of [SyncState].
@ -206,6 +191,96 @@ interface Session :
*/ */
fun identityService(): IdentityService fun identityService(): IdentityService
/**
* Returns the HomeServerCapabilities service associated with the session
*/
fun homeServerCapabilitiesService(): HomeServerCapabilitiesService
/**
* Returns the RoomService associated with the session
*/
fun roomService(): RoomService
/**
* Returns the RoomDirectoryService associated with the session
*/
fun roomDirectoryService(): RoomDirectoryService
/**
* Returns the GroupService associated with the session
*/
fun groupService(): GroupService
/**
* Returns the UserService associated with the session
*/
fun userService(): UserService
/**
* Returns the SignOutService associated with the session
*/
fun signOutService(): SignOutService
/**
* Returns the FilterService associated with the session
*/
fun filterService(): FilterService
/**
* Returns the PushRuleService associated with the session
*/
fun pushRuleService(): PushRuleService
/**
* Returns the PushersService associated with the session
*/
fun pushersService(): PushersService
/**
* Returns the EventService associated with the session
*/
fun eventService(): EventService
/**
* Returns the TermsService associated with the session
*/
fun termsService(): TermsService
/**
* Returns the SyncStatusService associated with the session
*/
fun syncStatusService(): SyncStatusService
/**
* Returns the SecureStorageService associated with the session
*/
fun secureStorageService(): SecureStorageService
/**
* Returns the ProfileService associated with the session
*/
fun profileService(): ProfileService
/**
* Returns the PresenceService associated with the session
*/
fun presenceService(): PresenceService
/**
* Returns the AccountService associated with the session
*/
fun accountService(): AccountService
/**
* Returns the ToDeviceService associated with the session
*/
fun toDeviceService(): ToDeviceService
/**
* Returns the EventStreamService associated with the session
*/
fun eventStreamService(): EventStreamService
/** /**
* Returns the widget service associated with the session * Returns the widget service associated with the session
*/ */
@ -266,6 +341,11 @@ interface Session :
*/ */
fun accountDataService(): SessionAccountDataService fun accountDataService(): SessionAccountDataService
/**
* Returns the SharedSecretStorageService associated with the session
*/
fun sharedSecretStorageService(): SharedSecretStorageService
/** /**
* Add a listener to the session. * Add a listener to the session.
* @param listener the listener to add. * @param listener the listener to add.
@ -303,8 +383,6 @@ interface Session :
fun onGlobalError(session: Session, globalError: GlobalError) = Unit fun onGlobalError(session: Session, globalError: GlobalError) = Unit
} }
val sharedSecretStorageService: SharedSecretStorageService
fun getUiaSsoFallbackUrl(authenticationSessionId: String): String fun getUiaSsoFallbackUrl(authenticationSessionId: String): String
/** /**

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.api.session
import org.matrix.android.sdk.api.session.room.Room
import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.session.user.model.User
/**
* Get a room using the RoomService of a Session
*/
fun Session.getRoom(roomId: String): Room? = roomService().getRoom(roomId)
/**
* Get a room summary using the RoomService of a Session
*/
fun Session.getRoomSummary(roomIdOrAlias: String): RoomSummary? = roomService().getRoomSummary(roomIdOrAlias)
/**
* Get a user using the UserService of a Session
*/
fun Session.getUser(userId: String): User? = userService().getUser(userId)

View File

@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.matrix.android.sdk.api.pushrules package org.matrix.android.sdk.api.session.pushrules
import org.matrix.android.sdk.api.pushrules.rest.PushRule import org.matrix.android.sdk.api.session.pushrules.rest.PushRule
import timber.log.Timber import timber.log.Timber
sealed class Action { sealed class Action {

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.matrix.android.sdk.api.pushrules package org.matrix.android.sdk.api.session.pushrules
import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.Event

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.matrix.android.sdk.api.pushrules package org.matrix.android.sdk.api.session.pushrules
import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.Event

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.matrix.android.sdk.api.pushrules package org.matrix.android.sdk.api.session.pushrules
import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.EventType

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.matrix.android.sdk.api.pushrules package org.matrix.android.sdk.api.session.pushrules
import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.internal.di.MoshiProvider import org.matrix.android.sdk.internal.di.MoshiProvider

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.matrix.android.sdk.api.pushrules package org.matrix.android.sdk.api.session.pushrules
enum class Kind(val value: String) { enum class Kind(val value: String) {
EventMatch("event_match"), EventMatch("event_match"),

View File

@ -13,10 +13,10 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.matrix.android.sdk.api.pushrules package org.matrix.android.sdk.api.session.pushrules
import org.matrix.android.sdk.api.pushrules.rest.PushRule
import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.pushrules.rest.PushRule
data class PushEvents( data class PushEvents(
val matchedEvents: List<Pair<Event, PushRule>>, val matchedEvents: List<Pair<Event, PushRule>>,

View File

@ -13,12 +13,12 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.matrix.android.sdk.api.pushrules package org.matrix.android.sdk.api.session.pushrules
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import org.matrix.android.sdk.api.pushrules.rest.PushRule
import org.matrix.android.sdk.api.pushrules.rest.RuleSet
import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.pushrules.rest.PushRule
import org.matrix.android.sdk.api.session.pushrules.rest.RuleSet
interface PushRuleService { interface PushRuleService {
/** /**

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.matrix.android.sdk.api.pushrules package org.matrix.android.sdk.api.session.pushrules
import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.internal.session.room.RoomGetter import org.matrix.android.sdk.internal.session.room.RoomGetter
@ -44,7 +44,7 @@ class RoomMemberCountCondition(
// Parse the is field into prefix and number the first time // Parse the is field into prefix and number the first time
val (prefix, count) = parseIsField() ?: return false val (prefix, count) = parseIsField() ?: return false
val numMembers = room.getNumberOfJoinedMembers() val numMembers = room.membershipService().getNumberOfJoinedMembers()
return when (prefix) { return when (prefix) {
"<" -> numMembers < count "<" -> numMembers < count

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.matrix.android.sdk.api.pushrules package org.matrix.android.sdk.api.session.pushrules
/** /**
* Known rule ids * Known rule ids

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.matrix.android.sdk.api.pushrules package org.matrix.android.sdk.api.session.pushrules
object RuleScope { object RuleScope {
const val GLOBAL = "global" const val GLOBAL = "global"

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.matrix.android.sdk.api.pushrules package org.matrix.android.sdk.api.session.pushrules
/** /**
* Ref: https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-pushrules * Ref: https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-pushrules

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.matrix.android.sdk.api.pushrules package org.matrix.android.sdk.api.session.pushrules
import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent

View File

@ -13,17 +13,17 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.matrix.android.sdk.api.pushrules.rest package org.matrix.android.sdk.api.session.pushrules.rest
import com.squareup.moshi.Json import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass import com.squareup.moshi.JsonClass
import org.matrix.android.sdk.api.pushrules.Condition import org.matrix.android.sdk.api.session.pushrules.Condition
import org.matrix.android.sdk.api.pushrules.ContainsDisplayNameCondition import org.matrix.android.sdk.api.session.pushrules.ContainsDisplayNameCondition
import org.matrix.android.sdk.api.pushrules.EventMatchCondition import org.matrix.android.sdk.api.session.pushrules.EventMatchCondition
import org.matrix.android.sdk.api.pushrules.Kind import org.matrix.android.sdk.api.session.pushrules.Kind
import org.matrix.android.sdk.api.pushrules.RoomMemberCountCondition import org.matrix.android.sdk.api.session.pushrules.RoomMemberCountCondition
import org.matrix.android.sdk.api.pushrules.RuleIds import org.matrix.android.sdk.api.session.pushrules.RuleIds
import org.matrix.android.sdk.api.pushrules.SenderNotificationPermissionCondition import org.matrix.android.sdk.api.session.pushrules.SenderNotificationPermissionCondition
import timber.log.Timber import timber.log.Timber
/** /**

View File

@ -14,14 +14,14 @@
* limitations under the License. * limitations under the License.
*/ */
package org.matrix.android.sdk.api.pushrules.rest package org.matrix.android.sdk.api.session.pushrules.rest
import com.squareup.moshi.Json import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass import com.squareup.moshi.JsonClass
import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.pushrules.Action import org.matrix.android.sdk.api.session.pushrules.Action
import org.matrix.android.sdk.api.pushrules.getActions import org.matrix.android.sdk.api.session.pushrules.getActions
import org.matrix.android.sdk.api.pushrules.toJson import org.matrix.android.sdk.api.session.pushrules.toJson
/** /**
* Ref: https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-pushrules * Ref: https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-pushrules

View File

@ -13,12 +13,12 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.matrix.android.sdk.api.pushrules.rest package org.matrix.android.sdk.api.session.pushrules.rest
import com.squareup.moshi.Json import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass import com.squareup.moshi.JsonClass
import org.matrix.android.sdk.api.pushrules.RuleIds import org.matrix.android.sdk.api.session.pushrules.RuleIds
import org.matrix.android.sdk.api.pushrules.RuleSetKey import org.matrix.android.sdk.api.session.pushrules.RuleSetKey
/** /**
* Ref: https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-pushrules * Ref: https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-pushrules

View File

@ -38,33 +38,13 @@ import org.matrix.android.sdk.api.session.room.timeline.TimelineService
import org.matrix.android.sdk.api.session.room.typing.TypingService import org.matrix.android.sdk.api.session.room.typing.TypingService
import org.matrix.android.sdk.api.session.room.uploads.UploadsService import org.matrix.android.sdk.api.session.room.uploads.UploadsService
import org.matrix.android.sdk.api.session.room.version.RoomVersionService import org.matrix.android.sdk.api.session.room.version.RoomVersionService
import org.matrix.android.sdk.api.session.search.SearchResult
import org.matrix.android.sdk.api.session.space.Space import org.matrix.android.sdk.api.session.space.Space
import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.api.util.Optional
/** /**
* This interface defines methods to interact within a room. * This interface defines methods to interact within a room.
*/ */
interface Room : interface Room {
TimelineService,
ThreadsService,
ThreadsLocalService,
SendService,
DraftService,
ReadService,
TypingService,
AliasService,
TagsService,
MembershipService,
StateService,
UploadsService,
ReportingService,
RoomCallService,
RelationService,
RoomCryptoService,
RoomPushRuleService,
RoomAccountDataService,
RoomVersionService {
val coroutineDispatchers: MatrixCoroutineDispatchers val coroutineDispatchers: MatrixCoroutineDispatchers
@ -84,28 +64,103 @@ interface Room :
*/ */
fun roomSummary(): RoomSummary? fun roomSummary(): RoomSummary?
/**
* Generic function to search a term in a room.
* Ref: https://matrix.org/docs/spec/client_server/latest#module-search
* @param searchTerm the term to search
* @param nextBatch the token that retrieved from the previous response. Should be provided to get the next batch of results
* @param orderByRecent if true, the most recent message events will return in the first places of the list
* @param limit the maximum number of events to return.
* @param beforeLimit how many events before the result are returned.
* @param afterLimit how many events after the result are returned.
* @param includeProfile requests that the server returns the historic profile information for the users that sent the events that were returned.
* @return The search result
*/
suspend fun search(searchTerm: String,
nextBatch: String?,
orderByRecent: Boolean,
limit: Int,
beforeLimit: Int,
afterLimit: Int,
includeProfile: Boolean): SearchResult
/** /**
* Use this room as a Space, if the type is correct. * Use this room as a Space, if the type is correct.
*/ */
fun asSpace(): Space? fun asSpace(): Space?
/**
* Get the TimelineService associated to this Room
*/
fun timelineService(): TimelineService
/**
* Get the ThreadsService associated to this Room
*/
fun threadsService(): ThreadsService
/**
* Get the ThreadsLocalService associated to this Room
*/
fun threadsLocalService(): ThreadsLocalService
/**
* Get the SendService associated to this Room
*/
fun sendService(): SendService
/**
* Get the DraftService associated to this Room
*/
fun draftService(): DraftService
/**
* Get the ReadService associated to this Room
*/
fun readService(): ReadService
/**
* Get the TypingService associated to this Room
*/
fun typingService(): TypingService
/**
* Get the AliasService associated to this Room
*/
fun aliasService(): AliasService
/**
* Get the TagsService associated to this Room
*/
fun tagsService(): TagsService
/**
* Get the MembershipService associated to this Room
*/
fun membershipService(): MembershipService
/**
* Get the StateService associated to this Room
*/
fun stateService(): StateService
/**
* Get the UploadsService associated to this Room
*/
fun uploadsService(): UploadsService
/**
* Get the ReportingService associated to this Room
*/
fun reportingService(): ReportingService
/**
* Get the RoomCallService associated to this Room
*/
fun roomCallService(): RoomCallService
/**
* Get the RelationService associated to this Room
*/
fun relationService(): RelationService
/**
* Get the RoomCryptoService associated to this Room
*/
fun roomCryptoService(): RoomCryptoService
/**
* Get the RoomPushRuleService associated to this Room
*/
fun roomPushRuleService(): RoomPushRuleService
/**
* Get the RoomAccountDataService associated to this Room
*/
fun roomAccountDataService(): RoomAccountDataService
/**
* Get the RoomVersionService associated to this Room
*/
fun roomVersionService(): RoomVersionService
} }

View File

@ -0,0 +1,33 @@
/*
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.api.session.room
import org.matrix.android.sdk.api.query.QueryStringValue
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
/**
* Get a TimelineEvent using the TimelineService of a Room
*/
fun Room.getTimelineEvent(eventId: String): TimelineEvent? =
timelineService().getTimelineEvent(eventId)
/**
* Get a StateEvent using the StateService of a Room
*/
fun Room.getStateEvent(eventType: String, stateKey: QueryStringValue = QueryStringValue.NoCondition): Event? =
stateService().getStateEvent(eventType, stateKey)

View File

@ -15,10 +15,12 @@
*/ */
package org.matrix.android.sdk.api.session.room.model package org.matrix.android.sdk.api.session.room.model
import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationShareAggregatedSummary
data class EventAnnotationsSummary( data class EventAnnotationsSummary(
val eventId: String,
val reactionsSummary: List<ReactionAggregatedSummary> = emptyList(), val reactionsSummary: List<ReactionAggregatedSummary> = emptyList(),
val editSummary: EditAggregatedSummary? = null, val editSummary: EditAggregatedSummary? = null,
val pollResponseSummary: PollResponseAggregatedSummary? = null, val pollResponseSummary: PollResponseAggregatedSummary? = null,
val referencesAggregatedSummary: ReferencesAggregatedSummary? = null val referencesAggregatedSummary: ReferencesAggregatedSummary? = null,
val liveLocationShareAggregatedSummary: LiveLocationShareAggregatedSummary? = null,
) )

View File

@ -22,7 +22,6 @@ import org.matrix.android.sdk.api.session.events.model.Content
* of all events that are referencing the 'eventId' event via the RelationType.REFERENCE * of all events that are referencing the 'eventId' event via the RelationType.REFERENCE
*/ */
data class ReferencesAggregatedSummary( data class ReferencesAggregatedSummary(
val eventId: String,
val content: Content?, val content: Content?,
val sourceEvents: List<String>, val sourceEvents: List<String>,
val localEchos: List<String> val localEchos: List<String>

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2022 The Matrix.org Foundation C.I.C. * Copyright (c) 2022 The Matrix.org Foundation C.I.C.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,18 +16,13 @@
package org.matrix.android.sdk.api.session.room.model.livelocation package org.matrix.android.sdk.api.session.room.model.livelocation
import com.squareup.moshi.Json import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconLocationDataContent
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true) /**
data class BeaconInfo( * Aggregation info concerning a live location share.
@Json(name = "description") val description: String? = null, */
/** data class LiveLocationShareAggregatedSummary(
* Beacon should be considered as inactive after this timeout as milliseconds. val isActive: Boolean?,
*/ val endOfLiveTimestampMillis: Long?,
@Json(name = "timeout") val timeout: Long? = null, val lastLocationDataContent: MessageBeaconLocationDataContent?,
/**
* Should be set true to start sharing beacon.
*/
@Json(name = "live") val isLive: Boolean? = null
) )

View File

@ -14,60 +14,59 @@
* limitations under the License. * limitations under the License.
*/ */
package org.matrix.android.sdk.api.session.room.model.livelocation package org.matrix.android.sdk.api.session.room.model.message
import com.squareup.moshi.Json import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass import com.squareup.moshi.JsonClass
import org.matrix.android.sdk.api.session.events.model.Content import org.matrix.android.sdk.api.session.events.model.Content
import org.matrix.android.sdk.api.session.room.model.message.LocationAsset
import org.matrix.android.sdk.api.session.room.model.message.LocationAssetType
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
import org.matrix.android.sdk.api.session.room.model.message.MessageLiveLocationContent
import org.matrix.android.sdk.api.session.room.model.message.MessageType
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
/**
* Content of the state event of type
* [EventType.STATE_ROOM_BEACON_INFO][org.matrix.android.sdk.api.session.events.model.EventType.STATE_ROOM_BEACON_INFO]
*
* It contains general info related to a live location share.
* Locations are sent in a different message related to the state event.
* See [MessageBeaconLocationDataContent][org.matrix.android.sdk.api.session.room.model.message.MessageBeaconLocationDataContent]
*/
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
data class LiveLocationBeaconContent( data class MessageBeaconInfoContent(
/** /**
* Local message type, not from server * Local message type, not from server
*/ */
@Transient @Transient
override val msgType: String = MessageType.MSGTYPE_LIVE_LOCATION_STATE, override val msgType: String = MessageType.MSGTYPE_BEACON_INFO,
@Json(name = "body") override val body: String = "", @Json(name = "body") override val body: String = "",
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null, @Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
@Json(name = "m.new_content") override val newContent: Content? = null, @Json(name = "m.new_content") override val newContent: Content? = null,
/** /**
* Indicates user's intent to share ephemeral location. * Optional description of the beacon.
*/ */
@Json(name = "org.matrix.msc3672.beacon_info") val unstableBeaconInfo: BeaconInfo? = null, @Json(name = "description") val description: String? = null,
@Json(name = "m.beacon_info") val beaconInfo: BeaconInfo? = null, /**
* Beacon should be considered as inactive after this timeout as milliseconds.
*/
@Json(name = "timeout") val timeout: Long? = null,
/**
* Should be set true to start sharing beacon.
*/
@Json(name = "live") val isLive: Boolean? = null,
/** /**
* Beacon creation timestamp. * Beacon creation timestamp.
*/ */
@Json(name = "org.matrix.msc3488.ts") val unstableTimestampAsMilliseconds: Long? = null, @Json(name = "org.matrix.msc3488.ts") val unstableTimestampMillis: Long? = null,
@Json(name = "m.ts") val timestampAsMilliseconds: Long? = null, @Json(name = "m.ts") val timestampMillis: Long? = null,
/** /**
* Live location asset type. * Live location asset type.
*/ */
@Json(name = "org.matrix.msc3488.asset") val unstableLocationAsset: LocationAsset = LocationAsset(LocationAssetType.SELF), @Json(name = "org.matrix.msc3488.asset") val unstableLocationAsset: LocationAsset = LocationAsset(LocationAssetType.SELF),
@Json(name = "m.asset") val locationAsset: LocationAsset? = null, @Json(name = "m.asset") val locationAsset: LocationAsset? = null,
/**
* Client side tracking of the last location
*/
var lastLocationContent: MessageLiveLocationContent? = null,
/**
* Client side tracking of whether the beacon has timed out.
*/
var hasTimedOut: Boolean = false
) : MessageContent { ) : MessageContent {
fun getBestBeaconInfo() = beaconInfo ?: unstableBeaconInfo fun getBestTimestampMillis() = timestampMillis ?: unstableTimestampMillis
fun getBestTimestampAsMilliseconds() = timestampAsMilliseconds ?: unstableTimestampAsMilliseconds
fun getBestLocationAsset() = locationAsset ?: unstableLocationAsset fun getBestLocationAsset() = locationAsset ?: unstableLocationAsset
} }

View File

@ -21,13 +21,21 @@ import com.squareup.moshi.JsonClass
import org.matrix.android.sdk.api.session.events.model.Content import org.matrix.android.sdk.api.session.events.model.Content
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
/**
* Content of the state event of type
* [EventType.BEACON_LOCATION_DATA][org.matrix.android.sdk.api.session.events.model.EventType.BEACON_LOCATION_DATA]
*
* It contains location data related to a live location share.
* It is related to the state event that originally started the live.
* See [MessageBeaconInfoContent][org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent]
*/
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
data class MessageLiveLocationContent( data class MessageBeaconLocationDataContent(
/** /**
* Local message type, not from server * Local message type, not from server
*/ */
@Transient @Transient
override val msgType: String = MessageType.MSGTYPE_LIVE_LOCATION, override val msgType: String = MessageType.MSGTYPE_BEACON_LOCATION_DATA,
@Json(name = "body") override val body: String = "", @Json(name = "body") override val body: String = "",
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null, @Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
@ -42,11 +50,11 @@ data class MessageLiveLocationContent(
/** /**
* Exact time that the data in the event refers to (milliseconds since the UNIX epoch) * Exact time that the data in the event refers to (milliseconds since the UNIX epoch)
*/ */
@Json(name = "org.matrix.msc3488.ts") val unstableTimestampAsMilliseconds: Long? = null, @Json(name = "org.matrix.msc3488.ts") val unstableTimestampMillis: Long? = null,
@Json(name = "m.ts") val timestampAsMilliseconds: Long? = null @Json(name = "m.ts") val timestampMillis: Long? = null
) : MessageContent { ) : MessageContent {
fun getBestLocationInfo() = locationInfo ?: unstableLocationInfo fun getBestLocationInfo() = locationInfo ?: unstableLocationInfo
fun getBestTimestampAsMilliseconds() = timestampAsMilliseconds ?: unstableTimestampAsMilliseconds fun getBestTimestampMillis() = timestampMillis ?: unstableTimestampMillis
} }

View File

@ -49,8 +49,8 @@ data class MessageLocationContent(
/** /**
* Exact time that the data in the event refers to (milliseconds since the UNIX epoch) * Exact time that the data in the event refers to (milliseconds since the UNIX epoch)
*/ */
@Json(name = "org.matrix.msc3488.ts") val unstableTs: Long? = null, @Json(name = "org.matrix.msc3488.ts") val unstableTimestampMillis: Long? = null,
@Json(name = "m.ts") val ts: Long? = null, @Json(name = "m.ts") val timestampMillis: Long? = null,
@Json(name = "org.matrix.msc1767.text") val unstableText: String? = null, @Json(name = "org.matrix.msc1767.text") val unstableText: String? = null,
@Json(name = "m.text") val text: String? = null, @Json(name = "m.text") val text: String? = null,
/** /**
@ -66,7 +66,7 @@ data class MessageLocationContent(
fun getBestLocationInfo() = locationInfo ?: unstableLocationInfo fun getBestLocationInfo() = locationInfo ?: unstableLocationInfo
fun getBestTs() = ts ?: unstableTs fun getBestTimestampMillis() = timestampMillis ?: unstableTimestampMillis
fun getBestText() = text ?: unstableText fun getBestText() = text ?: unstableText

View File

@ -41,6 +41,6 @@ object MessageType {
const val MSGTYPE_SNOWFALL = "io.element.effect.snowfall" const val MSGTYPE_SNOWFALL = "io.element.effect.snowfall"
// Fake message types for live location events to be able to inherit them from MessageContent // Fake message types for live location events to be able to inherit them from MessageContent
const val MSGTYPE_LIVE_LOCATION_STATE = "org.matrix.android.sdk.livelocation.state" const val MSGTYPE_BEACON_INFO = "org.matrix.android.sdk.beacon.info"
const val MSGTYPE_LIVE_LOCATION = "org.matrix.android.sdk.livelocation" const val MSGTYPE_BEACON_LOCATION_DATA = "org.matrix.android.sdk.beacon.location.data"
} }

View File

@ -84,8 +84,9 @@ interface StateService {
* @param eventType The type of event to send. * @param eventType The type of event to send.
* @param stateKey The state_key for the state to send. Can be an empty string. * @param stateKey The state_key for the state to send. Can be an empty string.
* @param body The content object of the event; the fields in this object will vary depending on the type of event * @param body The content object of the event; the fields in this object will vary depending on the type of event
* @return the id of the created state event
*/ */
suspend fun sendStateEvent(eventType: String, stateKey: String, body: JsonDict) suspend fun sendStateEvent(eventType: String, stateKey: String, body: JsonDict): String
/** /**
* Get a state event of the room * Get a state event of the room

View File

@ -29,7 +29,7 @@ import org.matrix.android.sdk.api.session.events.model.isSticker
import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.model.EventAnnotationsSummary import org.matrix.android.sdk.api.session.room.model.EventAnnotationsSummary
import org.matrix.android.sdk.api.session.room.model.ReadReceipt import org.matrix.android.sdk.api.session.room.model.ReadReceipt
import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationBeaconContent import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent
import org.matrix.android.sdk.api.session.room.model.message.MessageContent import org.matrix.android.sdk.api.session.room.model.message.MessageContent
import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent
import org.matrix.android.sdk.api.session.room.model.message.MessageStickerContent import org.matrix.android.sdk.api.session.room.model.message.MessageStickerContent
@ -139,7 +139,7 @@ fun TimelineEvent.getLastMessageContent(): MessageContent? {
return when (root.getClearType()) { return when (root.getClearType()) {
EventType.STICKER -> root.getClearContent().toModel<MessageStickerContent>() EventType.STICKER -> root.getClearContent().toModel<MessageStickerContent>()
in EventType.POLL_START -> (annotations?.editSummary?.latestContent ?: root.getClearContent()).toModel<MessagePollContent>() in EventType.POLL_START -> (annotations?.editSummary?.latestContent ?: root.getClearContent()).toModel<MessagePollContent>()
in EventType.STATE_ROOM_BEACON_INFO -> (annotations?.editSummary?.latestContent ?: root.getClearContent()).toModel<LiveLocationBeaconContent>() in EventType.STATE_ROOM_BEACON_INFO -> (annotations?.editSummary?.latestContent ?: root.getClearContent()).toModel<MessageBeaconInfoContent>()
else -> (annotations?.editSummary?.latestContent ?: root.getClearContent()).toModel() else -> (annotations?.editSummary?.latestContent ?: root.getClearContent()).toModel()
} }
} }

View File

@ -106,5 +106,8 @@ interface SpaceService {
suspend fun removeSpaceParent(childRoomId: String, parentSpaceId: String) suspend fun removeSpaceParent(childRoomId: String, parentSpaceId: String)
fun getRootSpaceSummaries(): List<RoomSummary> /**
* Get the root spaces, i.e. all the spaces which do not have a parent space.
*/
suspend fun getRootSpaceSummaries(): List<RoomSummary>
} }

View File

@ -0,0 +1,23 @@
/*
* Copyright 2022 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.api.session.uia
enum class UiaResult {
SUCCESS,
FAILURE,
CANCELLED
}

View File

@ -0,0 +1,19 @@
/*
* Copyright 2022 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.api.session.uia.exceptions
class UiaCancelledException(message: String? = null) : Exception(message)

View File

@ -20,6 +20,8 @@ import org.matrix.android.sdk.api.auth.UIABaseAuth
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.failure.toRegistrationFlowResponse import org.matrix.android.sdk.api.failure.toRegistrationFlowResponse
import org.matrix.android.sdk.api.session.uia.UiaResult
import org.matrix.android.sdk.api.session.uia.exceptions.UiaCancelledException
import timber.log.Timber import timber.log.Timber
import kotlin.coroutines.suspendCoroutine import kotlin.coroutines.suspendCoroutine
@ -30,14 +32,15 @@ import kotlin.coroutines.suspendCoroutine
* @param interceptor see doc in [UserInteractiveAuthInterceptor] * @param interceptor see doc in [UserInteractiveAuthInterceptor]
* @param retryBlock called at the end of the process, in this block generally retry executing the task, with * @param retryBlock called at the end of the process, in this block generally retry executing the task, with
* provided authUpdate * provided authUpdate
* @return true if UIA is handled without error * @return UiaResult if UIA handled, failed or cancelled
*
*/ */
internal suspend fun handleUIA(failure: Throwable, internal suspend fun handleUIA(failure: Throwable,
interceptor: UserInteractiveAuthInterceptor, interceptor: UserInteractiveAuthInterceptor,
retryBlock: suspend (UIABaseAuth) -> Unit): Boolean { retryBlock: suspend (UIABaseAuth) -> Unit): UiaResult {
Timber.d("## UIA: check error ${failure.message}") Timber.d("## UIA: check error ${failure.message}")
val flowResponse = failure.toRegistrationFlowResponse() val flowResponse = failure.toRegistrationFlowResponse()
?: return false.also { ?: return UiaResult.FAILURE.also {
Timber.d("## UIA: not a UIA error") Timber.d("## UIA: not a UIA error")
} }
@ -50,14 +53,19 @@ internal suspend fun handleUIA(failure: Throwable,
interceptor.performStage(flowResponse, (failure as? Failure.ServerError)?.error?.code, continuation) interceptor.performStage(flowResponse, (failure as? Failure.ServerError)?.error?.code, continuation)
} }
} catch (failure2: Throwable) { } catch (failure2: Throwable) {
Timber.w(failure2, "## UIA: failed to participate") return if (failure2 is UiaCancelledException) {
return false Timber.w(failure2, "## UIA: cancelled")
UiaResult.CANCELLED
} else {
Timber.w(failure2, "## UIA: failed to participate")
UiaResult.FAILURE
}
} }
Timber.d("## UIA: updated auth") Timber.d("## UIA: updated auth")
return try { return try {
retryBlock(authUpdate) retryBlock(authUpdate)
true UiaResult.SUCCESS
} catch (failure3: Throwable) { } catch (failure3: Throwable) {
handleUIA(failure3, interceptor, retryBlock) handleUIA(failure3, interceptor, retryBlock)
} }

View File

@ -66,7 +66,8 @@ internal class OlmSessionStore @Inject constructor(private val store: IMXCryptoS
olmSessions.getOrPut(deviceKey) { mutableListOf() }.forEach { cached -> olmSessions.getOrPut(deviceKey) { mutableListOf() }.forEach { cached ->
getSafeSessionIdentifier(cached.olmSession)?.let { cachedSessionId -> getSafeSessionIdentifier(cached.olmSession)?.let { cachedSessionId ->
if (!persistedKnownSessions.contains(cachedSessionId)) { if (!persistedKnownSessions.contains(cachedSessionId)) {
persistedKnownSessions.add(cachedSessionId) // as it's in cache put in on top
persistedKnownSessions.add(0, cachedSessionId)
} }
} }
} }

View File

@ -66,7 +66,7 @@ internal class DefaultSharedSecretStorageService @Inject constructor(
key: SsssKeySpec?, key: SsssKeySpec?,
keyName: String, keyName: String,
keySigner: KeySigner?): SsssKeyCreationInfo { keySigner: KeySigner?): SsssKeyCreationInfo {
return withContext(cryptoCoroutineScope.coroutineContext + coroutineDispatchers.main) { return withContext(cryptoCoroutineScope.coroutineContext + coroutineDispatchers.computation) {
val bytes = (key as? RawBytesKeySpec)?.privateKey val bytes = (key as? RawBytesKeySpec)?.privateKey
?: ByteArray(32).also { ?: ByteArray(32).also {
SecureRandom().nextBytes(it) SecureRandom().nextBytes(it)
@ -99,7 +99,7 @@ internal class DefaultSharedSecretStorageService @Inject constructor(
passphrase: String, passphrase: String,
keySigner: KeySigner, keySigner: KeySigner,
progressListener: ProgressListener?): SsssKeyCreationInfo { progressListener: ProgressListener?): SsssKeyCreationInfo {
return withContext(cryptoCoroutineScope.coroutineContext + coroutineDispatchers.main) { return withContext(cryptoCoroutineScope.coroutineContext + coroutineDispatchers.computation) {
val privatePart = generatePrivateKeyWithPassword(passphrase, progressListener) val privatePart = generatePrivateKeyWithPassword(passphrase, progressListener)
val storageKeyContent = SecretStorageKeyContent( val storageKeyContent = SecretStorageKeyContent(
@ -158,7 +158,7 @@ internal class DefaultSharedSecretStorageService @Inject constructor(
} }
override suspend fun storeSecret(name: String, secretBase64: String, keys: List<SharedSecretStorageService.KeyRef>) { override suspend fun storeSecret(name: String, secretBase64: String, keys: List<SharedSecretStorageService.KeyRef>) {
withContext(cryptoCoroutineScope.coroutineContext + coroutineDispatchers.main) { withContext(cryptoCoroutineScope.coroutineContext + coroutineDispatchers.computation) {
val encryptedContents = HashMap<String, EncryptedSecretContent>() val encryptedContents = HashMap<String, EncryptedSecretContent>()
keys.forEach { keys.forEach {
val keyId = it.keyId val keyId = it.keyId
@ -316,7 +316,7 @@ internal class DefaultSharedSecretStorageService @Inject constructor(
val algorithm = key.keyInfo.content val algorithm = key.keyInfo.content
if (SSSS_ALGORITHM_CURVE25519_AES_SHA2 == algorithm.algorithm) { if (SSSS_ALGORITHM_CURVE25519_AES_SHA2 == algorithm.algorithm) {
val keySpec = secretKey as? RawBytesKeySpec ?: throw SharedSecretStorageError.BadKeyFormat val keySpec = secretKey as? RawBytesKeySpec ?: throw SharedSecretStorageError.BadKeyFormat
return withContext(cryptoCoroutineScope.coroutineContext + coroutineDispatchers.main) { return withContext(cryptoCoroutineScope.coroutineContext + coroutineDispatchers.computation) {
// decrypt from recovery key // decrypt from recovery key
withOlmDecryption { olmPkDecryption -> withOlmDecryption { olmPkDecryption ->
olmPkDecryption.setPrivateKey(keySpec.privateKey) olmPkDecryption.setPrivateKey(keySpec.privateKey)
@ -331,7 +331,7 @@ internal class DefaultSharedSecretStorageService @Inject constructor(
} }
} else if (SSSS_ALGORITHM_AES_HMAC_SHA2 == algorithm.algorithm) { } else if (SSSS_ALGORITHM_AES_HMAC_SHA2 == algorithm.algorithm) {
val keySpec = secretKey as? RawBytesKeySpec ?: throw SharedSecretStorageError.BadKeyFormat val keySpec = secretKey as? RawBytesKeySpec ?: throw SharedSecretStorageError.BadKeyFormat
return withContext(cryptoCoroutineScope.coroutineContext + coroutineDispatchers.main) { return withContext(cryptoCoroutineScope.coroutineContext + coroutineDispatchers.computation) {
decryptAesHmacSha2(keySpec, name, secretContent) decryptAesHmacSha2(keySpec, name, secretContent)
} }
} else { } else {

View File

@ -715,6 +715,7 @@ internal class RealmCryptoStore @Inject constructor(
return doWithRealm(realmConfiguration) { return doWithRealm(realmConfiguration) {
it.where<OlmSessionEntity>() it.where<OlmSessionEntity>()
.equalTo(OlmSessionEntityFields.DEVICE_KEY, deviceKey) .equalTo(OlmSessionEntityFields.DEVICE_KEY, deviceKey)
.sort(OlmSessionEntityFields.LAST_RECEIVED_MESSAGE_TS, Sort.DESCENDING)
.findAll() .findAll()
.mapNotNull { sessionEntity -> .mapNotNull { sessionEntity ->
sessionEntity.sessionId sessionEntity.sessionId

View File

@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.crypto.tasks
import org.matrix.android.sdk.api.auth.UIABaseAuth import org.matrix.android.sdk.api.auth.UIABaseAuth
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
import org.matrix.android.sdk.api.session.uia.UiaResult
import org.matrix.android.sdk.internal.auth.registration.handleUIA import org.matrix.android.sdk.internal.auth.registration.handleUIA
import org.matrix.android.sdk.internal.crypto.api.CryptoApi import org.matrix.android.sdk.internal.crypto.api.CryptoApi
import org.matrix.android.sdk.internal.crypto.model.rest.DeleteDeviceParams import org.matrix.android.sdk.internal.crypto.model.rest.DeleteDeviceParams
@ -47,13 +48,13 @@ internal class DefaultDeleteDeviceTask @Inject constructor(
} }
} catch (throwable: Throwable) { } catch (throwable: Throwable) {
if (params.userInteractiveAuthInterceptor == null || if (params.userInteractiveAuthInterceptor == null ||
!handleUIA( handleUIA(
failure = throwable, failure = throwable,
interceptor = params.userInteractiveAuthInterceptor, interceptor = params.userInteractiveAuthInterceptor,
retryBlock = { authUpdate -> retryBlock = { authUpdate ->
execute(params.copy(userAuthParam = authUpdate)) execute(params.copy(userAuthParam = authUpdate))
} }
) ) != UiaResult.SUCCESS
) { ) {
Timber.d("## UIA: propagate failure") Timber.d("## UIA: propagate failure")
throw throwable throw throwable

View File

@ -20,6 +20,7 @@ import dagger.Lazy
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
import org.matrix.android.sdk.api.session.crypto.crosssigning.CryptoCrossSigningKey import org.matrix.android.sdk.api.session.crypto.crosssigning.CryptoCrossSigningKey
import org.matrix.android.sdk.api.session.crypto.crosssigning.KeyUsage import org.matrix.android.sdk.api.session.crypto.crosssigning.KeyUsage
import org.matrix.android.sdk.api.session.uia.UiaResult
import org.matrix.android.sdk.api.util.toBase64NoPadding import org.matrix.android.sdk.api.util.toBase64NoPadding
import org.matrix.android.sdk.internal.auth.registration.handleUIA import org.matrix.android.sdk.internal.auth.registration.handleUIA
import org.matrix.android.sdk.internal.crypto.MXOlmDevice import org.matrix.android.sdk.internal.crypto.MXOlmDevice
@ -126,13 +127,13 @@ internal class DefaultInitializeCrossSigningTask @Inject constructor(
uploadSigningKeysTask.execute(uploadSigningKeysParams) uploadSigningKeysTask.execute(uploadSigningKeysParams)
} catch (failure: Throwable) { } catch (failure: Throwable) {
if (params.interactiveAuthInterceptor == null || if (params.interactiveAuthInterceptor == null ||
!handleUIA( handleUIA(
failure = failure, failure = failure,
interceptor = params.interactiveAuthInterceptor, interceptor = params.interactiveAuthInterceptor,
retryBlock = { authUpdate -> retryBlock = { authUpdate ->
uploadSigningKeysTask.execute(uploadSigningKeysParams.copy(userAuthParam = authUpdate)) uploadSigningKeysTask.execute(uploadSigningKeysParams.copy(userAuthParam = authUpdate))
} }
) ) != UiaResult.SUCCESS
) { ) {
Timber.d("## UIA: propagate failure") Timber.d("## UIA: propagate failure")
throw failure throw failure

View File

@ -19,6 +19,9 @@ package org.matrix.android.sdk.internal.database
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import io.realm.RealmConfiguration import io.realm.RealmConfiguration
import io.realm.RealmResults import io.realm.RealmResults
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.matrix.android.sdk.internal.database.mapper.asDomain import org.matrix.android.sdk.internal.database.mapper.asDomain
import org.matrix.android.sdk.internal.database.model.EventEntity import org.matrix.android.sdk.internal.database.model.EventEntity
@ -38,10 +41,22 @@ internal class EventInsertLiveObserver @Inject constructor(@SessionDatabase real
it.where(EventInsertEntity::class.java).equalTo(EventInsertEntityFields.CAN_BE_PROCESSED, true) it.where(EventInsertEntity::class.java).equalTo(EventInsertEntityFields.CAN_BE_PROCESSED, true)
} }
private val onResultsChangedFlow = MutableSharedFlow<RealmResults<EventInsertEntity>>()
init {
onResultsChangedFlow
.onEach { handleChange(it) }
.launchIn(observerScope)
}
override fun onChange(results: RealmResults<EventInsertEntity>) { override fun onChange(results: RealmResults<EventInsertEntity>) {
if (!results.isLoaded || results.isEmpty()) { if (!results.isLoaded || results.isEmpty()) {
return return
} }
observerScope.launch { onResultsChangedFlow.emit(results) }
}
private suspend fun handleChange(results: RealmResults<EventInsertEntity>) {
val idsToDeleteAfterProcess = ArrayList<String>() val idsToDeleteAfterProcess = ArrayList<String>()
val filteredEvents = ArrayList<EventInsertEntity>(results.size) val filteredEvents = ArrayList<EventInsertEntity>(results.size)
Timber.v("EventInsertEntity updated with ${results.size} results in db") Timber.v("EventInsertEntity updated with ${results.size} results in db")
@ -58,30 +73,29 @@ internal class EventInsertLiveObserver @Inject constructor(@SessionDatabase real
} }
idsToDeleteAfterProcess.add(it.eventId) idsToDeleteAfterProcess.add(it.eventId)
} }
observerScope.launch {
awaitTransaction(realmConfiguration) { realm -> awaitTransaction(realmConfiguration) { realm ->
Timber.v("##Transaction: There are ${filteredEvents.size} events to process ") Timber.v("##Transaction: There are ${filteredEvents.size} events to process ")
filteredEvents.forEach { eventInsert -> filteredEvents.forEach { eventInsert ->
val eventId = eventInsert.eventId val eventId = eventInsert.eventId
val event = EventEntity.where(realm, eventId).findFirst() val event = EventEntity.where(realm, eventId).findFirst()
if (event == null) { if (event == null) {
Timber.v("Event $eventId not found") Timber.v("Event $eventId not found")
return@forEach return@forEach
} }
val domainEvent = event.asDomain() val domainEvent = event.asDomain()
processors.filter { processors.filter {
it.shouldProcess(eventId, domainEvent.getClearType(), eventInsert.insertType) it.shouldProcess(eventId, domainEvent.getClearType(), eventInsert.insertType)
}.forEach { }.forEach {
it.process(realm, domainEvent) it.process(realm, domainEvent)
}
} }
realm.where(EventInsertEntity::class.java)
.`in`(EventInsertEntityFields.EVENT_ID, idsToDeleteAfterProcess.toTypedArray())
.findAll()
.deleteAllFromRealm()
} }
processors.forEach { it.onPostProcess() } realm.where(EventInsertEntity::class.java)
.`in`(EventInsertEntityFields.EVENT_ID, idsToDeleteAfterProcess.toTypedArray())
.findAll()
.deleteAllFromRealm()
} }
processors.forEach { it.onPostProcess() }
} }
private fun shouldProcess(eventInsertEntity: EventInsertEntity): Boolean { private fun shouldProcess(eventInsertEntity: EventInsertEntity): Boolean {

View File

@ -44,6 +44,7 @@ import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo023
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo024 import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo024
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo025 import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo025
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo026 import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo026
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo027
import org.matrix.android.sdk.internal.util.Normalizer import org.matrix.android.sdk.internal.util.Normalizer
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@ -58,7 +59,7 @@ internal class RealmSessionStoreMigration @Inject constructor(
override fun equals(other: Any?) = other is RealmSessionStoreMigration override fun equals(other: Any?) = other is RealmSessionStoreMigration
override fun hashCode() = 1000 override fun hashCode() = 1000
val schemaVersion = 26L val schemaVersion = 27L
override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) { override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) {
Timber.d("Migrating Realm Session from $oldVersion to $newVersion") Timber.d("Migrating Realm Session from $oldVersion to $newVersion")
@ -89,5 +90,6 @@ internal class RealmSessionStoreMigration @Inject constructor(
if (oldVersion < 24) MigrateSessionTo024(realm).perform() if (oldVersion < 24) MigrateSessionTo024(realm).perform()
if (oldVersion < 25) MigrateSessionTo025(realm).perform() if (oldVersion < 25) MigrateSessionTo025(realm).perform()
if (oldVersion < 26) MigrateSessionTo026(realm).perform() if (oldVersion < 26) MigrateSessionTo026(realm).perform()
if (oldVersion < 27) MigrateSessionTo027(realm).perform()
} }
} }

View File

@ -20,6 +20,7 @@ import io.realm.Realm
import io.realm.RealmQuery import io.realm.RealmQuery
import io.realm.Sort import io.realm.Sort
import io.realm.kotlin.createObject import io.realm.kotlin.createObject
import kotlinx.coroutines.runBlocking
import org.matrix.android.sdk.api.session.crypto.CryptoService import org.matrix.android.sdk.api.session.crypto.CryptoService
import org.matrix.android.sdk.api.session.crypto.MXCryptoError import org.matrix.android.sdk.api.session.crypto.MXCryptoError
import org.matrix.android.sdk.api.session.crypto.model.OlmDecryptionResult import org.matrix.android.sdk.api.session.crypto.model.OlmDecryptionResult
@ -127,7 +128,7 @@ private fun EventEntity.toTimelineEventEntity(roomMemberContentsByUser: HashMap<
return timelineEventEntity return timelineEventEntity
} }
internal suspend fun ThreadSummaryEntity.Companion.createOrUpdate( internal fun ThreadSummaryEntity.Companion.createOrUpdate(
threadSummaryType: ThreadSummaryUpdateType, threadSummaryType: ThreadSummaryUpdateType,
realm: Realm, realm: Realm,
roomId: String, roomId: String,
@ -153,10 +154,18 @@ internal suspend fun ThreadSummaryEntity.Companion.createOrUpdate(
} }
val rootThreadEventEntity = createEventEntity(roomId, rootThreadEvent, realm).also { val rootThreadEventEntity = createEventEntity(roomId, rootThreadEvent, realm).also {
decryptIfNeeded(cryptoService, it, roomId) try {
decryptIfNeeded(cryptoService, it, roomId)
} catch (e: InterruptedException) {
Timber.i("Decryption got interrupted")
}
} }
val latestThreadEventEntity = createLatestEventEntity(roomId, rootThreadEvent, roomMemberContentsByUser, realm)?.also { val latestThreadEventEntity = createLatestEventEntity(roomId, rootThreadEvent, roomMemberContentsByUser, realm)?.also {
decryptIfNeeded(cryptoService, it, roomId) try {
decryptIfNeeded(cryptoService, it, roomId)
} catch (e: InterruptedException) {
Timber.i("Decryption got interrupted")
}
} }
val isUserParticipating = rootThreadEvent.unsignedData.relations.latestThread.isUserParticipating == true || rootThreadEvent.senderId == userId val isUserParticipating = rootThreadEvent.unsignedData.relations.latestThread.isUserParticipating == true || rootThreadEvent.senderId == userId
roomMemberContentsByUser.addSenderState(realm, roomId, rootThreadEvent.senderId) roomMemberContentsByUser.addSenderState(realm, roomId, rootThreadEvent.senderId)
@ -204,14 +213,15 @@ internal suspend fun ThreadSummaryEntity.Companion.createOrUpdate(
} }
} }
private suspend fun decryptIfNeeded(cryptoService: CryptoService?, eventEntity: EventEntity, roomId: String) { private fun decryptIfNeeded(cryptoService: CryptoService?, eventEntity: EventEntity, roomId: String) {
cryptoService ?: return cryptoService ?: return
val event = eventEntity.asDomain() val event = eventEntity.asDomain()
if (event.isEncrypted() && event.mxDecryptionResult == null && event.eventId != null) { if (event.isEncrypted() && event.mxDecryptionResult == null && event.eventId != null) {
try { try {
Timber.i("###THREADS ThreadSummaryHelper request decryption for eventId:${event.eventId}") Timber.i("###THREADS ThreadSummaryHelper request decryption for eventId:${event.eventId}")
// Event from sync does not have roomId, so add it to the event first // Event from sync does not have roomId, so add it to the event first
val result = cryptoService.decryptEvent(event.copy(roomId = roomId), "") // note: runBlocking should be used here while we are in realm single thread executor, to avoid thread switching
val result = runBlocking { cryptoService.decryptEvent(event.copy(roomId = roomId), "") }
event.mxDecryptionResult = OlmDecryptionResult( event.mxDecryptionResult = OlmDecryptionResult(
payload = result.clearEvent, payload = result.clearEvent,
senderKey = result.senderCurve25519Key, senderKey = result.senderCurve25519Key,

Some files were not shown because too many files have changed in this diff Show More