Merge tag 'v1.4.0' into sc
Change-Id: If953d524434cd8a7073a16014f4ec6850b72a226 Conflicts: library/ui-styles/src/main/res/values/styles_timeline.xml matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/media/PreviewUrlData.kt matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MessageInformationDataFactory.kt vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageItem.kt vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/BaseEventItem.kt vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageBlockCodeItem.kt vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageFileItem.kt vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageImageVideoItem.kt vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageTextItem.kt vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceItem.kt vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/ReadReceiptsItem.kt vector/src/main/java/im/vector/app/features/home/room/list/RoomListDisplayModeFilter.kt vector/src/main/java/im/vector/app/features/html/EventHtmlRenderer.kt vector/src/main/java/im/vector/app/features/media/ImageContentRenderer.kt vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt vector/src/main/java/im/vector/app/features/settings/VectorSettingsLabsFragment.kt vector/src/main/res/drawable/ic_filter.xml vector/src/main/res/layout/fragment_qr_code_scanner_with_button.xml vector/src/main/res/layout/fragment_timeline.xml vector/src/main/res/layout/item_bottom_sheet_message_preview.xml vector/src/main/res/layout/item_timeline_event_base.xml vector/src/main/res/layout/item_timeline_event_file_stub.xml vector/src/main/res/layout/item_timeline_event_notice_stub.xml vector/src/main/res/layout/item_timeline_event_voice_stub.xml vector/src/main/res/layout/view_voice_message_recorder.xml vector/src/main/res/menu/menu_timeline.xml
This commit is contained in:
commit
ecd28447c9
|
@ -1,6 +1,6 @@
|
|||
name: Release checklist
|
||||
description: Checklist for each release. This template is only for the core team.
|
||||
title: "[Release] Element Android v"
|
||||
title: "[Release] Element Android v"
|
||||
labels: [🚀 Release]
|
||||
assignees:
|
||||
- bmarty
|
||||
|
@ -10,7 +10,7 @@ body:
|
|||
id: checklist
|
||||
attributes:
|
||||
label: Release checklist
|
||||
description: For the template example, we are releasing the version 1.1.10. Replace 1.1.10 with the version in the issue body.
|
||||
description: For the template example, we are releasing the version 1.2.3. Replace 1.2.3 with the version in the issue body.
|
||||
placeholder: |
|
||||
If you are reading this, you have deleted the content of the release template: undo the deletion or start again.
|
||||
value: |
|
||||
|
@ -22,35 +22,41 @@ body:
|
|||
|
||||
### Do the release
|
||||
|
||||
- [ ] Create release with gitflow, branch name `release/1.1.10`
|
||||
- [ ] Create release with gitflow, branch name `release/1.2.3`
|
||||
- [ ] Check the crashes from the PlayStore
|
||||
- [ ] Check the rageshake with the current dev version: https://github.com/matrix-org/element-android-rageshakes/labels/1.1.10-dev
|
||||
- [ ] Check the rageshake with the current dev version: https://github.com/matrix-org/element-android-rageshakes/labels/1.2.3-dev
|
||||
- [ ] Run the integration test, and especially `UiAllScreensSanityTest.allScreensTest()`
|
||||
- [ ] Create an account on matrix.org
|
||||
- [ ] Run towncrier: `towncrier --version v1.1.10 --draft` (remove `--draft` do write the file CHANGES.md)
|
||||
- [ ] Create an account on matrix.org and do some smoke tests that the sanity test does not cover like: 1-1 call, 1-1 video call, Jitsi call for instance
|
||||
- [ ] Run towncrier: `towncrier --version v1.2.3 --draft` (remove `--draft` do write the file CHANGES.md)
|
||||
- [ ] Check that the folder `changelog.d` is empty. It can happen that some remaining files stay here
|
||||
- [ ] Check the file CHANGES.md consistency. It's possible to reorder items (most important changes first) or change their section if relevant. Also an opportunity to fix some typo, or rewrite things
|
||||
- [ ] Add file for fastlane under ./fastlane/metadata/android/en-US/changelogs
|
||||
- [ ] Push the branch and start a draft PR (will not be merged), to check that the CI is happy with all the changes.
|
||||
- [ ] Finish release with gitflow, delete the draft PR
|
||||
- [ ] Push `main` and the new tag `v1.1.10` to origin
|
||||
- [ ] (optional) Push the branch and start a draft PR (will not be merged), to check that the CI is happy with all the changes.
|
||||
- [ ] Finish release with gitflow, delete the draft PR (if created)
|
||||
- [ ] Push `main` and the new tag `v1.2.3` to origin
|
||||
- [ ] Checkout `develop`
|
||||
- [ ] Increase version in `./vector/build.gradle`
|
||||
- [ ] Increase version (versionPatch + 2) in `./vector/build.gradle`
|
||||
- [ ] Change the value of SDK_VERSION in the file `./matrix-sdk-android/build.gradle`
|
||||
- [ ] Commit and push `develop`
|
||||
- [ ] Wait for [Buildkite](https://buildkite.com/matrix-dot-org/element-android/builds?branch=main) to build the `main` branch.
|
||||
- [ ] Run the script `~/scripts/releaseElement.sh`. It will download the APKs from Buildkite check them and sign them.
|
||||
- [ ] Install the APK on your phone to check that the upgrade went well (no init sync, etc.)
|
||||
- [ ] 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
|
||||
- [ ] Add the 4 signed APKs to the GitHub release
|
||||
- [ ] Ping the Android Internal room
|
||||
|
||||
### Once tested and validated internally
|
||||
|
||||
- [ ] Create a new beta release on the GooglePlay console and upload the 4 signed Apks.
|
||||
- [ ] Check that the version codes are correct
|
||||
- [ ] Copy the fastlane change to the GooglePlay console in the section en-GB.
|
||||
- [ ] Push to beta release to 100% of the users
|
||||
- [ ] 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
|
||||
- [ ] Add the 4 signed APKs to the GitHub release
|
||||
- [ ] Ping the Android Internal room
|
||||
- [ ] Add an entry in the internal diary
|
||||
- [ ] Notify the F-Droid team so that they can schedule the publication on F-Droid
|
||||
|
||||
### Once Live on PlayStore
|
||||
|
||||
- [ ] Ping the Android public room and update its topic
|
||||
- [ ] Add an entry in the internal diary
|
||||
|
||||
### After at least 2 days
|
||||
|
||||
|
@ -62,6 +68,8 @@ body:
|
|||
|
||||
### Android SDK2
|
||||
|
||||
The SDK2 and the sample app are released only when Element has been pushed to production.
|
||||
|
||||
- [ ] Checkout the `main` branch on Element Android project
|
||||
|
||||
#### On the SDK2 project
|
||||
|
|
|
@ -180,6 +180,7 @@ jobs:
|
|||
body="$(cat ./matrix-sdk-android/build/outputs/androidTest-results/connected/*.xml | grep "<testsuite" | sed "s@.*tests=\(.*\)time=.*@\1@")"
|
||||
echo "::set-output name=permalink::passed=$body"
|
||||
- name: Find Comment
|
||||
if: github.event_name == 'pull_request'
|
||||
uses: peter-evans/find-comment@v1
|
||||
id: fc
|
||||
with:
|
||||
|
@ -187,6 +188,7 @@ jobs:
|
|||
comment-author: 'github-actions[bot]'
|
||||
body-includes: Integration Tests Results
|
||||
- name: Publish results to PR
|
||||
if: github.event_name == 'pull_request'
|
||||
uses: peter-evans/create-or-update-comment@v1
|
||||
with:
|
||||
comment-id: ${{ steps.fc.outputs.comment-id }}
|
||||
|
|
|
@ -54,7 +54,7 @@ jobs:
|
|||
echo "::set-output name=body::$body"
|
||||
fi
|
||||
- name: Find Comment
|
||||
if: always()
|
||||
if: always() && github.event_name == 'pull_request'
|
||||
uses: peter-evans/find-comment@v1
|
||||
id: fc
|
||||
with:
|
||||
|
@ -62,7 +62,7 @@ jobs:
|
|||
comment-author: 'github-actions[bot]'
|
||||
body-includes: Ktlint Results
|
||||
- name: Add comment if needed
|
||||
if: always() && steps.ktlint-results.outputs.add_comment == 'true'
|
||||
if: always() && github.event_name == 'pull_request' && steps.ktlint-results.outputs.add_comment == 'true'
|
||||
uses: peter-evans/create-or-update-comment@v1
|
||||
with:
|
||||
comment-id: ${{ steps.fc.outputs.comment-id }}
|
||||
|
@ -73,7 +73,7 @@ jobs:
|
|||
${{ steps.ktlint-results.outputs.body }}
|
||||
edit-mode: replace
|
||||
- name: Delete comment if needed
|
||||
if: always() && steps.fc.outputs.comment-id != '' && steps.ktlint-results.outputs.add_comment == 'false'
|
||||
if: always() && github.event_name == 'pull_request' && steps.fc.outputs.comment-id != '' && steps.ktlint-results.outputs.add_comment == 'false'
|
||||
uses: actions/github-script@v3
|
||||
with:
|
||||
script: |
|
||||
|
|
|
@ -202,3 +202,53 @@ jobs:
|
|||
env:
|
||||
PROJECT_ID: "PN_kwDOAM0swc3m-g"
|
||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
||||
move_ftue_issues:
|
||||
name: Z-FTUE to Mobile FTUE board
|
||||
runs-on: ubuntu-latest
|
||||
# Skip in forks
|
||||
if: >
|
||||
github.repository == 'vector-im/element-android' &&
|
||||
contains(github.event.issue.labels.*.name, 'Z-FTUE')
|
||||
steps:
|
||||
- uses: octokit/graphql-action@v2.x
|
||||
with:
|
||||
headers: '{"GraphQL-Features": "projects_next_graphql"}'
|
||||
query: |
|
||||
mutation add_to_project($projectid:ID!,$contentid:ID!) {
|
||||
addProjectNextItem(input:{projectId:$projectid contentId:$contentid}) {
|
||||
projectNextItem {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
projectid: ${{ env.PROJECT_ID }}
|
||||
contentid: ${{ github.event.issue.node_id }}
|
||||
env:
|
||||
PROJECT_ID: "PN_kwDOAM0swc4AAqVx"
|
||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
||||
move_WTF_issues:
|
||||
name: Z-WTF to WTF board
|
||||
runs-on: ubuntu-latest
|
||||
# Skip in forks
|
||||
if: >
|
||||
github.repository == 'vector-im/element-android' &&
|
||||
contains(github.event.issue.labels.*.name, 'Z-WTF')
|
||||
steps:
|
||||
- uses: octokit/graphql-action@v2.x
|
||||
with:
|
||||
headers: '{"GraphQL-Features": "projects_next_graphql"}'
|
||||
query: |
|
||||
mutation add_to_project($projectid:ID!,$contentid:ID!) {
|
||||
addProjectNextItem(input:{projectId:$projectid contentId:$contentid}) {
|
||||
projectNextItem {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
projectid: ${{ env.PROJECT_ID }}
|
||||
contentid: ${{ github.event.issue.node_id }}
|
||||
env:
|
||||
PROJECT_ID: "PN_kwDOAM0swc4AArk0"
|
||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
|
57
CHANGES.md
57
CHANGES.md
|
@ -1,3 +1,55 @@
|
|||
Changes in Element v1.4.0 (2022-02-09)
|
||||
======================================
|
||||
|
||||
Features ✨
|
||||
----------
|
||||
- Initial implementation of thread messages ([#4746](https://github.com/vector-im/element-android/issues/4746))
|
||||
- Support message bubbles in timeline. ([#4937](https://github.com/vector-im/element-android/issues/4937))
|
||||
- Support generic location pin ([#5146](https://github.com/vector-im/element-android/issues/5146))
|
||||
- Retrieve map style url from .well-known ([#5175](https://github.com/vector-im/element-android/issues/5175))
|
||||
|
||||
Bugfixes 🐛
|
||||
----------
|
||||
- Fixes non sans-serif font weights being ignored ([#3907](https://github.com/vector-im/element-android/issues/3907))
|
||||
- Fixing missing/intermittent notifications on the google play variant when wifi is enabled ([#5038](https://github.com/vector-im/element-android/issues/5038))
|
||||
- Fixes call statuses in the timeline for missed/rejected calls and connected calls. ([#5088](https://github.com/vector-im/element-android/issues/5088))
|
||||
- Fix fallback permalink when threads are disabled ([#5128](https://github.com/vector-im/element-android/issues/5128))
|
||||
- Analytics: aligns use case identifying with iOS implementation ([#5142](https://github.com/vector-im/element-android/issues/5142))
|
||||
- Fix location rendering in timeline if map cannot be loaded ([#5143](https://github.com/vector-im/element-android/issues/5143))
|
||||
|
||||
Other changes
|
||||
-------------
|
||||
- "Invite users to space" dialog now closed when user choose invite method ([#4295](https://github.com/vector-im/element-android/issues/4295))
|
||||
- Changed layout for space card and room card used at "explore room" screen and space/room invite dialogs ([#4304](https://github.com/vector-im/element-android/issues/4304))
|
||||
- Removed spaces restricted search hint dialogs ([#4315](https://github.com/vector-im/element-android/issues/4315))
|
||||
- Remove Search from room options if not available ([#4641](https://github.com/vector-im/element-android/issues/4641))
|
||||
- Qr code scanning fragments merged into one ([#4873](https://github.com/vector-im/element-android/issues/4873))
|
||||
- Fix CI/CD errors after merges for quality and integration tests ([#5118](https://github.com/vector-im/element-android/issues/5118))
|
||||
- Added automation for the Z-FTUE label to add issues to the FTUE Project Board ([#5120](https://github.com/vector-im/element-android/issues/5120))
|
||||
- Added automation for WTF labels to move to WTF project board ([#5148](https://github.com/vector-im/element-android/issues/5148))
|
||||
- Update WTF automation to fix it ([#5173](https://github.com/vector-im/element-android/issues/5173))
|
||||
|
||||
|
||||
Changes in Element v1.3.18 (2022-02-03)
|
||||
=======================================
|
||||
|
||||
Bugfixes 🐛
|
||||
----------
|
||||
- Avoid deleting root event of CurrentState on gappy sync. In order to restore lost Events an initial sync may be triggered. ([#5137](https://github.com/vector-im/element-android/issues/5137))
|
||||
|
||||
|
||||
Changes in Element v1.3.17 (2022-01-31)
|
||||
=======================================
|
||||
|
||||
Bugfixes 🐛
|
||||
----------
|
||||
- Display static map images in the timeline and improve Location sharing feature ([#5084](https://github.com/vector-im/element-android/issues/5084))
|
||||
- Show the legal mention of mapbox when sharing location ([#5062](https://github.com/vector-im/element-android/issues/5062))
|
||||
- Poll cannot end in some unencrypted rooms ([#5067](https://github.com/vector-im/element-android/issues/5067))
|
||||
- Selecting Transfer in a call should immediately put the other person on hold until the call connects or the Transfer is cancelled. ([#5081](https://github.com/vector-im/element-android/issues/5081))
|
||||
- Fixing crashes when quickly scrolling or restoring the room timeline ([#5091](https://github.com/vector-im/element-android/issues/5091))
|
||||
|
||||
|
||||
Changes in Element 1.3.16 (2022-01-25)
|
||||
======================================
|
||||
|
||||
|
@ -47,6 +99,11 @@ Other changes
|
|||
- Exclude dependabot upgrade for @github-script@v3 ([#4988](https://github.com/vector-im/element-android/issues/4988))
|
||||
- Small iteration on command parser and unit test it. ([#4998](https://github.com/vector-im/element-android/issues/4998))
|
||||
|
||||
SDK API changes ⚠️
|
||||
------------------
|
||||
- `StateService.sendStateEvent()` now takes a non-nullable String for the parameter `stateKey`. If null was used, just now use an empty string. ([#4895](https://github.com/vector-im/element-android/issues/4895))
|
||||
- 429 are not automatically retried anymore in case of too long retry delay ([#4995](https://github.com/vector-im/element-android/issues/4995))
|
||||
|
||||
|
||||
Changes in Element v1.3.15 (2022-01-18)
|
||||
=======================================
|
||||
|
|
|
@ -14,7 +14,8 @@ It is a total rewrite of [Riot-Android](https://github.com/vector-im/riot-androi
|
|||
[<img src="resources/img/google-play-badge.png" alt="Get it on Google Play" height="60">](https://play.google.com/store/apps/details?id=im.vector.app)
|
||||
[<img src="resources/img/f-droid-badge.png" alt="Get it on F-Droid" height="60">](https://f-droid.org/app/im.vector.app)
|
||||
|
||||
Nightly build: [![Buildkite](https://badge.buildkite.com/ad0065c1b70f557cd3b1d3d68f9c2154010f83c4d6f71706a9.svg?branch=develop)](https://buildkite.com/matrix-dot-org/element-android/builds?branch=develop)
|
||||
Nightly build: [![Buildkite](https://badge.buildkite.com/ad0065c1b70f557cd3b1d3d68f9c2154010f83c4d6f71706a9.svg?branch=develop)](https://buildkite.com/matrix-dot-org/element-android/builds?branch=develop) Nighly sanity test status: [![allScreensTest](https://github.com/vector-im/element-android/actions/workflows/sanity_test.yml/badge.svg)](https://github.com/vector-im/element-android/actions/workflows/sanity_test.yml)
|
||||
|
||||
|
||||
# New Android SDK
|
||||
|
||||
|
|
17
build.gradle
17
build.gradle
|
@ -36,6 +36,12 @@ allprojects {
|
|||
apply plugin: "org.jlleitschuh.gradle.ktlint"
|
||||
|
||||
repositories {
|
||||
mavenCentral {
|
||||
content {
|
||||
groups.mavenCentral.regex.each { includeGroupByRegex it }
|
||||
groups.mavenCentral.group.each { includeGroup it }
|
||||
}
|
||||
}
|
||||
maven {
|
||||
url 'https://jitpack.io'
|
||||
content {
|
||||
|
@ -59,12 +65,6 @@ allprojects {
|
|||
groups.google.group.each { includeGroup it }
|
||||
}
|
||||
}
|
||||
mavenCentral {
|
||||
content {
|
||||
groups.mavenCentral.regex.each { includeGroupByRegex it }
|
||||
groups.mavenCentral.group.each { includeGroup it }
|
||||
}
|
||||
}
|
||||
//noinspection JcenterRepositoryObsolete
|
||||
jcenter {
|
||||
content {
|
||||
|
@ -144,6 +144,11 @@ project(":diff-match-patch") {
|
|||
}
|
||||
}
|
||||
|
||||
// Global configurations across all modules
|
||||
ext {
|
||||
isThreadingEnabled = true
|
||||
}
|
||||
|
||||
//project(":matrix-sdk-android") {
|
||||
// sonarqube {
|
||||
// properties {
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
`StateService.sendStateEvent()` now takes a non-nullable String for the parameter `stateKey`. If null was used, just now use an empty string.
|
|
@ -1 +0,0 @@
|
|||
429 are not automatically retried anymore in case of too long retry delay
|
|
@ -0,0 +1,2 @@
|
|||
Hlavní změny v této verzi: Změna na úvodních obrazovkách, včetně přihlášení do služby Analytics. V experimentálních funkcích byla přidána podpora pro události s matematikou.
|
||||
Úplný seznam změn: https://github.com/vector-im/element-android/releases/tag/v1.3.13
|
|
@ -0,0 +1,2 @@
|
|||
Hlavní změny v této verzi: Změna na úvodních obrazovkách, včetně přihlášení do služby Analytics. V experimentálních funkcích byla přidána podpora pro události s matematikou.
|
||||
Úplný seznam změn: https://github.com/vector-im/element-android/releases/tag/v1.3.14
|
|
@ -0,0 +1,2 @@
|
|||
Hlavní změny v této verzi: Změna na úvodních obrazovkách, včetně přihlášení do služby Analytics. V experimentálních funkcích byla přidána podpora pro události s matematikou.
|
||||
Úplný seznam změn: https://github.com/vector-im/element-android/releases/tag/v1.3.15
|
|
@ -0,0 +1,2 @@
|
|||
Hlavní změny v této verzi: Odeslání vlastní polohy do libovolné místnosti. Možnost úpravy hlasování.
|
||||
Úplný seznam změn: https://github.com/vector-im/element-android/releases/tag/v1.3.16
|
|
@ -0,0 +1,2 @@
|
|||
Hauptänderungen: Neues Onboarding, Unterstützung für Mathematische Ausdrücke in Labs
|
||||
Änderungsliste: https://github.com/vector-im/element-android/releases/tag/v1.3.13
|
|
@ -0,0 +1,2 @@
|
|||
Hauptänderungen: Neues Onboarding, Unterstützung für Mathematische Ausdrücke in Labs
|
||||
Änderungsliste: https://github.com/vector-im/element-android/releases/tag/v1.3.14
|
|
@ -0,0 +1,2 @@
|
|||
Hauptänderungen: Neues Onboarding, Unterstützung für Mathematische Ausdrücke in Labs
|
||||
Änderungsliste: https://github.com/vector-im/element-android/releases/tag/v1.3.15
|
|
@ -0,0 +1,2 @@
|
|||
Hauptänderungen: Du kannst ab sofort deinen Standort an deine Räume senden und Abstimmungen bearbeiten.
|
||||
Alle Änderungen: https://github.com/vector-im/element-android/releases/tag/v1.3.16
|
|
@ -0,0 +1,2 @@
|
|||
Main changes in this version: send your location to any room. Edit poll.
|
||||
Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.3.17
|
|
@ -0,0 +1,2 @@
|
|||
Main changes in this version: send your location to any room. Edit poll.
|
||||
Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.3.18
|
|
@ -0,0 +1,2 @@
|
|||
Main changes in this version: Initial implementation of thread messages. Message bubbles.
|
||||
Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.4.0
|
|
@ -0,0 +1,2 @@
|
|||
Põhilised muutused selles versioonis: liitumisvaate täiendused, võimalus saata meile analüütikat. Katsete alla on lisandunud üritused ning matemaatiliste valemite kirjutamise võimalus.
|
||||
Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases/tag/v1.3.13
|
|
@ -0,0 +1,2 @@
|
|||
Põhilised muutused selles versioonis: liitumisvaate täiendused, võimalus saata meile analüütikat. Katsete alla on lisandunud üritused ning matemaatiliste valemite kirjutamise võimalus.
|
||||
Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases/tag/v1.3.14
|
|
@ -0,0 +1,2 @@
|
|||
Põhilised muutused selles versioonis: liitumisvaate täiendused, võimalus saata meile analüütikat. Katsete alla on lisandunud üritused ning matemaatiliste valemite kirjutamise võimalus.
|
||||
Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases/tag/v1.3.15
|
|
@ -0,0 +1,2 @@
|
|||
Põhilised muutused selles versioonis: oma asukoha saatmine jututuppa ja küsitluste muutmise võimalus.
|
||||
Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases/tag/v1.3.16
|
|
@ -0,0 +1,2 @@
|
|||
تغییرات عمده در این نگارشنخستین تغییر در صفحههای راهاندازی شامل وارد شدن به تجزیهها. پشتیبانی از رویدادهایی با ریاضیات افزوده در آزمایشگاهها.
|
||||
گزارش دگرگونی کامل: https://github.com/vector-im/element-android/releases/tag/v1.3.13
|
|
@ -0,0 +1,2 @@
|
|||
تغییرات عمده در این نگارشنخستین تغییر در صفحههای راهاندازی شامل وارد شدن به تجزیهها. پشتیبانی از رویدادهایی با ریاضیات افزوده در آزمایشگاهها.
|
||||
گزارش دگرگونی کامل: https://github.com/vector-im/element-android/releases/tag/v1.3.14
|
|
@ -0,0 +1,2 @@
|
|||
تغییرات عمده در این نگارشنخستین تغییر در صفحههای راهاندازی شامل وارد شدن به تجزیهها. پشتیبانی از رویدادهایی با ریاضیات افزوده در آزمایشگاهها.
|
||||
گزارش دگرگونی کامل: https://github.com/vector-im/element-android/releases/tag/v1.3.15
|
|
@ -0,0 +1,2 @@
|
|||
تغییرات عمده در این نگارش: فرستادن مکانتان به هر اتاقی. ویرایش نظرسنجی.
|
||||
گزارش دگرگونی کامل: https://github.com/vector-im/element-android/releases/tag/v1.3.16
|
|
@ -0,0 +1,2 @@
|
|||
Principaux changements pour cette version : Premier changement dans l’écran de bienvenue, y compris l’adhésion aux données d’analyses. Support des événements avec opération mathématiques ajoutées dans les labs.
|
||||
Intégralité des changements : https://github.com/vector-im/element-android/releases/tag/v1.3.13
|
|
@ -0,0 +1,2 @@
|
|||
Principaux changements pour cette version : Premier changement dans l’écran de bienvenue, y compris l’adhésion aux données d’analyses. Support des événements avec opération mathématiques ajoutées dans les labs.
|
||||
Intégralité des changements : https://github.com/vector-im/element-android/releases/tag/v1.3.14
|
|
@ -0,0 +1,2 @@
|
|||
Principaux changements pour cette version : Premier changement dans l’écran de bienvenue, y compris l’adhésion aux données d’analyses. Support des événements avec opération mathématiques ajoutées dans les labs.
|
||||
Intégralité des changements : https://github.com/vector-im/element-android/releases/tag/v1.3.15
|
|
@ -0,0 +1,2 @@
|
|||
Principaux changements pour cette version : envoi de votre position dans n’importe quelle salon. Édition des sondage.
|
||||
Intégralité des changements : https://github.com/vector-im/element-android/releases/tag/v1.3.16
|
|
@ -0,0 +1,2 @@
|
|||
Fő változás ebben a verzióban: Első változások a bemutató képernyőn, beleértve az analitikai adatküldés engedélyezésének lehetőségét. Matematikai formulák támogatása a Laborok között.
|
||||
Teljes változásnapló: https://github.com/vector-im/element-android/releases/tag/v1.3.13
|
|
@ -0,0 +1,2 @@
|
|||
Fő változás ebben a verzióban: Első változások a bemutató képernyőn, beleértve az analitikai adatküldés engedélyezésének lehetőségét. Matematikai formulák támogatása a Laborok között.
|
||||
Teljes változásnapló: https://github.com/vector-im/element-android/releases/tag/v1.3.14
|
|
@ -0,0 +1,2 @@
|
|||
Fő változás ebben a verzióban: Első változások a bemutató képernyőn, beleértve az analitikai adatküldés engedélyezésének lehetőségét. Matematikai formulák támogatása a Laborok között.
|
||||
Teljes változásnapló: https://github.com/vector-im/element-android/releases/tag/v1.3.15
|
|
@ -0,0 +1,2 @@
|
|||
Fő változás ebben a verzióban: földrajzi helyzet küldése bármely szobába. Szavazás szerkesztése
|
||||
Teljes változásnapló: https://github.com/vector-im/element-android/releases/tag/v1.3.16
|
|
@ -0,0 +1,2 @@
|
|||
Perubahan utama dalam versi ini: Perubahan pertama di layar permulaan, termasuk analitik opt-in. Dukungan untuk Peristiwa dengan Matematika ditambahkan di Uji Coba.
|
||||
Changelog lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.3.13
|
|
@ -0,0 +1,2 @@
|
|||
Perubahan utama dalam versi ini: Perubahan pertama di layar permulaan, termasuk analitik opt-in. Dukungan untuk Peristiwa dengan Matematika ditambahkan di Uji Coba.
|
||||
Changelog lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.3.14
|
|
@ -0,0 +1,2 @@
|
|||
Perubahan utama dalam versi ini: Perubahan pertama di layar permulaan, termasuk analitik opt-in. Dukungan untuk Peristiwa dengan Matematika ditambahkan di Uji Coba.
|
||||
Changelog lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.3.15
|
|
@ -0,0 +1,2 @@
|
|||
Perubahan utama dalam versi ini: Kirim lokasi Anda ke ruangan apa saja. Edit poll.
|
||||
Changelog lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.3.16
|
|
@ -0,0 +1,2 @@
|
|||
Modifiche principali in questa versione: prima modifica nelle schermate onboarding, incluso l'opt-in di Analytics. Supporto agli eventi con Math aggiunto nei laboratori.
|
||||
Cronologia completa: https://github.com/vector-im/element-android/releases/tag/v1.3.13
|
|
@ -0,0 +1,2 @@
|
|||
Modifiche principali in questa versione: prima modifica nelle schermate onboarding, incluso l'opt-in di Analytics. Supporto agli eventi con Math aggiunto nei laboratori.
|
||||
Cronologia completa: https://github.com/vector-im/element-android/releases/tag/v1.3.14
|
|
@ -0,0 +1,2 @@
|
|||
Modifiche principali in questa versione: prima modifica nelle schermate onboarding, incluso l'opt-in di Analytics. Supporto agli eventi con Math aggiunto nei laboratori.
|
||||
Cronologia completa: https://github.com/vector-im/element-android/releases/tag/v1.3.15
|
|
@ -0,0 +1,2 @@
|
|||
Modifiche principali in questa versione: invia la tua posizione in qualsiasi stanza. Modifica sondaggi.
|
||||
Cronologia completa: https://github.com/vector-im/element-android/releases/tag/v1.3.16
|
|
@ -0,0 +1,2 @@
|
|||
Principais mudanças nesta versão: Primeira mudança em telas de onboarding, incluindo opt-in de Analítica. Suporte para Eventos com Matemática adicionado nos labs.
|
||||
Changelog completo: https://github.com/vector-im/element-android/releases/tag/v1.3.13
|
|
@ -0,0 +1,2 @@
|
|||
Principais mudanças nesta versão: Primeira mudança em telas de onboarding, incluindo opt-in de Analítica. Suporte para Eventos com Matemática adicionado nos labs.
|
||||
Changelog completo: https://github.com/vector-im/element-android/releases/tag/v1.3.14
|
|
@ -0,0 +1,2 @@
|
|||
Principais mudanças nesta versão: Primeira mudança em telas de onboarding, incluindo opt-in de Analítica. Suporte para Eventos com Matemática adicionado nos labs.
|
||||
Changelog completo: https://github.com/vector-im/element-android/releases/tag/v1.3.15
|
|
@ -0,0 +1,2 @@
|
|||
Principais mudanças nesta versão: envie sua localização para qualquer sala. Editar sondagem.
|
||||
Changelog completo: https://github.com/vector-im/element-android/releases/tag/v1.3.16
|
|
@ -0,0 +1,2 @@
|
|||
Основные изменения в этой версии: отправьте свое местоположение в любую комнату. Редактирование опроса.
|
||||
Полный список изменений: https://github.com/vector-im/element-android/releases/tag/v1.3.16
|
|
@ -0,0 +1,2 @@
|
|||
Hlavné zmeny v tejto verzii: Prvá zmena v obrazovkách pri vstupe do systému vrátane prihlásenia do služby Analytics. Pridanie podpory pre udalosti s matematikou v laboratóriách.
|
||||
Úplný zoznam zmien: https://github.com/vector-im/element-android/releases/tag/v1.3.13
|
|
@ -0,0 +1,2 @@
|
|||
Hlavné zmeny v tejto verzii: Prvá zmena v obrazovkách pri vstupe do systému vrátane prihlásenia do služby Analytics. Pridanie podpory pre udalosti s matematikou v laboratóriách.
|
||||
Úplný zoznam zmien: https://github.com/vector-im/element-android/releases/tag/v1.3.14
|
|
@ -0,0 +1,2 @@
|
|||
Hlavné zmeny v tejto verzii: Prvá zmena v obrazovkách pri vstupe do systému vrátane prihlásenia do služby Analytics. Pridanie podpory pre udalosti s matematikou v laboratóriách.
|
||||
Úplný zoznam zmien: https://github.com/vector-im/element-android/releases/tag/v1.3.15
|
|
@ -0,0 +1,2 @@
|
|||
Hlavné zmeny v tejto verzii: odoslanie polohy do ľubovoľnej miestnosti. Úprava ankety.
|
||||
Úplný zoznam zmien: https://github.com/vector-im/element-android/releases/tag/v1.3.16
|
|
@ -0,0 +1,2 @@
|
|||
Ndryshimet kryesore në këtë version: Ndryshimi i parë në skenat e mirëseardhjes, përfshi zgjedhje për pjesëmarrje në Analiza. Në laboratorë u shtua mbulim për Akte me Formula Matematikore.
|
||||
Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.3.14
|
|
@ -0,0 +1,2 @@
|
|||
Ndryshimet kryesore në këtë version: Ndryshimi i parë në skenat e mirëseardhjes, përfshi zgjedhje për pjesëmarrje në Analiza. Në laboratorë u shtua mbulim për Akte me Formula Matematikore.
|
||||
Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.3.15
|
|
@ -0,0 +1,2 @@
|
|||
Ndryshimet kryesore në këtë version: dërgojeni vendndodhjen tuaj te cilado dhomë. Përpunoni pyetësor.
|
||||
Regjistër i plotës ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.3.16
|
|
@ -0,0 +1,2 @@
|
|||
Huvudsakliga ändringar i den här versionen: Första ändringen på introduktionsskärmar, inklusive opt-in för statistik. Stöd för händelser med matte tillagd i experiment.
|
||||
Full ändringslogg: https://github.com/vector-im/element-android/releases/tag/v1.3.13
|
|
@ -0,0 +1,2 @@
|
|||
Huvudsakliga ändringar i den här versionen: Första ändringen på introduktionsskärmar, inklusive opt-in för statistik. Stöd för händelser med matte tillagd i experiment.
|
||||
Full ändringslogg: https://github.com/vector-im/element-android/releases/tag/v1.3.14
|
|
@ -0,0 +1,2 @@
|
|||
Huvudsakliga ändringar i den här versionen: Första ändringen på introduktionsskärmar, inklusive opt-in för statistik. Stöd för händelser med matte tillagd i experiment.
|
||||
Full ändringslogg: https://github.com/vector-im/element-android/releases/tag/v1.3.15
|
|
@ -0,0 +1,2 @@
|
|||
Huvudsakliga ändringar i den här versionen: skicka din plats till vilket rum som helst. Redigera omröstningar.
|
||||
Full ändringslogg: https://github.com/vector-im/element-android/releases/tag/v1.3.16
|
|
@ -0,0 +1,2 @@
|
|||
Основні зміни у цій версії: перша зміна на екрані привітання, включно з увімкненням аналітики. У лабораторії додано підтримку подій з математичними формулами.
|
||||
Вичерпний перелік змін: https://github.com/vector-im/element-android/releases/tag/v1.3.13
|
|
@ -0,0 +1,2 @@
|
|||
Основні зміни у цій версії: перша зміна на екрані привітання, включно з увімкненням аналітики. У лабораторії додано підтримку подій з математичними формулами.
|
||||
Вичерпний перелік змін: https://github.com/vector-im/element-android/releases/tag/v1.3.14
|
|
@ -0,0 +1,2 @@
|
|||
Основні зміни у цій версії: перша зміна на екрані привітання, включно з увімкненням аналітики. У лабораторії додано підтримку подій з математичними формулами.
|
||||
Вичерпний перелік змін: https://github.com/vector-im/element-android/releases/tag/v1.3.15
|
|
@ -0,0 +1,2 @@
|
|||
Основні зміни у цій версії: надсилання свого місцеперебування у будь-яку кімнату. Редагування опитувань.
|
||||
Вичерпний перелік змін: https://github.com/vector-im/element-android/releases/tag/v1.3.16
|
|
@ -0,0 +1,2 @@
|
|||
此版本中的主要變動:首次使用畫面的第一個變化,包含了選擇加入的分析功能。新增對數學活動的支援至實驗室中。
|
||||
完整的變更紀錄:https://github.com/vector-im/element-android/releases/tag/v1.3.13
|
|
@ -0,0 +1,2 @@
|
|||
此版本中的主要變動:首次使用畫面的第一個變化,包含了選擇加入的分析功能。新增對數學活動的支援至實驗室中。
|
||||
完整的變更紀錄:https://github.com/vector-im/element-android/releases/tag/v1.3.14
|
|
@ -0,0 +1,2 @@
|
|||
此版本中的主要變動:首次使用畫面的第一個變化,包含了選擇加入的分析功能。新增對數學活動的支援至實驗室中。
|
||||
完整的變更紀錄:https://github.com/vector-im/element-android/releases/tag/v1.3.15
|
|
@ -0,0 +1,2 @@
|
|||
此版本中的主要變動:將您的位置傳送給任何聊天室。編輯投票。
|
||||
完整的變更紀錄:https://github.com/vector-im/element-android/releases/tag/v1.3.16
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<translate
|
||||
android:fromXDelta="-100%p" android:toXDelta="0"
|
||||
android:duration="@android:integer/config_mediumAnimTime"/>
|
||||
</set>
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<translate
|
||||
android:startOffset="250"
|
||||
android:fromXDelta="100%p" android:toXDelta="0"
|
||||
android:duration="@android:integer/config_mediumAnimTime"/>
|
||||
</set>
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<translate
|
||||
android:startOffset="250"
|
||||
android:fromXDelta="0" android:toXDelta="-100%p"
|
||||
android:duration="@android:integer/config_mediumAnimTime"/>
|
||||
</set>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<translate
|
||||
android:fromXDelta="0" android:toXDelta="100%p"
|
||||
android:duration="@android:integer/config_mediumAnimTime"/>
|
||||
</set>
|
|
@ -2,9 +2,6 @@
|
|||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- Tint color is provided by the theme -->
|
||||
<solid android:color="@android:color/black" />
|
||||
<size
|
||||
android:width="240dp"
|
||||
android:height="44dp" />
|
||||
<corners
|
||||
android:bottomLeftRadius="12dp"
|
||||
android:bottomRightRadius="12dp"
|
|
@ -2,16 +2,14 @@
|
|||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item android:id="@android:id/background">
|
||||
<shape>
|
||||
<corners android:radius="8dp" />
|
||||
<solid android:color="?vctr_room_active_widgets_banner_bg" />
|
||||
<shape android:shape="oval">
|
||||
<solid android:color="?vctr_system" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<item android:id="@android:id/progress">
|
||||
<clip>
|
||||
<shape>
|
||||
<corners android:radius="8dp" />
|
||||
<shape android:shape="oval">
|
||||
<solid android:color="@color/vctr_notice_secondary_alpha12" />
|
||||
</shape>
|
||||
</clip>
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<dimen name="menu_item_ripple_size">28dp</dimen>
|
||||
</resources>
|
|
@ -140,4 +140,5 @@
|
|||
<attr name="vctr_presence_indicator_offline" format="color" />
|
||||
<color name="vctr_presence_indicator_offline_light">@color/palette_gray_100</color>
|
||||
<color name="vctr_presence_indicator_offline_dark">@color/palette_gray_450</color>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<!-- Timeline bubble background colors -->
|
||||
<attr name="vctr_message_bubble_inbound" format="color" />
|
||||
<color name="vctr_message_bubble_inbound_light">#E8EDF4</color>
|
||||
<color name="vctr_message_bubble_inbound_dark">#21262C</color>
|
||||
<attr name="vctr_message_bubble_outbound" format="color" />
|
||||
<color name="vctr_message_bubble_outbound_light">#E7F8F3</color>
|
||||
<color name="vctr_message_bubble_outbound_dark">#133A34</color>
|
||||
</resources>
|
|
@ -15,6 +15,8 @@
|
|||
<dimen name="item_decoration_left_margin">72dp</dimen>
|
||||
<dimen name="item_event_message_state_size">16dp</dimen>
|
||||
|
||||
<dimen name="item_event_message_media_button_size">32dp</dimen>
|
||||
|
||||
<dimen name="chat_avatar_size">40dp</dimen>
|
||||
<dimen name="member_list_avatar_size">60dp</dimen>
|
||||
|
||||
|
@ -42,6 +44,11 @@
|
|||
|
||||
<!-- Preview Url -->
|
||||
<dimen name="preview_url_view_corner_radius">8dp</dimen>
|
||||
<dimen name="preview_url_view_image_max_height">160dp</dimen>
|
||||
|
||||
<dimen name="menu_item_icon_size">24dp</dimen>
|
||||
<dimen name="menu_item_size">48dp</dimen>
|
||||
<dimen name="menu_item_ripple_size">48dp</dimen>
|
||||
|
||||
<!-- Composer -->
|
||||
<dimen name="composer_min_height">48dp</dimen>
|
||||
|
@ -52,6 +59,12 @@
|
|||
<dimen name="composer_attachment_height">48dp</dimen>
|
||||
<dimen name="composer_attachment_margin">1dp</dimen>
|
||||
|
||||
|
||||
<dimen name="chat_bubble_margin_start">28dp</dimen>
|
||||
<dimen name="chat_bubble_margin_end">62dp</dimen>
|
||||
<dimen name="chat_bubble_fixed_size">300dp</dimen>
|
||||
<dimen name="chat_bubble_corner_radius">12dp</dimen>
|
||||
|
||||
<!-- Onboarding -->
|
||||
<item name="ftue_auth_gutter_start_percent" format="float" type="dimen">0.05</item>
|
||||
<item name="ftue_auth_gutter_end_percent" format="float" type="dimen">0.95</item>
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<declare-styleable name="MessageBubble">
|
||||
<attr name="incoming_style" format="boolean" />
|
||||
<attr name="show_time_overlay" format="boolean" />
|
||||
<attr name="is_first" format="boolean" />
|
||||
<attr name="is_last" format="boolean" />
|
||||
</declare-styleable>
|
||||
|
||||
</resources>
|
|
@ -6,6 +6,7 @@
|
|||
<style name="Widget.Vector.ProgressBar.Horizontal.File">
|
||||
<item name="android:indeterminateOnly">false</item>
|
||||
<item name="android:progressDrawable">@drawable/file_progress_bar</item>
|
||||
<item name="android:progressBackgroundTint">?android:colorBackground</item>
|
||||
<item name="android:minHeight">10dp</item>
|
||||
<item name="android:maxHeight">40dp</item>
|
||||
</style>
|
||||
|
|
|
@ -4,12 +4,23 @@
|
|||
<style name="TimelineContentStubBaseParams">
|
||||
<item name="android:layout_width">match_parent</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:layout_marginStart">0dp</item>
|
||||
<item name="android:layout_marginLeft">0dp</item>
|
||||
<item name="android:layout_marginEnd">0dp</item>
|
||||
<item name="android:layout_marginRight">0dp</item>
|
||||
<item name="android:layout_marginBottom">0dp</item>
|
||||
<item name="android:layout_marginTop">0dp</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
<style name="TimelineContentStubContainerParams">
|
||||
<item name="android:paddingStart">8dp</item>
|
||||
<item name="android:paddingEnd">8dp</item>
|
||||
<item name="android:paddingTop">4dp</item>
|
||||
<item name="android:paddingBottom">4dp</item>
|
||||
</style>
|
||||
|
||||
<style name="TimelineContentMediaPillStyle">
|
||||
<item name="android:paddingStart">8dp</item>
|
||||
<item name="android:paddingEnd">8dp</item>
|
||||
<item name="android:paddingTop">6dp</item>
|
||||
<item name="android:paddingBottom">6dp</item>
|
||||
<item name="minHeight">48dp</item>
|
||||
<item name="android:background">@drawable/bg_media_pill</item>
|
||||
<item name="android:backgroundTint">?voice_message_playback_background_color</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
<item name="vctr_waiting_background_color">@color/vctr_waiting_background_color_dark</item>
|
||||
<item name="vctr_chat_effect_snow_background">@color/vctr_chat_effect_snow_background_dark</item>
|
||||
<item name="vctr_toolbar_background">@color/element_system_dark</item>
|
||||
<item name="vctr_message_bubble_inbound">@color/vctr_message_bubble_inbound_dark</item>
|
||||
<item name="vctr_message_bubble_outbound">@color/vctr_message_bubble_outbound_dark</item>
|
||||
|
||||
<!-- room message colors -->
|
||||
<item name="vctr_notice_secondary">#61708B</item>
|
||||
|
@ -105,9 +107,6 @@
|
|||
<!-- disable the overscroll because setOverscrollHeader/Footer don't always work -->
|
||||
<item name="android:overScrollMode">never</item>
|
||||
|
||||
<!-- fonts -->
|
||||
<item name="android:typeface">sans</item>
|
||||
|
||||
<item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>
|
||||
|
||||
<item name="pf_lock_screen">@style/PinCodeScreenStyle</item>
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
<item name="vctr_waiting_background_color">@color/vctr_waiting_background_color_light</item>
|
||||
<item name="vctr_chat_effect_snow_background">@color/vctr_chat_effect_snow_background_light</item>
|
||||
<item name="vctr_toolbar_background">@color/element_background_light</item>
|
||||
<item name="vctr_message_bubble_inbound">@color/vctr_message_bubble_inbound_light</item>
|
||||
<item name="vctr_message_bubble_outbound">@color/vctr_message_bubble_outbound_light</item>
|
||||
|
||||
<!-- room message colors -->
|
||||
<item name="vctr_notice_secondary">#61708B</item>
|
||||
|
@ -105,9 +107,6 @@
|
|||
<!-- disable the overscroll because setOverscrollHeader/Footer don't always work -->
|
||||
<item name="android:overScrollMode">never</item>
|
||||
|
||||
<!-- fonts -->
|
||||
<item name="android:typeface">sans</item>
|
||||
|
||||
<item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>
|
||||
|
||||
<item name="pf_lock_screen">@style/PinCodeScreenStyle</item>
|
||||
|
|
|
@ -32,6 +32,8 @@ 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
|
||||
|
||||
typealias ThreadRootEvent = TimelineEvent
|
||||
|
||||
class FlowRoom(private val room: Room) {
|
||||
|
||||
fun liveRoomSummary(): Flow<Optional<RoomSummary>> {
|
||||
|
@ -98,6 +100,20 @@ class FlowRoom(private val room: Room) {
|
|||
fun liveNotificationState(): Flow<RoomNotificationState> {
|
||||
return room.getLiveRoomNotificationState().asFlow()
|
||||
}
|
||||
|
||||
fun liveThreadList(): Flow<List<ThreadRootEvent>> {
|
||||
return room.getAllThreadsLive().asFlow()
|
||||
.startWith(room.coroutineDispatchers.io) {
|
||||
room.getAllThreads()
|
||||
}
|
||||
}
|
||||
|
||||
fun liveLocalUnreadThreadList(): Flow<List<ThreadRootEvent>> {
|
||||
return room.getMarkedThreadNotificationsLive().asFlow()
|
||||
.startWith(room.coroutineDispatchers.io) {
|
||||
room.getMarkedThreadNotifications()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Room.flow(): FlowRoom {
|
||||
|
|
|
@ -31,13 +31,15 @@ android {
|
|||
// that the app's state is completely cleared between tests.
|
||||
testInstrumentationRunnerArguments clearPackageData: 'true'
|
||||
|
||||
buildConfigField "String", "SDK_VERSION", "\"1.3.16\""
|
||||
buildConfigField "String", "SDK_VERSION", "\"1.3.19\""
|
||||
|
||||
buildConfigField "String", "GIT_SDK_REVISION", "\"${gitRevision()}\""
|
||||
resValue "string", "git_sdk_revision", "\"${gitRevision()}\""
|
||||
resValue "string", "git_sdk_revision_unix_date", "\"${gitRevisionUnixDate()}\""
|
||||
resValue "string", "git_sdk_revision_date", "\"${gitRevisionDate()}\""
|
||||
|
||||
// Indicates whether or not threading support is enabled
|
||||
buildConfigField "Boolean", "THREADING_ENABLED", "${isThreadingEnabled}"
|
||||
defaultConfig {
|
||||
consumerProguardFiles 'proguard-rules.pro'
|
||||
}
|
||||
|
@ -139,6 +141,9 @@ dependencies {
|
|||
|
||||
kapt 'dk.ilios:realmfieldnameshelper:2.0.0'
|
||||
|
||||
// Shared Preferences
|
||||
implementation libs.androidx.preferenceKtx
|
||||
|
||||
// Work
|
||||
implementation libs.androidx.work
|
||||
|
||||
|
@ -164,7 +169,7 @@ dependencies {
|
|||
implementation libs.apache.commonsImaging
|
||||
|
||||
// Phone number https://github.com/google/libphonenumber
|
||||
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.41'
|
||||
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.42'
|
||||
|
||||
testImplementation libs.tests.junit
|
||||
testImplementation 'org.robolectric:robolectric:4.7.3'
|
||||
|
|
|
@ -157,14 +157,20 @@ class CommonTestHelper(context: Context) {
|
|||
/**
|
||||
* Will send nb of messages provided by count parameter but waits every 10 messages to avoid gap in sync
|
||||
*/
|
||||
private fun sendTextMessagesBatched(timeline: Timeline, room: Room, message: String, count: Int, timeout: Long): List<TimelineEvent> {
|
||||
private fun sendTextMessagesBatched(timeline: Timeline, room: Room, message: String, count: Int, timeout: Long, rootThreadEventId: String? = null): List<TimelineEvent> {
|
||||
val sentEvents = ArrayList<TimelineEvent>(count)
|
||||
(1 until count + 1)
|
||||
.map { "$message #$it" }
|
||||
.chunked(10)
|
||||
.forEach { batchedMessages ->
|
||||
batchedMessages.forEach { formattedMessage ->
|
||||
room.sendTextMessage(formattedMessage)
|
||||
if (rootThreadEventId != null) {
|
||||
room.replyInThread(
|
||||
rootThreadEventId = rootThreadEventId,
|
||||
replyInThreadText = formattedMessage)
|
||||
} else {
|
||||
room.sendTextMessage(formattedMessage)
|
||||
}
|
||||
}
|
||||
waitWithLatch(timeout) { latch ->
|
||||
val timelineListener = object : Timeline.Listener {
|
||||
|
@ -196,6 +202,27 @@ class CommonTestHelper(context: Context) {
|
|||
return sentEvents
|
||||
}
|
||||
|
||||
/**
|
||||
* Reply in a thread
|
||||
* @param room the room where to send the messages
|
||||
* @param message the message to send
|
||||
* @param numberOfMessages the number of time the message will be sent
|
||||
*/
|
||||
fun replyInThreadMessage(
|
||||
room: Room,
|
||||
message: String,
|
||||
numberOfMessages: Int,
|
||||
rootThreadEventId: String,
|
||||
timeout: Long = TestConstants.timeOutMillis): List<TimelineEvent> {
|
||||
val timeline = room.createTimeline(null, TimelineSettings(10))
|
||||
timeline.start()
|
||||
val sentEvents = sendTextMessagesBatched(timeline, room, message, numberOfMessages, timeout, rootThreadEventId)
|
||||
timeline.dispose()
|
||||
// Check that all events has been created
|
||||
assertEquals("Message number do not match $sentEvents", numberOfMessages.toLong(), sentEvents.size.toLong())
|
||||
return sentEvents
|
||||
}
|
||||
|
||||
// PRIVATE METHODS *****************************************************************************
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,339 @@
|
|||
/*
|
||||
* 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.session.room.threads
|
||||
|
||||
import org.amshove.kluent.shouldBe
|
||||
import org.amshove.kluent.shouldBeEqualTo
|
||||
import org.amshove.kluent.shouldBeFalse
|
||||
import org.amshove.kluent.shouldBeNull
|
||||
import org.amshove.kluent.shouldBeTrue
|
||||
import org.junit.FixMethodOrder
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.JUnit4
|
||||
import org.junit.runners.MethodSorters
|
||||
import org.matrix.android.sdk.InstrumentedTest
|
||||
import org.matrix.android.sdk.api.session.events.model.getRootThreadEventId
|
||||
import org.matrix.android.sdk.api.session.events.model.isTextMessage
|
||||
import org.matrix.android.sdk.api.session.events.model.isThread
|
||||
import org.matrix.android.sdk.api.session.room.timeline.Timeline
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
|
||||
import org.matrix.android.sdk.common.CommonTestHelper
|
||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
||||
import java.util.concurrent.CountDownLatch
|
||||
|
||||
@RunWith(JUnit4::class)
|
||||
@FixMethodOrder(MethodSorters.JVM)
|
||||
class ThreadMessagingTest : InstrumentedTest {
|
||||
|
||||
@Test
|
||||
fun reply_in_thread_should_create_a_thread() {
|
||||
val commonTestHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceInARoom(false)
|
||||
|
||||
val aliceSession = cryptoTestData.firstSession
|
||||
val aliceRoomId = cryptoTestData.roomId
|
||||
|
||||
val aliceRoom = aliceSession.getRoom(aliceRoomId)!!
|
||||
|
||||
// Let's send a message in the normal timeline
|
||||
val textMessage = "This is a normal timeline message"
|
||||
val sentMessages = commonTestHelper.sendTextMessage(
|
||||
room = aliceRoom,
|
||||
message = textMessage,
|
||||
nbOfMessages = 1)
|
||||
|
||||
val initMessage = sentMessages.first()
|
||||
|
||||
initMessage.root.isThread().shouldBeFalse()
|
||||
initMessage.root.isTextMessage().shouldBeTrue()
|
||||
initMessage.root.getRootThreadEventId().shouldBeNull()
|
||||
initMessage.root.threadDetails?.isRootThread?.shouldBeFalse()
|
||||
|
||||
// Let's reply in timeline to that message
|
||||
val repliesInThread = commonTestHelper.replyInThreadMessage(
|
||||
room = aliceRoom,
|
||||
message = "Reply In the above thread",
|
||||
numberOfMessages = 1,
|
||||
rootThreadEventId = initMessage.root.eventId.orEmpty())
|
||||
|
||||
val replyInThread = repliesInThread.first()
|
||||
replyInThread.root.isThread().shouldBeTrue()
|
||||
replyInThread.root.isTextMessage().shouldBeTrue()
|
||||
replyInThread.root.getRootThreadEventId().shouldBeEqualTo(initMessage.root.eventId)
|
||||
|
||||
// The init normal message should now be a root thread event
|
||||
val timeline = aliceRoom.createTimeline(null, TimelineSettings(30))
|
||||
timeline.start()
|
||||
|
||||
aliceSession.startSync(true)
|
||||
run {
|
||||
val lock = CountDownLatch(1)
|
||||
val eventsListener = commonTestHelper.createEventListener(lock) { snapshot ->
|
||||
val initMessageThreadDetails = snapshot.firstOrNull {
|
||||
it.root.eventId == initMessage.root.eventId
|
||||
}?.root?.threadDetails
|
||||
initMessageThreadDetails?.isRootThread?.shouldBeTrue()
|
||||
initMessageThreadDetails?.numberOfThreads?.shouldBe(1)
|
||||
true
|
||||
}
|
||||
timeline.addListener(eventsListener)
|
||||
commonTestHelper.await(lock, 600_000)
|
||||
}
|
||||
aliceSession.stopSync()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun reply_in_thread_should_create_a_thread_from_other_user() {
|
||||
val commonTestHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(false)
|
||||
|
||||
val aliceSession = cryptoTestData.firstSession
|
||||
val aliceRoomId = cryptoTestData.roomId
|
||||
val aliceRoom = aliceSession.getRoom(aliceRoomId)!!
|
||||
|
||||
// Let's send a message in the normal timeline
|
||||
val textMessage = "This is a normal timeline message"
|
||||
val sentMessages = commonTestHelper.sendTextMessage(
|
||||
room = aliceRoom,
|
||||
message = textMessage,
|
||||
nbOfMessages = 1)
|
||||
|
||||
val initMessage = sentMessages.first()
|
||||
|
||||
initMessage.root.isThread().shouldBeFalse()
|
||||
initMessage.root.isTextMessage().shouldBeTrue()
|
||||
initMessage.root.getRootThreadEventId().shouldBeNull()
|
||||
initMessage.root.threadDetails?.isRootThread?.shouldBeFalse()
|
||||
|
||||
// Let's reply in timeline to that message from another user
|
||||
val bobSession = cryptoTestData.secondSession!!
|
||||
val bobRoomId = cryptoTestData.roomId
|
||||
val bobRoom = bobSession.getRoom(bobRoomId)!!
|
||||
|
||||
val repliesInThread = commonTestHelper.replyInThreadMessage(
|
||||
room = bobRoom,
|
||||
message = "Reply In the above thread",
|
||||
numberOfMessages = 1,
|
||||
rootThreadEventId = initMessage.root.eventId.orEmpty())
|
||||
|
||||
val replyInThread = repliesInThread.first()
|
||||
replyInThread.root.isThread().shouldBeTrue()
|
||||
replyInThread.root.isTextMessage().shouldBeTrue()
|
||||
replyInThread.root.getRootThreadEventId().shouldBeEqualTo(initMessage.root.eventId)
|
||||
|
||||
// The init normal message should now be a root thread event
|
||||
val timeline = aliceRoom.createTimeline(null, TimelineSettings(30))
|
||||
timeline.start()
|
||||
|
||||
aliceSession.startSync(true)
|
||||
run {
|
||||
val lock = CountDownLatch(1)
|
||||
val eventsListener = commonTestHelper.createEventListener(lock) { snapshot ->
|
||||
val initMessageThreadDetails = snapshot.firstOrNull { it.root.eventId == initMessage.root.eventId }?.root?.threadDetails
|
||||
initMessageThreadDetails?.isRootThread?.shouldBeTrue()
|
||||
initMessageThreadDetails?.numberOfThreads?.shouldBe(1)
|
||||
true
|
||||
}
|
||||
timeline.addListener(eventsListener)
|
||||
commonTestHelper.await(lock, 600_000)
|
||||
}
|
||||
aliceSession.stopSync()
|
||||
|
||||
bobSession.startSync(true)
|
||||
run {
|
||||
val lock = CountDownLatch(1)
|
||||
val eventsListener = commonTestHelper.createEventListener(lock) { snapshot ->
|
||||
val initMessageThreadDetails = snapshot.firstOrNull { it.root.eventId == initMessage.root.eventId }?.root?.threadDetails
|
||||
initMessageThreadDetails?.isRootThread?.shouldBeTrue()
|
||||
initMessageThreadDetails?.numberOfThreads?.shouldBe(1)
|
||||
true
|
||||
}
|
||||
timeline.addListener(eventsListener)
|
||||
commonTestHelper.await(lock, 600_000)
|
||||
}
|
||||
bobSession.stopSync()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun reply_in_thread_to_timeline_message_multiple_times() {
|
||||
val commonTestHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceInARoom(false)
|
||||
|
||||
val aliceSession = cryptoTestData.firstSession
|
||||
val aliceRoomId = cryptoTestData.roomId
|
||||
|
||||
val aliceRoom = aliceSession.getRoom(aliceRoomId)!!
|
||||
|
||||
// Let's send 5 messages in the normal timeline
|
||||
val textMessage = "This is a normal timeline message"
|
||||
val sentMessages = commonTestHelper.sendTextMessage(
|
||||
room = aliceRoom,
|
||||
message = textMessage,
|
||||
nbOfMessages = 5)
|
||||
|
||||
sentMessages.forEach {
|
||||
it.root.isThread().shouldBeFalse()
|
||||
it.root.isTextMessage().shouldBeTrue()
|
||||
it.root.getRootThreadEventId().shouldBeNull()
|
||||
it.root.threadDetails?.isRootThread?.shouldBeFalse()
|
||||
}
|
||||
// let's start the thread from the second message
|
||||
val selectedInitMessage = sentMessages[1]
|
||||
|
||||
// Let's reply 40 times in the timeline to the second message
|
||||
val repliesInThread = commonTestHelper.replyInThreadMessage(
|
||||
room = aliceRoom,
|
||||
message = "Reply In the above thread",
|
||||
numberOfMessages = 40,
|
||||
rootThreadEventId = selectedInitMessage.root.eventId.orEmpty())
|
||||
|
||||
repliesInThread.forEach {
|
||||
it.root.isThread().shouldBeTrue()
|
||||
it.root.isTextMessage().shouldBeTrue()
|
||||
it.root.getRootThreadEventId()?.shouldBeEqualTo(selectedInitMessage.root.eventId.orEmpty()) ?: assert(false)
|
||||
}
|
||||
|
||||
// The init normal message should now be a root thread event
|
||||
val timeline = aliceRoom.createTimeline(null, TimelineSettings(30))
|
||||
timeline.start()
|
||||
|
||||
aliceSession.startSync(true)
|
||||
run {
|
||||
val lock = CountDownLatch(1)
|
||||
val eventsListener = commonTestHelper.createEventListener(lock) { snapshot ->
|
||||
val initMessageThreadDetails = snapshot.firstOrNull { it.root.eventId == selectedInitMessage.root.eventId }?.root?.threadDetails
|
||||
// Selected init message should be the thread root
|
||||
initMessageThreadDetails?.isRootThread?.shouldBeTrue()
|
||||
// All threads should be 40
|
||||
initMessageThreadDetails?.numberOfThreads?.shouldBeEqualTo(40)
|
||||
true
|
||||
}
|
||||
// Because we sent more than 30 messages we should paginate a bit more
|
||||
timeline.paginate(Timeline.Direction.BACKWARDS, 50)
|
||||
timeline.addListener(eventsListener)
|
||||
commonTestHelper.await(lock, 600_000)
|
||||
}
|
||||
aliceSession.stopSync()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun thread_summary_advanced_validation_after_multiple_messages_in_multiple_threads() {
|
||||
val commonTestHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(false)
|
||||
|
||||
val aliceSession = cryptoTestData.firstSession
|
||||
val aliceRoomId = cryptoTestData.roomId
|
||||
|
||||
val aliceRoom = aliceSession.getRoom(aliceRoomId)!!
|
||||
|
||||
// Let's send 5 messages in the normal timeline
|
||||
val textMessage = "This is a normal timeline message"
|
||||
val sentMessages = commonTestHelper.sendTextMessage(
|
||||
room = aliceRoom,
|
||||
message = textMessage,
|
||||
nbOfMessages = 5)
|
||||
|
||||
sentMessages.forEach {
|
||||
it.root.isThread().shouldBeFalse()
|
||||
it.root.isTextMessage().shouldBeTrue()
|
||||
it.root.getRootThreadEventId().shouldBeNull()
|
||||
it.root.threadDetails?.isRootThread?.shouldBeFalse()
|
||||
}
|
||||
// let's start the thread from the second message
|
||||
val firstMessage = sentMessages[0]
|
||||
val secondMessage = sentMessages[1]
|
||||
|
||||
// Alice will reply in thread to the second message 35 times
|
||||
val aliceThreadRepliesInSecondMessage = commonTestHelper.replyInThreadMessage(
|
||||
room = aliceRoom,
|
||||
message = "Alice reply In the above second thread message",
|
||||
numberOfMessages = 35,
|
||||
rootThreadEventId = secondMessage.root.eventId.orEmpty())
|
||||
|
||||
// Let's reply in timeline to that message from another user
|
||||
val bobSession = cryptoTestData.secondSession!!
|
||||
val bobRoomId = cryptoTestData.roomId
|
||||
val bobRoom = bobSession.getRoom(bobRoomId)!!
|
||||
|
||||
// Bob will reply in thread to the first message 35 times
|
||||
val bobThreadRepliesInFirstMessage = commonTestHelper.replyInThreadMessage(
|
||||
room = bobRoom,
|
||||
message = "Bob reply In the above first thread message",
|
||||
numberOfMessages = 42,
|
||||
rootThreadEventId = firstMessage.root.eventId.orEmpty())
|
||||
|
||||
// Bob will also reply in second thread 5 times
|
||||
val bobThreadRepliesInSecondMessage = commonTestHelper.replyInThreadMessage(
|
||||
room = bobRoom,
|
||||
message = "Another Bob reply In the above second thread message",
|
||||
numberOfMessages = 20,
|
||||
rootThreadEventId = secondMessage.root.eventId.orEmpty())
|
||||
|
||||
aliceThreadRepliesInSecondMessage.forEach {
|
||||
it.root.isThread().shouldBeTrue()
|
||||
it.root.isTextMessage().shouldBeTrue()
|
||||
it.root.getRootThreadEventId()?.shouldBeEqualTo(secondMessage.root.eventId.orEmpty()) ?: assert(false)
|
||||
}
|
||||
|
||||
bobThreadRepliesInFirstMessage.forEach {
|
||||
it.root.isThread().shouldBeTrue()
|
||||
it.root.isTextMessage().shouldBeTrue()
|
||||
it.root.getRootThreadEventId()?.shouldBeEqualTo(firstMessage.root.eventId.orEmpty()) ?: assert(false)
|
||||
}
|
||||
|
||||
bobThreadRepliesInSecondMessage.forEach {
|
||||
it.root.isThread().shouldBeTrue()
|
||||
it.root.isTextMessage().shouldBeTrue()
|
||||
it.root.getRootThreadEventId()?.shouldBeEqualTo(secondMessage.root.eventId.orEmpty()) ?: assert(false)
|
||||
}
|
||||
|
||||
// The init normal message should now be a root thread event
|
||||
val timeline = aliceRoom.createTimeline(null, TimelineSettings(30))
|
||||
timeline.start()
|
||||
|
||||
aliceSession.startSync(true)
|
||||
run {
|
||||
val lock = CountDownLatch(1)
|
||||
val eventsListener = commonTestHelper.createEventListener(lock) { snapshot ->
|
||||
val firstMessageThreadDetails = snapshot.firstOrNull { it.root.eventId == firstMessage.root.eventId }?.root?.threadDetails
|
||||
val secondMessageThreadDetails = snapshot.firstOrNull { it.root.eventId == secondMessage.root.eventId }?.root?.threadDetails
|
||||
|
||||
// first & second message should be the thread root
|
||||
firstMessageThreadDetails?.isRootThread?.shouldBeTrue()
|
||||
secondMessageThreadDetails?.isRootThread?.shouldBeTrue()
|
||||
|
||||
// First thread message should contain 42
|
||||
firstMessageThreadDetails?.numberOfThreads shouldBeEqualTo 42
|
||||
// Second thread message should contain 35+20
|
||||
secondMessageThreadDetails?.numberOfThreads shouldBeEqualTo 55
|
||||
|
||||
true
|
||||
}
|
||||
// Because we sent more than 30 messages we should paginate a bit more
|
||||
timeline.paginate(Timeline.Direction.BACKWARDS, 50)
|
||||
timeline.paginate(Timeline.Direction.BACKWARDS, 50)
|
||||
timeline.addListener(eventsListener)
|
||||
commonTestHelper.await(lock, 600_000)
|
||||
}
|
||||
aliceSession.stopSync()
|
||||
}
|
||||
}
|
|
@ -25,9 +25,14 @@ import org.matrix.android.sdk.api.session.crypto.MXCryptoError
|
|||
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomMemberContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageStickerContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageType
|
||||
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
|
||||
import org.matrix.android.sdk.api.session.room.model.relation.shouldRenderInThread
|
||||
import org.matrix.android.sdk.api.session.room.send.SendState
|
||||
import org.matrix.android.sdk.api.session.threads.ThreadDetails
|
||||
import org.matrix.android.sdk.api.util.ContentUtils
|
||||
import org.matrix.android.sdk.api.util.JsonDict
|
||||
import org.matrix.android.sdk.internal.crypto.algorithms.olm.OlmDecryptionResult
|
||||
import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent
|
||||
|
@ -98,6 +103,9 @@ data class Event(
|
|||
@Transient
|
||||
var sendStateDetails: String? = null
|
||||
|
||||
@Transient
|
||||
var threadDetails: ThreadDetails? = null
|
||||
|
||||
fun sendStateError(): MatrixError? {
|
||||
return sendStateDetails?.let {
|
||||
val matrixErrorAdapter = MoshiProvider.providesMoshi().adapter(MatrixError::class.java)
|
||||
|
@ -123,6 +131,7 @@ data class Event(
|
|||
it.mCryptoErrorReason = mCryptoErrorReason
|
||||
it.sendState = sendState
|
||||
it.ageLocalTs = ageLocalTs
|
||||
it.threadDetails = threadDetails
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -185,6 +194,51 @@ data class Event(
|
|||
return contentMap?.let { JSONObject(adapter.toJson(it)).toString(4) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a user friendly content depending on the message type.
|
||||
* It can be used especially for message summaries.
|
||||
* It will return a decrypted text message or an empty string otherwise.
|
||||
*/
|
||||
fun getDecryptedTextSummary(): String? {
|
||||
if (isRedacted()) return "Message Deleted"
|
||||
val text = getDecryptedValue() ?: return null
|
||||
return when {
|
||||
isReplyRenderedInThread() || isQuote() -> ContentUtils.extractUsefulTextFromReply(text)
|
||||
isFileMessage() -> "sent a file."
|
||||
isAudioMessage() -> "sent an audio file."
|
||||
isImageMessage() -> "sent an image."
|
||||
isVideoMessage() -> "sent a video."
|
||||
isSticker() -> "sent a sticker"
|
||||
isPoll() -> getPollQuestion() ?: "created a poll."
|
||||
else -> text
|
||||
}
|
||||
}
|
||||
|
||||
private fun Event.isQuote(): Boolean {
|
||||
if (isReplyRenderedInThread()) return false
|
||||
return getDecryptedValue("formatted_body")?.contains("<blockquote>") ?: false
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether or not current event has mentioned the user
|
||||
*/
|
||||
fun isUserMentioned(userId: String): Boolean {
|
||||
return getDecryptedValue("formatted_body")?.contains(userId) ?: false
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt the message, or return the pure payload value if there is no encryption
|
||||
*/
|
||||
private fun getDecryptedValue(key: String = "body"): String? {
|
||||
return if (isEncrypted()) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val decryptedContent = mxDecryptionResult?.payload?.get("content") as? JsonDict
|
||||
decryptedContent?.get(key) as? String
|
||||
} else {
|
||||
content?.get(key) as? String
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if the event is redacted
|
||||
*/
|
||||
|
@ -217,7 +271,7 @@ data class Event(
|
|||
if (mCryptoError != other.mCryptoError) return false
|
||||
if (mCryptoErrorReason != other.mCryptoErrorReason) return false
|
||||
if (sendState != other.sendState) return false
|
||||
|
||||
if (threadDetails != other.threadDetails) return false
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -236,6 +290,8 @@ data class Event(
|
|||
result = 31 * result + (mCryptoError?.hashCode() ?: 0)
|
||||
result = 31 * result + (mCryptoErrorReason?.hashCode() ?: 0)
|
||||
result = 31 * result + sendState.hashCode()
|
||||
result = 31 * result + threadDetails.hashCode()
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
@ -243,70 +299,101 @@ data class Event(
|
|||
fun Event.isTextMessage(): Boolean {
|
||||
return getClearType() == EventType.MESSAGE &&
|
||||
when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) {
|
||||
MessageType.MSGTYPE_TEXT,
|
||||
MessageType.MSGTYPE_EMOTE,
|
||||
MessageType.MSGTYPE_NOTICE -> true
|
||||
else -> false
|
||||
}
|
||||
MessageType.MSGTYPE_TEXT,
|
||||
MessageType.MSGTYPE_EMOTE,
|
||||
MessageType.MSGTYPE_NOTICE -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
fun Event.isImageMessage(): Boolean {
|
||||
return getClearType() == EventType.MESSAGE &&
|
||||
when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) {
|
||||
MessageType.MSGTYPE_IMAGE -> true
|
||||
else -> false
|
||||
}
|
||||
MessageType.MSGTYPE_IMAGE -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
fun Event.isVideoMessage(): Boolean {
|
||||
return getClearType() == EventType.MESSAGE &&
|
||||
when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) {
|
||||
MessageType.MSGTYPE_VIDEO -> true
|
||||
else -> false
|
||||
}
|
||||
MessageType.MSGTYPE_VIDEO -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
fun Event.isAudioMessage(): Boolean {
|
||||
return getClearType() == EventType.MESSAGE &&
|
||||
when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) {
|
||||
MessageType.MSGTYPE_AUDIO -> true
|
||||
else -> false
|
||||
}
|
||||
MessageType.MSGTYPE_AUDIO -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
fun Event.isFileMessage(): Boolean {
|
||||
return getClearType() == EventType.MESSAGE &&
|
||||
when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) {
|
||||
MessageType.MSGTYPE_FILE -> true
|
||||
else -> false
|
||||
}
|
||||
MessageType.MSGTYPE_FILE -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
fun Event.isAttachmentMessage(): Boolean {
|
||||
return getClearType() == EventType.MESSAGE &&
|
||||
when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) {
|
||||
MessageType.MSGTYPE_IMAGE,
|
||||
MessageType.MSGTYPE_AUDIO,
|
||||
MessageType.MSGTYPE_VIDEO,
|
||||
MessageType.MSGTYPE_FILE -> true
|
||||
else -> false
|
||||
}
|
||||
MessageType.MSGTYPE_IMAGE,
|
||||
MessageType.MSGTYPE_AUDIO,
|
||||
MessageType.MSGTYPE_VIDEO,
|
||||
MessageType.MSGTYPE_FILE -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
fun Event.isPoll(): Boolean = getClearType() == EventType.POLL_START || getClearType() == EventType.POLL_END
|
||||
|
||||
fun Event.isSticker(): Boolean = getClearType() == EventType.STICKER
|
||||
|
||||
fun Event.getRelationContent(): RelationDefaultContent? {
|
||||
return if (isEncrypted()) {
|
||||
content.toModel<EncryptedEventContent>()?.relatesTo
|
||||
} else {
|
||||
content.toModel<MessageContent>()?.relatesTo
|
||||
content.toModel<MessageContent>()?.relatesTo ?: run {
|
||||
// Special case to handle stickers, while there is only a local msgtype for stickers
|
||||
if (getClearType() == EventType.STICKER) {
|
||||
getClearContent().toModel<MessageStickerContent>()?.relatesTo
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the poll question or null otherwise
|
||||
*/
|
||||
fun Event.getPollQuestion(): String? =
|
||||
getPollContent()?.pollCreationInfo?.question?.question
|
||||
|
||||
/**
|
||||
* Returns the relation content for a specific type or null otherwise
|
||||
*/
|
||||
fun Event.getRelationContentForType(type: String): RelationDefaultContent? =
|
||||
getRelationContent()?.takeIf { it.type == type }
|
||||
|
||||
fun Event.isReply(): Boolean {
|
||||
return getRelationContent()?.inReplyTo?.eventId != null
|
||||
}
|
||||
|
||||
fun Event.isReplyRenderedInThread(): Boolean {
|
||||
return isReply() && getRelationContent()?.inReplyTo?.shouldRenderInThread() == true
|
||||
}
|
||||
|
||||
fun Event.isThread(): Boolean = getRelationContentForType(RelationType.IO_THREAD)?.eventId != null
|
||||
|
||||
fun Event.getRootThreadEventId(): String? = getRelationContentForType(RelationType.IO_THREAD)?.eventId
|
||||
|
||||
fun Event.isEdition(): Boolean {
|
||||
return getRelationContent()?.takeIf { it.type == RelationType.REPLACE }?.eventId != null
|
||||
return getRelationContentForType(RelationType.REPLACE)?.eventId != null
|
||||
}
|
||||
|
||||
fun Event.getPresenceContent(): PresenceContent? {
|
||||
|
@ -315,3 +402,7 @@ fun Event.getPresenceContent(): PresenceContent? {
|
|||
|
||||
fun Event.isInvitation(): Boolean = type == EventType.STATE_ROOM_MEMBER &&
|
||||
content?.toModel<RoomMemberContent>()?.membership == Membership.INVITE
|
||||
|
||||
fun Event.getPollContent(): MessagePollContent? {
|
||||
return content.toModel<MessagePollContent>()
|
||||
}
|
||||
|
|
|
@ -28,9 +28,9 @@ object RelationType {
|
|||
/** Lets you define an event which references an existing event.*/
|
||||
const val REFERENCE = "m.reference"
|
||||
|
||||
/** Lets you define an thread event that belongs to another existing event.*/
|
||||
// const val THREAD = "m.thread" // m.thread is not yet released in the backend
|
||||
const val THREAD = "io.element.thread" // io.element.thread will be replaced by m.thread when it is released
|
||||
/** Lets you define an event which is a thread reply to an existing event.*/
|
||||
const val THREAD = "m.thread"
|
||||
const val IO_THREAD = "io.element.thread"
|
||||
|
||||
/** Lets you define an event which adds a response to an existing event.*/
|
||||
const val RESPONSE = "org.matrix.response"
|
||||
|
|
|
@ -47,7 +47,11 @@ data class PreviewUrlData(
|
|||
// Value of field "og:description"
|
||||
val description: String?,
|
||||
// Value of field "og:image"
|
||||
val mxcUrl: String?
|
||||
val mxcUrl: String?,
|
||||
// Value of field "og:image:width"
|
||||
val imageWidth: Int?,
|
||||
// Value of field "og:image:height"
|
||||
val imageHeight: Int?
|
||||
) {
|
||||
fun isPreviewable(): Boolean {
|
||||
return siteName?.isNotEmpty() == true ||
|
||||
|
|
|
@ -32,6 +32,7 @@ import org.matrix.android.sdk.api.session.room.send.DraftService
|
|||
import org.matrix.android.sdk.api.session.room.send.SendService
|
||||
import org.matrix.android.sdk.api.session.room.state.StateService
|
||||
import org.matrix.android.sdk.api.session.room.tags.TagsService
|
||||
import org.matrix.android.sdk.api.session.room.threads.ThreadsService
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineService
|
||||
import org.matrix.android.sdk.api.session.room.typing.TypingService
|
||||
import org.matrix.android.sdk.api.session.room.uploads.UploadsService
|
||||
|
@ -45,6 +46,7 @@ import org.matrix.android.sdk.api.util.Optional
|
|||
*/
|
||||
interface Room :
|
||||
TimelineService,
|
||||
ThreadsService,
|
||||
SendService,
|
||||
DraftService,
|
||||
ReadService,
|
||||
|
|
|
@ -48,7 +48,8 @@ data class MessageLocationContent(
|
|||
@Json(name = "m.new_content") override val newContent: Content? = null,
|
||||
|
||||
/**
|
||||
* m.asset defines a generic asset that can be used for location tracking but also in other places like inventories, geofencing, checkins/checkouts etc.
|
||||
* m.asset defines a generic asset that can be used for location tracking but also in other places like
|
||||
* inventories, geofencing, checkins/checkouts etc.
|
||||
* It should contain a mandatory namespaced type key defining what particular asset is being referred to.
|
||||
* For the purposes of user location tracking m.self should be used in order to avoid duplicating the mxid.
|
||||
*/
|
||||
|
@ -62,5 +63,13 @@ data class MessageLocationContent(
|
|||
@Json(name = "org.matrix.msc1767.text") val text: String? = null
|
||||
) : MessageContent {
|
||||
|
||||
fun getUri() = locationInfo?.geoUri ?: geoUri
|
||||
fun getBestGeoUri() = locationInfo?.geoUri ?: geoUri
|
||||
|
||||
/**
|
||||
* @return true if the location asset is a user location, not a generic one.
|
||||
*/
|
||||
fun isSelfLocation(): Boolean {
|
||||
// Should behave like m.self if locationAsset is null
|
||||
return locationAsset?.type == null || locationAsset.type == LocationAssetType.SELF
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.matrix.android.sdk.api.session.room.model.relation
|
|||
import androidx.lifecycle.LiveData
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.room.model.EventAnnotationsSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageType
|
||||
import org.matrix.android.sdk.api.session.room.model.message.PollType
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||
import org.matrix.android.sdk.api.util.Cancelable
|
||||
|
@ -45,6 +46,9 @@ import org.matrix.android.sdk.api.util.Optional
|
|||
* m.reference - lets you define an event which references an existing event.
|
||||
* When aggregated, currently doesn't do anything special, but in future could bundle chains of references (i.e. threads).
|
||||
* These are primarily intended for handling replies (and in future threads).
|
||||
*
|
||||
* m.thread - lets you define an event which is a thread reply to an existing event.
|
||||
* When aggregated, returns the most thread event
|
||||
*/
|
||||
interface RelationService {
|
||||
|
||||
|
@ -62,8 +66,8 @@ interface RelationService {
|
|||
* @param targetEventId the id of the event being reacted
|
||||
* @param reaction the reaction (preferably emoji)
|
||||
*/
|
||||
fun undoReaction(targetEventId: String,
|
||||
reaction: String): Cancelable
|
||||
suspend fun undoReaction(targetEventId: String,
|
||||
reaction: String): Cancelable
|
||||
|
||||
/**
|
||||
* Edit a poll.
|
||||
|
@ -118,10 +122,15 @@ interface RelationService {
|
|||
* @param eventReplied the event referenced by the reply
|
||||
* @param replyText the reply text
|
||||
* @param autoMarkdown If true, the SDK will generate a formatted HTML message from the body text if markdown syntax is present
|
||||
* @param showInThread If true, relation will be added to the reply in order to be visible from within threads
|
||||
* @param rootThreadEventId If show in thread is true then we need the rootThreadEventId to generate the relation
|
||||
*/
|
||||
fun replyToMessage(eventReplied: TimelineEvent,
|
||||
replyText: CharSequence,
|
||||
autoMarkdown: Boolean = false): Cancelable?
|
||||
autoMarkdown: Boolean = false,
|
||||
showInThread: Boolean = false,
|
||||
rootThreadEventId: String? = null
|
||||
): Cancelable?
|
||||
|
||||
/**
|
||||
* Get the current EventAnnotationsSummary
|
||||
|
@ -136,4 +145,31 @@ interface RelationService {
|
|||
* @return the LiveData of EventAnnotationsSummary
|
||||
*/
|
||||
fun getEventAnnotationsSummaryLive(eventId: String): LiveData<Optional<EventAnnotationsSummary>>
|
||||
|
||||
/**
|
||||
* Creates a thread reply for an existing timeline event
|
||||
* The replyInThreadText can be a Spannable and contains special spans (MatrixItemSpan) that will be translated
|
||||
* by the sdk into pills.
|
||||
* @param rootThreadEventId the root thread eventId
|
||||
* @param replyInThreadText the reply text
|
||||
* @param msgType the message type: MessageType.MSGTYPE_TEXT (default) or MessageType.MSGTYPE_EMOTE
|
||||
* @param formattedText The formatted body using MessageType#FORMAT_MATRIX_HTML
|
||||
* @param autoMarkdown If true, the SDK will generate a formatted HTML message from the body text if markdown syntax is present
|
||||
* @param eventReplied the event referenced by the reply within a thread
|
||||
*/
|
||||
fun replyInThread(rootThreadEventId: String,
|
||||
replyInThreadText: CharSequence,
|
||||
msgType: String = MessageType.MSGTYPE_TEXT,
|
||||
autoMarkdown: Boolean = false,
|
||||
formattedText: String? = null,
|
||||
eventReplied: TimelineEvent? = null): Cancelable?
|
||||
|
||||
/**
|
||||
* Get all the thread replies for the specified rootThreadEventId
|
||||
* The return list will contain the original root thread event and all the thread replies to that event
|
||||
* Note: We will use a large limit value in order to avoid using pagination until it would be 100% ready
|
||||
* from the backend
|
||||
* @param rootThreadEventId the root thread eventId
|
||||
*/
|
||||
suspend fun fetchThreadTimeline(rootThreadEventId: String): Boolean
|
||||
}
|
||||
|
|
|
@ -21,5 +21,8 @@ import com.squareup.moshi.JsonClass
|
|||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ReplyToContent(
|
||||
@Json(name = "event_id") val eventId: String? = null
|
||||
@Json(name = "event_id") val eventId: String? = null,
|
||||
@Json(name = "render_in") val renderIn: List<String>? = null
|
||||
)
|
||||
|
||||
fun ReplyToContent.shouldRenderInThread(): Boolean = renderIn?.contains("m.thread") == true
|
||||
|
|
|
@ -64,7 +64,7 @@ interface SendService {
|
|||
* @param autoMarkdown If true, the SDK will generate a formatted HTML message from the body text if markdown syntax is present
|
||||
* @return a [Cancelable]
|
||||
*/
|
||||
fun sendQuotedTextMessage(quotedEvent: TimelineEvent, text: String, autoMarkdown: Boolean): Cancelable
|
||||
fun sendQuotedTextMessage(quotedEvent: TimelineEvent, text: String, autoMarkdown: Boolean, rootThreadEventId: String? = null): Cancelable
|
||||
|
||||
/**
|
||||
* Method to send a media asynchronously.
|
||||
|
@ -72,11 +72,13 @@ interface SendService {
|
|||
* @param compressBeforeSending set to true to compress images before sending them
|
||||
* @param roomIds set of roomIds to where the media will be sent. The current roomId will be add to this set if not present.
|
||||
* It can be useful to send media to multiple room. It's safe to include the current roomId in this set
|
||||
* @param rootThreadEventId when this param is not null, the Media will be sent in this specific thread
|
||||
* @return a [Cancelable]
|
||||
*/
|
||||
fun sendMedia(attachment: ContentAttachmentData,
|
||||
compressBeforeSending: Boolean,
|
||||
roomIds: Set<String>): Cancelable
|
||||
roomIds: Set<String>,
|
||||
rootThreadEventId: String? = null): Cancelable
|
||||
|
||||
/**
|
||||
* Method to send a list of media asynchronously.
|
||||
|
@ -84,11 +86,13 @@ interface SendService {
|
|||
* @param compressBeforeSending set to true to compress images before sending them
|
||||
* @param roomIds set of roomIds to where the media will be sent. The current roomId will be add to this set if not present.
|
||||
* It can be useful to send media to multiple room. It's safe to include the current roomId in this set
|
||||
* @param rootThreadEventId when this param is not null, all the Media will be sent in this specific thread
|
||||
* @return a [Cancelable]
|
||||
*/
|
||||
fun sendMedias(attachments: List<ContentAttachmentData>,
|
||||
compressBeforeSending: Boolean,
|
||||
roomIds: Set<String>): Cancelable
|
||||
roomIds: Set<String>,
|
||||
rootThreadEventId: String? = null): Cancelable
|
||||
|
||||
/**
|
||||
* Send a poll to the room.
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* 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 androidx.lifecycle.LiveData
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||
|
||||
/**
|
||||
* This interface defines methods to interact with threads related features.
|
||||
* It's implemented at the room level within the main timeline.
|
||||
*/
|
||||
interface ThreadsService {
|
||||
|
||||
/**
|
||||
* Returns a [LiveData] list of all the thread root TimelineEvents that exists at the room level
|
||||
*/
|
||||
fun getAllThreadsLive(): LiveData<List<TimelineEvent>>
|
||||
|
||||
/**
|
||||
* Returns a list of all the thread root TimelineEvents that exists at the room level
|
||||
*/
|
||||
fun getAllThreads(): List<TimelineEvent>
|
||||
|
||||
/**
|
||||
* Returns a [LiveData] list of all the marked unread threads that exists at the room level
|
||||
*/
|
||||
fun getMarkedThreadNotificationsLive(): LiveData<List<TimelineEvent>>
|
||||
|
||||
/**
|
||||
* Returns a list of all the marked unread threads that exists at the room level
|
||||
*/
|
||||
fun getMarkedThreadNotifications(): List<TimelineEvent>
|
||||
|
||||
/**
|
||||
* Returns whether or not the current user is participating in the thread
|
||||
* @param rootThreadEventId the eventId of the current thread
|
||||
*/
|
||||
fun isUserParticipatingInThread(rootThreadEventId: String): Boolean
|
||||
|
||||
/**
|
||||
* Enhance the provided root thread TimelineEvent [List] by adding the latest
|
||||
* message edition for that thread
|
||||
* @return the enhanced [List] with edited updates
|
||||
*/
|
||||
fun mapEventsWithEdition(threads: List<TimelineEvent>): List<TimelineEvent>
|
||||
|
||||
/**
|
||||
* Marks the current thread as read in local DB.
|
||||
* note: read receipts within threads are not yet supported with the API
|
||||
* @param rootThreadEventId the root eventId of the current thread
|
||||
*/
|
||||
suspend fun markThreadAsRead(rootThreadEventId: String)
|
||||
}
|
|
@ -43,7 +43,7 @@ interface Timeline {
|
|||
/**
|
||||
* This must be called before any other method after creating the timeline. It ensures the underlying database is open
|
||||
*/
|
||||
fun start()
|
||||
fun start(rootThreadEventId: String? = null)
|
||||
|
||||
/**
|
||||
* This must be called when you don't need the timeline. It ensures the underlying database get closed.
|
||||
|
|
|
@ -22,7 +22,9 @@ import org.matrix.android.sdk.api.session.events.model.EventType
|
|||
import org.matrix.android.sdk.api.session.events.model.RelationType
|
||||
import org.matrix.android.sdk.api.session.events.model.getRelationContent
|
||||
import org.matrix.android.sdk.api.session.events.model.isEdition
|
||||
import org.matrix.android.sdk.api.session.events.model.isPoll
|
||||
import org.matrix.android.sdk.api.session.events.model.isReply
|
||||
import org.matrix.android.sdk.api.session.events.model.isSticker
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.room.model.EventAnnotationsSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.ReadReceipt
|
||||
|
@ -149,6 +151,13 @@ fun TimelineEvent.isEdition(): Boolean {
|
|||
return root.isEdition()
|
||||
}
|
||||
|
||||
fun TimelineEvent.isPoll(): Boolean =
|
||||
root.isPoll()
|
||||
|
||||
fun TimelineEvent.isSticker(): Boolean {
|
||||
return root.isSticker()
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latest message body, after a possible edition, stripping the reply prefix if necessary
|
||||
*/
|
||||
|
|
|
@ -27,5 +27,14 @@ data class TimelineSettings(
|
|||
/**
|
||||
* If true, will build read receipts for each event.
|
||||
*/
|
||||
val buildReadReceipts: Boolean = true
|
||||
)
|
||||
val buildReadReceipts: Boolean = true,
|
||||
/**
|
||||
* The root thread eventId if this is a thread timeline, or null if this is NOT a thread timeline
|
||||
*/
|
||||
val rootThreadEventId: String? = null) {
|
||||
|
||||
/**
|
||||
* Returns true if this is a thread timeline or false otherwise
|
||||
*/
|
||||
fun isThreadTimeline() = rootThreadEventId != null
|
||||
}
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright 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.threads
|
||||
|
||||
import org.matrix.android.sdk.api.session.room.sender.SenderInfo
|
||||
|
||||
/**
|
||||
* This class contains all the details needed for threads.
|
||||
* Is is mainly used from within an Event.
|
||||
*/
|
||||
data class ThreadDetails(
|
||||
val isRootThread: Boolean = false,
|
||||
val numberOfThreads: Int = 0,
|
||||
val threadSummarySenderInfo: SenderInfo? = null,
|
||||
val threadSummaryLatestTextMessage: String? = null,
|
||||
val lastMessageTimestamp: Long? = null,
|
||||
var threadNotificationState: ThreadNotificationState = ThreadNotificationState.NO_NEW_MESSAGE,
|
||||
val isThread: Boolean = false,
|
||||
val lastRootThreadEdition: String? = null
|
||||
)
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue