mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-21 17:05:39 +03:00
Merge tag 'v1.5.12' into sc
Change-Id: I4c53d64845ee05ff395e5df436a0739c87798737 Conflicts: matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/AccountDataAPI.kt vector-app/build.gradle vector-app/src/gplay/java/im/vector/app/nightly/FirebaseNightlyProxy.kt vector-config/src/main/res/values/config-settings.xml vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerFragment.kt vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/DisplayableEventFormatter.kt vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItemFactory.kt vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt
This commit is contained in:
commit
330f0cf5cc
163 changed files with 2311 additions and 519 deletions
1
.gitattributes
vendored
1
.gitattributes
vendored
|
@ -1 +1,2 @@
|
|||
**/snapshots/**/*.png filter=lfs diff=lfs merge=lfs -text
|
||||
**/src/androidTest/assets/*.realm filter=lfs diff=lfs merge=lfs -text
|
||||
|
|
2
.github/workflows/danger.yml
vendored
2
.github/workflows/danger.yml
vendored
|
@ -13,7 +13,7 @@ jobs:
|
|||
- name: Danger
|
||||
uses: danger/danger-js@11.2.0
|
||||
with:
|
||||
args: "--dangerfile tools/danger/dangerfile.js"
|
||||
args: "--dangerfile ./tools/danger/dangerfile.js"
|
||||
env:
|
||||
DANGER_GITHUB_API_TOKEN: ${{ secrets.DANGER_GITHUB_API_TOKEN }}
|
||||
# Fallback for forks
|
||||
|
|
2
.github/workflows/quality.yml
vendored
2
.github/workflows/quality.yml
vendored
|
@ -68,7 +68,7 @@ jobs:
|
|||
if: always()
|
||||
uses: danger/danger-js@11.2.0
|
||||
with:
|
||||
args: "--dangerfile tools/danger/dangerfile-lint.js"
|
||||
args: "--dangerfile ./tools/danger/dangerfile-lint.js"
|
||||
env:
|
||||
DANGER_GITHUB_API_TOKEN: ${{ secrets.DANGER_GITHUB_API_TOKEN }}
|
||||
# Fallback for forks
|
||||
|
|
16
.github/workflows/triage-labelled.yml
vendored
16
.github/workflows/triage-labelled.yml
vendored
|
@ -89,7 +89,7 @@ jobs:
|
|||
projectid: ${{ env.PROJECT_ID }}
|
||||
contentid: ${{ github.event.issue.node_id }}
|
||||
env:
|
||||
PROJECT_ID: "PN_kwDOAM0swc0sUA"
|
||||
PROJECT_ID: "PVT_kwDOAM0swc0sUA"
|
||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
||||
add_product_issues:
|
||||
|
@ -113,7 +113,7 @@ jobs:
|
|||
projectid: ${{ env.PROJECT_ID }}
|
||||
contentid: ${{ github.event.issue.node_id }}
|
||||
env:
|
||||
PROJECT_ID: "PN_kwDOAM0swc4AAg6N"
|
||||
PROJECT_ID: "PVT_kwDOAM0swc4AAg6N"
|
||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
||||
delight_issues_to_board:
|
||||
|
@ -139,7 +139,7 @@ jobs:
|
|||
projectid: ${{ env.PROJECT_ID }}
|
||||
contentid: ${{ github.event.issue.node_id }}
|
||||
env:
|
||||
PROJECT_ID: "PN_kwDOAM0swc1HvQ"
|
||||
PROJECT_ID: "PVT_kwDOAM0swc1HvQ"
|
||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
||||
move_voice-message_issues:
|
||||
|
@ -164,7 +164,7 @@ jobs:
|
|||
projectid: ${{ env.PROJECT_ID }}
|
||||
contentid: ${{ github.event.issue.node_id }}
|
||||
env:
|
||||
PROJECT_ID: "PN_kwDOAM0swc2KCw"
|
||||
PROJECT_ID: "PVT_kwDOAM0swc2KCw"
|
||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
move_message_bubbles_issues:
|
||||
name: A-Message-Bubbles to Message bubbles board
|
||||
|
@ -188,7 +188,7 @@ jobs:
|
|||
projectid: ${{ env.PROJECT_ID }}
|
||||
contentid: ${{ github.event.issue.node_id }}
|
||||
env:
|
||||
PROJECT_ID: "PN_kwDOAM0swc3m-g"
|
||||
PROJECT_ID: "PVT_kwDOAM0swc3m-g"
|
||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
||||
move_ftue_issues:
|
||||
|
@ -213,7 +213,7 @@ jobs:
|
|||
projectid: ${{ env.PROJECT_ID }}
|
||||
contentid: ${{ github.event.issue.node_id }}
|
||||
env:
|
||||
PROJECT_ID: "PN_kwDOAM0swc4AAqVx"
|
||||
PROJECT_ID: "PVT_kwDOAM0swc4AAqVx"
|
||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
||||
move_WTF_issues:
|
||||
|
@ -238,7 +238,7 @@ jobs:
|
|||
projectid: ${{ env.PROJECT_ID }}
|
||||
contentid: ${{ github.event.issue.node_id }}
|
||||
env:
|
||||
PROJECT_ID: "PN_kwDOAM0swc4AArk0"
|
||||
PROJECT_ID: "PVT_kwDOAM0swc4AArk0"
|
||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
||||
move_element_x_issues:
|
||||
|
@ -268,7 +268,7 @@ jobs:
|
|||
projectid: ${{ env.PROJECT_ID }}
|
||||
contentid: ${{ github.event.issue.node_id }}
|
||||
env:
|
||||
PROJECT_ID: "PN_kwDOAM0swc4ABTXY"
|
||||
PROJECT_ID: "PVT_kwDOAM0swc4ABTXY"
|
||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
||||
ps_features1:
|
||||
|
|
|
@ -69,7 +69,7 @@ jobs:
|
|||
projectid: ${{ env.PROJECT_ID }}
|
||||
contentid: ${{ github.event.pull_request.node_id }}
|
||||
env:
|
||||
PROJECT_ID: "PN_kwDOAM0swc0sUA"
|
||||
PROJECT_ID: "PVT_kwDOAM0swc0sUA"
|
||||
TEAM: "design"
|
||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
||||
|
@ -138,6 +138,6 @@ jobs:
|
|||
projectid: ${{ env.PROJECT_ID }}
|
||||
contentid: ${{ github.event.pull_request.node_id }}
|
||||
env:
|
||||
PROJECT_ID: "PN_kwDOAM0swc4AAg6N"
|
||||
PROJECT_ID: "PVT_kwDOAM0swc4AAg6N"
|
||||
TEAM: "product"
|
||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
|
51
CHANGES.md
51
CHANGES.md
|
@ -1,3 +1,54 @@
|
|||
Changes in Element v1.5.12 (2022-12-15)
|
||||
=======================================
|
||||
|
||||
Features ✨
|
||||
----------
|
||||
- [Threads] - Threads Labs Flag is enabled by default and forced to be enabled for existing users, but sill can be disabled manually ([#5503](https://github.com/vector-im/element-android/issues/5503))
|
||||
- [Session manager] Add action to signout all the other session ([#7693](https://github.com/vector-im/element-android/issues/7693))
|
||||
- Remind unverified sessions with a banner once a week ([#7694](https://github.com/vector-im/element-android/issues/7694))
|
||||
- [Session manager] Add actions to rename and signout current session ([#7697](https://github.com/vector-im/element-android/issues/7697))
|
||||
- Voice Broadcast - Update last message in the room list ([#7719](https://github.com/vector-im/element-android/issues/7719))
|
||||
- Delete unused client information from account data ([#7754](https://github.com/vector-im/element-android/issues/7754))
|
||||
|
||||
Bugfixes 🐛
|
||||
----------
|
||||
- Fix bad pills color background. For light and dark theme the color is now 61708B (iso EleWeb) ([#7274](https://github.com/vector-im/element-android/issues/7274))
|
||||
- [Notifications] Fixed a bug when push notification was automatically dismissed while app is on background ([#7643](https://github.com/vector-im/element-android/issues/7643))
|
||||
- ANR when asking to select the notification method ([#7653](https://github.com/vector-im/element-android/issues/7653))
|
||||
- [Rich text editor] Fix design and spacing of rich text editor ([#7658](https://github.com/vector-im/element-android/issues/7658))
|
||||
- [Rich text editor] Fix keyboard closing after collapsing editor ([#7659](https://github.com/vector-im/element-android/issues/7659))
|
||||
- Rich Text Editor: fix several issues related to insets:
|
||||
* Empty space displayed at the bottom when you don't have permissions to send messages into a room.
|
||||
* Wrong insets being kept when you exit the room screen and the keyboard is displayed, then come back to it. ([#7680](https://github.com/vector-im/element-android/issues/7680))
|
||||
- Fix crash in message composer when room is missing ([#7683](https://github.com/vector-im/element-android/issues/7683))
|
||||
- Fix crash when invalid homeserver url is entered. ([#7684](https://github.com/vector-im/element-android/issues/7684))
|
||||
- Rich Text Editor: improve performance when entering reply/edit/quote mode. ([#7691](https://github.com/vector-im/element-android/issues/7691))
|
||||
- [Rich text editor] Add error tracking for rich text editor ([#7695](https://github.com/vector-im/element-android/issues/7695))
|
||||
- Fix E2EE set up failure whilst signing in using QR code ([#7699](https://github.com/vector-im/element-android/issues/7699))
|
||||
- Fix usage of unknown shield in room summary ([#7710](https://github.com/vector-im/element-android/issues/7710))
|
||||
- Fix crash when the network is not available. ([#7725](https://github.com/vector-im/element-android/issues/7725))
|
||||
- [Session manager] Sessions without encryption support should not prompt to verify ([#7733](https://github.com/vector-im/element-android/issues/7733))
|
||||
- Fix issue of Scan QR code button sometimes not showing when it should be available ([#7737](https://github.com/vector-im/element-android/issues/7737))
|
||||
- Verification request is not showing when verify session popup is displayed ([#7743](https://github.com/vector-im/element-android/issues/7743))
|
||||
- Fix crash when inviting by email. ([#7744](https://github.com/vector-im/element-android/issues/7744))
|
||||
- Revert usage of stable fields in live location sharing and polls ([#7751](https://github.com/vector-im/element-android/issues/7751))
|
||||
- [Poll] Poll end event is not recognized ([#7753](https://github.com/vector-im/element-android/issues/7753))
|
||||
- [Push Notifications] When push notification for threaded message is clicked, thread timeline will be opened instead of room's main timeline ([#7770](https://github.com/vector-im/element-android/issues/7770))
|
||||
|
||||
Other changes
|
||||
-------------
|
||||
- [Threads] - added API to fetch threads list from the server instead of building it locally from events ([#5819](https://github.com/vector-im/element-android/issues/5819))
|
||||
- Add Z-Labs label for rich text editor and migrate to new label naming. ([#7477](https://github.com/vector-im/element-android/issues/7477))
|
||||
- Crypto database migration tests ([#7645](https://github.com/vector-im/element-android/issues/7645))
|
||||
- Add tracing Id for to device messages ([#7708](https://github.com/vector-im/element-android/issues/7708))
|
||||
- Disable nightly popup and add an entry point in the advanced settings instead. ([#7723](https://github.com/vector-im/element-android/issues/7723))
|
||||
- Save m.local_notification_settings.<device-id> event in account_data ([#7596](https://github.com/vector-im/element-android/issues/7596))
|
||||
- Update notifications setting when m.local_notification_settings.<device-id> event changes for current device ([#7632](https://github.com/vector-im/element-android/issues/7632))
|
||||
|
||||
SDK API changes ⚠️
|
||||
------------------
|
||||
- Handle account data removal ([#7740](https://github.com/vector-im/element-android/issues/7740))
|
||||
|
||||
Changes in Element 1.5.11 (2022-12-07)
|
||||
======================================
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ buildscript {
|
|||
classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.5.0.2730'
|
||||
classpath 'com.google.android.gms:oss-licenses-plugin:0.10.5'
|
||||
classpath "com.likethesalad.android:stem-plugin:2.2.3"
|
||||
classpath 'org.owasp:dependency-check-gradle:7.3.0'
|
||||
classpath 'org.owasp:dependency-check-gradle:7.4.1'
|
||||
classpath "org.jetbrains.dokka:dokka-gradle-plugin:1.7.20"
|
||||
classpath "org.jetbrains.kotlinx:kotlinx-knit:0.4.0"
|
||||
classpath 'com.jakewharton:butterknife-gradle-plugin:10.2.3'
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
Fix bad pills color background. For light and dark theme the color is now 61708B (iso EleWeb)
|
|
@ -1 +0,0 @@
|
|||
Add Z-Labs label for rich text editor and migrate to new label naming.
|
|
@ -1 +0,0 @@
|
|||
Save m.local_notification_settings.<device-id> event in account_data
|
|
@ -1 +0,0 @@
|
|||
Update notifications setting when m.local_notification_settings.<device-id> event changes for current device
|
|
@ -1 +0,0 @@
|
|||
ANR when asking to select the notification method
|
|
@ -1 +0,0 @@
|
|||
[Rich text editor] Fix design and spacing of rich text editor
|
|
@ -1 +0,0 @@
|
|||
[Rich text editor] Fix keyboard closing after collapsing editor
|
|
@ -1,3 +0,0 @@
|
|||
Rich Text Editor: fix several issues related to insets:
|
||||
* Empty space displayed at the bottom when you don't have permissions to send messages into a room.
|
||||
* Wrong insets being kept when you exit the room screen and the keyboard is displayed, then come back to it.
|
|
@ -1,2 +0,0 @@
|
|||
Fix crash in message composer when room is missing
|
||||
|
|
@ -1 +0,0 @@
|
|||
Fix crash when invalid homeserver url is entered.
|
|
@ -1 +0,0 @@
|
|||
[Session manager] Add action to signout all the other session
|
|
@ -1 +0,0 @@
|
|||
Remind unverified sessions with a banner once a week
|
|
@ -1 +0,0 @@
|
|||
Fix usage of unknown shield in room summary
|
|
@ -1 +0,0 @@
|
|||
Fix crash when the network is not available.
|
|
@ -8,7 +8,7 @@ ext.versions = [
|
|||
|
||||
def gradle = "7.3.1"
|
||||
// Ref: https://kotlinlang.org/releases.html
|
||||
def kotlin = "1.7.21"
|
||||
def kotlin = "1.7.22"
|
||||
def kotlinCoroutines = "1.6.4"
|
||||
def dagger = "2.44.2"
|
||||
def appDistribution = "16.0.0-beta05"
|
||||
|
@ -27,7 +27,7 @@ def jjwt = "0.11.5"
|
|||
// the whole commit which set version 0.16.0-SNAPSHOT
|
||||
def vanniktechEmoji = "0.16.0-SNAPSHOT"
|
||||
def sentry = "6.9.0"
|
||||
def fragment = "1.5.4"
|
||||
def fragment = "1.5.5"
|
||||
// Testing
|
||||
def mockk = "1.12.3" // We need to use 1.12.3 to have mocking in androidTest until a new version is released: https://github.com/mockk/mockk/issues/819
|
||||
def espresso = "3.4.0"
|
||||
|
@ -99,7 +99,7 @@ ext.libs = [
|
|||
],
|
||||
element : [
|
||||
'opusencoder' : "io.element.android:opusencoder:1.1.0",
|
||||
'wysiwyg' : "io.element.android:wysiwyg:0.8.0"
|
||||
'wysiwyg' : "io.element.android:wysiwyg:0.9.0"
|
||||
],
|
||||
squareup : [
|
||||
'moshi' : "com.squareup.moshi:moshi:$moshi",
|
||||
|
|
55
docs/database_migration_test.md
Normal file
55
docs/database_migration_test.md
Normal file
|
@ -0,0 +1,55 @@
|
|||
<!--- TOC -->
|
||||
|
||||
* [Testing database migration](#testing-database-migration)
|
||||
* [Creating a reference database](#creating-a-reference-database)
|
||||
* [Testing](#testing)
|
||||
|
||||
<!--- END -->
|
||||
|
||||
## Testing database migration
|
||||
|
||||
### Creating a reference database
|
||||
|
||||
Databases are encrypted, the key to decrypt is needed to setup the test.
|
||||
A special build property must be enabled to extract it.
|
||||
|
||||
Set `vector.debugPrivateData=true` in `~/.gradle/gradle.properties` (to avoid committing by mistake)
|
||||
|
||||
Launch the app in your emulator, login and use the app to fill up the database.
|
||||
|
||||
Save the key for the tested database
|
||||
```
|
||||
RealmKeysUtils W Database key for alias `session_db_fe9f212a611ccf6dea1141777065ed0a`: 935a6dfa0b0fc5cce1414194ed190....
|
||||
RealmKeysUtils W Database key for alias `crypto_module_fe9f212a611ccf6dea1141777065ed0a`: 7b9a21a8a311e85d75b069a343.....
|
||||
```
|
||||
|
||||
|
||||
Use the [Device File Explorer](https://developer.android.com/studio/debug/device-file-explorer) to extrat the database file from the emulator.
|
||||
|
||||
Go to `data/data/im.vector.app.debug/files/<hash>/`
|
||||
Pick the database you want to test (name can be found in SessionRealmConfigurationFactory):
|
||||
- crypto_store.realm for crypto
|
||||
- disk_store.realm for session
|
||||
- etc...
|
||||
|
||||
Download the file on your disk
|
||||
|
||||
### Testing
|
||||
|
||||
Copy the file in `src/AndroidTest/assets`
|
||||
|
||||
see `CryptoSanityMigrationTest` or `RealmSessionStoreMigration43Test` for sample tests.
|
||||
|
||||
There are already some databases in the assets folder.
|
||||
The existing test will properly detect schema changes, and fail with such errors if a migration is missing:
|
||||
|
||||
```
|
||||
io.realm.exceptions.RealmMigrationNeededException: Migration is required due to the following errors:
|
||||
- Property 'CryptoMetadataEntity.foo' has been added.
|
||||
```
|
||||
|
||||
If you want to test properly more complex database migration (dynamic transforms) ensure that the database contains
|
||||
the entity you want to migrate.
|
||||
|
||||
You can explore the database with [realm studio](https://www.mongodb.com/docs/realm/studio/) if needed.
|
||||
|
2
fastlane/metadata/android/en-US/changelogs/40105120.txt
Normal file
2
fastlane/metadata/android/en-US/changelogs/40105120.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Main changes in this version: Thread are now enabled by default.
|
||||
Full changelog: https://github.com/vector-im/element-android/releases
|
|
@ -134,6 +134,9 @@
|
|||
<string name="notice_crypto_unable_to_decrypt">** Unable to decrypt: %s **</string>
|
||||
<string name="notice_crypto_error_unknown_inbound_session_id">The sender\'s device has not sent us the keys for this message.</string>
|
||||
|
||||
<string name="notice_voice_broadcast_ended">%1$s ended a voice broadcast.</string>
|
||||
<string name="notice_voice_broadcast_ended_by_you">You ended a voice broadcast.</string>
|
||||
|
||||
<!-- Messages -->
|
||||
|
||||
<!-- Room Screen -->
|
||||
|
@ -2487,6 +2490,9 @@
|
|||
<string name="settings_key_requests">Key Requests</string>
|
||||
<string name="settings_export_trail">Export Audit</string>
|
||||
|
||||
<string name="settings_nightly_build">Nightly build</string>
|
||||
<string name="settings_nightly_build_update">Get the latest build (note: you may have trouble to sign in)</string>
|
||||
|
||||
<string name="e2e_use_keybackup">Unlock encrypted messages history</string>
|
||||
|
||||
<string name="refresh">Refresh</string>
|
||||
|
@ -3029,7 +3035,7 @@
|
|||
|
||||
<string name="labs_auto_report_uisi">Auto Report Decryption Errors.</string>
|
||||
<string name="labs_auto_report_uisi_desc">Your system will automatically send logs when an unable to decrypt error occurs</string>
|
||||
<string name="labs_enable_thread_messages">Enable Thread Messages</string>
|
||||
<string name="labs_enable_thread_messages">Enable threaded messages</string>
|
||||
<string name="labs_enable_thread_messages_desc">Note: app will be restarted</string>
|
||||
<string name="settings_show_latest_profile">Show latest user info</string>
|
||||
<string name="settings_show_latest_profile_description">Show the latest profile info (avatar and display name) for all the messages.</string>
|
||||
|
@ -3098,6 +3104,7 @@
|
|||
<string name="audio_message_file_size">(%1$s)</string>
|
||||
|
||||
<string name="voice_broadcast_live">Live</string>
|
||||
<string name="voice_broadcast_live_broadcast">Live broadcast</string>
|
||||
<!-- TODO Rename id to voice_broadcast_buffering -->
|
||||
<string name="a11y_voice_broadcast_buffering">Buffering…</string>
|
||||
<string name="a11y_resume_voice_broadcast_record">Resume voice broadcast record</string>
|
||||
|
@ -3305,6 +3312,7 @@
|
|||
<string name="device_manager_verification_status_detail_current_session_unverified">Verify your current session for enhanced secure messaging.</string>
|
||||
<string name="device_manager_verification_status_detail_other_session_unverified">Verify or sign out from this session for best security and reliability.</string>
|
||||
<string name="device_manager_verification_status_detail_other_session_unknown">Verify your current session to reveal this session\'s verification status.</string>
|
||||
<string name="device_manager_verification_status_detail_session_encryption_not_supported">This session doesn\'t support encryption and thus can\'t be verified.</string>
|
||||
<string name="device_manager_verify_session">Verify Session</string>
|
||||
<string name="device_manager_view_details">View Details</string>
|
||||
<string name="device_manager_other_sessions_view_all">View All (%1$d)</string>
|
||||
|
@ -3397,6 +3405,7 @@
|
|||
<!-- TODO TO BE REMOVED -->
|
||||
<string name="device_manager_learn_more_sessions_verified" tools:ignore="UnusedResources">Verified sessions have logged in with your credentials and then been verified, either using your secure passphrase or by cross-verifying.\n\nThis means they hold encryption keys for your previous messages, and confirm to other users you are communicating with that these sessions are really you.</string>
|
||||
<string name="device_manager_learn_more_sessions_verified_description">Verified sessions are anywhere you are using this account after entering your passphrase or confirming your identity with another verified session.\n\nThis means that you have all the keys needed to unlock your encrypted messages and confirm to other users that you trust this session.</string>
|
||||
<string name="device_manager_learn_more_sessions_encryption_not_supported">This session doesn\'t support encryption, so it can\'t be verified.\n\nYou won\'t be able to participate in rooms where encryption is enabled when using this session.\n\nFor best security and privacy, it is recommended to use Matrix clients that support encryption.</string>
|
||||
<string name="device_manager_learn_more_session_rename_title">Renaming sessions</string>
|
||||
<string name="device_manager_learn_more_session_rename">Other users in direct messages and rooms that you join are able to view a full list of your sessions.\n\nThis provides them with confidence that they are really speaking to you, but it also means they can see the session name you enter here.</string>
|
||||
<string name="labs_enable_session_manager_title">Enable new session manager</string>
|
||||
|
|
|
@ -31,7 +31,6 @@ import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
|
|||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState
|
||||
import org.matrix.android.sdk.api.session.room.send.UserDraft
|
||||
import org.matrix.android.sdk.api.session.room.threads.model.ThreadSummary
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||
import org.matrix.android.sdk.api.util.Optional
|
||||
import org.matrix.android.sdk.api.util.toOptional
|
||||
|
@ -119,13 +118,6 @@ class FlowRoom(private val room: Room) {
|
|||
return room.roomPushRuleService().getLiveRoomNotificationState().asFlow()
|
||||
}
|
||||
|
||||
fun liveThreadSummaries(): Flow<List<ThreadSummary>> {
|
||||
return room.threadsService().getAllThreadSummariesLive().asFlow()
|
||||
.startWith(room.coroutineDispatchers.io) {
|
||||
room.threadsService().getAllThreadSummaries()
|
||||
}
|
||||
}
|
||||
|
||||
fun liveThreadList(): Flow<List<ThreadRootEvent>> {
|
||||
return room.threadsLocalService().getAllThreadsLive().asFlow()
|
||||
.startWith(room.coroutineDispatchers.io) {
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a7acd69f37612bab0a1ab7f456656712d7ba19dbb679f81b97b58ef44e239f42
|
||||
size 8523776
|
Binary file not shown.
|
@ -16,7 +16,7 @@
|
|||
|
||||
package org.matrix.android.sdk.common
|
||||
|
||||
import org.matrix.android.sdk.api.RoomDisplayNameFallbackProvider
|
||||
import org.matrix.android.sdk.api.provider.RoomDisplayNameFallbackProvider
|
||||
|
||||
class TestRoomDisplayNameFallbackProvider : RoomDisplayNameFallbackProvider {
|
||||
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* 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.internal.database
|
||||
|
||||
import android.content.Context
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import io.realm.Realm
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStoreMigration
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStoreModule
|
||||
import org.matrix.android.sdk.internal.util.time.Clock
|
||||
|
||||
class CryptoSanityMigrationTest {
|
||||
@get:Rule val configurationFactory = TestRealmConfigurationFactory()
|
||||
|
||||
lateinit var context: Context
|
||||
var realm: Realm? = null
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
context = InstrumentationRegistry.getInstrumentation().context
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
realm?.close()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun cryptoDatabaseShouldMigrateGracefully() {
|
||||
val realmName = "crypto_store_20.realm"
|
||||
val migration = RealmCryptoStoreMigration(object : Clock {
|
||||
override fun epochMillis(): Long {
|
||||
return 0L
|
||||
}
|
||||
})
|
||||
val realmConfiguration = configurationFactory.createConfiguration(
|
||||
realmName,
|
||||
"7b9a21a8a311e85d75b069a343c23fc952fc3fec5e0c83ecfa13f24b787479c487c3ed587db3dd1f5805d52041fc0ac246516e94b27ffa699ff928622e621aca",
|
||||
RealmCryptoStoreModule(),
|
||||
migration.schemaVersion,
|
||||
migration
|
||||
)
|
||||
configurationFactory.copyRealmFromAssets(context, realmName, realmName)
|
||||
|
||||
realm = Realm.getInstance(realmConfiguration)
|
||||
}
|
||||
}
|
|
@ -20,6 +20,9 @@ import okhttp3.ConnectionSpec
|
|||
import okhttp3.Interceptor
|
||||
import org.matrix.android.sdk.api.crypto.MXCryptoConfig
|
||||
import org.matrix.android.sdk.api.metrics.MetricPlugin
|
||||
import org.matrix.android.sdk.api.provider.CustomEventTypesProvider
|
||||
import org.matrix.android.sdk.api.provider.MatrixItemDisplayNameFallbackProvider
|
||||
import org.matrix.android.sdk.api.provider.RoomDisplayNameFallbackProvider
|
||||
import java.net.Proxy
|
||||
|
||||
data class MatrixConfiguration(
|
||||
|
@ -66,7 +69,7 @@ data class MatrixConfiguration(
|
|||
/**
|
||||
* Thread messages default enable/disabled value.
|
||||
*/
|
||||
val threadMessagesEnabledDefault: Boolean = false,
|
||||
val threadMessagesEnabledDefault: Boolean = true,
|
||||
/**
|
||||
* List of network interceptors, they will be added when building an OkHttp client.
|
||||
*/
|
||||
|
@ -75,9 +78,12 @@ data class MatrixConfiguration(
|
|||
* Sync configuration.
|
||||
*/
|
||||
val syncConfig: SyncConfig = SyncConfig(),
|
||||
|
||||
/**
|
||||
* Metrics plugin that can be used to capture metrics from matrix-sdk-android.
|
||||
*/
|
||||
val metricPlugins: List<MetricPlugin> = emptyList()
|
||||
val metricPlugins: List<MetricPlugin> = emptyList(),
|
||||
/**
|
||||
* CustomEventTypesProvider to provide custom event types to the sdk which should be processed with internal events.
|
||||
*/
|
||||
val customEventTypesProvider: CustomEventTypesProvider? = null,
|
||||
)
|
||||
|
|
|
@ -125,12 +125,6 @@ interface AuthenticationService {
|
|||
deviceId: String? = null
|
||||
): Session
|
||||
|
||||
/**
|
||||
* @param homeServerConnectionConfig the information about the homeserver and other configuration
|
||||
* Return true if qr code login is supported by the server, false otherwise.
|
||||
*/
|
||||
suspend fun isQrLoginSupported(homeServerConnectionConfig: HomeServerConnectionConfig): Boolean
|
||||
|
||||
/**
|
||||
* Authenticate using m.login.token method during sign in with QR code.
|
||||
* @param homeServerConnectionConfig the information about the homeserver and other configuration
|
||||
|
|
|
@ -22,5 +22,6 @@ data class LoginFlowResult(
|
|||
val isLoginAndRegistrationSupported: Boolean,
|
||||
val homeServerUrl: String,
|
||||
val isOutdatedHomeserver: Boolean,
|
||||
val isLogoutDevicesSupported: Boolean
|
||||
val isLogoutDevicesSupported: Boolean,
|
||||
val isLoginWithQrSupported: Boolean,
|
||||
)
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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.provider
|
||||
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
|
||||
/**
|
||||
* Provide custom event types which should be processed with the internal event types.
|
||||
*/
|
||||
interface CustomEventTypesProvider {
|
||||
|
||||
/**
|
||||
* Custom event types to include when computing [RoomSummary.latestPreviewableEvent].
|
||||
*/
|
||||
val customPreviewableEventTypes: List<String>
|
||||
}
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.api
|
||||
package org.matrix.android.sdk.api.provider
|
||||
|
||||
import org.matrix.android.sdk.api.util.MatrixItem
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.api
|
||||
package org.matrix.android.sdk.api.provider
|
||||
|
||||
/**
|
||||
* This interface exists to let the implementation provide localized room display name fallback.
|
|
@ -35,7 +35,10 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_S
|
|||
import org.matrix.android.sdk.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME
|
||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME
|
||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME
|
||||
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
|
||||
import org.matrix.android.sdk.api.session.crypto.model.MXUsersDevicesMap
|
||||
import org.matrix.android.sdk.api.util.MatrixJsonParser
|
||||
import org.matrix.android.sdk.api.util.awaitCallback
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
|
@ -147,6 +150,14 @@ class Rendezvous(
|
|||
val deviceKey = crypto.getMyDevice().fingerprint()
|
||||
send(Payload(PayloadType.PROGRESS, outcome = Outcome.SUCCESS, deviceId = deviceId, deviceKey = deviceKey))
|
||||
|
||||
try {
|
||||
// explicitly download keys for ourself rather than racing with initial sync which might not complete in time
|
||||
awaitCallback<MXUsersDevicesMap<CryptoDeviceInfo>> { crypto.downloadKeys(listOf(userId), false, it) }
|
||||
} catch (e: Throwable) {
|
||||
// log as warning and continue as initial sync might still complete
|
||||
Timber.tag(TAG).w(e, "Failed to download keys for self")
|
||||
}
|
||||
|
||||
// await confirmation of verification
|
||||
val verificationResponse = receive()
|
||||
if (verificationResponse?.outcome == Outcome.VERIFIED) {
|
||||
|
|
|
@ -63,4 +63,17 @@ interface SessionAccountDataService {
|
|||
* Update the account data with the provided type and the provided account data content.
|
||||
*/
|
||||
suspend fun updateUserAccountData(type: String, content: Content)
|
||||
|
||||
/**
|
||||
* Retrieve user account data list whose type starts with the given type.
|
||||
* @param type the type or the starting part of a type
|
||||
* @return list of account data whose type starts with the given type
|
||||
*/
|
||||
fun getUserAccountDataEventsStartWith(type: String): List<UserAccountDataEvent>
|
||||
|
||||
/**
|
||||
* Deletes user account data of the given type.
|
||||
* @param type the type to delete from user account data
|
||||
*/
|
||||
suspend fun deleteUserAccountData(type: String)
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package org.matrix.android.sdk.api.session.events.model
|
||||
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageType.MSGTYPE_VERIFICATION_REQUEST
|
||||
|
||||
/**
|
||||
* Constants defining known event types from Matrix specifications.
|
||||
*/
|
||||
|
@ -129,6 +131,7 @@ object EventType {
|
|||
|
||||
fun isVerificationEvent(type: String): Boolean {
|
||||
return when (type) {
|
||||
MSGTYPE_VERIFICATION_REQUEST,
|
||||
KEY_VERIFICATION_START,
|
||||
KEY_VERIFICATION_ACCEPT,
|
||||
KEY_VERIFICATION_KEY,
|
||||
|
|
|
@ -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.room.threads
|
||||
|
||||
sealed class FetchThreadsResult {
|
||||
data class ShouldFetchMore(val nextBatch: String) : FetchThreadsResult()
|
||||
object ReachedEnd : FetchThreadsResult()
|
||||
object Failed : FetchThreadsResult()
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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.room.threads
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = false)
|
||||
enum class ThreadFilter {
|
||||
@Json(name = "all") ALL,
|
||||
@Json(name = "participated") PARTICIPATED,
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) 2021 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.api.session.room.threads
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.paging.PagedList
|
||||
import org.matrix.android.sdk.api.session.room.ResultBoundaries
|
||||
import org.matrix.android.sdk.api.session.room.threads.model.ThreadSummary
|
||||
|
||||
data class ThreadLivePageResult(
|
||||
val livePagedList: LiveData<PagedList<ThreadSummary>>,
|
||||
val liveBoundaries: LiveData<ResultBoundaries>
|
||||
)
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
package org.matrix.android.sdk.api.session.room.threads
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.paging.PagedList
|
||||
import org.matrix.android.sdk.api.session.room.threads.model.ThreadSummary
|
||||
|
||||
/**
|
||||
|
@ -27,15 +27,14 @@ import org.matrix.android.sdk.api.session.room.threads.model.ThreadSummary
|
|||
*/
|
||||
interface ThreadsService {
|
||||
|
||||
/**
|
||||
* Returns a [LiveData] list of all the [ThreadSummary] that exists at the room level.
|
||||
*/
|
||||
fun getAllThreadSummariesLive(): LiveData<List<ThreadSummary>>
|
||||
suspend fun getPagedThreadsList(userParticipating: Boolean, pagedListConfig: PagedList.Config): ThreadLivePageResult
|
||||
|
||||
suspend fun fetchThreadList(nextBatchId: String?, limit: Int, filter: ThreadFilter = ThreadFilter.ALL): FetchThreadsResult
|
||||
|
||||
/**
|
||||
* Returns a list of all the [ThreadSummary] that exists at the room level.
|
||||
*/
|
||||
fun getAllThreadSummaries(): List<ThreadSummary>
|
||||
suspend fun getAllThreadSummaries(): List<ThreadSummary>
|
||||
|
||||
/**
|
||||
* Enhance the provided ThreadSummary[List] by adding the latest
|
||||
|
@ -51,9 +50,4 @@ interface ThreadsService {
|
|||
* @param limit defines the number of max results the api will respond with
|
||||
*/
|
||||
suspend fun fetchThreadTimeline(rootThreadEventId: String, from: String, limit: Int)
|
||||
|
||||
/**
|
||||
* Fetch all thread summaries for the current room using the enhanced /messages api.
|
||||
*/
|
||||
suspend fun fetchThreadSummaries()
|
||||
}
|
||||
|
|
|
@ -30,7 +30,6 @@ import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
|
|||
import org.matrix.android.sdk.api.auth.login.LoginWizard
|
||||
import org.matrix.android.sdk.api.auth.registration.RegistrationWizard
|
||||
import org.matrix.android.sdk.api.auth.wellknown.WellknownResult
|
||||
import org.matrix.android.sdk.api.extensions.orFalse
|
||||
import org.matrix.android.sdk.api.failure.Failure
|
||||
import org.matrix.android.sdk.api.failure.MatrixIdFailure
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
|
@ -299,7 +298,8 @@ internal class DefaultAuthenticationService @Inject constructor(
|
|||
isLoginAndRegistrationSupported = versions.isLoginAndRegistrationSupportedBySdk(),
|
||||
homeServerUrl = homeServerUrl,
|
||||
isOutdatedHomeserver = !versions.isSupportedBySdk(),
|
||||
isLogoutDevicesSupported = versions.doesServerSupportLogoutDevices()
|
||||
isLogoutDevicesSupported = versions.doesServerSupportLogoutDevices(),
|
||||
isLoginWithQrSupported = versions.doesServerSupportQrCodeLogin(),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -408,20 +408,6 @@ internal class DefaultAuthenticationService @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
override suspend fun isQrLoginSupported(homeServerConnectionConfig: HomeServerConnectionConfig): Boolean {
|
||||
val authAPI = buildAuthAPI(homeServerConnectionConfig)
|
||||
val versions = runCatching {
|
||||
executeRequest(null) {
|
||||
authAPI.versions()
|
||||
}
|
||||
}
|
||||
return if (versions.isSuccess) {
|
||||
versions.getOrNull()?.doesServerSupportQrCodeLogin().orFalse()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun loginUsingQrLoginToken(
|
||||
homeServerConnectionConfig: HomeServerConnectionConfig,
|
||||
loginToken: String,
|
||||
|
|
|
@ -17,14 +17,22 @@
|
|||
package org.matrix.android.sdk.internal.crypto.tasks
|
||||
|
||||
import org.matrix.android.sdk.api.session.crypto.model.MXUsersDevicesMap
|
||||
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.toContent
|
||||
import org.matrix.android.sdk.internal.crypto.api.CryptoApi
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.SendToDeviceBody
|
||||
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
|
||||
import org.matrix.android.sdk.internal.network.executeRequest
|
||||
import org.matrix.android.sdk.internal.task.Task
|
||||
import timber.log.Timber
|
||||
import java.util.UUID
|
||||
import javax.inject.Inject
|
||||
|
||||
const val TO_DEVICE_TRACING_ID_KEY = "org.matrix.msgid"
|
||||
|
||||
fun Event.toDeviceTracingId(): String? = content?.get(TO_DEVICE_TRACING_ID_KEY) as? String
|
||||
|
||||
internal interface SendToDeviceTask : Task<SendToDeviceTask.Params, Unit> {
|
||||
data class Params(
|
||||
// the type of event to send
|
||||
|
@ -32,7 +40,9 @@ internal interface SendToDeviceTask : Task<SendToDeviceTask.Params, Unit> {
|
|||
// the content to send. Map from user_id to device_id to content dictionary.
|
||||
val contentMap: MXUsersDevicesMap<Any>,
|
||||
// the transactionId. If not provided, a transactionId will be created by the task
|
||||
val transactionId: String? = null
|
||||
val transactionId: String? = null,
|
||||
// add tracing id, notice that to device events that do signature on content might be broken by it
|
||||
val addTracingIds: Boolean = !EventType.isVerificationEvent(eventType),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -42,15 +52,22 @@ internal class DefaultSendToDeviceTask @Inject constructor(
|
|||
) : SendToDeviceTask {
|
||||
|
||||
override suspend fun execute(params: SendToDeviceTask.Params) {
|
||||
val sendToDeviceBody = SendToDeviceBody(
|
||||
messages = params.contentMap.map
|
||||
)
|
||||
|
||||
// If params.transactionId is not provided, we create a unique txnId.
|
||||
// It's important to do that outside the requestBlock parameter of executeRequest()
|
||||
// to use the same value if the request is retried
|
||||
val txnId = params.transactionId ?: createUniqueTxnId()
|
||||
|
||||
// add id tracing to debug
|
||||
val decorated = if (params.addTracingIds) {
|
||||
decorateWithToDeviceTracingIds(params)
|
||||
} else {
|
||||
params.contentMap.map to emptyList()
|
||||
}
|
||||
|
||||
val sendToDeviceBody = SendToDeviceBody(
|
||||
messages = decorated.first
|
||||
)
|
||||
|
||||
return executeRequest(
|
||||
globalErrorReceiver,
|
||||
canRetry = true,
|
||||
|
@ -61,8 +78,35 @@ internal class DefaultSendToDeviceTask @Inject constructor(
|
|||
transactionId = txnId,
|
||||
body = sendToDeviceBody
|
||||
)
|
||||
Timber.i("Sent to device type=${params.eventType} txnid=$txnId [${decorated.second.joinToString(",")}]")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* To make it easier to track down where to-device messages are getting lost,
|
||||
* add a custom property to each one, and that will be logged after sent and on reception. Synapse will also log
|
||||
* this property.
|
||||
* @return A pair, first is the decorated content, and second info to log out after sending
|
||||
*/
|
||||
private fun decorateWithToDeviceTracingIds(params: SendToDeviceTask.Params): Pair<Map<String, Map<String, Any>>, List<String>> {
|
||||
val tracingInfo = mutableListOf<String>()
|
||||
val decoratedContent = params.contentMap.map.map { userToDeviceMap ->
|
||||
val userId = userToDeviceMap.key
|
||||
userId to userToDeviceMap.value.map {
|
||||
val deviceId = it.key
|
||||
deviceId to it.value.toContent().toMutableMap().apply {
|
||||
put(
|
||||
TO_DEVICE_TRACING_ID_KEY,
|
||||
UUID.randomUUID().toString().also {
|
||||
tracingInfo.add("$userId/$deviceId (msgid $it)")
|
||||
}
|
||||
)
|
||||
}
|
||||
}.toMap()
|
||||
}.toMap()
|
||||
|
||||
return decoratedContent to tracingInfo
|
||||
}
|
||||
}
|
||||
|
||||
internal fun createUniqueTxnId() = UUID.randomUUID().toString()
|
||||
|
|
|
@ -69,6 +69,7 @@ import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo042
|
|||
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo043
|
||||
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo044
|
||||
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo045
|
||||
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo046
|
||||
import org.matrix.android.sdk.internal.util.Normalizer
|
||||
import org.matrix.android.sdk.internal.util.database.MatrixRealmMigration
|
||||
import timber.log.Timber
|
||||
|
@ -92,7 +93,7 @@ internal class RealmSessionStoreMigration @Inject constructor(
|
|||
private val scSchemaVersion = 7L
|
||||
private val scSchemaVersionOffset = (1L shl 12)
|
||||
|
||||
val schemaVersion = 45L +
|
||||
val schemaVersion = 46L +
|
||||
scSchemaVersion * scSchemaVersionOffset
|
||||
}
|
||||
|
||||
|
@ -154,6 +155,7 @@ internal class RealmSessionStoreMigration @Inject constructor(
|
|||
if (oldVersion < 43) MigrateSessionTo043(realm).perform()
|
||||
if (oldVersion < 44) MigrateSessionTo044(realm).perform()
|
||||
if (oldVersion < 45) MigrateSessionTo045(realm).perform()
|
||||
if (oldVersion < 46) MigrateSessionTo046(realm).perform()
|
||||
|
||||
if (oldScVersion <= 0) MigrateScSessionTo001(realm).perform()
|
||||
if (oldScVersion <= 1) MigrateScSessionTo002(realm).perform()
|
||||
|
|
|
@ -37,9 +37,11 @@ import org.matrix.android.sdk.internal.database.model.EventEntity
|
|||
import org.matrix.android.sdk.internal.database.model.EventInsertType
|
||||
import org.matrix.android.sdk.internal.database.model.RoomEntity
|
||||
import org.matrix.android.sdk.internal.database.model.TimelineEventEntity
|
||||
import org.matrix.android.sdk.internal.database.model.threads.ThreadListPageEntity
|
||||
import org.matrix.android.sdk.internal.database.model.threads.ThreadSummaryEntity
|
||||
import org.matrix.android.sdk.internal.database.model.threads.ThreadSummaryEntityFields
|
||||
import org.matrix.android.sdk.internal.database.query.copyToRealmOrIgnore
|
||||
import org.matrix.android.sdk.internal.database.query.get
|
||||
import org.matrix.android.sdk.internal.database.query.getOrCreate
|
||||
import org.matrix.android.sdk.internal.database.query.getOrNull
|
||||
import org.matrix.android.sdk.internal.database.query.where
|
||||
|
@ -113,16 +115,16 @@ internal fun ThreadSummaryEntity.Companion.createOrUpdate(
|
|||
userId: String,
|
||||
cryptoService: CryptoService? = null,
|
||||
currentTimeMillis: Long,
|
||||
) {
|
||||
): ThreadSummaryEntity? {
|
||||
when (threadSummaryType) {
|
||||
ThreadSummaryUpdateType.REPLACE -> {
|
||||
rootThreadEvent?.eventId ?: return
|
||||
rootThreadEvent.senderId ?: return
|
||||
rootThreadEvent?.eventId ?: return null
|
||||
rootThreadEvent.senderId ?: return null
|
||||
|
||||
val numberOfThreads = rootThreadEvent.unsignedData?.relations?.latestThread?.count ?: return
|
||||
val numberOfThreads = rootThreadEvent.unsignedData?.relations?.latestThread?.count ?: return null
|
||||
|
||||
// Something is wrong with the server return
|
||||
if (numberOfThreads <= 0) return
|
||||
if (numberOfThreads <= 0) return null
|
||||
|
||||
val threadSummary = ThreadSummaryEntity.getOrCreate(realm, roomId, rootThreadEvent.eventId).also {
|
||||
Timber.i("###THREADS ThreadSummaryHelper REPLACE eventId:${it.rootThreadEventId} ")
|
||||
|
@ -153,12 +155,13 @@ internal fun ThreadSummaryEntity.Companion.createOrUpdate(
|
|||
)
|
||||
|
||||
roomEntity.addIfNecessary(threadSummary)
|
||||
return threadSummary
|
||||
}
|
||||
ThreadSummaryUpdateType.ADD -> {
|
||||
val rootThreadEventId = threadEventEntity?.rootThreadEventId ?: return
|
||||
val rootThreadEventId = threadEventEntity?.rootThreadEventId ?: return null
|
||||
Timber.i("###THREADS ThreadSummaryHelper ADD for root eventId:$rootThreadEventId")
|
||||
|
||||
val threadSummary = ThreadSummaryEntity.getOrNull(realm, roomId, rootThreadEventId)
|
||||
var threadSummary = ThreadSummaryEntity.getOrNull(realm, roomId, rootThreadEventId)
|
||||
if (threadSummary != null) {
|
||||
// ThreadSummary exists so lets add the latest event
|
||||
Timber.i("###THREADS ThreadSummaryHelper ADD root eventId:$rootThreadEventId exists, lets update latest thread event.")
|
||||
|
@ -172,7 +175,7 @@ internal fun ThreadSummaryEntity.Companion.createOrUpdate(
|
|||
Timber.i("###THREADS ThreadSummaryHelper ADD root eventId:$rootThreadEventId do not exists, lets try to create one")
|
||||
threadEventEntity.findRootThreadEvent()?.let { rootThreadEventEntity ->
|
||||
// Root thread event entity exists so lets create a new record
|
||||
ThreadSummaryEntity.getOrCreate(realm, roomId, rootThreadEventEntity.eventId).let {
|
||||
threadSummary = ThreadSummaryEntity.getOrCreate(realm, roomId, rootThreadEventEntity.eventId).also {
|
||||
it.updateThreadSummary(
|
||||
rootThreadEventEntity = rootThreadEventEntity,
|
||||
numberOfThreads = 1,
|
||||
|
@ -183,7 +186,12 @@ internal fun ThreadSummaryEntity.Companion.createOrUpdate(
|
|||
roomEntity.addIfNecessary(it)
|
||||
}
|
||||
}
|
||||
|
||||
threadSummary?.let {
|
||||
ThreadListPageEntity.get(realm, roomId)?.threadSummaries?.add(it)
|
||||
}
|
||||
}
|
||||
return threadSummary
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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.internal.database.migration
|
||||
|
||||
import io.realm.DynamicRealm
|
||||
import org.matrix.android.sdk.internal.database.model.threads.ThreadListPageEntityFields
|
||||
import org.matrix.android.sdk.internal.util.database.RealmMigrator
|
||||
|
||||
internal class MigrateSessionTo046(realm: DynamicRealm) : RealmMigrator(realm, 46) {
|
||||
|
||||
override fun doMigrate(realm: DynamicRealm) {
|
||||
realm.schema.create("ThreadListPageEntity")
|
||||
.addField(ThreadListPageEntityFields.ROOM_ID, String::class.java)
|
||||
.addPrimaryKey(ThreadListPageEntityFields.ROOM_ID)
|
||||
.setRequired(ThreadListPageEntityFields.ROOM_ID, true)
|
||||
.addRealmListField(ThreadListPageEntityFields.THREAD_SUMMARIES.`$`, realm.schema.get("ThreadSummaryEntity")!!)
|
||||
}
|
||||
}
|
|
@ -19,6 +19,7 @@ package org.matrix.android.sdk.internal.database.model
|
|||
import io.realm.annotations.RealmModule
|
||||
import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntity
|
||||
import org.matrix.android.sdk.internal.database.model.presence.UserPresenceEntity
|
||||
import org.matrix.android.sdk.internal.database.model.threads.ThreadListPageEntity
|
||||
import org.matrix.android.sdk.internal.database.model.threads.ThreadSummaryEntity
|
||||
|
||||
/**
|
||||
|
@ -72,6 +73,7 @@ import org.matrix.android.sdk.internal.database.model.threads.ThreadSummaryEntit
|
|||
UserPresenceEntity::class,
|
||||
ThreadSummaryEntity::class,
|
||||
SyncFilterParamsEntity::class,
|
||||
ThreadListPageEntity::class
|
||||
]
|
||||
)
|
||||
internal class SessionRealmModule
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* 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.internal.database.model.threads
|
||||
|
||||
import io.realm.RealmList
|
||||
import io.realm.RealmObject
|
||||
import io.realm.annotations.PrimaryKey
|
||||
|
||||
internal open class ThreadListPageEntity(
|
||||
@PrimaryKey var roomId: String = "",
|
||||
var threadSummaries: RealmList<ThreadSummaryEntity> = RealmList()
|
||||
) : RealmObject() {
|
||||
companion object
|
||||
}
|
|
@ -40,5 +40,8 @@ internal open class ThreadSummaryEntity(
|
|||
@LinkingObjects("threadSummaries")
|
||||
val room: RealmResults<RoomEntity>? = null
|
||||
|
||||
@LinkingObjects("threadSummaries")
|
||||
val page: RealmResults<ThreadListPageEntity>? = null
|
||||
|
||||
companion object
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import io.realm.RealmQuery
|
|||
import io.realm.kotlin.where
|
||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||
import org.matrix.android.sdk.internal.database.model.EventEntity
|
||||
import org.matrix.android.sdk.internal.database.model.RoomAccountDataEntityFields
|
||||
import org.matrix.android.sdk.internal.database.model.RoomEntity
|
||||
import org.matrix.android.sdk.internal.database.model.RoomEntityFields
|
||||
|
||||
|
@ -44,3 +45,11 @@ internal fun RoomEntity.Companion.where(realm: Realm, membership: Membership? =
|
|||
internal fun RoomEntity.fastContains(eventId: String): Boolean {
|
||||
return EventEntity.where(realm, eventId = eventId).findFirst() != null
|
||||
}
|
||||
|
||||
internal fun RoomEntity.removeAccountData(type: String) {
|
||||
accountData
|
||||
.where()
|
||||
.equalTo(RoomAccountDataEntityFields.TYPE, type)
|
||||
.findFirst()
|
||||
?.deleteFromRealm()
|
||||
}
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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.internal.database.query
|
||||
|
||||
import io.realm.Realm
|
||||
import io.realm.kotlin.createObject
|
||||
import io.realm.kotlin.where
|
||||
import org.matrix.android.sdk.internal.database.model.threads.ThreadListPageEntity
|
||||
import org.matrix.android.sdk.internal.database.model.threads.ThreadListPageEntityFields
|
||||
|
||||
internal fun ThreadListPageEntity.Companion.get(realm: Realm, roomId: String): ThreadListPageEntity? {
|
||||
return realm.where<ThreadListPageEntity>().equalTo(ThreadListPageEntityFields.ROOM_ID, roomId).findFirst()
|
||||
}
|
||||
|
||||
internal fun ThreadListPageEntity.Companion.getOrCreate(realm: Realm, roomId: String): ThreadListPageEntity {
|
||||
return get(realm, roomId) ?: realm.createObject(roomId)
|
||||
}
|
|
@ -22,6 +22,7 @@ import io.realm.RealmQuery
|
|||
import io.realm.RealmResults
|
||||
import io.realm.Sort
|
||||
import io.realm.kotlin.where
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.room.send.SendState
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEventFilters
|
||||
import org.matrix.android.sdk.internal.database.model.ChunkEntity
|
||||
|
@ -151,14 +152,27 @@ internal fun RealmQuery<TimelineEventEntity>.filterEvents(filters: TimelineEvent
|
|||
if (filters.filterTypes && filters.allowedTypes.isNotEmpty()) {
|
||||
beginGroup()
|
||||
filters.allowedTypes.forEachIndexed { index, filter ->
|
||||
if (filter.stateKey == null) {
|
||||
equalTo(TimelineEventEntityFields.ROOT.TYPE, filter.eventType)
|
||||
if (filter.eventType == EventType.ENCRYPTED) {
|
||||
val otherTypes = filters.allowedTypes.minus(filter).map { it.eventType }
|
||||
if (filter.stateKey == null) {
|
||||
filterEncryptedTypes(otherTypes)
|
||||
} else {
|
||||
beginGroup()
|
||||
filterEncryptedTypes(otherTypes)
|
||||
and()
|
||||
equalTo(TimelineEventEntityFields.ROOT.STATE_KEY, filter.stateKey)
|
||||
endGroup()
|
||||
}
|
||||
} else {
|
||||
beginGroup()
|
||||
equalTo(TimelineEventEntityFields.ROOT.TYPE, filter.eventType)
|
||||
and()
|
||||
equalTo(TimelineEventEntityFields.ROOT.STATE_KEY, filter.stateKey)
|
||||
endGroup()
|
||||
if (filter.stateKey == null) {
|
||||
equalTo(TimelineEventEntityFields.ROOT.TYPE, filter.eventType)
|
||||
} else {
|
||||
beginGroup()
|
||||
equalTo(TimelineEventEntityFields.ROOT.TYPE, filter.eventType)
|
||||
and()
|
||||
equalTo(TimelineEventEntityFields.ROOT.STATE_KEY, filter.stateKey)
|
||||
endGroup()
|
||||
}
|
||||
}
|
||||
if (index != filters.allowedTypes.size - 1) {
|
||||
or()
|
||||
|
@ -172,7 +186,6 @@ internal fun RealmQuery<TimelineEventEntity>.filterEvents(filters: TimelineEvent
|
|||
if (filters.filterEdits) {
|
||||
not().like(TimelineEventEntityFields.ROOT.CONTENT, TimelineEventFilter.Content.EDIT)
|
||||
not().like(TimelineEventEntityFields.ROOT.CONTENT, TimelineEventFilter.Content.RESPONSE)
|
||||
not().like(TimelineEventEntityFields.ROOT.CONTENT, TimelineEventFilter.Content.REFERENCE)
|
||||
}
|
||||
if (filters.filterRedacted) {
|
||||
not().like(TimelineEventEntityFields.ROOT.UNSIGNED_DATA, TimelineEventFilter.Unsigned.REDACTED)
|
||||
|
@ -181,6 +194,21 @@ internal fun RealmQuery<TimelineEventEntity>.filterEvents(filters: TimelineEvent
|
|||
return this
|
||||
}
|
||||
|
||||
internal fun RealmQuery<TimelineEventEntity>.filterEncryptedTypes(allowedTypes: List<String>): RealmQuery<TimelineEventEntity> {
|
||||
beginGroup()
|
||||
equalTo(TimelineEventEntityFields.ROOT.TYPE, EventType.ENCRYPTED)
|
||||
and()
|
||||
beginGroup()
|
||||
isNull(TimelineEventEntityFields.ROOT.DECRYPTION_RESULT_JSON)
|
||||
allowedTypes.forEach { eventType ->
|
||||
or()
|
||||
like(TimelineEventEntityFields.ROOT.DECRYPTION_RESULT_JSON, TimelineEventFilter.DecryptedContent.type(eventType))
|
||||
}
|
||||
endGroup()
|
||||
endGroup()
|
||||
return this
|
||||
}
|
||||
|
||||
internal fun RealmQuery<TimelineEventEntity>.filterTypes(filterTypes: List<String>): RealmQuery<TimelineEventEntity> {
|
||||
return if (filterTypes.isEmpty()) {
|
||||
this
|
||||
|
|
|
@ -34,6 +34,7 @@ internal object TimelineEventFilter {
|
|||
*/
|
||||
internal object DecryptedContent {
|
||||
internal const val URL = """{*"file":*"url":*}"""
|
||||
fun type(type: String) = """{*"type":*"$type"*}"""
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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.internal.database.query
|
||||
|
||||
import io.realm.Realm
|
||||
import io.realm.kotlin.where
|
||||
import org.matrix.android.sdk.internal.database.model.UserAccountDataEntity
|
||||
import org.matrix.android.sdk.internal.database.model.UserAccountDataEntityFields
|
||||
|
||||
/**
|
||||
* Delete an account_data event.
|
||||
*/
|
||||
internal fun UserAccountDataEntity.Companion.delete(realm: Realm, type: String) {
|
||||
realm
|
||||
.where<UserAccountDataEntity>()
|
||||
.equalTo(UserAccountDataEntityFields.TYPE, type)
|
||||
.findFirst()
|
||||
?.deleteFromRealm()
|
||||
}
|
|
@ -35,6 +35,7 @@ import org.matrix.android.sdk.internal.session.room.membership.joining.InviteBod
|
|||
import org.matrix.android.sdk.internal.session.room.membership.threepid.ThreePidInviteBody
|
||||
import org.matrix.android.sdk.internal.session.room.read.ReadBody
|
||||
import org.matrix.android.sdk.internal.session.room.relation.RelationsResponse
|
||||
import org.matrix.android.sdk.internal.session.room.relation.threads.ThreadSummariesResponse
|
||||
import org.matrix.android.sdk.internal.session.room.reporting.ReportContentBody
|
||||
import org.matrix.android.sdk.internal.session.room.send.SendResponse
|
||||
import org.matrix.android.sdk.internal.session.room.tags.TagBody
|
||||
|
@ -427,6 +428,19 @@ internal interface RoomAPI {
|
|||
@Body content: JsonDict
|
||||
)
|
||||
|
||||
/**
|
||||
* Remove an account_data event from the room.
|
||||
* @param userId the user id
|
||||
* @param roomId the room id
|
||||
* @param type the type
|
||||
*/
|
||||
@DELETE(NetworkConstants.URI_API_PREFIX_PATH_UNSTABLE + "org.matrix.msc3391/user/{userId}/rooms/{roomId}/account_data/{type}")
|
||||
suspend fun deleteRoomAccountData(
|
||||
@Path("userId") userId: String,
|
||||
@Path("roomId") roomId: String,
|
||||
@Path("type") type: String
|
||||
)
|
||||
|
||||
/**
|
||||
* Upgrades the given room to a particular room version.
|
||||
* Errors:
|
||||
|
@ -451,4 +465,12 @@ internal interface RoomAPI {
|
|||
@Path("roomIdOrAlias") roomidOrAlias: String,
|
||||
@Query("via") viaServers: List<String>?
|
||||
): RoomStrippedState
|
||||
|
||||
@GET(NetworkConstants.URI_API_PREFIX_PATH_V1 + "rooms/{roomId}/threads")
|
||||
suspend fun getThreadsList(
|
||||
@Path("roomId") roomId: String,
|
||||
@Query("include") include: String? = "all",
|
||||
@Query("from") from: String? = null,
|
||||
@Query("limit") limit: Int? = null
|
||||
): ThreadSummariesResponse
|
||||
}
|
||||
|
|
|
@ -29,7 +29,6 @@ import org.matrix.android.sdk.api.session.room.getTimelineEvent
|
|||
import org.matrix.android.sdk.api.session.room.model.PollSummaryContent
|
||||
import org.matrix.android.sdk.api.session.room.model.VoteInfo
|
||||
import org.matrix.android.sdk.api.session.room.model.VoteSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageEndPollContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessagePollResponseContent
|
||||
import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
|
||||
|
@ -78,7 +77,7 @@ class DefaultPollAggregationProcessor @Inject constructor() : PollAggregationPro
|
|||
val content = event.getClearContent()?.toModel<MessagePollResponseContent>() ?: return false
|
||||
val roomId = event.roomId ?: return false
|
||||
val senderId = event.senderId ?: return false
|
||||
val targetEventId = (event.getRelationContent() ?: content.relatesTo)?.eventId ?: return false
|
||||
val targetEventId = event.getRelationContent()?.eventId ?: return false
|
||||
val targetPollContent = getPollContent(session, roomId, targetEventId) ?: return false
|
||||
|
||||
val annotationsSummaryEntity = getAnnotationsSummaryEntity(realm, roomId, targetEventId)
|
||||
|
@ -154,9 +153,8 @@ class DefaultPollAggregationProcessor @Inject constructor() : PollAggregationPro
|
|||
}
|
||||
|
||||
override fun handlePollEndEvent(session: Session, powerLevelsHelper: PowerLevelsHelper, realm: Realm, event: Event): Boolean {
|
||||
val content = event.getClearContent()?.toModel<MessageEndPollContent>() ?: return false
|
||||
val roomId = event.roomId ?: return false
|
||||
val pollEventId = content.relatesTo?.eventId ?: return false
|
||||
val pollEventId = event.getRelationContent()?.eventId ?: return false
|
||||
val pollOwnerId = getPollEvent(session, roomId, pollEventId)?.root?.senderId
|
||||
val isPollOwner = pollOwnerId == event.senderId
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ internal class DefaultStartLiveLocationShareTask @Inject constructor(
|
|||
isLive = true,
|
||||
unstableTimestampMillis = clock.epochMillis()
|
||||
).toContent()
|
||||
val eventType = EventType.STATE_ROOM_BEACON_INFO.stable
|
||||
val eventType = EventType.STATE_ROOM_BEACON_INFO.unstable
|
||||
val sendStateTaskParams = SendStateTask.Params(
|
||||
roomId = params.roomId,
|
||||
stateKey = userId,
|
||||
|
|
|
@ -45,7 +45,7 @@ internal class DefaultStopLiveLocationShareTask @Inject constructor(
|
|||
val sendStateTaskParams = SendStateTask.Params(
|
||||
roomId = params.roomId,
|
||||
stateKey = stateKey,
|
||||
eventType = EventType.STATE_ROOM_BEACON_INFO.stable,
|
||||
eventType = EventType.STATE_ROOM_BEACON_INFO.unstable,
|
||||
body = updatedContent
|
||||
)
|
||||
return try {
|
||||
|
|
|
@ -16,37 +16,38 @@
|
|||
package org.matrix.android.sdk.internal.session.room.relation.threads
|
||||
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import io.realm.RealmList
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomMemberContent
|
||||
import org.matrix.android.sdk.api.session.room.threads.FetchThreadsResult
|
||||
import org.matrix.android.sdk.api.session.room.threads.ThreadFilter
|
||||
import org.matrix.android.sdk.api.session.room.threads.model.ThreadSummaryUpdateType
|
||||
import org.matrix.android.sdk.internal.crypto.DefaultCryptoService
|
||||
import org.matrix.android.sdk.internal.database.helper.createOrUpdate
|
||||
import org.matrix.android.sdk.internal.database.model.RoomEntity
|
||||
import org.matrix.android.sdk.internal.database.model.threads.ThreadListPageEntity
|
||||
import org.matrix.android.sdk.internal.database.model.threads.ThreadSummaryEntity
|
||||
import org.matrix.android.sdk.internal.database.query.getOrCreate
|
||||
import org.matrix.android.sdk.internal.database.query.where
|
||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||
import org.matrix.android.sdk.internal.di.UserId
|
||||
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
|
||||
import org.matrix.android.sdk.internal.network.executeRequest
|
||||
import org.matrix.android.sdk.internal.session.filter.FilterFactory
|
||||
import org.matrix.android.sdk.internal.session.room.RoomAPI
|
||||
import org.matrix.android.sdk.internal.session.room.timeline.PaginationDirection
|
||||
import org.matrix.android.sdk.internal.session.room.timeline.PaginationResponse
|
||||
import org.matrix.android.sdk.internal.task.Task
|
||||
import org.matrix.android.sdk.internal.util.awaitTransaction
|
||||
import org.matrix.android.sdk.internal.util.time.Clock
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
/***
|
||||
* This class is responsible to Fetch all the thread in the current room,
|
||||
* To fetch all threads in a room, the /messages API is used with newly added filtering options.
|
||||
*/
|
||||
internal interface FetchThreadSummariesTask : Task<FetchThreadSummariesTask.Params, DefaultFetchThreadSummariesTask.Result> {
|
||||
internal interface FetchThreadSummariesTask : Task<FetchThreadSummariesTask.Params, FetchThreadsResult> {
|
||||
data class Params(
|
||||
val roomId: String,
|
||||
val from: String = "",
|
||||
val limit: Int = 500,
|
||||
val isUserParticipating: Boolean = true
|
||||
val from: String? = null,
|
||||
val limit: Int = 5,
|
||||
val filter: ThreadFilter? = null,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -59,39 +60,43 @@ internal class DefaultFetchThreadSummariesTask @Inject constructor(
|
|||
private val clock: Clock,
|
||||
) : FetchThreadSummariesTask {
|
||||
|
||||
override suspend fun execute(params: FetchThreadSummariesTask.Params): Result {
|
||||
val filter = FilterFactory.createThreadsFilter(
|
||||
numberOfEvents = params.limit,
|
||||
userId = if (params.isUserParticipating) userId else null
|
||||
).toJSONString()
|
||||
|
||||
val response = executeRequest(
|
||||
globalErrorReceiver,
|
||||
canRetry = true
|
||||
) {
|
||||
roomAPI.getRoomMessagesFrom(params.roomId, params.from, PaginationDirection.BACKWARDS.value, params.limit, filter)
|
||||
override suspend fun execute(params: FetchThreadSummariesTask.Params): FetchThreadsResult {
|
||||
val response = executeRequest(globalErrorReceiver) {
|
||||
roomAPI.getThreadsList(
|
||||
roomId = params.roomId,
|
||||
include = params.filter?.toString()?.lowercase(),
|
||||
from = params.from,
|
||||
limit = params.limit
|
||||
)
|
||||
}
|
||||
|
||||
Timber.i("###THREADS DefaultFetchThreadSummariesTask Fetched size:${response.events.size} nextBatch:${response.end} ")
|
||||
handleResponse(response, params)
|
||||
|
||||
return handleResponse(response, params)
|
||||
return when {
|
||||
response.nextBatch != null -> FetchThreadsResult.ShouldFetchMore(response.nextBatch)
|
||||
else -> FetchThreadsResult.ReachedEnd
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun handleResponse(
|
||||
response: PaginationResponse,
|
||||
response: ThreadSummariesResponse,
|
||||
params: FetchThreadSummariesTask.Params
|
||||
): Result {
|
||||
val rootThreadList = response.events
|
||||
) {
|
||||
val rootThreadList = response.chunk
|
||||
|
||||
val threadSummaries = RealmList<ThreadSummaryEntity>()
|
||||
|
||||
monarchy.awaitTransaction { realm ->
|
||||
val roomEntity = RoomEntity.where(realm, roomId = params.roomId).findFirst() ?: return@awaitTransaction
|
||||
|
||||
val roomMemberContentsByUser = HashMap<String, RoomMemberContent?>()
|
||||
|
||||
for (rootThreadEvent in rootThreadList) {
|
||||
if (rootThreadEvent.eventId == null || rootThreadEvent.senderId == null || rootThreadEvent.type == null) {
|
||||
continue
|
||||
}
|
||||
|
||||
ThreadSummaryEntity.createOrUpdate(
|
||||
val threadSummary = ThreadSummaryEntity.createOrUpdate(
|
||||
threadSummaryType = ThreadSummaryUpdateType.REPLACE,
|
||||
realm = realm,
|
||||
roomId = params.roomId,
|
||||
|
@ -102,14 +107,16 @@ internal class DefaultFetchThreadSummariesTask @Inject constructor(
|
|||
cryptoService = cryptoService,
|
||||
currentTimeMillis = clock.epochMillis(),
|
||||
)
|
||||
|
||||
threadSummaries.add(threadSummary)
|
||||
}
|
||||
|
||||
val page = ThreadListPageEntity.getOrCreate(realm, params.roomId)
|
||||
threadSummaries.forEach {
|
||||
if (!page.threadSummaries.contains(it)) {
|
||||
page.threadSummaries.add(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
return Result.SUCCESS
|
||||
}
|
||||
|
||||
enum class Result {
|
||||
SHOULD_FETCH_MORE,
|
||||
REACHED_END,
|
||||
SUCCESS
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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.internal.session.room.relation.threads
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class ThreadSummariesResponse(
|
||||
@Json(name = "chunk") val chunk: List<Event>,
|
||||
@Json(name = "next_batch") val nextBatch: String?,
|
||||
@Json(name = "prev_batch") val prevBatch: String?
|
||||
)
|
|
@ -182,7 +182,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||
originServerTs = dummyOriginServerTs(),
|
||||
senderId = userId,
|
||||
eventId = localId,
|
||||
type = EventType.POLL_START.stable,
|
||||
type = EventType.POLL_START.unstable,
|
||||
content = newContent.toContent().plus(additionalContent.orEmpty())
|
||||
)
|
||||
}
|
||||
|
@ -207,7 +207,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||
originServerTs = dummyOriginServerTs(),
|
||||
senderId = userId,
|
||||
eventId = localId,
|
||||
type = EventType.POLL_RESPONSE.stable,
|
||||
type = EventType.POLL_RESPONSE.unstable,
|
||||
content = content.toContent().plus(additionalContent.orEmpty()),
|
||||
unsignedData = UnsignedData(age = null, transactionId = localId)
|
||||
)
|
||||
|
@ -227,7 +227,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||
originServerTs = dummyOriginServerTs(),
|
||||
senderId = userId,
|
||||
eventId = localId,
|
||||
type = EventType.POLL_START.stable,
|
||||
type = EventType.POLL_START.unstable,
|
||||
content = content.toContent().plus(additionalContent.orEmpty()),
|
||||
unsignedData = UnsignedData(age = null, transactionId = localId)
|
||||
)
|
||||
|
@ -250,7 +250,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||
originServerTs = dummyOriginServerTs(),
|
||||
senderId = userId,
|
||||
eventId = localId,
|
||||
type = EventType.POLL_END.stable,
|
||||
type = EventType.POLL_END.unstable,
|
||||
content = content.toContent().plus(additionalContent.orEmpty()),
|
||||
unsignedData = UnsignedData(age = null, transactionId = localId)
|
||||
)
|
||||
|
@ -301,7 +301,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||
originServerTs = dummyOriginServerTs(),
|
||||
senderId = userId,
|
||||
eventId = localId,
|
||||
type = EventType.BEACON_LOCATION_DATA.stable,
|
||||
type = EventType.BEACON_LOCATION_DATA.unstable,
|
||||
content = content.toContent().plus(additionalContent.orEmpty()),
|
||||
unsignedData = UnsignedData(age = null, transactionId = localId)
|
||||
)
|
||||
|
|
|
@ -17,18 +17,24 @@
|
|||
package org.matrix.android.sdk.internal.session.room.summary
|
||||
|
||||
import io.realm.Realm
|
||||
import org.matrix.android.sdk.api.MatrixConfiguration
|
||||
import org.matrix.android.sdk.api.session.room.summary.RoomSummaryConstants
|
||||
import org.matrix.android.sdk.api.session.room.timeline.EventTypeFilter
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEventFilters
|
||||
import org.matrix.android.sdk.internal.database.model.TimelineEventEntity
|
||||
import org.matrix.android.sdk.internal.database.query.bestTimestampPreviewEvent
|
||||
import org.matrix.android.sdk.internal.database.query.latestEvent
|
||||
import javax.inject.Inject
|
||||
|
||||
internal object RoomSummaryEventsHelper {
|
||||
internal class RoomSummaryEventsHelper @Inject constructor(
|
||||
matrixConfiguration: MatrixConfiguration,
|
||||
) {
|
||||
|
||||
private val previewFilters = TimelineEventFilters(
|
||||
filterTypes = true,
|
||||
allowedTypes = RoomSummaryConstants.PREVIEWABLE_TYPES.map { EventTypeFilter(eventType = it, stateKey = null) },
|
||||
allowedTypes = RoomSummaryConstants.PREVIEWABLE_TYPES
|
||||
.plus(matrixConfiguration.customEventTypesProvider?.customPreviewableEventTypes.orEmpty())
|
||||
.map { EventTypeFilter(eventType = it, stateKey = null) },
|
||||
filterUseless = true,
|
||||
filterRedacted = true,
|
||||
filterEdits = true
|
||||
|
|
|
@ -84,14 +84,15 @@ internal class RoomSummaryUpdater @Inject constructor(
|
|||
private val crossSigningService: DefaultCrossSigningService,
|
||||
private val roomAccountDataDataSource: RoomAccountDataDataSource,
|
||||
private val homeServerCapabilitiesService: HomeServerCapabilitiesService,
|
||||
private val roomSummaryEventsHelper: RoomSummaryEventsHelper,
|
||||
) {
|
||||
|
||||
fun refreshLatestPreviewContent(realm: Realm, roomId: String, attemptDecrypt: Boolean = true) {
|
||||
val roomSummaryEntity = RoomSummaryEntity.getOrNull(realm, roomId)
|
||||
if (roomSummaryEntity != null) {
|
||||
//roomSummaryEntity.latestPreviewableEvent = RoomSummaryEventsHelper.getLatestPreviewableEventScAll(realm, roomId)?.first
|
||||
//roomSummaryEntity.latestPreviewableOriginalContentEvent = RoomSummaryEventsHelper.getLatestPreviewableEvent(realm, roomId)?.first
|
||||
//val latestPreviewableOriginalContentEvent = RoomSummaryEventsHelper.getLatestPreviewableEventScOriginalContent(realm, roomId)?.first
|
||||
//roomSummaryEntity.latestPreviewableEvent = roomSummaryEventsHelper.getLatestPreviewableEventScAll(realm, roomId)?.first
|
||||
//roomSummaryEntity.latestPreviewableOriginalContentEvent = roomSummaryEventsHelper.getLatestPreviewableEvent(realm, roomId)?.first
|
||||
//val latestPreviewableOriginalContentEvent = roomSummaryEventsHelper.getLatestPreviewableEventScOriginalContent(realm, roomId)?.first
|
||||
//attemptToDecryptLatestPreviewables(latestPreviewableEvent, latestPreviewableContentEvent, latestPreviewableOriginalContentEvent)
|
||||
refreshLatestPreviewContent(roomSummaryEntity, realm, roomId, attemptDecrypt)
|
||||
}
|
||||
|
@ -105,9 +106,9 @@ internal class RoomSummaryUpdater @Inject constructor(
|
|||
}
|
||||
|
||||
private fun refreshLatestPreviewContent(roomSummaryEntity: RoomSummaryEntity, realm: Realm, roomId: String, attemptDecrypt: Boolean = true) {
|
||||
val latestPreviewableEvent = RoomSummaryEventsHelper.getLatestPreviewableEventScAll(realm, roomId)
|
||||
val latestPreviewableContentEvent = RoomSummaryEventsHelper.getLatestPreviewableEvent(realm, roomId)
|
||||
val latestPreviewableOriginalContentEvent = RoomSummaryEventsHelper.getLatestPreviewableEventScOriginalContent(realm, roomId)
|
||||
val latestPreviewableEvent = roomSummaryEventsHelper.getLatestPreviewableEventScAll(realm, roomId)
|
||||
val latestPreviewableContentEvent = roomSummaryEventsHelper.getLatestPreviewableEvent(realm, roomId)
|
||||
val latestPreviewableOriginalContentEvent = roomSummaryEventsHelper.getLatestPreviewableEventScOriginalContent(realm, roomId)
|
||||
|
||||
roomSummaryEntity.latestPreviewableEvent = latestPreviewableEvent.previewable()
|
||||
roomSummaryEntity.latestPreviewableContentEvent = latestPreviewableContentEvent.previewable()
|
||||
|
@ -307,9 +308,9 @@ internal class RoomSummaryUpdater @Inject constructor(
|
|||
val roomSummaryEntity = RoomSummaryEntity.getOrCreate(realm, roomId)
|
||||
roomSummaryEntity.updateHasFailedSending()
|
||||
refreshLatestPreviewContent(realm, roomId, attemptDecrypt = false)
|
||||
//roomSummaryEntity.latestPreviewableEvent = RoomSummaryEventsHelper.getLatestPreviewableEvent(realm, roomId).previewable()
|
||||
//roomSummaryEntity.latestPreviewableContentEvent = RoomSummaryEventsHelper.getLatestPreviewableEvent(realm, roomId).previewable()
|
||||
//roomSummaryEntity.latestPreviewableOriginalContentEvent = RoomSummaryEventsHelper.getLatestPreviewableEventScOriginalContent(realm, roomId).previewable()
|
||||
//roomSummaryEntity.latestPreviewableEvent = roomSummaryEventsHelper.getLatestPreviewableEvent(realm, roomId).previewable()
|
||||
//roomSummaryEntity.latestPreviewableContentEvent = roomSummaryEventsHelper.getLatestPreviewableEvent(realm, roomId).previewable()
|
||||
//roomSummaryEntity.latestPreviewableOriginalContentEvent = roomSummaryEventsHelper.getLatestPreviewableEventScOriginalContent(realm, roomId).previewable()
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -16,32 +16,39 @@
|
|||
|
||||
package org.matrix.android.sdk.internal.session.room.threads
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.paging.LivePagedListBuilder
|
||||
import androidx.paging.PagedList
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import io.realm.Realm
|
||||
import io.realm.Sort
|
||||
import io.realm.kotlin.where
|
||||
import org.matrix.android.sdk.api.session.room.ResultBoundaries
|
||||
import org.matrix.android.sdk.api.session.room.threads.FetchThreadsResult
|
||||
import org.matrix.android.sdk.api.session.room.threads.ThreadFilter
|
||||
import org.matrix.android.sdk.api.session.room.threads.ThreadLivePageResult
|
||||
import org.matrix.android.sdk.api.session.room.threads.ThreadsService
|
||||
import org.matrix.android.sdk.api.session.room.threads.model.ThreadSummary
|
||||
import org.matrix.android.sdk.internal.database.helper.enhanceWithEditions
|
||||
import org.matrix.android.sdk.internal.database.helper.findAllThreadsForRoomId
|
||||
import org.matrix.android.sdk.internal.database.mapper.ThreadSummaryMapper
|
||||
import org.matrix.android.sdk.internal.database.mapper.TimelineEventMapper
|
||||
import org.matrix.android.sdk.internal.database.model.threads.ThreadListPageEntity
|
||||
import org.matrix.android.sdk.internal.database.model.threads.ThreadSummaryEntity
|
||||
import org.matrix.android.sdk.internal.database.model.threads.ThreadSummaryEntityFields
|
||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||
import org.matrix.android.sdk.internal.di.UserId
|
||||
import org.matrix.android.sdk.internal.session.room.relation.threads.FetchThreadSummariesTask
|
||||
import org.matrix.android.sdk.internal.session.room.relation.threads.FetchThreadTimelineTask
|
||||
import org.matrix.android.sdk.internal.util.awaitTransaction
|
||||
|
||||
internal class DefaultThreadsService @AssistedInject constructor(
|
||||
@Assisted private val roomId: String,
|
||||
@UserId private val userId: String,
|
||||
private val fetchThreadTimelineTask: FetchThreadTimelineTask,
|
||||
private val fetchThreadSummariesTask: FetchThreadSummariesTask,
|
||||
@SessionDatabase private val monarchy: Monarchy,
|
||||
private val timelineEventMapper: TimelineEventMapper,
|
||||
private val threadSummaryMapper: ThreadSummaryMapper
|
||||
private val threadSummaryMapper: ThreadSummaryMapper,
|
||||
private val fetchThreadSummariesTask: FetchThreadSummariesTask,
|
||||
) : ThreadsService {
|
||||
|
||||
@AssistedFactory
|
||||
|
@ -49,16 +56,58 @@ internal class DefaultThreadsService @AssistedInject constructor(
|
|||
fun create(roomId: String): DefaultThreadsService
|
||||
}
|
||||
|
||||
override fun getAllThreadSummariesLive(): LiveData<List<ThreadSummary>> {
|
||||
return monarchy.findAllMappedWithChanges(
|
||||
{ ThreadSummaryEntity.findAllThreadsForRoomId(it, roomId = roomId) },
|
||||
{
|
||||
threadSummaryMapper.map(it)
|
||||
override suspend fun getPagedThreadsList(userParticipating: Boolean, pagedListConfig: PagedList.Config): ThreadLivePageResult {
|
||||
monarchy.awaitTransaction { realm ->
|
||||
realm.where<ThreadListPageEntity>().findAll().deleteAllFromRealm()
|
||||
}
|
||||
|
||||
val realmDataSourceFactory = monarchy.createDataSourceFactory { realm ->
|
||||
realm
|
||||
.where<ThreadSummaryEntity>().equalTo(ThreadSummaryEntityFields.PAGE.ROOM_ID, roomId)
|
||||
.sort(ThreadSummaryEntityFields.LATEST_THREAD_EVENT_ENTITY.ORIGIN_SERVER_TS, Sort.DESCENDING)
|
||||
}
|
||||
|
||||
val dataSourceFactory = realmDataSourceFactory.map {
|
||||
threadSummaryMapper.map(it)
|
||||
}
|
||||
|
||||
val boundaries = MutableLiveData(ResultBoundaries())
|
||||
|
||||
val builder = LivePagedListBuilder(dataSourceFactory, pagedListConfig).also {
|
||||
it.setBoundaryCallback(object : PagedList.BoundaryCallback<ThreadSummary>() {
|
||||
override fun onItemAtEndLoaded(itemAtEnd: ThreadSummary) {
|
||||
boundaries.postValue(boundaries.value?.copy(endLoaded = true))
|
||||
}
|
||||
|
||||
override fun onItemAtFrontLoaded(itemAtFront: ThreadSummary) {
|
||||
boundaries.postValue(boundaries.value?.copy(frontLoaded = true))
|
||||
}
|
||||
|
||||
override fun onZeroItemsLoaded() {
|
||||
boundaries.postValue(boundaries.value?.copy(zeroItemLoaded = true))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
val livePagedList = monarchy.findAllPagedWithChanges(
|
||||
realmDataSourceFactory,
|
||||
builder
|
||||
)
|
||||
return ThreadLivePageResult(livePagedList, boundaries)
|
||||
}
|
||||
|
||||
override suspend fun fetchThreadList(nextBatchId: String?, limit: Int, filter: ThreadFilter): FetchThreadsResult {
|
||||
return fetchThreadSummariesTask.execute(
|
||||
FetchThreadSummariesTask.Params(
|
||||
roomId = roomId,
|
||||
from = nextBatchId,
|
||||
limit = limit,
|
||||
filter = filter
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun getAllThreadSummaries(): List<ThreadSummary> {
|
||||
override suspend fun getAllThreadSummaries(): List<ThreadSummary> {
|
||||
return monarchy.fetchAllMappedSync(
|
||||
{ ThreadSummaryEntity.findAllThreadsForRoomId(it, roomId = roomId) },
|
||||
{ threadSummaryMapper.map(it) }
|
||||
|
@ -81,12 +130,4 @@ internal class DefaultThreadsService @AssistedInject constructor(
|
|||
)
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun fetchThreadSummaries() {
|
||||
fetchThreadSummariesTask.execute(
|
||||
FetchThreadSummariesTask.Params(
|
||||
roomId = roomId
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
|||
import org.matrix.android.sdk.api.session.sync.model.SyncResponse
|
||||
import org.matrix.android.sdk.api.session.sync.model.ToDeviceSyncResponse
|
||||
import org.matrix.android.sdk.internal.crypto.DefaultCryptoService
|
||||
import org.matrix.android.sdk.internal.crypto.tasks.toDeviceTracingId
|
||||
import org.matrix.android.sdk.internal.crypto.verification.DefaultVerificationService
|
||||
import org.matrix.android.sdk.internal.session.sync.ProgressReporter
|
||||
import timber.log.Timber
|
||||
|
@ -48,12 +49,14 @@ internal class CryptoSyncHandler @Inject constructor(
|
|||
?.forEachIndexed { index, event ->
|
||||
progressReporter?.reportProgress(index * 100F / total)
|
||||
// Decrypt event if necessary
|
||||
Timber.tag(loggerTag.value).i("To device event from ${event.senderId} of type:${event.type}")
|
||||
Timber.tag(loggerTag.value).d("To device event msgid:${event.toDeviceTracingId()}")
|
||||
decryptToDeviceEvent(event, null)
|
||||
|
||||
if (event.getClearType() == EventType.MESSAGE &&
|
||||
event.getClearContent()?.toModel<MessageContent>()?.msgType == "m.bad.encrypted") {
|
||||
Timber.tag(loggerTag.value).e("handleToDeviceEvent() : Warning: Unable to decrypt to-device event : ${event.content}")
|
||||
} else {
|
||||
Timber.tag(loggerTag.value).d("received to-device ${event.getClearType()} from:${event.senderId} msgid:${event.toDeviceTracingId()}")
|
||||
verificationService.onToDeviceEvent(event)
|
||||
cryptoService.onToDeviceEvent(event)
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@ import org.matrix.android.sdk.internal.database.model.TimelineEventEntity
|
|||
import org.matrix.android.sdk.internal.database.model.UserAccountDataEntity
|
||||
import org.matrix.android.sdk.internal.database.model.UserAccountDataEntityFields
|
||||
import org.matrix.android.sdk.internal.database.model.deleteOnCascade
|
||||
import org.matrix.android.sdk.internal.database.query.delete
|
||||
import org.matrix.android.sdk.internal.database.query.findAllFrom
|
||||
import org.matrix.android.sdk.internal.database.query.getDirectRooms
|
||||
import org.matrix.android.sdk.internal.database.query.getOrCreate
|
||||
|
@ -94,7 +95,7 @@ internal class UserAccountDataSyncHandler @Inject constructor(
|
|||
|
||||
// If we get some direct chat invites, we synchronize the user account data including those.
|
||||
suspend fun synchronizeWithServerIfNeeded(invites: Map<String, InvitedRoomSync>) {
|
||||
if (invites.isNullOrEmpty()) return
|
||||
if (invites.isEmpty()) return
|
||||
val directChats = directChatsHelper.getLocalDirectMessages().toMutable()
|
||||
var hasUpdate = false
|
||||
monarchy.doWithRealm { realm ->
|
||||
|
@ -252,9 +253,17 @@ internal class UserAccountDataSyncHandler @Inject constructor(
|
|||
}
|
||||
|
||||
fun handleGenericAccountData(realm: Realm, type: String, content: Content?) {
|
||||
if (content.isNullOrEmpty()) {
|
||||
// This is a response for a deleted account data according to
|
||||
// https://github.com/ShadowJonathan/matrix-doc/blob/account-data-delete/proposals/3391-account-data-delete.md#sync
|
||||
UserAccountDataEntity.delete(realm, type)
|
||||
return
|
||||
}
|
||||
|
||||
val existing = realm.where<UserAccountDataEntity>()
|
||||
.equalTo(UserAccountDataEntityFields.TYPE, type)
|
||||
.findFirst()
|
||||
|
||||
if (existing != null) {
|
||||
// Update current value
|
||||
existing.contentStr = ContentMapper.map(content)
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.matrix.android.sdk.internal.database.model.RoomAccountDataEntity
|
|||
import org.matrix.android.sdk.internal.database.model.RoomAccountDataEntityFields
|
||||
import org.matrix.android.sdk.internal.database.model.RoomEntity
|
||||
import org.matrix.android.sdk.internal.database.query.getOrCreate
|
||||
import org.matrix.android.sdk.internal.database.query.removeAccountData
|
||||
import org.matrix.android.sdk.internal.session.room.read.FullyReadContent
|
||||
import org.matrix.android.sdk.internal.session.room.read.MarkedUnreadContent
|
||||
import org.matrix.android.sdk.internal.session.sync.handler.room.RoomFullyReadHandler
|
||||
|
@ -62,6 +63,13 @@ internal class RoomSyncAccountDataHandler @Inject constructor(
|
|||
}
|
||||
|
||||
private fun handleGeneric(roomEntity: RoomEntity, content: JsonDict?, eventType: String) {
|
||||
if (content.isNullOrEmpty()) {
|
||||
// This is a response for a deleted account data according to
|
||||
// https://github.com/ShadowJonathan/matrix-doc/blob/account-data-delete/proposals/3391-account-data-delete.md#sync
|
||||
roomEntity.removeAccountData(eventType)
|
||||
return
|
||||
}
|
||||
|
||||
val existing = roomEntity.accountData.where().equalTo(RoomAccountDataEntityFields.TYPE, eventType).findFirst()
|
||||
if (existing != null) {
|
||||
existing.contentStr = ContentMapper.map(content)
|
||||
|
|
|
@ -18,13 +18,14 @@ package org.matrix.android.sdk.internal.session.user.accountdata
|
|||
|
||||
import org.matrix.android.sdk.internal.network.NetworkConstants
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.DELETE
|
||||
import retrofit2.http.PUT
|
||||
import retrofit2.http.Path
|
||||
|
||||
internal interface AccountDataAPI {
|
||||
|
||||
/**
|
||||
* Set some account_data for the client.
|
||||
* Set some account_data for the user.
|
||||
*
|
||||
* @param userId the user id
|
||||
* @param type the type
|
||||
|
@ -52,4 +53,16 @@ internal interface AccountDataAPI {
|
|||
@Path("type") type: String,
|
||||
@Body params: Any
|
||||
)
|
||||
|
||||
/**
|
||||
* Remove an account_data for the user.
|
||||
*
|
||||
* @param userId the user id
|
||||
* @param type the type
|
||||
*/
|
||||
@DELETE(NetworkConstants.URI_API_PREFIX_PATH_UNSTABLE + "org.matrix.msc3391/user/{userId}/account_data/{type}")
|
||||
suspend fun deleteAccountData(
|
||||
@Path("userId") userId: String,
|
||||
@Path("type") type: String
|
||||
)
|
||||
}
|
||||
|
|
|
@ -42,4 +42,7 @@ internal abstract class AccountDataModule {
|
|||
|
||||
@Binds
|
||||
abstract fun bindUpdateBreadcrumbsTask(task: DefaultUpdateBreadcrumbsTask): UpdateBreadcrumbsTask
|
||||
|
||||
@Binds
|
||||
abstract fun bindDeleteUserAccountDataTask(task: DefaultDeleteUserAccountDataTask): DeleteUserAccountDataTask
|
||||
}
|
||||
|
|
|
@ -34,10 +34,11 @@ import javax.inject.Inject
|
|||
internal class DefaultSessionAccountDataService @Inject constructor(
|
||||
@SessionDatabase private val monarchy: Monarchy,
|
||||
private val updateUserAccountDataTask: UpdateUserAccountDataTask,
|
||||
private val deleteUserAccountDataTask: DeleteUserAccountDataTask,
|
||||
private val userAccountDataSyncHandler: UserAccountDataSyncHandler,
|
||||
private val userAccountDataDataSource: UserAccountDataDataSource,
|
||||
private val roomAccountDataDataSource: RoomAccountDataDataSource,
|
||||
private val taskExecutor: TaskExecutor
|
||||
private val taskExecutor: TaskExecutor,
|
||||
) : SessionAccountDataService {
|
||||
|
||||
override fun getUserAccountDataEvent(type: String): UserAccountDataEvent? {
|
||||
|
@ -78,4 +79,12 @@ internal class DefaultSessionAccountDataService @Inject constructor(
|
|||
userAccountDataSyncHandler.handleGenericAccountData(realm, type, content)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getUserAccountDataEventsStartWith(type: String): List<UserAccountDataEvent> {
|
||||
return userAccountDataDataSource.getAccountDataEventsStartWith(type)
|
||||
}
|
||||
|
||||
override suspend fun deleteUserAccountData(type: String) {
|
||||
deleteUserAccountDataTask.execute(DeleteUserAccountDataTask.Params(type))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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.internal.session.user.accountdata
|
||||
|
||||
import org.matrix.android.sdk.internal.di.UserId
|
||||
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
|
||||
import org.matrix.android.sdk.internal.network.executeRequest
|
||||
import org.matrix.android.sdk.internal.task.Task
|
||||
import javax.inject.Inject
|
||||
|
||||
internal interface DeleteUserAccountDataTask : Task<DeleteUserAccountDataTask.Params, Unit> {
|
||||
|
||||
data class Params(
|
||||
val type: String,
|
||||
)
|
||||
}
|
||||
|
||||
internal class DefaultDeleteUserAccountDataTask @Inject constructor(
|
||||
private val accountDataApi: AccountDataAPI,
|
||||
@UserId private val userId: String,
|
||||
private val globalErrorReceiver: GlobalErrorReceiver,
|
||||
) : DeleteUserAccountDataTask {
|
||||
|
||||
override suspend fun execute(params: DeleteUserAccountDataTask.Params) {
|
||||
return executeRequest(globalErrorReceiver) {
|
||||
accountDataApi.deleteAccountData(userId, params.type)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -60,6 +60,16 @@ internal class UserAccountDataDataSource @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
fun getAccountDataEventsStartWith(type: String): List<UserAccountDataEvent> {
|
||||
return realmSessionProvider.withRealm { realm ->
|
||||
realm
|
||||
.where(UserAccountDataEntity::class.java)
|
||||
.beginsWith(UserAccountDataEntityFields.TYPE, type)
|
||||
.findAll()
|
||||
.map(accountDataMapper::map)
|
||||
}
|
||||
}
|
||||
|
||||
private fun accountDataEventsQuery(realm: Realm, types: Set<String>): RealmQuery<UserAccountDataEntity> {
|
||||
val query = realm.where(UserAccountDataEntity::class.java)
|
||||
if (types.isNotEmpty()) {
|
||||
|
|
|
@ -0,0 +1,255 @@
|
|||
/*
|
||||
* 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.internal.crypto
|
||||
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.amshove.kluent.internal.assertEquals
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo
|
||||
import org.matrix.android.sdk.api.session.crypto.model.DevicesListResponse
|
||||
import org.matrix.android.sdk.api.session.crypto.model.MXUsersDevicesMap
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageType
|
||||
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.DeleteDevicesParams
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.KeyChangesResponse
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.KeysClaimBody
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.KeysClaimResponse
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.KeysQueryBody
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.KeysQueryResponse
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.KeysUploadBody
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.KeysUploadResponse
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.SendToDeviceBody
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.SignatureUploadResponse
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.UpdateDeviceInfoBody
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.UploadSigningKeysBody
|
||||
import org.matrix.android.sdk.internal.crypto.tasks.DefaultSendToDeviceTask
|
||||
import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask
|
||||
|
||||
class DefaultSendToDeviceTaskTest {
|
||||
|
||||
private val users = listOf(
|
||||
"@alice:example.com" to listOf("D0", "D1"),
|
||||
"bob@example.com" to listOf("D2", "D3")
|
||||
)
|
||||
|
||||
private val fakeEncryptedContent = mapOf(
|
||||
"algorithm" to "m.olm.v1.curve25519-aes-sha2",
|
||||
"sender_key" to "gMObR+/4dqL5T4DisRRRYBJpn+OjzFnkyCFOktP6Eyw",
|
||||
"ciphertext" to mapOf(
|
||||
"tdwXf7006FDgzmufMCVI4rDdVPO51ecRTTT6HkRxUwE" to mapOf(
|
||||
"type" to 0,
|
||||
"body" to "AwogCA1ULEc0abGIFxMDIC9iv7ul3jqJSnapTHQ+8JJx"
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
private val fakeStartVerificationContent = mapOf(
|
||||
"method" to "m.sas.v1",
|
||||
"from_device" to "MNQHVEISFQ",
|
||||
"key_agreement_protocols" to listOf(
|
||||
"curve25519-hkdf-sha256",
|
||||
"curve25519"
|
||||
),
|
||||
"hashes" to listOf("sha256"),
|
||||
"message_authentication_codes" to listOf(
|
||||
"org.matrix.msc3783.hkdf-hmac-sha256",
|
||||
"hkdf-hmac-sha256",
|
||||
"hmac-sha256"
|
||||
),
|
||||
"short_authentication_string" to listOf(
|
||||
"decimal",
|
||||
"emoji"
|
||||
),
|
||||
"transaction_id" to "4wNOpkHGwGZPXjkZToooCDWfb8hsf7vW"
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `tracing id should be added to to_device contents`() {
|
||||
val fakeCryptoAPi = FakeCryptoApi()
|
||||
|
||||
val sendToDeviceTask = DefaultSendToDeviceTask(
|
||||
cryptoApi = fakeCryptoAPi,
|
||||
globalErrorReceiver = mockk(relaxed = true)
|
||||
)
|
||||
|
||||
val contentMap = MXUsersDevicesMap<Any>()
|
||||
|
||||
users.forEach { pairOfUserDevices ->
|
||||
val userId = pairOfUserDevices.first
|
||||
pairOfUserDevices.second.forEach {
|
||||
contentMap.setObject(userId, it, fakeEncryptedContent)
|
||||
}
|
||||
}
|
||||
|
||||
val params = SendToDeviceTask.Params(
|
||||
eventType = EventType.ENCRYPTED,
|
||||
contentMap = contentMap
|
||||
)
|
||||
|
||||
runBlocking {
|
||||
sendToDeviceTask.execute(params)
|
||||
}
|
||||
|
||||
val generatedIds = mutableListOf<String>()
|
||||
users.forEach { pairOfUserDevices ->
|
||||
val userId = pairOfUserDevices.first
|
||||
pairOfUserDevices.second.forEach {
|
||||
val modifiedContent = fakeCryptoAPi.body!!.messages!![userId]!![it] as Map<*, *>
|
||||
Assert.assertNotNull("Tracing id should have been added", modifiedContent["org.matrix.msgid"])
|
||||
generatedIds.add(modifiedContent["org.matrix.msgid"] as String)
|
||||
|
||||
assertEquals(
|
||||
"The rest of the content should be the same",
|
||||
fakeEncryptedContent.keys,
|
||||
modifiedContent.toMutableMap().apply { remove("org.matrix.msgid") }.keys
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals("Id should be unique per content", generatedIds.size, generatedIds.toSet().size)
|
||||
println("modified content ${fakeCryptoAPi.body}")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `tracing id should not be added to verification start to_device contents`() {
|
||||
val fakeCryptoAPi = FakeCryptoApi()
|
||||
|
||||
val sendToDeviceTask = DefaultSendToDeviceTask(
|
||||
cryptoApi = fakeCryptoAPi,
|
||||
globalErrorReceiver = mockk(relaxed = true)
|
||||
)
|
||||
val contentMap = MXUsersDevicesMap<Any>()
|
||||
contentMap.setObject("@alice:example.com", "MNQHVEISFQ", fakeStartVerificationContent)
|
||||
|
||||
val params = SendToDeviceTask.Params(
|
||||
eventType = EventType.KEY_VERIFICATION_START,
|
||||
contentMap = contentMap
|
||||
)
|
||||
|
||||
runBlocking {
|
||||
sendToDeviceTask.execute(params)
|
||||
}
|
||||
|
||||
val modifiedContent = fakeCryptoAPi.body!!.messages!!["@alice:example.com"]!!["MNQHVEISFQ"] as Map<*, *>
|
||||
Assert.assertNull("Tracing id should not have been added", modifiedContent["org.matrix.msgid"])
|
||||
|
||||
// try to force
|
||||
runBlocking {
|
||||
sendToDeviceTask.execute(
|
||||
SendToDeviceTask.Params(
|
||||
eventType = EventType.KEY_VERIFICATION_START,
|
||||
contentMap = contentMap,
|
||||
addTracingIds = true
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
val modifiedContentForced = fakeCryptoAPi.body!!.messages!!["@alice:example.com"]!!["MNQHVEISFQ"] as Map<*, *>
|
||||
Assert.assertNotNull("Tracing id should have been added", modifiedContentForced["org.matrix.msgid"])
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `tracing id should not be added to all verification to_device contents`() {
|
||||
val fakeCryptoAPi = FakeCryptoApi()
|
||||
|
||||
val sendToDeviceTask = DefaultSendToDeviceTask(
|
||||
cryptoApi = fakeCryptoAPi,
|
||||
globalErrorReceiver = mockk(relaxed = true)
|
||||
)
|
||||
val contentMap = MXUsersDevicesMap<Any>()
|
||||
contentMap.setObject("@alice:example.com", "MNQHVEISFQ", emptyMap<String, Any>())
|
||||
|
||||
val verificationEvents = listOf(
|
||||
MessageType.MSGTYPE_VERIFICATION_REQUEST,
|
||||
EventType.KEY_VERIFICATION_START,
|
||||
EventType.KEY_VERIFICATION_ACCEPT,
|
||||
EventType.KEY_VERIFICATION_KEY,
|
||||
EventType.KEY_VERIFICATION_MAC,
|
||||
EventType.KEY_VERIFICATION_CANCEL,
|
||||
EventType.KEY_VERIFICATION_DONE,
|
||||
EventType.KEY_VERIFICATION_READY
|
||||
)
|
||||
|
||||
for (type in verificationEvents) {
|
||||
val params = SendToDeviceTask.Params(
|
||||
eventType = type,
|
||||
contentMap = contentMap
|
||||
)
|
||||
runBlocking {
|
||||
sendToDeviceTask.execute(params)
|
||||
}
|
||||
|
||||
val modifiedContent = fakeCryptoAPi.body!!.messages!!["@alice:example.com"]!!["MNQHVEISFQ"] as Map<*, *>
|
||||
Assert.assertNull("Tracing id should not have been added", modifiedContent["org.matrix.msgid"])
|
||||
}
|
||||
}
|
||||
|
||||
internal class FakeCryptoApi : CryptoApi {
|
||||
override suspend fun getDevices(): DevicesListResponse {
|
||||
throw java.lang.AssertionError("Should not be called")
|
||||
}
|
||||
|
||||
override suspend fun getDeviceInfo(deviceId: String): DeviceInfo {
|
||||
throw java.lang.AssertionError("Should not be called")
|
||||
}
|
||||
|
||||
override suspend fun uploadKeys(body: KeysUploadBody): KeysUploadResponse {
|
||||
throw java.lang.AssertionError("Should not be called")
|
||||
}
|
||||
|
||||
override suspend fun downloadKeysForUsers(params: KeysQueryBody): KeysQueryResponse {
|
||||
throw java.lang.AssertionError("Should not be called")
|
||||
}
|
||||
|
||||
override suspend fun uploadSigningKeys(params: UploadSigningKeysBody): KeysQueryResponse {
|
||||
throw java.lang.AssertionError("Should not be called")
|
||||
}
|
||||
|
||||
override suspend fun uploadSignatures(params: Map<String, Any>?): SignatureUploadResponse {
|
||||
throw java.lang.AssertionError("Should not be called")
|
||||
}
|
||||
|
||||
override suspend fun claimOneTimeKeysForUsersDevices(body: KeysClaimBody): KeysClaimResponse {
|
||||
throw java.lang.AssertionError("Should not be called")
|
||||
}
|
||||
|
||||
var body: SendToDeviceBody? = null
|
||||
override suspend fun sendToDevice(eventType: String, transactionId: String, body: SendToDeviceBody) {
|
||||
this.body = body
|
||||
}
|
||||
|
||||
override suspend fun deleteDevice(deviceId: String, params: DeleteDeviceParams) {
|
||||
throw java.lang.AssertionError("Should not be called")
|
||||
}
|
||||
|
||||
override suspend fun deleteDevices(params: DeleteDevicesParams) {
|
||||
throw java.lang.AssertionError("Should not be called")
|
||||
}
|
||||
|
||||
override suspend fun updateDeviceInfo(deviceId: String, params: UpdateDeviceInfoBody) {
|
||||
throw java.lang.AssertionError("Should not be called")
|
||||
}
|
||||
|
||||
override suspend fun getKeyChanges(oldToken: String, newToken: String): KeyChangesResponse {
|
||||
throw java.lang.AssertionError("Should not be called")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -47,7 +47,7 @@ import org.matrix.android.sdk.test.fakes.FakeRealm
|
|||
import org.matrix.android.sdk.test.fakes.givenEqualTo
|
||||
import org.matrix.android.sdk.test.fakes.givenFindFirst
|
||||
|
||||
class PollAggregationProcessorTest {
|
||||
class DefaultPollAggregationProcessorTest {
|
||||
|
||||
private val pollAggregationProcessor: PollAggregationProcessor = DefaultPollAggregationProcessor()
|
||||
private val realm = FakeRealm()
|
|
@ -87,7 +87,7 @@ object PollEventsTestData {
|
|||
)
|
||||
|
||||
internal val A_POLL_START_EVENT = Event(
|
||||
type = EventType.POLL_START.stable,
|
||||
type = EventType.POLL_START.unstable,
|
||||
eventId = AN_EVENT_ID,
|
||||
originServerTs = 1652435922563,
|
||||
senderId = A_USER_ID_1,
|
||||
|
@ -96,7 +96,7 @@ object PollEventsTestData {
|
|||
)
|
||||
|
||||
internal val A_POLL_RESPONSE_EVENT = Event(
|
||||
type = EventType.POLL_RESPONSE.stable,
|
||||
type = EventType.POLL_RESPONSE.unstable,
|
||||
eventId = AN_EVENT_ID,
|
||||
originServerTs = 1652435922563,
|
||||
senderId = A_USER_ID_1,
|
||||
|
@ -105,7 +105,7 @@ object PollEventsTestData {
|
|||
)
|
||||
|
||||
internal val A_POLL_END_EVENT = Event(
|
||||
type = EventType.POLL_END.stable,
|
||||
type = EventType.POLL_END.unstable,
|
||||
eventId = AN_EVENT_ID,
|
||||
originServerTs = 1652435922563,
|
||||
senderId = A_USER_ID_1,
|
||||
|
|
|
@ -69,7 +69,7 @@ class DefaultGetActiveBeaconInfoForUserTaskTest {
|
|||
result shouldBeEqualTo currentStateEvent
|
||||
fakeStateEventDataSource.verifyGetStateEvent(
|
||||
roomId = params.roomId,
|
||||
eventType = EventType.STATE_ROOM_BEACON_INFO.stable,
|
||||
eventType = EventType.STATE_ROOM_BEACON_INFO.unstable,
|
||||
stateKey = QueryStringValue.Equals(A_USER_ID)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -75,7 +75,7 @@ internal class DefaultStartLiveLocationShareTaskTest {
|
|||
val expectedParams = SendStateTask.Params(
|
||||
roomId = params.roomId,
|
||||
stateKey = A_USER_ID,
|
||||
eventType = EventType.STATE_ROOM_BEACON_INFO.stable,
|
||||
eventType = EventType.STATE_ROOM_BEACON_INFO.unstable,
|
||||
body = expectedBeaconContent
|
||||
)
|
||||
fakeSendStateTask.verifyExecuteRetry(
|
||||
|
|
|
@ -79,7 +79,7 @@ class DefaultStopLiveLocationShareTaskTest {
|
|||
val expectedSendParams = SendStateTask.Params(
|
||||
roomId = params.roomId,
|
||||
stateKey = A_USER_ID,
|
||||
eventType = EventType.STATE_ROOM_BEACON_INFO.stable,
|
||||
eventType = EventType.STATE_ROOM_BEACON_INFO.unstable,
|
||||
body = expectedBeaconContent
|
||||
)
|
||||
fakeSendStateTask.verifyExecuteRetry(
|
||||
|
|
|
@ -79,7 +79,7 @@ class LiveLocationShareRedactionEventProcessorTest {
|
|||
@Test
|
||||
fun `given a redacted live location share event when processing it then related summaries are deleted from database`() = runTest {
|
||||
val event = Event(eventId = AN_EVENT_ID, redacts = A_REDACTED_EVENT_ID)
|
||||
val redactedEventEntity = EventEntity(eventId = A_REDACTED_EVENT_ID, type = EventType.STATE_ROOM_BEACON_INFO.stable)
|
||||
val redactedEventEntity = EventEntity(eventId = A_REDACTED_EVENT_ID, type = EventType.STATE_ROOM_BEACON_INFO.unstable)
|
||||
fakeRealm.givenWhere<EventEntity>()
|
||||
.givenEqualTo(EventEntityFields.EVENT_ID, A_REDACTED_EVENT_ID)
|
||||
.givenFindFirst(redactedEventEntity)
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* 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.internal.session.user.accountdata
|
||||
|
||||
import io.mockk.coVerify
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
import org.matrix.android.sdk.test.fakes.FakeAccountDataApi
|
||||
import org.matrix.android.sdk.test.fakes.FakeGlobalErrorReceiver
|
||||
|
||||
private const val A_TYPE = "a-type"
|
||||
private const val A_USER_ID = "a-user-id"
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
class DefaultDeleteUserAccountDataTaskTest {
|
||||
|
||||
private val fakeGlobalErrorReceiver = FakeGlobalErrorReceiver()
|
||||
private val fakeAccountDataApi = FakeAccountDataApi()
|
||||
|
||||
private val deleteUserAccountDataTask = DefaultDeleteUserAccountDataTask(
|
||||
accountDataApi = fakeAccountDataApi.instance,
|
||||
userId = A_USER_ID,
|
||||
globalErrorReceiver = fakeGlobalErrorReceiver
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `given parameters when executing the task then api is called`() = runTest {
|
||||
// Given
|
||||
val params = DeleteUserAccountDataTask.Params(type = A_TYPE)
|
||||
fakeAccountDataApi.givenParamsToDeleteAccountData(A_USER_ID, A_TYPE)
|
||||
|
||||
// When
|
||||
deleteUserAccountDataTask.execute(params)
|
||||
|
||||
// Then
|
||||
coVerify { fakeAccountDataApi.instance.deleteAccountData(A_USER_ID, A_TYPE) }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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.test.fakes
|
||||
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.just
|
||||
import io.mockk.mockk
|
||||
import io.mockk.runs
|
||||
import org.matrix.android.sdk.internal.session.user.accountdata.AccountDataAPI
|
||||
|
||||
internal class FakeAccountDataApi {
|
||||
|
||||
val instance: AccountDataAPI = mockk()
|
||||
|
||||
fun givenParamsToDeleteAccountData(userId: String, type: String) {
|
||||
coEvery { instance.deleteAccountData(userId, type) } just runs
|
||||
}
|
||||
}
|
|
@ -345,7 +345,8 @@ ${buildToolsPath}/aapt dump badging ${targetPath}/vector-gplay-x86-release-signe
|
|||
printf "File vector-gplay-x86_64-release-signed.apk:\n"
|
||||
${buildToolsPath}/aapt dump badging ${targetPath}/vector-gplay-x86_64-release-signed.apk | grep package
|
||||
|
||||
read -p "\nDoes it look correct? Press enter when it's done."
|
||||
printf "\n"
|
||||
read -p "Does it look correct? Press enter when it's done."
|
||||
|
||||
printf "\n================================================================================\n"
|
||||
read -p "Installing apk on a real device, press enter when a real device is connected. "
|
||||
|
@ -356,7 +357,7 @@ read -p "Please run the APK on your phone to check that the upgrade went well (n
|
|||
# TODO Get the block to copy from towncrier earlier (be may be edited by the release manager)?
|
||||
read -p "Create the release on gitHub from the tag https://github.com/vector-im/element-android/tags, copy paste the block from the file CHANGES.md. Press enter when it's done."
|
||||
|
||||
read -p "Add the 4 signed APKs to the GitHub release. Press enter when it's done."
|
||||
read -p "Add the 4 signed APKs to the GitHub release. They are located at ${targetPath}. Press enter when it's done."
|
||||
|
||||
printf "\n================================================================================\n"
|
||||
printf "Message for the Android internal room:\n\n"
|
||||
|
|
|
@ -359,7 +359,7 @@ dependencies {
|
|||
debugImplementation(libs.flipper.flipperNetworkPlugin) {
|
||||
exclude group: 'com.facebook.fbjni', module: 'fbjni'
|
||||
}
|
||||
debugImplementation 'com.facebook.soloader:soloader:0.10.4'
|
||||
debugImplementation 'com.facebook.soloader:soloader:0.10.5'
|
||||
debugImplementation "com.kgurgul.flipper:flipper-realm-android:2.2.0"
|
||||
|
||||
gplayImplementation "com.google.android.gms:play-services-location:21.0.1"
|
||||
|
@ -374,7 +374,7 @@ dependencies {
|
|||
// API-only library
|
||||
//gplayImplementation libs.google.appdistributionApi
|
||||
// Full SDK implementation
|
||||
//gplayImplementation libs.google.appdistribution
|
||||
//nightlyImplementation libs.google.appdistribution
|
||||
|
||||
// OSS License, gplay flavor only
|
||||
gplayImplementation 'com.google.android.gms:play-services-oss-licenses:17.0.0'
|
||||
|
|
|
@ -46,9 +46,9 @@ abstract class FlavorModule {
|
|||
|
||||
@Provides
|
||||
fun provideNightlyProxy() = object : NightlyProxy {
|
||||
override fun onHomeResumed() {
|
||||
// no op
|
||||
}
|
||||
override fun canDisplayPopup() = false
|
||||
override fun isNightlyBuild() = false
|
||||
override fun updateApplication() = Unit
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
|
|
@ -46,8 +46,10 @@ import im.vector.app.core.utils.AndroidSystemSettingsProvider
|
|||
import im.vector.app.core.utils.SystemSettingsProvider
|
||||
import im.vector.app.features.analytics.AnalyticsTracker
|
||||
import im.vector.app.features.analytics.VectorAnalytics
|
||||
import im.vector.app.features.analytics.errors.ErrorTracker
|
||||
import im.vector.app.features.analytics.impl.DefaultVectorAnalytics
|
||||
import im.vector.app.features.analytics.metrics.VectorPlugins
|
||||
import im.vector.app.features.configuration.VectorCustomEventTypesProvider
|
||||
import im.vector.app.features.invite.AutoAcceptInvites
|
||||
import im.vector.app.features.invite.CompileTimeAutoAcceptInvites
|
||||
import im.vector.app.features.navigation.DefaultNavigator
|
||||
|
@ -84,6 +86,9 @@ import javax.inject.Singleton
|
|||
@Binds
|
||||
abstract fun bindVectorAnalytics(analytics: DefaultVectorAnalytics): VectorAnalytics
|
||||
|
||||
@Binds
|
||||
abstract fun bindErrorTracker(analytics: DefaultVectorAnalytics): ErrorTracker
|
||||
|
||||
@Binds
|
||||
abstract fun bindAnalyticsTracker(analytics: DefaultVectorAnalytics): AnalyticsTracker
|
||||
|
||||
|
@ -141,6 +146,7 @@ import javax.inject.Singleton
|
|||
vectorRoomDisplayNameFallbackProvider: VectorRoomDisplayNameFallbackProvider,
|
||||
flipperProxy: FlipperProxy,
|
||||
vectorPlugins: VectorPlugins,
|
||||
vectorCustomEventTypesProvider: VectorCustomEventTypesProvider,
|
||||
): MatrixConfiguration {
|
||||
return MatrixConfiguration(
|
||||
applicationFlavor = BuildConfig.FLAVOR_DESCRIPTION,
|
||||
|
@ -150,6 +156,7 @@ import javax.inject.Singleton
|
|||
flipperProxy.networkInterceptor(),
|
||||
),
|
||||
metricPlugins = vectorPlugins.plugins(),
|
||||
customEventTypesProvider = vectorCustomEventTypesProvider,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
<!-- Level 1: Labs -->
|
||||
<bool name="settings_labs_deferred_dm_visible">true</bool>
|
||||
<bool name="settings_labs_deferred_dm_default">true</bool>
|
||||
<bool name="settings_labs_thread_messages_default">false</bool>
|
||||
<bool name="settings_labs_thread_messages_default">true</bool>
|
||||
<bool name="settings_labs_new_app_layout_default">false</bool>
|
||||
<bool name="settings_labs_new_session_manager_default">false</bool>
|
||||
<bool name="settings_labs_new_session_manager_visible">true</bool>
|
||||
|
|
|
@ -27,7 +27,7 @@ import im.vector.app.features.voicebroadcast.listening.VoiceBroadcastPlayer
|
|||
import im.vector.app.features.voicebroadcast.listening.VoiceBroadcastPlayerImpl
|
||||
import im.vector.app.features.voicebroadcast.recording.VoiceBroadcastRecorder
|
||||
import im.vector.app.features.voicebroadcast.recording.VoiceBroadcastRecorderQ
|
||||
import im.vector.app.features.voicebroadcast.usecase.GetMostRecentVoiceBroadcastStateEventUseCase
|
||||
import im.vector.app.features.voicebroadcast.usecase.GetVoiceBroadcastStateEventLiveUseCase
|
||||
import javax.inject.Singleton
|
||||
|
||||
@InstallIn(SingletonComponent::class)
|
||||
|
@ -40,13 +40,13 @@ abstract class VoiceModule {
|
|||
fun providesVoiceBroadcastRecorder(
|
||||
context: Context,
|
||||
sessionHolder: ActiveSessionHolder,
|
||||
getMostRecentVoiceBroadcastStateEventUseCase: GetMostRecentVoiceBroadcastStateEventUseCase,
|
||||
getVoiceBroadcastStateEventLiveUseCase: GetVoiceBroadcastStateEventLiveUseCase,
|
||||
): VoiceBroadcastRecorder? {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
VoiceBroadcastRecorderQ(
|
||||
context = context,
|
||||
sessionHolder = sessionHolder,
|
||||
getVoiceBroadcastEventUseCase = getMostRecentVoiceBroadcastStateEventUseCase
|
||||
getVoiceBroadcastEventUseCase = getVoiceBroadcastStateEventLiveUseCase
|
||||
)
|
||||
} else {
|
||||
null
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.core.session.clientinfo
|
||||
|
||||
import im.vector.app.core.di.ActiveSessionHolder
|
||||
import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo
|
||||
import javax.inject.Inject
|
||||
|
||||
class DeleteUnusedClientInformationUseCase @Inject constructor(
|
||||
private val activeSessionHolder: ActiveSessionHolder,
|
||||
) {
|
||||
|
||||
suspend fun execute(deviceInfoList: List<DeviceInfo>): Result<Unit> = runCatching {
|
||||
// A defensive approach against local storage reports an empty device list (although it is not a seen situation).
|
||||
if (deviceInfoList.isEmpty()) return Result.success(Unit)
|
||||
|
||||
val expectedClientInfoKeyList = deviceInfoList.map { MATRIX_CLIENT_INFO_KEY_PREFIX + it.deviceId }
|
||||
activeSessionHolder
|
||||
.getSafeActiveSession()
|
||||
?.accountDataService()
|
||||
?.getUserAccountDataEventsStartWith(MATRIX_CLIENT_INFO_KEY_PREFIX)
|
||||
?.map { it.type }
|
||||
?.subtract(expectedClientInfoKeyList.toSet())
|
||||
?.forEach { userAccountDataKeyToDelete ->
|
||||
activeSessionHolder.getSafeActiveSession()?.accountDataService()?.deleteUserAccountData(userAccountDataKeyToDelete)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -40,20 +40,26 @@ class ShieldImageView @JvmOverloads constructor(
|
|||
|
||||
/**
|
||||
* Renders device shield with the support of unknown shields instead of black shields which is used for rooms.
|
||||
* @param roomEncryptionTrustLevel trust level that is usally calculated with [im.vector.app.features.settings.devices.TrustUtils.shieldForTrust]
|
||||
* @param roomEncryptionTrustLevel trust level that is usually calculated with [im.vector.app.features.settings.devices.TrustUtils.shieldForTrust]
|
||||
* @param borderLess if true then the shield icon with border around is used
|
||||
*/
|
||||
fun renderDeviceShield(roomEncryptionTrustLevel: RoomEncryptionTrustLevel?, borderLess: Boolean = false) {
|
||||
isVisible = roomEncryptionTrustLevel != null
|
||||
|
||||
if (roomEncryptionTrustLevel == RoomEncryptionTrustLevel.Default) {
|
||||
contentDescription = context.getString(R.string.a11y_trust_level_default)
|
||||
setImageResource(
|
||||
if (borderLess) R.drawable.ic_shield_unknown_no_border
|
||||
else R.drawable.ic_shield_unknown
|
||||
)
|
||||
} else {
|
||||
render(roomEncryptionTrustLevel, borderLess)
|
||||
when (roomEncryptionTrustLevel) {
|
||||
null -> {
|
||||
contentDescription = context.getString(R.string.a11y_trust_level_warning)
|
||||
setImageResource(
|
||||
if (borderLess) R.drawable.ic_shield_warning_no_border
|
||||
else R.drawable.ic_shield_warning
|
||||
)
|
||||
}
|
||||
RoomEncryptionTrustLevel.Default -> {
|
||||
contentDescription = context.getString(R.string.a11y_trust_level_default)
|
||||
setImageResource(
|
||||
if (borderLess) R.drawable.ic_shield_unknown_no_border
|
||||
else R.drawable.ic_shield_unknown
|
||||
)
|
||||
}
|
||||
else -> render(roomEncryptionTrustLevel, borderLess)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,9 +16,10 @@
|
|||
|
||||
package im.vector.app.features.analytics
|
||||
|
||||
import im.vector.app.features.analytics.errors.ErrorTracker
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface VectorAnalytics : AnalyticsTracker {
|
||||
interface VectorAnalytics : AnalyticsTracker, ErrorTracker {
|
||||
/**
|
||||
* Return a Flow of Boolean, true if the user has given their consent.
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.analytics.errors
|
||||
|
||||
interface ErrorTracker {
|
||||
fun trackError(throwable: Throwable)
|
||||
}
|
|
@ -41,7 +41,7 @@ private val IGNORED_OPTIONS: Options? = null
|
|||
@Singleton
|
||||
class DefaultVectorAnalytics @Inject constructor(
|
||||
postHogFactory: PostHogFactory,
|
||||
private val sentryFactory: SentryFactory,
|
||||
private val sentryAnalytics: SentryAnalytics,
|
||||
analyticsConfig: AnalyticsConfig,
|
||||
private val analyticsStore: AnalyticsStore,
|
||||
private val lateInitUserPropertiesFactory: LateInitUserPropertiesFactory,
|
||||
|
@ -97,7 +97,7 @@ class DefaultVectorAnalytics @Inject constructor(
|
|||
setAnalyticsId("")
|
||||
|
||||
// Close Sentry SDK.
|
||||
sentryFactory.stopSentry()
|
||||
sentryAnalytics.stopSentry()
|
||||
}
|
||||
|
||||
private fun observeAnalyticsId() {
|
||||
|
@ -135,8 +135,8 @@ class DefaultVectorAnalytics @Inject constructor(
|
|||
private fun initOrStopSentry() {
|
||||
userConsent?.let {
|
||||
when (it) {
|
||||
true -> sentryFactory.initSentry()
|
||||
false -> sentryFactory.stopSentry()
|
||||
true -> sentryAnalytics.initSentry()
|
||||
false -> sentryAnalytics.stopSentry()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -180,4 +180,10 @@ class DefaultVectorAnalytics @Inject constructor(
|
|||
putAll(this@toPostHogUserProperties.filter { it.value != null })
|
||||
}
|
||||
}
|
||||
|
||||
override fun trackError(throwable: Throwable) {
|
||||
sentryAnalytics
|
||||
.takeIf { userConsent == true }
|
||||
?.trackError(throwable)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ package im.vector.app.features.analytics.impl
|
|||
|
||||
import android.content.Context
|
||||
import im.vector.app.features.analytics.AnalyticsConfig
|
||||
import im.vector.app.features.analytics.errors.ErrorTracker
|
||||
import im.vector.app.features.analytics.log.analyticsTag
|
||||
import io.sentry.Sentry
|
||||
import io.sentry.SentryOptions
|
||||
|
@ -25,10 +26,10 @@ import io.sentry.android.core.SentryAndroid
|
|||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
class SentryFactory @Inject constructor(
|
||||
class SentryAnalytics @Inject constructor(
|
||||
private val context: Context,
|
||||
private val analyticsConfig: AnalyticsConfig,
|
||||
) {
|
||||
) : ErrorTracker {
|
||||
|
||||
fun initSentry() {
|
||||
Timber.tag(analyticsTag.value).d("Initializing Sentry")
|
||||
|
@ -47,4 +48,8 @@ class SentryFactory @Inject constructor(
|
|||
Timber.tag(analyticsTag.value).d("Stopping Sentry")
|
||||
Sentry.close()
|
||||
}
|
||||
|
||||
override fun trackError(throwable: Throwable) {
|
||||
Sentry.captureException(throwable)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.configuration
|
||||
|
||||
import im.vector.app.features.voicebroadcast.VoiceBroadcastConstants
|
||||
import org.matrix.android.sdk.api.provider.CustomEventTypesProvider
|
||||
import javax.inject.Inject
|
||||
|
||||
class VectorCustomEventTypesProvider @Inject constructor() : CustomEventTypesProvider {
|
||||
|
||||
override val customPreviewableEventTypes = listOf(
|
||||
VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO
|
||||
)
|
||||
}
|
|
@ -72,10 +72,11 @@ class IncomingVerificationRequestHandler @Inject constructor(
|
|||
val user = session.getUserOrDefault(tx.otherUserId).toMatrixItem()
|
||||
val name = user.getBestName()
|
||||
val alert = VerificationVectorAlert(
|
||||
uid,
|
||||
context.getString(R.string.sas_incoming_request_notif_title),
|
||||
context.getString(R.string.sas_incoming_request_notif_content, name),
|
||||
R.drawable.ic_shield_black,
|
||||
uid = uid,
|
||||
title = context.getString(R.string.sas_incoming_request_notif_title),
|
||||
description = context.getString(R.string.sas_incoming_request_notif_content, name),
|
||||
iconId = R.drawable.ic_shield_black,
|
||||
priority = PopupAlertManager.INCOMING_VERIFICATION_REQUEST_PRIORITY,
|
||||
shouldBeDisplayedIn = { activity ->
|
||||
if (activity is VectorBaseActivity<*>) {
|
||||
// TODO a bit too ugly :/
|
||||
|
@ -85,7 +86,7 @@ class IncomingVerificationRequestHandler @Inject constructor(
|
|||
}
|
||||
} ?: true
|
||||
} else true
|
||||
}
|
||||
},
|
||||
)
|
||||
.apply {
|
||||
viewBinder = VerificationVectorAlert.ViewBinder(user, avatarRenderer.get())
|
||||
|
@ -127,12 +128,9 @@ class IncomingVerificationRequestHandler @Inject constructor(
|
|||
// For incoming request we should prompt (if not in activity where this request apply)
|
||||
if (pr.isIncoming) {
|
||||
// if it's a self verification for my devices, we can discard the review login alert
|
||||
// if not this request will be underneath and not visible by the user...
|
||||
// if not, this request will be underneath and not visible by the user...
|
||||
// it will re-appear later
|
||||
if (pr.otherUserId == session?.myUserId) {
|
||||
// XXX this is a bit hard coded :/
|
||||
popupAlertManager.cancelAlert("review_login")
|
||||
}
|
||||
cancelAnyVerifySessionAlerts(pr)
|
||||
val user = session.getUserOrDefault(pr.otherUserId).toMatrixItem()
|
||||
val name = user.getBestName()
|
||||
val description = if (name == pr.otherUserId) {
|
||||
|
@ -142,21 +140,23 @@ class IncomingVerificationRequestHandler @Inject constructor(
|
|||
}
|
||||
|
||||
val alert = VerificationVectorAlert(
|
||||
uniqueIdForVerificationRequest(pr),
|
||||
context.getString(R.string.sas_incoming_request_notif_title),
|
||||
description,
|
||||
R.drawable.ic_shield_black,
|
||||
uid = uniqueIdForVerificationRequest(pr),
|
||||
title = context.getString(R.string.sas_incoming_request_notif_title),
|
||||
description = description,
|
||||
iconId = R.drawable.ic_shield_black,
|
||||
priority = PopupAlertManager.INCOMING_VERIFICATION_REQUEST_PRIORITY,
|
||||
shouldBeDisplayedIn = { activity ->
|
||||
if (activity is RoomDetailActivity) {
|
||||
activity.intent?.extras?.getParcelableCompat<TimelineArgs>(RoomDetailActivity.EXTRA_ROOM_DETAIL_ARGS)?.let {
|
||||
it.roomId != pr.roomId
|
||||
} ?: true
|
||||
} else true
|
||||
}
|
||||
},
|
||||
)
|
||||
.apply {
|
||||
viewBinder = VerificationVectorAlert.ViewBinder(user, avatarRenderer.get())
|
||||
contentAction = Runnable {
|
||||
cancelAnyVerifySessionAlerts(pr)
|
||||
(weakCurrentActivity?.get() as? VectorBaseActivity<*>)?.let {
|
||||
val roomId = pr.roomId
|
||||
if (roomId.isNullOrBlank()) {
|
||||
|
@ -186,6 +186,13 @@ class IncomingVerificationRequestHandler @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun cancelAnyVerifySessionAlerts(pr: PendingVerificationRequest) {
|
||||
if (pr.otherUserId == session?.myUserId) {
|
||||
popupAlertManager.cancelAlert(PopupAlertManager.REVIEW_LOGIN_UID)
|
||||
popupAlertManager.cancelAlert(PopupAlertManager.VERIFY_SESSION_UID)
|
||||
}
|
||||
}
|
||||
|
||||
override fun verificationRequestUpdated(pr: PendingVerificationRequest) {
|
||||
// If an incoming request is readied (by another device?) we should discard the alert
|
||||
if (pr.isIncoming && (pr.isReady || pr.handledByOtherSession || pr.cancelConclusion != null)) {
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
package im.vector.app.features.displayname
|
||||
|
||||
import org.matrix.android.sdk.api.MatrixItemDisplayNameFallbackProvider
|
||||
import org.matrix.android.sdk.api.provider.MatrixItemDisplayNameFallbackProvider
|
||||
import org.matrix.android.sdk.api.util.MatrixItem
|
||||
|
||||
// Used to provide the fallback to the MatrixSDK, in the MatrixConfiguration
|
||||
|
|
|
@ -452,9 +452,10 @@ class HomeActivity :
|
|||
private fun handleAskPasswordToInitCrossSigning(events: HomeActivityViewEvents.AskPasswordToInitCrossSigning) {
|
||||
// We need to ask
|
||||
promptSecurityEvent(
|
||||
events.userItem,
|
||||
R.string.upgrade_security,
|
||||
R.string.security_prompt_text
|
||||
uid = PopupAlertManager.UPGRADE_SECURITY_UID,
|
||||
userItem = events.userItem,
|
||||
titleRes = R.string.upgrade_security,
|
||||
descRes = R.string.security_prompt_text,
|
||||
) {
|
||||
it.navigator.upgradeSessionSecurity(it, true)
|
||||
}
|
||||
|
@ -463,9 +464,10 @@ class HomeActivity :
|
|||
private fun handleCrossSigningInvalidated(event: HomeActivityViewEvents.OnCrossSignedInvalidated) {
|
||||
// We need to ask
|
||||
promptSecurityEvent(
|
||||
event.userItem,
|
||||
R.string.crosssigning_verify_this_session,
|
||||
R.string.confirm_your_identity
|
||||
uid = PopupAlertManager.VERIFY_SESSION_UID,
|
||||
userItem = event.userItem,
|
||||
titleRes = R.string.crosssigning_verify_this_session,
|
||||
descRes = R.string.confirm_your_identity,
|
||||
) {
|
||||
it.navigator.waitSessionVerification(it)
|
||||
}
|
||||
|
@ -474,9 +476,10 @@ class HomeActivity :
|
|||
private fun handleOnNewSession(event: HomeActivityViewEvents.CurrentSessionNotVerified) {
|
||||
// We need to ask
|
||||
promptSecurityEvent(
|
||||
event.userItem,
|
||||
R.string.crosssigning_verify_this_session,
|
||||
R.string.confirm_your_identity
|
||||
uid = PopupAlertManager.VERIFY_SESSION_UID,
|
||||
userItem = event.userItem,
|
||||
titleRes = R.string.crosssigning_verify_this_session,
|
||||
descRes = R.string.confirm_your_identity,
|
||||
) {
|
||||
if (event.waitForIncomingRequest) {
|
||||
it.navigator.waitSessionVerification(it)
|
||||
|
@ -489,9 +492,10 @@ class HomeActivity :
|
|||
private fun handleCantVerify(event: HomeActivityViewEvents.CurrentSessionCannotBeVerified) {
|
||||
// We need to ask
|
||||
promptSecurityEvent(
|
||||
event.userItem,
|
||||
R.string.crosssigning_cannot_verify_this_session,
|
||||
R.string.crosssigning_cannot_verify_this_session_desc
|
||||
uid = PopupAlertManager.UPGRADE_SECURITY_UID,
|
||||
userItem = event.userItem,
|
||||
titleRes = R.string.crosssigning_cannot_verify_this_session,
|
||||
descRes = R.string.crosssigning_cannot_verify_this_session_desc,
|
||||
) {
|
||||
it.navigator.open4SSetup(it, SetupMode.PASSPHRASE_AND_NEEDED_SECRETS_RESET)
|
||||
}
|
||||
|
@ -500,7 +504,7 @@ class HomeActivity :
|
|||
private fun handlePromptToEnablePush() {
|
||||
popupAlertManager.postVectorAlert(
|
||||
DefaultVectorAlert(
|
||||
uid = "enablePush",
|
||||
uid = PopupAlertManager.ENABLE_PUSH_UID,
|
||||
title = getString(R.string.alert_push_are_disabled_title),
|
||||
description = getString(R.string.alert_push_are_disabled_description),
|
||||
iconId = R.drawable.ic_room_actions_notifications_mutes,
|
||||
|
@ -533,10 +537,16 @@ class HomeActivity :
|
|||
)
|
||||
}
|
||||
|
||||
private fun promptSecurityEvent(userItem: MatrixItem.UserItem, titleRes: Int, descRes: Int, action: ((VectorBaseActivity<*>) -> Unit)) {
|
||||
private fun promptSecurityEvent(
|
||||
uid: String,
|
||||
userItem: MatrixItem.UserItem,
|
||||
titleRes: Int,
|
||||
descRes: Int,
|
||||
action: ((VectorBaseActivity<*>) -> Unit),
|
||||
) {
|
||||
popupAlertManager.postVectorAlert(
|
||||
VerificationVectorAlert(
|
||||
uid = "upgradeSecurity",
|
||||
uid = uid,
|
||||
title = getString(titleRes),
|
||||
description = getString(descRes),
|
||||
iconId = R.drawable.ic_shield_warning
|
||||
|
@ -595,7 +605,9 @@ class HomeActivity :
|
|||
serverBackupStatusViewModel.refreshRemoteStateIfNeeded()
|
||||
|
||||
// Check nightly
|
||||
nightlyProxy.onHomeResumed()
|
||||
if (nightlyProxy.canDisplayPopup()) {
|
||||
nightlyProxy.updateApplication()
|
||||
}
|
||||
|
||||
checkNewAppLayoutFlagChange()
|
||||
}
|
||||
|
|
|
@ -256,6 +256,12 @@ class HomeActivityViewModel @AssistedInject constructor(
|
|||
// }
|
||||
|
||||
when {
|
||||
!vectorPreferences.areThreadMessagesEnabled() && !vectorPreferences.wasThreadFlagChangedManually() -> {
|
||||
vectorPreferences.setThreadMessagesEnabled()
|
||||
lightweightSettingsStorage.setThreadMessagesEnabled(vectorPreferences.areThreadMessagesEnabled())
|
||||
// Clear Cache
|
||||
_viewEvents.post(HomeActivityViewEvents.MigrateThreads(checkSession = false))
|
||||
}
|
||||
// Notify users
|
||||
vectorPreferences.shouldNotifyUserAboutThreads() && vectorPreferences.areThreadMessagesEnabled() -> {
|
||||
Timber.i("----> Notify users about threads")
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue