Merge branch 'develop' into feature/aris/threads_ui_enhancements

# Conflicts:
#	vector/src/main/res/menu/menu_timeline.xml
This commit is contained in:
ariskotsomitopoulos 2022-04-05 16:32:49 +03:00
commit 6e06aed627
387 changed files with 5969 additions and 2562 deletions

View file

@ -26,7 +26,7 @@ jobs:
cancel-in-progress: true
steps:
- uses: actions/checkout@v3
- uses: actions/cache@v2
- uses: actions/cache@v3
with:
path: |
~/.gradle/caches
@ -50,7 +50,7 @@ jobs:
# Only runs on main, no concurrency.
steps:
- uses: actions/checkout@v3
- uses: actions/cache@v2
- uses: actions/cache@v3
with:
path: |
~/.gradle/caches

View file

@ -34,7 +34,7 @@ jobs:
uses: actions/setup-python@v3
with:
python-version: 3.8
- uses: actions/cache@v2
- uses: actions/cache@v3
with:
path: |
~/.gradle/caches
@ -43,7 +43,7 @@ jobs:
restore-keys: |
${{ runner.os }}-gradle-
- name: Start synapse server
uses: michaelkaye/setup-matrix-synapse@v0.3.0
uses: michaelkaye/setup-matrix-synapse@v0.4.0
with:
uploadLogs: true
httpPort: 8080
@ -174,7 +174,7 @@ jobs:
# package: class PermalinkParserTest
- name: Find Comment
if: always() && github.event_name == 'pull_request'
uses: peter-evans/find-comment@v1
uses: peter-evans/find-comment@v2
id: fc
with:
issue-number: ${{ github.event.pull_request.number }}
@ -182,7 +182,7 @@ jobs:
body-includes: Integration Tests Results
- name: Publish results to PR
if: always() && github.event_name == 'pull_request'
uses: peter-evans/create-or-update-comment@v1
uses: peter-evans/create-or-update-comment@v2
with:
comment-id: ${{ steps.fc.outputs.comment-id }}
issue-number: ${{ github.event.pull_request.number }}
@ -221,7 +221,7 @@ jobs:
uses: actions/setup-python@v3
with:
python-version: 3.8
- uses: actions/cache@v2
- uses: actions/cache@v3
with:
path: |
~/.gradle/caches
@ -230,7 +230,7 @@ jobs:
restore-keys: |
${{ runner.os }}-gradle-
- name: Start synapse server
uses: michaelkaye/setup-matrix-synapse@v0.3.0
uses: michaelkaye/setup-matrix-synapse@v0.4.0
with:
uploadLogs: true
httpPort: 8080
@ -273,7 +273,7 @@ jobs:
with:
distribution: 'adopt'
java-version: '11'
- uses: actions/cache@v2
- uses: actions/cache@v3
with:
path: |
~/.gradle/caches
@ -293,7 +293,7 @@ jobs:
sonarqube:
name: Sonarqube upload
runs-on: macos-latest
if: always()
if: always() && github.event_name == 'schedule'
needs:
- codecov-units
steps:
@ -302,7 +302,7 @@ jobs:
with:
distribution: 'adopt'
java-version: '11'
- uses: actions/cache@v2
- uses: actions/cache@v3
with:
path: |
~/.gradle/caches
@ -319,7 +319,7 @@ jobs:
env:
ORG_GRADLE_PROJECT_SONAR_LOGIN: ${{ secrets.SONAR_TOKEN }}
# Notify the channel about scheduled runs, do not notify for manually triggered runs
# Notify the channel about scheduled runs, or pushes to the release branches, do not notify for manually triggered runs
notify:
name: Notify matrix
runs-on: ubuntu-latest
@ -335,5 +335,5 @@ jobs:
github_token: ${{ secrets.GITHUB_TOKEN }}
matrix_access_token: ${{ secrets.ELEMENT_ANDROID_NOTIFICATION_ACCESS_TOKEN }}
matrix_room_id: ${{ secrets.ELEMENT_ANDROID_INTERNAL_ROOM_ID }}
text_template: "Nightly test run: {{#each job_statuses }}{{#with this }}{{#if completed }} {{name}} {{conclusion}} at {{completed_at}}, {{/if}}{{/with}}{{/each}}"
html_template: "Nightly test run results: {{#each job_statuses }}{{#with this }}{{#if completed }}<br />{{icon conclusion}} {{name}} <font color='{{color conclusion}}'>{{conclusion}} at {{completed_at}} <a href=\"{{html_url}}\">[details]</a></font>{{/if}}{{/with}}{{/each}}"
text_template: "{{#if '${{ github.event_name }}' == 'schedule' }}Nightly test run{{else}}Test run (on ${{ github.ref }}){{/if }}: {{#each job_statuses }}{{#with this }}{{#if completed }} {{name}} {{conclusion}} at {{completed_at}}, {{/if}}{{/with}}{{/each}}"
html_template: "{{#if '${{ github.event_name }}' == 'schedule' }}Nightly test run{{else}}Test run (on ${{ github.ref }}){{/if }}: {{#each job_statuses }}{{#with this }}{{#if completed }}<br />{{icon conclusion}} {{name}} <font color='{{color conclusion}}'>{{conclusion}} at {{completed_at}} <a href=\"{{html_url}}\">[details]</a></font>{{/if}}{{/with}}{{/each}}"

View file

@ -59,7 +59,7 @@ jobs:
fi
- name: Find Comment
if: always() && github.event_name == 'pull_request'
uses: peter-evans/find-comment@v1
uses: peter-evans/find-comment@v2
id: fc
with:
issue-number: ${{ github.event.pull_request.number }}
@ -67,7 +67,7 @@ jobs:
body-includes: Ktlint Results
- name: Add comment if needed
if: always() && github.event_name == 'pull_request' && steps.ktlint-results.outputs.add_comment == 'true'
uses: peter-evans/create-or-update-comment@v1
uses: peter-evans/create-or-update-comment@v2
with:
comment-id: ${{ steps.fc.outputs.comment-id }}
issue-number: ${{ github.event.pull_request.number }}
@ -97,7 +97,7 @@ jobs:
cancel-in-progress: true
steps:
- uses: actions/checkout@v3
- uses: actions/cache@v2
- uses: actions/cache@v3
with:
path: |
~/.gradle/caches
@ -130,7 +130,7 @@ jobs:
cancel-in-progress: true
steps:
- uses: actions/checkout@v3
- uses: actions/cache@v2
- uses: actions/cache@v3
with:
path: |
~/.gradle/caches

View file

@ -23,7 +23,7 @@ jobs:
- name: Run Emoji script
run: ./tools/import_emojis.py
- name: Create Pull Request for Emojis
uses: peter-evans/create-pull-request@v3
uses: peter-evans/create-pull-request@v4
with:
commit-message: Sync Emojis
title: Sync Emojis
@ -49,7 +49,7 @@ jobs:
- name: Run SAS String script
run: ./tools/import_sas_strings.py
- name: Create Pull Request for SAS Strings
uses: peter-evans/create-pull-request@v3
uses: peter-evans/create-pull-request@v4
with:
commit-message: Sync SAS Strings
title: Sync SAS Strings
@ -68,7 +68,7 @@ jobs:
- name: Run analytics import script
run: ./tools/import_analytic_plan.sh
- name: Create Pull Request for analytics plan
uses: peter-evans/create-pull-request@v3
uses: peter-evans/create-pull-request@v4
with:
commit-message: Sync analytics plan
title: Sync analytics plan

View file

@ -25,7 +25,7 @@ jobs:
with:
distribution: 'adopt'
java-version: 11
- uses: actions/cache@v2
- uses: actions/cache@v3
with:
path: |
~/.gradle/caches
@ -45,7 +45,7 @@ jobs:
cancel-in-progress: true
steps:
- uses: actions/checkout@v3
- uses: actions/cache@v2
- uses: actions/cache@v3
with:
path: |
~/.gradle/caches

View file

@ -1,3 +1,74 @@
Changes in Element v1.4.8 (2022-03-28)
======================================
Other changes
-------------
- Moving live location sharing permission to debug only builds whilst it is WIP ([#5636](https://github.com/vector-im/element-android/issues/5636))
Changes in Element v1.4.7 (2022-03-24)
======================================
Bugfixes 🐛
----------
- Fix inconsistencies between the arrow visibility and the collapse action on the room sections ([#5616](https://github.com/vector-im/element-android/issues/5616))
- Fix room list header count flickering
Changes in Element v1.4.6 (2022-03-23)
======================================
Features ✨
----------
- Thread timeline is now live and much faster especially for large or old threads ([#5230](https://github.com/vector-im/element-android/issues/5230))
- View all threads per room screen is now live when the home server supports threads ([#5232](https://github.com/vector-im/element-android/issues/5232))
- Add a custom view to display a picker for share location options ([#5395](https://github.com/vector-im/element-android/issues/5395))
- Add ability to pin a location on map for sharing ([#5417](https://github.com/vector-im/element-android/issues/5417))
- Poll Integration Tests ([#5522](https://github.com/vector-im/element-android/issues/5522))
- Live location sharing: adding build config field and show permission dialog ([#5536](https://github.com/vector-im/element-android/issues/5536))
- Live location sharing: Adding indicator view when enabled ([#5571](https://github.com/vector-im/element-android/issues/5571))
Bugfixes 🐛
----------
- Poll system notifications on Android are not user friendly ([#4780](https://github.com/vector-im/element-android/issues/4780))
- Add colors for shield vector drawable ([#4860](https://github.com/vector-im/element-android/issues/4860))
- Support both stable and unstable prefixes for Events about Polls and Location ([#5340](https://github.com/vector-im/element-android/issues/5340))
- Fix missing messages when loading messages forwards ([#5448](https://github.com/vector-im/element-android/issues/5448))
- Fix presence indicator being aligned to the center of the room image ([#5489](https://github.com/vector-im/element-android/issues/5489))
- Read receipt in wrong order ([#5514](https://github.com/vector-im/element-android/issues/5514))
- Fix mentions using matrix.to rather than client defined permalink base url ([#5521](https://github.com/vector-im/element-android/issues/5521))
- Fixes crash when tapping the timeline verification surround box instead of the buttons ([#5540](https://github.com/vector-im/element-android/issues/5540))
- [Notification mode] Wrong mode is displayed when the mention only is selected on the web client ([#5547](https://github.com/vector-im/element-android/issues/5547))
- Fix local echos not being shown when re-opening rooms ([#5551](https://github.com/vector-im/element-android/issues/5551))
- Fix crash when closing a room while decrypting timeline events ([#5552](https://github.com/vector-im/element-android/issues/5552))
- Fix sometimes read marker not properly updating ([#5564](https://github.com/vector-im/element-android/issues/5564))
In development 🚧
----------------
- Dynamically showing/hiding onboarding personalisation screens based on the users homeserver capabilities ([#5375](https://github.com/vector-im/element-android/issues/5375))
- Introduces FTUE personalisation complete screen along with confetti celebration ([#5389](https://github.com/vector-im/element-android/issues/5389))
SDK API changes ⚠️
------------------
- Adds support for MSC3440, additional threads homeserver capabilities ([#5271](https://github.com/vector-im/element-android/issues/5271))
Other changes
-------------
- Refactoring for safer olm and megolm session usage ([#5380](https://github.com/vector-im/element-android/issues/5380))
- Improve headers UI in Rooms/Messages lists ([#4533](https://github.com/vector-im/element-android/issues/4533))
- Number of unread messages on space badge now include number of unread DMs ([#5260](https://github.com/vector-im/element-android/issues/5260))
- Amend spaces menu to be consistent with iOS version ([#5270](https://github.com/vector-im/element-android/issues/5270))
- Selected space highlight changed in left panel ([#5346](https://github.com/vector-im/element-android/issues/5346))
- [Rooms list] Do not suggest collapse the unique section ([#5347](https://github.com/vector-im/element-android/issues/5347))
- Add analytics support for threads ([#5378](https://github.com/vector-im/element-android/issues/5378))
- Add top margin before our first message ([#5384](https://github.com/vector-im/element-android/issues/5384))
- Improved onboarding registration unit test coverage ([#5408](https://github.com/vector-im/element-android/issues/5408))
- Adds stable room hierarchy endpoint with a fallback to the unstable one ([#5443](https://github.com/vector-im/element-android/issues/5443))
- Use ColorPrimary for attachmentGalleryButton tint ([#5501](https://github.com/vector-im/element-android/issues/5501))
- Added online presence indicator attribute online to match offline styling ([#5513](https://github.com/vector-im/element-android/issues/5513))
- Add a presence sync enabling build config ([#5563](https://github.com/vector-im/element-android/issues/5563))
- Show stickers on click ([#5572](https://github.com/vector-im/element-android/issues/5572))
Changes in Element v1.4.4 (2022-03-09)
======================================

View file

@ -2,7 +2,7 @@
Please read https://github.com/matrix-org/synapse/blob/master/CONTRIBUTING.md
Android support can be found in this [![Element Android Matrix room #element-android:matrix.org](https://img.shields.io/matrix/element-android:matrix.org.svg?label=%23element-android:matrix.org&logo=matrix&server_fqdn=matrix.org)](https://matrix.to/#/#element-android:matrix.org) room.
Element Android support can be found in this room: [![Element Android Matrix room #element-android:matrix.org](https://img.shields.io/matrix/element-android:matrix.org.svg?label=%23element-android:matrix.org&logo=matrix&server_fqdn=matrix.org)](https://matrix.to/#/#element-android:matrix.org).
# Specific rules for Matrix Android projects
@ -44,6 +44,8 @@ If you want to fix an issue in other languages, or add a missing translation, or
## I want to submit a PR to fix an issue
Please have a look in the [dedicated documentation](./docs/pull_request.md) about pull request.
Please check if a corresponding issue exists. If yes, please let us know in a comment that you're working on it.
If an issue does not exist yet, it may be relevant to open a new issue and let us know that you're implementing it.

View file

@ -21,7 +21,7 @@ buildscript {
classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.3'
classpath 'com.google.android.gms:oss-licenses-plugin:0.10.5'
classpath "com.likethesalad.android:stem-plugin:2.0.0"
classpath 'org.owasp:dependency-check-gradle:7.0.3'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
@ -32,6 +32,16 @@ plugins {
id "org.jlleitschuh.gradle.ktlint" version "10.2.1"
}
// https://github.com/jeremylong/DependencyCheck
apply plugin: 'org.owasp.dependencycheck'
dependencyCheck {
// See https://jeremylong.github.io/DependencyCheck/general/suppression.html
suppressionFiles = [
"./tools/dependencycheck/suppressions.xml"
]
}
allprojects {
apply plugin: "org.jlleitschuh.gradle.ktlint"
@ -51,7 +61,7 @@ allprojects {
}
// Jitsi repo
maven {
url "https://github.com/vector-im/jitsi_libre_maven/raw/main/android-sdk-3.10.0"
url "https://github.com/vector-im/jitsi_libre_maven/raw/main/android-sdk-5.0.2"
// Note: to test Jitsi release you can use a local file like this:
// url "file:///Users/bmarty/workspaces/jitsi_libre_maven/android-sdk-3.10.0"
content {
@ -87,6 +97,8 @@ allprojects {
// See https://github.com/JLLeitschuh/ktlint-gradle#configuration
ktlint {
// See https://github.com/pinterest/ktlint/releases/
version = "0.45.1"
android = true
ignoreFailures = false
enableExperimentalRules = true
@ -96,7 +108,16 @@ allprojects {
"spacing-between-declarations-with-comments",
"no-multi-spaces",
"experimental:spacing-between-declarations-with-annotations",
"experimental:annotation"
"experimental:annotation",
// - Missing newline after "("
// - Missing newline before ")"
"wrapping",
// - Unnecessary trailing comma before ")"
"experimental:trailing-comma",
// - A block comment in between other elements on the same line is disallowed
"experimental:comment-wrapping",
// - A KDoc comment after any other element on the same line must be separated by a new line
"experimental:kdoc-wrapping",
]
}
}

1
changelog.d/4445.bugfix Normal file
View file

@ -0,0 +1 @@
Replace "open settings" button by "disable" action in RageShake dialog if there is no session

View file

@ -1 +0,0 @@
Improve headers UI in Rooms/Messages lists

View file

@ -1 +0,0 @@
Poll system notifications on Android are not user friendly

View file

@ -1 +0,0 @@
Add colors for shield vector drawable

1
changelog.d/4867.bugfix Normal file
View file

@ -0,0 +1 @@
Fixes room summaries showing encrypted content after verifying device

View file

@ -1 +0,0 @@
Thread timeline is now live and much faster especially for large or old threads

View file

@ -1 +0,0 @@
View all threads per room screen is now live when the home server supports threads

View file

@ -1 +0,0 @@
Number of unread messages on space badge now include number of unread DMs

View file

@ -1 +0,0 @@
Amend spaces menu to be consistent with iOS version

View file

@ -1 +0,0 @@
Adds support for MSC3440, additional threads homeserver capabilities

1
changelog.d/5277.wip Normal file
View file

@ -0,0 +1 @@
Adding combined account creation and server selection screen as part of the new FTUE

View file

@ -1 +0,0 @@
Support both stable and unstable prefixes for Events about Polls and Location

View file

@ -1 +0,0 @@
Selected space highlight changed in left panel

View file

@ -1 +0,0 @@
Dynamically showing/hiding onboarding personalisation screens based on the users homeserver capabilities

View file

@ -1 +0,0 @@
Add analytics support for threads

View file

@ -1 +0,0 @@
Add top margin before our first message

View file

@ -1 +0,0 @@
Introduces FTUE personalisation complete screen along with confetti celebration

View file

@ -1 +0,0 @@
Add a custom view to display a picker for share location options

View file

@ -1 +0,0 @@
Improved onboarding registration unit test coverage

View file

@ -1 +0,0 @@
Add ability to pin a location on map for sharing

1
changelog.d/5426.feature Normal file
View file

@ -0,0 +1 @@
Allow scrolling position of Voice Message playback

View file

@ -1 +0,0 @@
Adds stable room hierarchy endpoint with a fallback to the unstable one

View file

@ -1 +0,0 @@
Fix missing messages when loading messages forwards

1
changelog.d/5473.bugfix Normal file
View file

@ -0,0 +1 @@
Fixes polls being votable after being ended

1
changelog.d/5497.bugfix Normal file
View file

@ -0,0 +1 @@
[Subscribing] Blank display name

View file

@ -1 +0,0 @@
Use ColorPrimary for attachmentGalleryButton tint

View file

@ -1 +0,0 @@
Added online presence indicator attribute online to match offline styling

View file

@ -1 +0,0 @@
Read receipt in wrong order

1
changelog.d/5516.misc Normal file
View file

@ -0,0 +1 @@
"Add space" copy is replaced with "create space" in left sliding panel

1
changelog.d/5517.misc Normal file
View file

@ -0,0 +1 @@
Flattening the asynchronous onboarding state and passing all errors through the same pipeline

1
changelog.d/5519.wip Normal file
View file

@ -0,0 +1 @@
Finalising FTUE onboarding account creation personalization steps but keeping feature disabled until other parts are complete

View file

@ -1 +0,0 @@
Fix mentions using matrix.to rather than client defined permalink base url

View file

@ -1 +0,0 @@
Poll Integration Tests

View file

@ -1 +0,0 @@
Live location sharing: adding build config field and show permission dialog

View file

@ -1 +0,0 @@
Fixes crash when tapping the timeline verification surround box instead of the buttons

View file

@ -1 +0,0 @@
[Notification mode] Wrong mode is displayed when the mention only is selected on the web client

1
changelog.d/5548.bugfix Normal file
View file

@ -0,0 +1 @@
Fixes voice call button disappearing in DM rooms with more than 2 members

View file

@ -1 +0,0 @@
Fix local echos not being shown when re-opening rooms

View file

@ -1 +0,0 @@
Fix crash when closing a room while decrypting timeline events

1
changelog.d/5562.bugfix Normal file
View file

@ -0,0 +1 @@
Add loader in thread list

View file

@ -1 +0,0 @@
Add a presence sync enabling build config

View file

@ -1 +0,0 @@
Live location sharing: Adding indicator view when enabled

View file

@ -1,2 +0,0 @@
Show stickers on click

1
changelog.d/5581.misc Normal file
View file

@ -0,0 +1 @@
Live location sharing: adding way to override feature activation in debug

1
changelog.d/5595.feature Normal file
View file

@ -0,0 +1 @@
Live Location Sharing - Foreground Service and Notification

1
changelog.d/5628.misc Normal file
View file

@ -0,0 +1 @@
Adds unit tests around the login with matrix id flow

1
changelog.d/5654.feature Normal file
View file

@ -0,0 +1 @@
Update Jitsi lib from 3.10.0 to 5.0.2

1
changelog.d/5654.misc Normal file
View file

@ -0,0 +1 @@
Setup the plugin org.owasp.dependencycheck

1
changelog.d/5663.bugfix Normal file
View file

@ -0,0 +1 @@
Fixed key export when overwriting existing files

View file

@ -9,13 +9,13 @@ ext.versions = [
def gradle = "7.0.4"
// Ref: https://kotlinlang.org/releases.html
def kotlin = "1.5.31"
def kotlinCoroutines = "1.5.2"
def kotlin = "1.6.0"
def kotlinCoroutines = "1.6.0"
def dagger = "2.40.5"
def retrofit = "2.9.0"
def arrow = "0.8.2"
def markwon = "4.6.2"
def moshi = "1.12.0"
def moshi = "1.13.0"
def lifecycle = "2.4.0"
def flowBinding = "1.2.0"
def epoxy = "4.6.2"

View file

@ -7,6 +7,7 @@ ext.groups = [
'com.github.chrisbanes',
'com.github.hyuwah',
'com.github.jetradarmobile',
'com.github.MatrixFrog',
'com.github.tapadoo',
'com.github.vector-im',
'com.github.yalantis',
@ -39,6 +40,7 @@ ext.groups = [
regex: [
],
group: [
'ch.qos.logback',
'com.adevinta.android',
'com.airbnb.android',
'com.almworks.sqlite4java',
@ -48,10 +50,12 @@ ext.groups = [
'com.beust',
'com.davemorrissey.labs',
'com.dropbox.core',
'com.facebook.fbjni',
'com.facebook.fresco',
'com.facebook.infer.annotation',
'com.facebook.soloader',
'com.facebook.stetho',
'com.facebook.yoga',
'com.fasterxml',
'com.fasterxml.jackson',
'com.fasterxml.jackson.core',
@ -113,6 +117,7 @@ ext.groups = [
'info.picocli',
'io.arrow-kt',
'io.github.detekt.sarif4k',
'io.github.microutils',
'io.github.reactivecircus.flowbinding',
'io.grpc',
'io.jsonwebtoken',

View file

@ -18,6 +18,8 @@ The generated maven repository is then host in the project https://github.com/ve
Update the script `./tools/jitsi/build_jisti_libs.sh` with the tag of the project `https://github.com/jitsi/jitsi-meet`.
Latest tag can be found from this page: https://github.com/jitsi/jitsi-meet-release-notes/blob/master/CHANGELOG-MOBILE-SDKS.md
Currently we are building the version with the tag `android-sdk-3.10.0`.
### Run the build script

236
docs/pull_request.md Normal file
View file

@ -0,0 +1,236 @@
# Pull requests
## Introduction
This document gives some clue about how to efficiently manage Pull Requests (PR). This document is a first draft and may be improved later.
## Who should read this document?
Every pull request reviewers, but also probably every ones who submit PRs.
## Submitting PR
### Who can submit pull requests?
Basically every one who wants to contribute to the project! But there are some rules to follow.
#### Humans
People with write access to the project can directly clone the project, push their branches and create PR.
External contributors must first fork the project and create PR to the mainline from there.
##### Draft PR?
Draft PR can be created when the submitter does not expect the PR to be reviewed and merged yet. It can be useful to publicly show the work, or to do a self-review first.
Draft PR can also be created when it depends on other un-merged PR.
In any case, it is better to explicitly declare in the description why the PR is a draft PR.
Also, draft PR should not stay indefinitely in this state. It may be removed if it is the case and the submitter does not update it after a few days.
##### PR Review Assignment
We use automatic assignment for PR reviews. A PR is automatically routed by GitHub to a team member using the round robin algorithm. The process is the following:
- The PR creator assigns the [element-android](https://github.com/orgs/vector-im/teams/element-android) team as a reviewer. They can skip this process and assign directly a specific member if they think they should take a look at it.
- GitHub automatically assigns one reviewer. If the chosen reviewer is not available (holiday, etc.), remove them and set again the team, GitHub will select another reviewer.
- The reviewer gets a notification to make the review: they review the code following the good practice (see the rest of this document).
- After making their own review, if they feel not confident enough, they can ask another person for a full review, or they can tag someone within a PR comment to check specific lines.
For PRs coming from the community, the issue wrangler can assign either the team [element-android](https://github.com/orgs/vector-im/teams/element-android) or any member directly.
##### PR review time
As a PR submitter, you deserve a quick review. As a reviewer, you should do your best to unblock others.
Some tips to achieve it:
- Set up your GH notifications correctly
- Check your pulls page: [https://github.com/pulls](https://github.com/pulls)
- Check your pending assigned PRs before starting or resuming your day to day tasks
It is hard to define a deadline for a review. It depends on the PR size and the complexity. Let's start with a goal of 24h (working day!) for a PR smaller than 500 lines. If bigger, the submitter and the reviewer should discuss.
After this time, the submitter can ping the reviewer to get a status of the review.
##### Re-request PR review
Once all the remarks have been handled, it's possible to re-request a review from the (same) reviewer to let them know that the PR has been updated the PR is ready to be reviewed again. Use the double arrow next to the reviewer name to do that.
##### When create split PR?
To implement big new feature, it may be efficient to split the work into several smaller and scoped PRs. They will be easier to review, and they can be merged on `develop` faster.
Big PR can take time, and there is a risk of future merge conflict.
Feature flag can be used to avoid half implemented feature to be available in the application.
That said, splitting into several PRs should not have the side effect to have more review to do, for instance if some code is added, then finally removed.
##### Avoid fixing other unrelated issue in a big PR
Each PR should focus on a single task. If other issues may be fixed when working in the area of it, it's preferable to open a dedicated PR.
It will have the advantage to be reviewed and merged faster, and not interfere with the main PR.
It's also applicable for code rework (such as renaming for instance), or code formatting. Sometimes, it is more efficient to extract that work to a dedicated PR, and rebase your branch once this "rework" PR has been merged.
#### Bots
Some bots can create PR, but they still have to be reviewed by the team
##### Dependabot
Dependabot is a tool which maintain all our external dependencies up to date. A dedicated PR is created for each new available release for one of our external dependency.Dependabot
To review such PR, you have to
- **IMPORTANT** check the diff files (as always).
- Check the release note. Some existing bugs in Element project may be fixed by the upgrade
- Make sure that the CI is happy
- If the code does not compile (API break for instance), you have to checkout the branch and push new commits
- Do some smoke test, depending of the library which has been upgraded
For some reason dependabot sometimes does not upgrade some dependencies. In this case, and when detected, the upgrade has to be done manually.
##### Gradle wrapper
`Update Gradle Wrapper` is a tool which can create PR to upgrade our gradle.properties file.
Review such PR is the same recipe than for PR from Dependabot
##### Sync analytics plan
This tools imports any update in the analytics plan. See instruction in the PR itself to handle it.
More info can be found in the file [analytics.md]
## Reviewing PR
### Who can review pull requests?
As an open source project, every one can review each others PR. Of course an approval from internal developer is mandatory for a PR to be merged.
But comment in PR from the community are always appreciated!
### What to have in mind when reviewing a PR
1. User experience: is the UX and UI correct? You will probably be the second person to test the new thing, the first one is the developer.
2. Developer experience: does the code look nice and decoupled? No big functions, new classes added to the right module, etc.
3. Code maintenance. A bit similar to point 2. Tricky code must be documented for instance
4. Fork consideration. Will configuration of forks be easy? Some documentation may help in some cases.
5. We are building long term products. "Quick and dirty" code must be avoided.
6. The PR includes new tests for the added code, updated test for the existing code
7. All PRs from external contributors **MUST** include a sign-off. It's in the checklist, and sometimes it's checked by the submitter, but there is actually no sign-off. In this case, ask nicely for a sign-off and request changes (do not approve the PR, even if everything else is fine).
### Rules
#### Check the form
##### PR title
PR title should describe in one line what's brought by the PR. Reviewer can edit the title if it's not clear enough, or to add suffix like `[BLOCKED]` or similar. Fixing typo is also a good practice, since GitHub search is quite not efficient, so the words must be spelled without any issue. Adding suffix will help when viewing the PR list.
It's free form, but prefix tags could also be used to help understand what's in the PR.
Examples of prefixes:
- `[Refacto]`
- `[Feature]`
- `[Bugfix]`
- etc.
Also, it's still possible to add labels to the PRs, such as `A-` or `T-` labels, even if this is not a string requirement. We prefer to spend time to add labels on issues.
##### PR description
PR description should follow the PR template, and at least provide some context about the code change.
##### File change
1. Code should follow the guidelines
2. Code should be formatted correctly
3. XML attribute must be sorted
4. New code is added at the correct location
5. New classes are added to the correct location
6. Naming is correct. Naming is really important, it's considered part of the documentation
7. Architecture is followed. For instance, the logic is in the ViewModel and not in the Fragment
8. There is at least one file for the changelog. Exception if the PR fixes something which has not been released yet. Changelog content should target their audience: `.sdk` extension are mainly targeted for developers, other extensions are targeted for users and forks maintainers. It should generally describe visual change rather than give technical details. More details can be found [here](../CONTRIBUTING.md#changelog).
9. PR includes tests. allScreensTest when applicable, and unit tests
10. Avoid over complicating things. Keep it simple (KISS)!
11. PR contains only the expected change. Sometimes, the diff is showing changes that are already on `develop`. This is not good, submitter has to fix that up.
##### Check the commit
Commit message must be short, one line and valuable. "WIP" is not a good commit message. Commit message can contain issue number, starting with `#`. GitHub will add some link between the issue and such commit, which can be useful. It's possible to change a commit message at any time (may require a force push).
Commit messages can contain extra lines with more details, links, etc. But keep in mind that those lines are quite less visible than the first line.
Also commit history should be nice. Having commits like "Adding temporary code" then later "Removing temporary code" is not good. The branch has to be rebased and those commit have to be dropped.
PR merger could decide to squash and merge if commit history is not good.
Commit like "Code review fixes" is good when reviewing the PR, since new changes can be reviewed easily, but is less valuable when looking at git history. To avoid this, PR submitter should always push new commits after a review (no commit amend with force push), and when the PR is approved decide to interactive rebase the PR to improve the git history and reduce noise.
##### Check the substance
1. Test the changes!
2. Test the nominal case and the edge cases
3. Run the sanity test for critical PR
##### Make a dedicated meeting to review the PR
Sometimes a big PR can be hard to review. Setting up a call with the PR submitter can speed up the communication, rather than putting comments and questions in GitHub comments. It has the inconvenience of making the discussion non-public, consider including a summary of the main points of the "offline" conversation in the PR.
### What happen to the issue(s)?
The issue(s) should be referenced in the PR description using keywords like `Closes` of `Fixes` followed by the issue number.
Example:
> Closes #1
Note that you have to repeat the keyword in case of a list of issue
> Closes #1, Closes #2, etc.
When PR will be merged, such referenced issue will be automatically closed.
It is up to the person who has merged the PR to go to the (closed) issue(s) and to add a comment to inform in which version the issue fix will be available. Use the current version of `develop` branch.
> Closed in Element Android v1.x.y
### Merge conflict
It's up to the submitter to handle merge conflict. Sometimes, they can be fixed directly from GitHub, sometimes this is not possible. The branch can be rebased on `develop`, or the `develop` branch can be merged on the branch, it's up to the submitter to decide what is best.
Keep in mind that Github Actions are not run in case of conflict.
### When and who can merge PR
PR can be merged by the submitter, if and only if at least one approval from another developer is done. Approval from all people added as reviewer is also a good thing to have. Approval from design team may be mandatory, but is not sufficient to merge a PR.
PR can also be merged by the reviewer, to reduce the time the PR is open. But only if the PR is not in draft and the change are quite small, or behind a feature flag.
Dangerous PR should not be merged just before a release. Dangerous PR are PR that could break the app. Update of Realm library, rework in the chunk of Events management in the SDK, etc.
We prefer to merge such PR after a release so that it can be tested during several days by the team before behind included in a release candidate.
PR from bots will always be merged by the reviewer, right after approving the changes, or in case of critical changes, right after a release.
#### Merge type
Generally we use "Create a merge commit", which has the advantage to keep the branch visible.
If git history is noisy (code added, then removed, etc.), it's possible to use "Squash and merge". But the branch will not be visible anymore, a commit will be added on top of develop. Git commit message can (and probably must) be edited from the GitHub web app. It's better if the submitter do the work to cleanup the git history by using a git interactive rebase of their branch.
### Resolve conversation
Generally we do not close conversation added during PR review and update by clicking on "Resolve conversation"
If the submitter or the reviewer do so, it will more difficult for further readers to see again the content. They will have to open the conversation to see it again. it's a waste of time.
When remarks are handled, a small comment like "done" is enough, commit hash can also be added to the conversation.
Exception: for big PRs with lots of conversations, using "Resolve conversation" may help to see the remaining remarks.
Also "Resolve conversation" should probably be hit by the creator of the conversation.
## Responsibility
PR submitter is responsible of the incoming change. PR reviewers who approved the PR take a part of responsibility on the code which will land to develop, and then be used by our users, and the user of our forks.
That said, bug may still be merged on `develop`, this is still acceptable of course. In this case, please make sure an issue is created and correctly labelled. Ideally, such issues should be fixed before the next release candidate, i.e. with a higher priority. But as we release the application every 10 working days, it can be hard to fix every bugs. That's why PR should be fully tested and reviewed before being merge and we should never comment code review remark with "will be handled later", or similar comments.

View file

@ -0,0 +1,2 @@
Main changes in this version: Thread timeline are now live and faster. Various bug fixes and stability improvements.
Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.4.6

View file

@ -0,0 +1,2 @@
Main changes in this version: Various bug fixes and stability improvements.
Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.4.7

View file

@ -0,0 +1,2 @@
Main changes in this version: Thread timeline are now live and faster. Various bug fixes and stability improvements.
Full changelog: https://github.com/vector-im/element-android/releases

View file

@ -0,0 +1,2 @@
Principales cambios de esta versión: primera implementación de los hilos de mensajes. Burbujas de mensajes.
Todos los cambios en: https://github.com/vector-im/element-android/releases/tag/v1.4.0

View file

@ -0,0 +1,2 @@
Principales cambios de esta versión: añadir @room, encuestas cerradas y muchos cambios menores más.
Todos los cambios en: https://github.com/vector-im/element-android/releases/tag/v1.4.2

View file

@ -0,0 +1,2 @@
تغییرات اصلی در این نگارش: پیاده سازی نخستین پیام‌های رشته‌ای. حباب‌های پیام.
گزارش دگرگونی کامل: https://github.com/vector-im/element-android/releases/tag/v1.4.0

View file

@ -0,0 +1,2 @@
تغییرات اصلی در این نگارش: افزودن پشتیبانی به @room و نظرسنجی‌های فاش نشده در کنار تغییرات کوچک دیگر.
گزارش دگرگونی کامل: https://github.com/vector-im/element-android/releases/tag/v1.4.2

View file

@ -8,12 +8,13 @@ Az Element egy biztonságos üzenetküldő, és egy csapatmunka app, amely távo
- Videochat, VoIP, és képernyőmegosztási lehetőséggel
- Egyszerű integráció a kedvenc online kollaborációs eszközeiddel, projektkezelési eszközökkel, VoIP szolgáltatásokkal, és más csoportos üzenetküldő alkalmazásokkal
Element is completely different from other messaging and collaboration apps. It operates on Matrix, an open network for secure messaging and decentralized communication. It allows self-hosting to give users maximum ownership and control of their data and messages.
Az Element teljesen más, mint az összes többi üzenetküldő és kollaborációs alkalmazás. A biztonságos üzenetküldést és decentralizált kommunikációt biztosító Matrix platformot használja. Akár egyénileg üzemeltetett szervereket is lehet használni az adatok teljes kontrollálása érdekében.
<b>Privacy and encrypted messaging</b>
Element protects you from unwanted ads, data mining and walled gardens. It also secures all your data, one-to-one video and voice communication through end-to-end encryption and cross-signed device verification.
<b>Magánszféra és titkosított csevegés</b>
Az Element megvéd a nemkívánatos hirdetésektől, adatbányászattól, és a zárt platformoktól. Ezeken felül biztonságban tartja az összes adatod és 1:1 hívásod a végponti titkosításnak és az eszközök-közti hitelesítésnek köszönhetően.
Az Element átadja neked az irányítást a magánszférád felett, miközben lehetővé teszi, hogy biztonságosan kommunikálj bárkivel a Matrix hálózatban, vagy a többi üzleti kommunikációs eszközt használókkal, az olyan appok integrálásának köszönhetően, mint például a Slack.
Element gives you control over your privacy while allowing you to communicate securely with anyone on the Matrix network, or other business collaboration tools by integrating with apps such as Slack.
<b>Element can be self-hosted</b>
To allow more control of your sensitive data and conversations, Element can be self-hosted or you can choose any Matrix-based host - the standard for open source, decentralized communication. Element gives you privacy, security compliance and integration flexibility.

View file

@ -0,0 +1,2 @@
Основные изменения в этой версии: Начальная реализация веток сообщений. Сообщения пузыри.
Полный список изменений: https://github.com/vector-im/element-android/releases/tag/v1.4.0

View file

@ -0,0 +1,2 @@
Основные изменения в этой версии: добавлена поддержка @room и нераскрытых опросов, а также множество других мелких изменений.
Полный список изменений: https://github.com/vector-im/element-android/releases/tag/v1.4.2

View file

@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=a9a7b7baba105f6557c9dcf9c3c6e8f7e57e6b49889c5f1d133f015d0727e4be
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.1-all.zip
distributionSha256Sum=e6d864e3b5bc05cc62041842b306383fc1fefcec359e70cebb1d470a6094ca82
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View file

@ -59,7 +59,7 @@ dependencies {
implementation libs.jetbrains.coroutinesCore
implementation libs.jetbrains.coroutinesAndroid
testImplementation 'org.json:json:20211205'
testImplementation 'org.json:json:20220320'
testImplementation libs.tests.junit
androidTestImplementation libs.androidx.junit
androidTestImplementation libs.androidx.espressoCore

View file

@ -20,13 +20,12 @@ import android.content.Context
import android.view.View
import com.airbnb.epoxy.TypedEpoxyController
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Success
import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence
import me.gujun.android.span.Span
import me.gujun.android.span.span
internal class JSonViewerEpoxyController(private val context: Context) :
TypedEpoxyController<JSonViewerState>() {
TypedEpoxyController<JSonViewerState>() {
private var styleProvider: JSonViewerStyleProvider = JSonViewerStyleProvider.default(context)
@ -44,10 +43,8 @@ internal class JSonViewerEpoxyController(private val context: Context) :
text(async.error.localizedMessage?.toEpoxyCharSequence())
}
}
is Success -> {
val model = data.root.invoke()
model?.let {
else -> {
async.invoke()?.let {
buildRec(it, 0, "")
}
}
@ -55,9 +52,9 @@ internal class JSonViewerEpoxyController(private val context: Context) :
}
private fun buildRec(
model: JSonViewerModel,
depth: Int,
idBase: String
model: JSonViewerModel,
depth: Int,
idBase: String
) {
val host = this
val id = "$idBase/${model.key ?: model.index}_${model.isExpanded}}"
@ -74,34 +71,34 @@ internal class JSonViewerEpoxyController(private val context: Context) :
id(id + "_sum")
depth(depth)
text(
span {
if (model.key != null) {
span("\"${model.key}\"") {
textColor = host.styleProvider.keyColor
}
span(" : ") {
textColor = host.styleProvider.baseColor
}
}
if (model.index != null) {
span("${model.index}") {
textColor = host.styleProvider.secondaryColor
}
span(" : ") {
textColor = host.styleProvider.baseColor
}
}
span {
+"{+${model.keys.size}}"
textColor = host.styleProvider.baseColor
}
}.toEpoxyCharSequence()
if (model.key != null) {
span("\"${model.key}\"") {
textColor = host.styleProvider.keyColor
}
span(" : ") {
textColor = host.styleProvider.baseColor
}
}
if (model.index != null) {
span("${model.index}") {
textColor = host.styleProvider.secondaryColor
}
span(" : ") {
textColor = host.styleProvider.baseColor
}
}
span {
+"{+${model.keys.size}}"
textColor = host.styleProvider.baseColor
}
}.toEpoxyCharSequence()
)
itemClickListener(View.OnClickListener { host.itemClicked(model) })
}
}
}
is JSonViewerArray -> {
is JSonViewerArray -> {
if (model.isExpanded) {
open(id, model.key, model.index, depth, false, model)
model.items.forEach {
@ -113,6 +110,38 @@ internal class JSonViewerEpoxyController(private val context: Context) :
id(id + "_sum")
depth(depth)
text(
span {
if (model.key != null) {
span("\"${model.key}\"") {
textColor = host.styleProvider.keyColor
}
span(" : ") {
textColor = host.styleProvider.baseColor
}
}
if (model.index != null) {
span("${model.index}") {
textColor = host.styleProvider.secondaryColor
}
span(" : ") {
textColor = host.styleProvider.baseColor
}
}
span {
+"[+${model.items.size}]"
textColor = host.styleProvider.baseColor
}
}.toEpoxyCharSequence()
)
itemClickListener(View.OnClickListener { host.itemClicked(model) })
}
}
}
is JSonViewerLeaf -> {
valueItem {
id(id)
depth(depth)
text(
span {
if (model.key != null) {
span("\"${model.key}\"") {
@ -122,6 +151,7 @@ internal class JSonViewerEpoxyController(private val context: Context) :
textColor = host.styleProvider.baseColor
}
}
if (model.index != null) {
span("${model.index}") {
textColor = host.styleProvider.secondaryColor
@ -130,41 +160,8 @@ internal class JSonViewerEpoxyController(private val context: Context) :
textColor = host.styleProvider.baseColor
}
}
span {
+"[+${model.items.size}]"
textColor = host.styleProvider.baseColor
}
append(host.valueToSpan(model))
}.toEpoxyCharSequence()
)
itemClickListener(View.OnClickListener { host.itemClicked(model) })
}
}
}
is JSonViewerLeaf -> {
valueItem {
id(id)
depth(depth)
text(
span {
if (model.key != null) {
span("\"${model.key}\"") {
textColor = host.styleProvider.keyColor
}
span(" : ") {
textColor = host.styleProvider.baseColor
}
}
if (model.index != null) {
span("${model.index}") {
textColor = host.styleProvider.secondaryColor
}
span(" : ") {
textColor = host.styleProvider.baseColor
}
}
append(host.valueToSpan(model))
}.toEpoxyCharSequence()
)
copyValue(model.stringRes)
}
@ -175,12 +172,12 @@ internal class JSonViewerEpoxyController(private val context: Context) :
private fun valueToSpan(leaf: JSonViewerLeaf): Span {
val host = this
return when (leaf.type) {
JSONType.STRING -> {
JSONType.STRING -> {
span("\"${leaf.stringRes}\"") {
textColor = host.styleProvider.stringColor
}
}
JSONType.NUMBER -> {
JSONType.NUMBER -> {
span(leaf.stringRes) {
textColor = host.styleProvider.numberColor
}
@ -190,7 +187,7 @@ internal class JSonViewerEpoxyController(private val context: Context) :
textColor = host.styleProvider.booleanColor
}
}
JSONType.NULL -> {
JSONType.NULL -> {
span("null") {
textColor = host.styleProvider.booleanColor
}
@ -199,42 +196,42 @@ internal class JSonViewerEpoxyController(private val context: Context) :
}
private fun open(
id: String,
key: String?,
index: Int?,
depth: Int,
isObject: Boolean = true,
composed: JSonViewerModel
id: String,
key: String?,
index: Int?,
depth: Int,
isObject: Boolean = true,
composed: JSonViewerModel
) {
val host = this
valueItem {
id("${id}_Open")
depth(depth)
text(
span {
if (key != null) {
span("\"$key\"") {
textColor = host.styleProvider.keyColor
span {
if (key != null) {
span("\"$key\"") {
textColor = host.styleProvider.keyColor
}
span(" : ") {
textColor = host.styleProvider.baseColor
}
}
span(" : ") {
textColor = host.styleProvider.baseColor
if (index != null) {
span("$index") {
textColor = host.styleProvider.secondaryColor
}
span(" : ") {
textColor = host.styleProvider.baseColor
}
}
}
if (index != null) {
span("$index") {
span("- ") {
textColor = host.styleProvider.secondaryColor
}
span(" : ") {
span("{".takeIf { isObject } ?: "[") {
textColor = host.styleProvider.baseColor
}
}
span("- ") {
textColor = host.styleProvider.secondaryColor
}
span("{".takeIf { isObject } ?: "[") {
textColor = host.styleProvider.baseColor
}
}.toEpoxyCharSequence()
}.toEpoxyCharSequence()
)
itemClickListener(View.OnClickListener { host.itemClicked(composed) })
}
@ -251,10 +248,10 @@ internal class JSonViewerEpoxyController(private val context: Context) :
id("${id}_Close")
depth(depth)
text(
span {
text = "}".takeIf { isObject } ?: "]"
textColor = host.styleProvider.baseColor
}.toEpoxyCharSequence()
span {
text = "}".takeIf { isObject } ?: "]"
textColor = host.styleProvider.baseColor
}.toEpoxyCharSequence()
)
}
}

View file

@ -60,6 +60,4 @@ dependencies {
implementation 'com.github.vector-im:PFLockScreen-Android:1.0.0-beta12'
// dialpad dimen
implementation 'im.dlg:android-dialer:1.2.5'
// AudioRecordView attr
implementation 'com.github.Armen101:AudioRecordView:1.0.5'
}

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="AudioWaveformView">
<attr name="alignment" format="enum">
<enum name="center" value="0" />
<enum name="bottom" value="1" />
<enum name="top" value="2" />
</attr>
<attr name="flow" format="enum">
<enum name="leftToRight" value="0" />
<enum name="rightToLeft" value="1" />
</attr>
<attr name="verticalPadding" format="dimension" />
<attr name="horizontalPadding" format="dimension" />
<attr name="barWidth" format="dimension" />
<attr name="barSpace" format="dimension" />
<attr name="barMinHeight" format="dimension" />
<attr name="isBarRounded" format="boolean" />
</declare-styleable>
</resources>

View file

@ -9,6 +9,11 @@
<item name="endIconTint">?vctr_content_secondary</item>
</style>
<style name="Widget.Vector.TextInputLayout.Username">
<item name="endIconMode">clear_text</item>
<item name="endIconTint">?vctr_content_secondary</item>
</style>
<style name="Widget.Vector.TextInputLayout.Form">
<item name="boxStrokeColor">@color/form_edit_text_stroke_color_selector</item>
<item name="android:textColorHint">@color/form_edit_text_hint_color_selector</item>

View file

@ -2,14 +2,14 @@
<resources>
<style name="VoicePlaybackWaveform">
<item name="chunkColor">?vctr_content_secondary</item>
<item name="chunkAlignTo">center</item>
<item name="chunkMinHeight">1dp</item>
<item name="chunkRoundedCorners">true</item>
<item name="chunkSoftTransition">true</item>
<item name="chunkSpace">2dp</item>
<item name="chunkWidth">2dp</item>
<item name="direction">rightToLeft</item>
<item name="alignment">center</item>
<item name="flow">leftToRight</item>
<item name="verticalPadding">4dp</item>
<item name="horizontalPadding">4dp</item>
<item name="barWidth">2dp</item>
<item name="barSpace">2dp</item>
<item name="barMinHeight">1dp</item>
<item name="isBarRounded">true</item>
</style>
</resources>

View file

@ -31,7 +31,7 @@ android {
// that the app's state is completely cleared between tests.
testInstrumentationRunnerArguments clearPackageData: 'true'
buildConfigField "String", "SDK_VERSION", "\"1.4.6\""
buildConfigField "String", "SDK_VERSION", "\"1.4.10\""
buildConfigField "String", "GIT_SDK_REVISION", "\"${gitRevision()}\""
buildConfigField "String", "GIT_SDK_REVISION_UNIX_DATE", "\"${gitRevisionUnixDate()}\""
@ -166,7 +166,7 @@ dependencies {
implementation libs.apache.commonsImaging
// Phone number https://github.com/google/libphonenumber
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.45'
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.46'
testImplementation libs.tests.junit
testImplementation 'org.robolectric:robolectric:4.7.3'

View file

@ -138,7 +138,7 @@ class WithHeldTests : InstrumentedTest {
@Test
@Ignore("This test will be ignored until it is fixed")
fun test_WithHeldNoOlm() {
fun test_WithHeldNoOlm() {
val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = testData.firstSession
val bobSession = testData.secondSession!!

View file

@ -64,7 +64,11 @@ data class MatrixConfiguration(
/**
* True to enable presence information sync (if available). False to disable regardless of server setting.
*/
val presenceSyncEnabled: Boolean = true
val presenceSyncEnabled: Boolean = true,
/**
* Thread messages default enable/disabled value
*/
val threadMessagesEnabledDefault: Boolean = false,
) {
/**

View file

@ -58,12 +58,36 @@ fun Throwable.getRetryDelay(defaultValue: Long): Long {
?: defaultValue
}
fun Throwable.isUsernameInUse(): Boolean {
return this is Failure.ServerError && error.code == MatrixError.M_USER_IN_USE
}
fun Throwable.isInvalidUsername(): Boolean {
return this is Failure.ServerError &&
error.code == MatrixError.M_INVALID_USERNAME
}
fun Throwable.isInvalidPassword(): Boolean {
return this is Failure.ServerError &&
error.code == MatrixError.M_FORBIDDEN &&
error.message == "Invalid password"
}
fun Throwable.isRegistrationDisabled(): Boolean {
return this is Failure.ServerError && error.code == MatrixError.M_FORBIDDEN &&
httpCode == HttpsURLConnection.HTTP_FORBIDDEN
}
fun Throwable.isWeakPassword(): Boolean {
return this is Failure.ServerError && error.code == MatrixError.M_WEAK_PASSWORD
}
fun Throwable.isLoginEmailUnknown(): Boolean {
return this is Failure.ServerError &&
error.code == MatrixError.M_FORBIDDEN &&
error.message.isEmpty()
}
fun Throwable.isInvalidUIAAuth(): Boolean {
return this is Failure.ServerError &&
error.code == MatrixError.M_FORBIDDEN &&
@ -104,8 +128,8 @@ fun Throwable.isRegistrationAvailabilityError(): Boolean {
return this is Failure.ServerError &&
httpCode == HttpsURLConnection.HTTP_BAD_REQUEST && /* 400 */
(error.code == MatrixError.M_USER_IN_USE ||
error.code == MatrixError.M_INVALID_USERNAME ||
error.code == MatrixError.M_EXCLUSIVE)
error.code == MatrixError.M_INVALID_USERNAME ||
error.code == MatrixError.M_EXCLUSIVE)
}
/**

View file

@ -140,7 +140,6 @@ interface CryptoService {
fun getLiveCryptoDeviceInfo(userIds: List<String>): LiveData<List<CryptoDeviceInfo>>
fun addNewSessionListener(newSessionListener: NewSessionListener)
fun removeSessionListener(listener: NewSessionListener)
fun getOutgoingRoomKeyRequests(): List<OutgoingRoomKeyRequest>

View file

@ -46,3 +46,5 @@ data class UnsignedData(
@Json(name = "replaces_state") val replacesState: String? = null
)
fun UnsignedData?.isRedacted() = this?.redactedEvent != null

View file

@ -21,6 +21,7 @@ import android.net.Uri
import androidx.lifecycle.LiveData
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
import org.matrix.android.sdk.api.session.identity.ThreePid
import org.matrix.android.sdk.api.session.user.model.User
import org.matrix.android.sdk.api.util.JsonDict
import org.matrix.android.sdk.api.util.Optional
@ -118,4 +119,17 @@ interface ProfileService {
* Remove a 3Pid from the Matrix account.
*/
suspend fun deleteThreePid(threePid: ThreePid)
/**
* Return a User object from a userId
*/
suspend fun getProfileAsUser(userId: String): User {
return getProfile(userId).let { dict ->
User(
userId = userId,
displayName = dict[DISPLAY_NAME_KEY] as? String,
avatarUrl = dict[AVATAR_URL_KEY] as? String
)
}
}
}

View file

@ -18,7 +18,6 @@ package org.matrix.android.sdk.api.session.room
import androidx.lifecycle.LiveData
import androidx.paging.PagedList
import kotlinx.coroutines.flow.Flow
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
import org.matrix.android.sdk.api.session.room.model.Membership
@ -218,9 +217,10 @@ interface RoomService {
sortOrder: RoomSortOrder = RoomSortOrder.ACTIVITY): UpdatableLivePageResult
/**
* Retrieve a flow on the number of rooms.
* Return a LiveData on the number of rooms
* @param queryParams parameters to query the room summaries. It can be use to keep only joined rooms, for instance.
*/
fun getRoomCountFlow(queryParams: RoomSummaryQueryParams): Flow<Int>
fun getRoomCountLive(queryParams: RoomSummaryQueryParams): LiveData<Int>
/**
* TODO Doc
@ -242,4 +242,12 @@ interface RoomService {
*/
fun getFlattenRoomSummaryChildrenOfLive(spaceId: String?,
memberships: List<Membership> = Membership.activeMemberships()): LiveData<List<RoomSummary>>
/**
* Refreshes the RoomSummary LatestPreviewContent for the given @param roomId
* If the roomId is null, all rooms are updated
*
* This is useful for refreshing summary content with encrypted messages after receiving new room keys
*/
fun refreshJoinedRoomSummaryPreviews(roomId: String?)
}

View file

@ -137,8 +137,7 @@ internal abstract class CryptoModule {
@JvmStatic
@Provides
@CryptoDatabase
fun providesClearCacheTask(@CryptoDatabase
realmConfiguration: RealmConfiguration): ClearCacheTask {
fun providesClearCacheTask(@CryptoDatabase realmConfiguration: RealmConfiguration): ClearCacheTask {
return RealmClearCacheTask(realmConfiguration)
}

View file

@ -15,6 +15,15 @@
*/
package org.matrix.android.sdk.internal.crypto
/**
* This listener notifies on new Megolm sessions being created
*/
interface NewSessionListener {
/**
* @param roomId the room id where the new Megolm session has been created for, may be null when importing from external sessions
* @param senderKey the sender key of the device which the Megolm session is shared with
* @param sessionId the session id of the Megolm session
*/
fun onNewSession(roomId: String?, senderKey: String, sessionId: String)
}

View file

@ -22,6 +22,7 @@ import org.matrix.android.sdk.internal.crypto.MXOlmDevice
import org.matrix.android.sdk.internal.crypto.MegolmSessionData
import org.matrix.android.sdk.internal.crypto.OutgoingGossipingRequestManager
import org.matrix.android.sdk.internal.crypto.RoomDecryptorProvider
import org.matrix.android.sdk.internal.crypto.algorithms.megolm.MXMegolmDecryption
import org.matrix.android.sdk.internal.crypto.model.ImportRoomKeysResult
import org.matrix.android.sdk.internal.crypto.model.rest.RoomKeyRequestBody
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
@ -76,7 +77,11 @@ internal class MegolmSessionDataImporter @Inject constructor(private val olmDevi
outgoingGossipingRequestManager.cancelRoomKeyRequest(roomKeyRequestBody)
// Have another go at decrypting events sent with this session
decrypting.onNewSession(megolmSessionData.senderKey!!, sessionId!!)
when (decrypting) {
is MXMegolmDecryption -> {
decrypting.onNewSession(megolmSessionData.roomId, megolmSessionData.senderKey!!, sessionId!!)
}
}
} catch (e: Exception) {
Timber.e(e, "## importRoomKeys() : onNewSession failed")
}

View file

@ -45,14 +45,6 @@ internal interface IMXDecrypting {
*/
fun onRoomKeyEvent(event: Event, defaultKeysBackupService: DefaultKeysBackupService) {}
/**
* Check if the some messages can be decrypted with a new session
*
* @param senderKey the session sender key
* @param sessionId the session id
*/
fun onNewSession(senderKey: String, sessionId: String) {}
/**
* Determine if we have the keys necessary to respond to a room key request
*

View file

@ -318,19 +318,20 @@ internal class MXMegolmDecryption(private val userId: String,
outgoingGossipingRequestManager.cancelRoomKeyRequest(content)
onNewSession(senderKey, roomKeyContent.sessionId)
onNewSession(roomKeyContent.roomId, senderKey, roomKeyContent.sessionId)
}
}
/**
* Check if the some messages can be decrypted with a new session
*
* @param roomId the room id where the new Megolm session has been created for, may be null when importing from external sessions
* @param senderKey the session sender key
* @param sessionId the session id
*/
override fun onNewSession(senderKey: String, sessionId: String) {
fun onNewSession(roomId: String?, senderKey: String, sessionId: String) {
Timber.tag(loggerTag.value).v("ON NEW SESSION $sessionId - $senderKey")
newSessionListener?.onNewSession(null, senderKey, sessionId)
newSessionListener?.onNewSession(roomId, senderKey, sessionId)
}
override fun hasKeysForKeyRequest(request: IncomingRoomKeyRequest): Boolean {

View file

@ -52,7 +52,7 @@ import timber.log.Timber
import javax.inject.Inject
internal class UpdateTrustWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) :
SessionSafeCoroutineWorker<UpdateTrustWorker.Params>(context, params, sessionManager, Params::class.java) {
SessionSafeCoroutineWorker<UpdateTrustWorker.Params>(context, params, sessionManager, Params::class.java) {
@JsonClass(generateAdapter = true)
internal data class Params(

View file

@ -130,7 +130,7 @@ inline fun <T> MXUsersDevicesMap<T>.forEach(action: (String, String, T) -> Unit)
}
}
internal fun <T> MXUsersDevicesMap<T>.toDebugString() =
internal fun <T> MXUsersDevicesMap<T>.toDebugString() =
map.entries.joinToString { "${it.key} [${it.value.keys.joinToString { it }}]" }
internal fun <T> MXUsersDevicesMap<T>.toDebugCount() =

View file

@ -70,7 +70,7 @@ object HkdfSha256 {
T(2) = HMAC-Hash(PRK, T(1) | info | 0x02)
T(3) = HMAC-Hash(PRK, T(2) | info | 0x03)
...
*/
*/
val n = ceil(outputLength.toDouble() / HASH_LEN.toDouble()).toInt()
var stepHash = ByteArray(0) // T(0) empty string (zero length)

View file

@ -364,14 +364,14 @@ internal class DefaultVerificationService @Inject constructor(
dispatchRequestAdded(pendingVerificationRequest)
/*
* After the m.key.verification.ready event is sent, either party can send an m.key.verification.start event
* to begin the verification.
* If both parties send an m.key.verification.start event, and they both specify the same verification method,
* then the event sent by the user whose user ID is the smallest is used, and the other m.key.verification.start
* event is ignored.
* In the case of a single user verifying two of their devices, the device ID is compared instead.
* If both parties send an m.key.verification.start event, but they specify different verification methods,
* the verification should be cancelled with a code of m.unexpected_message.
* After the m.key.verification.ready event is sent, either party can send an m.key.verification.start event
* to begin the verification.
* If both parties send an m.key.verification.start event, and they both specify the same verification method,
* then the event sent by the user whose user ID is the smallest is used, and the other m.key.verification.start
* event is ignored.
* In the case of a single user verifying two of their devices, the device ID is compared instead.
* If both parties send an m.key.verification.start event, but they specify different verification methods,
* the verification should be cancelled with a code of m.unexpected_message.
*/
}

View file

@ -29,7 +29,6 @@ import org.matrix.android.sdk.internal.crypto.crosssigning.fromBase64Safe
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
import org.matrix.android.sdk.internal.crypto.verification.DefaultVerificationTransaction
import org.matrix.android.sdk.internal.crypto.verification.ValidVerificationInfoStart
import org.matrix.android.sdk.internal.util.exhaustive
import timber.log.Timber
internal class DefaultQrCodeVerificationTransaction(
@ -129,7 +128,7 @@ internal class DefaultQrCodeVerificationTransaction(
// Nothing special here, we will send a reciprocate start event, and then the other session will trust it's view of the MSK
}
}
}.exhaustive
}
val toVerifyDeviceIds = mutableListOf<String>()
@ -174,7 +173,7 @@ internal class DefaultQrCodeVerificationTransaction(
Unit
}
}
}.exhaustive
}
if (!canTrustOtherUserMasterKey && toVerifyDeviceIds.isEmpty()) {
// Nothing to verify
@ -272,6 +271,7 @@ internal class DefaultQrCodeVerificationTransaction(
// I now know that i can trust my MSK
trust(true, emptyList(), true)
}
null -> Unit
}
}

View file

@ -16,9 +16,12 @@
package org.matrix.android.sdk.internal.database.helper
import com.squareup.moshi.JsonDataException
import io.realm.Realm
import io.realm.RealmQuery
import io.realm.Sort
import org.matrix.android.sdk.api.session.events.model.UnsignedData
import org.matrix.android.sdk.api.session.events.model.isRedacted
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
import org.matrix.android.sdk.api.session.threads.ThreadNotificationState
import org.matrix.android.sdk.internal.database.mapper.asDomain
@ -33,6 +36,8 @@ import org.matrix.android.sdk.internal.database.query.findIncludingEvent
import org.matrix.android.sdk.internal.database.query.findLastForwardChunkOfRoom
import org.matrix.android.sdk.internal.database.query.where
import org.matrix.android.sdk.internal.database.query.whereRoomId
import org.matrix.android.sdk.internal.di.MoshiProvider
import timber.log.Timber
private typealias Summary = Pair<Int, TimelineEventEntity>?
@ -48,14 +53,14 @@ internal fun Map<String, EventEntity>.updateThreadSummaryIfNeeded(
for ((rootThreadEventId, eventEntity) in this) {
eventEntity.threadSummaryInThread(eventEntity.realm, rootThreadEventId, chunkEntity)?.let { threadSummary ->
val numberOfMessages = threadSummary.first
val inThreadMessages = threadSummary.first
val latestEventInThread = threadSummary.second
// If this is a thread message, find its root event if exists
val rootThreadEvent = if (eventEntity.isThread()) eventEntity.findRootThreadEvent() else eventEntity
rootThreadEvent?.markEventAsRoot(
threadsCounted = numberOfMessages,
inThreadMessages = inThreadMessages,
latestMessageTimelineEventEntity = latestEventInThread
)
}
@ -81,28 +86,27 @@ internal fun EventEntity.findRootThreadEvent(): EventEntity? =
* Mark or update the current event a root thread event
*/
internal fun EventEntity.markEventAsRoot(
threadsCounted: Int,
inThreadMessages: Int,
latestMessageTimelineEventEntity: TimelineEventEntity?) {
isRootThread = true
numberOfThreads = threadsCounted
numberOfThreads = inThreadMessages
threadSummaryLatestMessage = latestMessageTimelineEventEntity
}
/**
* Count the number of threads for the provided root thread eventId, and finds the latest event message
* note: Redactions are handled by RedactionEventProcessor
* @param rootThreadEventId The root eventId that will find the number of threads
* @return A ThreadSummary containing the counted threads and the latest event message
*/
internal fun EventEntity.threadSummaryInThread(realm: Realm, rootThreadEventId: String, chunkEntity: ChunkEntity?): Summary {
// Number of messages
val messages = TimelineEventEntity
.whereRoomId(realm, roomId = roomId)
.equalTo(TimelineEventEntityFields.ROOT.ROOT_THREAD_EVENT_ID, rootThreadEventId)
.distinct(TimelineEventEntityFields.ROOT.EVENT_ID)
.count()
.toInt()
val inThreadMessages = countInThreadMessages(
realm = realm,
roomId = roomId,
rootThreadEventId = rootThreadEventId
)
if (messages <= 0) return null
if (inThreadMessages <= 0) return null
// Find latest thread event, we know it exists
var chunk = ChunkEntity.findLastForwardChunkOfRoom(realm, roomId) ?: chunkEntity ?: return null
@ -124,9 +128,38 @@ internal fun EventEntity.threadSummaryInThread(realm: Realm, rootThreadEventId:
result ?: return null
return Summary(messages, result)
return Summary(inThreadMessages, result)
}
/**
* Counts the number of thread replies in the main timeline thread summary,
* with respect to redactions.
*/
internal fun countInThreadMessages(realm: Realm, roomId: String, rootThreadEventId: String): Int =
TimelineEventEntity
.whereRoomId(realm, roomId = roomId)
.equalTo(TimelineEventEntityFields.ROOT.ROOT_THREAD_EVENT_ID, rootThreadEventId)
.distinct(TimelineEventEntityFields.ROOT.EVENT_ID)
.findAll()
.filterNot { timelineEvent ->
timelineEvent.root
?.unsignedData
?.takeIf { it.isNotBlank() }
?.toUnsignedData()
.isRedacted()
}.size
/**
* Mapping string to UnsignedData using Moshi
*/
private fun String.toUnsignedData(): UnsignedData? =
try {
MoshiProvider.providesMoshi().adapter(UnsignedData::class.java).fromJson(this)
} catch (ex: JsonDataException) {
Timber.e(ex, "Failed to parse UnsignedData")
null
}
/**
* Lets compare them in case user is moving forward in the timeline and we cannot know the
* exact chunk sequence while currentChunk is not yet committed in the DB

View file

@ -19,15 +19,19 @@ package org.matrix.android.sdk.internal.database.lightweight
import android.content.Context
import androidx.core.content.edit
import androidx.preference.PreferenceManager
import org.matrix.android.sdk.api.MatrixConfiguration
import javax.inject.Inject
/**
* The purpose of this class is to provide an alternative and lightweight way to store settings/data
* on the sdi without using the database. This should be used just for sdk/user preferences and
* on the sdk without using the database. This should be used just for sdk/user preferences and
* not for large data sets
*/
class LightweightSettingsStorage @Inject constructor(context: Context) {
class LightweightSettingsStorage @Inject constructor(
context: Context,
private val matrixConfiguration: MatrixConfiguration
) {
private val sdkDefaultPrefs = PreferenceManager.getDefaultSharedPreferences(context.applicationContext)
@ -38,7 +42,7 @@ class LightweightSettingsStorage @Inject constructor(context: Context) {
}
fun areThreadMessagesEnabled(): Boolean {
return sdkDefaultPrefs.getBoolean(MATRIX_SDK_SETTINGS_THREAD_MESSAGES_ENABLED, false)
return sdkDefaultPrefs.getBoolean(MATRIX_SDK_SETTINGS_THREAD_MESSAGES_ENABLED, matrixConfiguration.threadMessagesEnabledDefault)
}
companion object {

View file

@ -44,6 +44,7 @@ internal open class EventEntity(@Index var eventId: String = "",
// Thread related, no need to create a new Entity for performance
@Index var isRootThread: Boolean = false,
@Index var rootThreadEventId: String? = null,
// Number messages within the thread
var numberOfThreads: Int = 0,
var threadSummaryLatestMessage: TimelineEventEntity? = null
) : RealmObject() {

View file

@ -49,6 +49,10 @@ internal fun RoomSummaryEntity.Companion.getOrCreate(realm: Realm, roomId: Strin
return where(realm, roomId).findFirst() ?: realm.createObject(roomId)
}
internal fun RoomSummaryEntity.Companion.getOrNull(realm: Realm, roomId: String): RoomSummaryEntity? {
return where(realm, roomId).findFirst()
}
internal fun RoomSummaryEntity.Companion.getDirectRooms(realm: Realm,
excludeRoomIds: Set<String>? = null): RealmResults<RoomSummaryEntity> {
return RoomSummaryEntity.where(realm)

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