mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-03-18 20:29:10 +03:00
Merge branch 'merge_v1.2.1' into sc
Change-Id: I003540479c02eae32fdeee7230ef11e5d373e9b0
This commit is contained in:
commit
25b11084f4
462 changed files with 9665 additions and 3454 deletions
50
CHANGES.md
50
CHANGES.md
|
@ -1,3 +1,53 @@
|
|||
Changes in Element v1.2.1 (2021-09-08)
|
||||
======================================
|
||||
|
||||
Features ✨
|
||||
----------
|
||||
- Support Android 11 Conversation features ([#1809](https://github.com/vector-im/element-android/issues/1809))
|
||||
- Introduces AutoAcceptInvites which can be enabled at compile time. ([#3531](https://github.com/vector-im/element-android/issues/3531))
|
||||
- New call designs ([#3599](https://github.com/vector-im/element-android/issues/3599))
|
||||
- Restricted Join Rule | Inform admins of new option ([#3631](https://github.com/vector-im/element-android/issues/3631))
|
||||
- Mention and Keyword Notification Settings: Turn on/off keyword notifications and edit keywords. ([#3650](https://github.com/vector-im/element-android/issues/3650))
|
||||
- Support accept 3pid invite when email is not bound to account ([#3691](https://github.com/vector-im/element-android/issues/3691))
|
||||
- Space summary pagination ([#3693](https://github.com/vector-im/element-android/issues/3693))
|
||||
- Update Email invite to be aware of spaces ([#3695](https://github.com/vector-im/element-android/issues/3695))
|
||||
- M11.12 Spaces | Default to 'Home' in settings ([#3754](https://github.com/vector-im/element-android/issues/3754))
|
||||
- Call: show dialog for some ended reasons. ([#3853](https://github.com/vector-im/element-android/issues/3853))
|
||||
- Add expired account error code in the matrix SDK ([#3900](https://github.com/vector-im/element-android/issues/3900))
|
||||
- Add password errors in the matrix SDK ([#3927](https://github.com/vector-im/element-android/issues/3927))
|
||||
- Upgrade AGP to 7.0.2.
|
||||
When compiling using command line, make sure to use the JDK 11 by adding for instance `-Dorg.gradle.java.home=/Applications/Android\ Studio\ Preview.app/Contents/jre/Contents/Home` or by setting JAVA_HOME. ([#3954](https://github.com/vector-im/element-android/issues/3954))
|
||||
- Check power level before displaying actions in the room details' timeline ([#3959](https://github.com/vector-im/element-android/issues/3959))
|
||||
|
||||
Bugfixes 🐛
|
||||
----------
|
||||
- Add mxid to autocomplete suggestion if more than one user in a room has the same displayname ([#1823](https://github.com/vector-im/element-android/issues/1823))
|
||||
- Use WebView cache for widgets to avoid excessive data use ([#2648](https://github.com/vector-im/element-android/issues/2648))
|
||||
- Jitsi-hosted jitsi conferences not loading ([#2846](https://github.com/vector-im/element-android/issues/2846))
|
||||
- Space Explore Rooms no feedback on failed to join ([#3207](https://github.com/vector-im/element-android/issues/3207))
|
||||
- Notifications - Fix missing sound on notifications. ([#3243](https://github.com/vector-im/element-android/issues/3243))
|
||||
- the element-based domain permalinks (e.g. https://app.element.io/#/user/@chagai95:matrix.org) don't have the mxid in the first param (like matrix.to does - https://matrix.to/#/@chagai95:matrix.org) but rather in the second after /user/ so /user/mxid ([#3735](https://github.com/vector-im/element-android/issues/3735))
|
||||
- Update the AccountData with the users' matrix Id instead of their email for those invited by email in a direct chat ([#3743](https://github.com/vector-im/element-android/issues/3743))
|
||||
- Send an empty body for POST rooms/{roomId}/receipt/{receiptType}/{eventId} ([#3789](https://github.com/vector-im/element-android/issues/3789))
|
||||
- Fix order in which the items of the attachment menu appear ([#3793](https://github.com/vector-im/element-android/issues/3793))
|
||||
- Authenticated Jitsi not working in release ([#3841](https://github.com/vector-im/element-android/issues/3841))
|
||||
- Home: Dial pad lost entry when config changes ([#3845](https://github.com/vector-im/element-android/issues/3845))
|
||||
- Message edition is not rendered in e2e rooms after pagination ([#3887](https://github.com/vector-im/element-android/issues/3887))
|
||||
- Crash on opening a room on Android 5.0 and 5.1 - Regression with Voice message ([#3897](https://github.com/vector-im/element-android/issues/3897))
|
||||
- Fix a crash at start-up if translated string is empty ([#3910](https://github.com/vector-im/element-android/issues/3910))
|
||||
- PushRule enabling request is not following the spec ([#3911](https://github.com/vector-im/element-android/issues/3911))
|
||||
- Enable image preview in Android's share sheet (Android 11+) ([#3965](https://github.com/vector-im/element-android/issues/3965))
|
||||
- Voice Message - Cannot render voice message if the waveform data is corrupted ([#3983](https://github.com/vector-im/element-android/issues/3983))
|
||||
- Fix memory leak on RoomDetailFragment (ValueAnimator) ([#3990](https://github.com/vector-im/element-android/issues/3990))
|
||||
|
||||
Other changes
|
||||
-------------
|
||||
- VoIP: Merge virtual room timeline in corresponding native room (call events only). ([#3520](https://github.com/vector-im/element-android/issues/3520))
|
||||
- Issue templates: modernise and sync with element-web ([#3883](https://github.com/vector-im/element-android/issues/3883))
|
||||
- Issue templates: modernise SDK and release checklists, and add homeserver question for bugs ([#3889](https://github.com/vector-im/element-android/issues/3889))
|
||||
- Issue templates: merge expected and actual results ([#3960](https://github.com/vector-im/element-android/issues/3960))
|
||||
|
||||
|
||||
Changes in Element v1.2.0 (2021-08-12)
|
||||
======================================
|
||||
|
||||
|
|
|
@ -116,9 +116,34 @@ You should consider adding Unit tests with your PR, and also integration tests (
|
|||
|
||||
### Internationalisation
|
||||
|
||||
When adding new string resources, please only add new entries in file `value/strings.xml`. Translations will be added later by the community of translators with a specific tool named [Weblate](https://translate.riot.im/projects/riot-android/).
|
||||
Translations are handled using an external tool: [Weblate](https://translate.element.io/projects/element-android/)
|
||||
|
||||
As a general rule, please never edit or add or remove translations to the project in a Pull Request. It can lead to merge conflict if the translations are also modified in Weblate side.
|
||||
|
||||
#### Adding new string
|
||||
|
||||
When adding new string resources, please only add new entries in file `value/strings.xml`. Translations will be added later by the community of translators using Weblate.
|
||||
|
||||
New strings can be added anywhere in the file `value/strings.xml`, not necessarily at the end of the file. Generally, it's even better to add the new strings in some dedicated section per feature, and not at the end of the file, to avoid merge conflict between 2 PR adding strings at the end of the same file.
|
||||
|
||||
Do not hesitate to use plurals when appropriate.
|
||||
|
||||
#### Editing existing strings
|
||||
|
||||
Two cases:
|
||||
- If the meaning stays the same, it's OK to edit the original string (i.e. the English version).
|
||||
- If the meaning is not the same, please create a new string and do not remove the existing string. See below for instructions to remove existing string.
|
||||
|
||||
#### Removing existing strings
|
||||
|
||||
If a string is not used anymore, it should be removed from the resource, but please do not remove the strings or its translations in the PR. It can lead to merge conflict with Weblate, and to lint error if new translations from deleted strings are added with Weblate.
|
||||
|
||||
Instead, please comment the original string with:
|
||||
```xml
|
||||
<!-- TO BE REMOVED -->
|
||||
```
|
||||
The string will be removed during the next sync with Weblate.
|
||||
|
||||
### Accessibility
|
||||
|
||||
Please consider accessibility as an important point. As a minimum requirement, in layout XML files please use attributes such as `android:contentDescription` and `android:importantForAccessibility`, and test with a screen reader if it's working well. You can add new string resources, dedicated to accessibility, in this case, please prefix theirs id with `a11y_`.
|
||||
|
|
|
@ -34,11 +34,11 @@ android {
|
|||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
jvmTarget = "11"
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
|
|
|
@ -1,21 +1,22 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView"
|
||||
android:visibility="visible"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
android:layout_height="match_parent"
|
||||
android:importantForAccessibility="no"
|
||||
android:visibility="visible" />
|
||||
|
||||
<ProgressBar
|
||||
android:layout_centerInParent="true"
|
||||
android:id="@+id/imageLoaderProgress"
|
||||
style="?android:attr/progressBarStyle"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_centerInParent="true"
|
||||
android:visibility="visible"
|
||||
tools:visibility="gone" />
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"
|
||||
android:importantForAccessibility="no"
|
||||
android:scaleType="centerInside" />
|
||||
|
||||
<VideoView
|
||||
|
@ -22,6 +23,7 @@
|
|||
android:layout_width="44dp"
|
||||
android:layout_height="44dp"
|
||||
android:layout_centerInParent="true"
|
||||
android:contentDescription="@string/a11y_play_pause"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
|
|
5
attachment-viewer/src/main/res/values/strings.xml
Normal file
5
attachment-viewer/src/main/res/values/strings.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- TODO Create a dedicated module for translations to be able to translate this string like the others (See #3955) -->
|
||||
<string name="a11y_play_pause">Play or pause the video</string>
|
||||
</resources>
|
|
@ -12,8 +12,10 @@ buildscript {
|
|||
}
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.2.2'
|
||||
classpath 'com.google.gms:google-services:4.3.8'
|
||||
// Release notes of Android Gradle Plugin (AGP):
|
||||
// https://developer.android.com/studio/releases/gradle-plugin
|
||||
classpath 'com.android.tools.build:gradle:7.0.2'
|
||||
classpath 'com.google.gms:google-services:4.3.10'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.3'
|
||||
classpath 'com.google.android.gms:oss-licenses-plugin:0.10.4'
|
||||
|
@ -44,6 +46,9 @@ allprojects {
|
|||
includeGroupByRegex 'com\\.github\\.chrisbanes'
|
||||
// PFLockScreen-Android
|
||||
includeGroupByRegex 'com\\.github\\.vector-im'
|
||||
// DraggableView
|
||||
includeGroupByRegex 'com\\.github\\.hyuwah'
|
||||
|
||||
// UnifiedPush
|
||||
includeGroupByRegex 'com\\.github\\.UnifiedPush'
|
||||
|
||||
|
|
|
@ -3,6 +3,3 @@ apply plugin: 'java-library'
|
|||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
}
|
||||
|
||||
sourceCompatibility = "8"
|
||||
targetCompatibility = "8"
|
||||
|
|
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,6 +1,6 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionSha256Sum=9bb8bc05f562f2d42bdf1ba8db62f6b6fa1c3bf6c392228802cc7cb0578fe7e0
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-all.zip
|
||||
distributionSha256Sum=a8da5b02437a60819cad23e10fc7e9cf32bcb57029d9cb277e26eeff76ce014b
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
|
269
gradlew
vendored
269
gradlew
vendored
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env sh
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
|
@ -17,67 +17,101 @@
|
|||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
APP_BASE_NAME=${0##*/}
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MSYS* | MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
|||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
|
|||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
JAVACMD=java
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
|
@ -106,80 +140,95 @@ location of your Java installation."
|
|||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
case $i in
|
||||
0) set -- ;;
|
||||
1) set -- "$args0" ;;
|
||||
2) set -- "$args0" "$args1" ;;
|
||||
3) set -- "$args0" "$args1" "$args2" ;;
|
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=`save "$@"`
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
|
|
|
@ -39,13 +39,16 @@ android {
|
|||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
jvmTarget = "11"
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
viewBinding true
|
||||
}
|
||||
|
|
|
@ -1,2 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="im.vector.lib.ui.styles"/>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="im.vector.lib.ui.styles">
|
||||
|
||||
<application android:supportsRtl="true" />
|
||||
|
||||
</manifest>
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="@color/color_primary_alpha25" android:state_enabled="false" />
|
||||
<item android:color="?colorPrimary" />
|
||||
</selector>
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:alpha="0.25" android:color="?attr/colorOnPrimary" android:state_enabled="false" />
|
||||
<item android:color="?attr/colorOnPrimary" />
|
||||
</selector>
|
|
@ -2,7 +2,8 @@
|
|||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="wrap_content"
|
||||
tools:ignore="UselessParent">
|
||||
|
||||
<!-- Inspired from https://android.googlesource.com/platform/frameworks/base/+/master/core/res/res/layout/progress_dialog.xml -->
|
||||
<LinearLayout
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
<!-- Other useful color -->
|
||||
<!-- Emoji text has to use a black text color -->
|
||||
<color name="emoji_color">@android:color/black</color>
|
||||
<color name="join_conference_animated_color">#0BAC7E</color>
|
||||
|
||||
<color name="half_transparent_status_bar">#80000000</color>
|
||||
|
||||
|
|
|
@ -28,6 +28,10 @@
|
|||
<dimen name="pill_min_height">20dp</dimen>
|
||||
<dimen name="pill_text_padding">4dp</dimen>
|
||||
|
||||
<dimen name="call_pip_height">128dp</dimen>
|
||||
<dimen name="call_pip_width">88dp</dimen>
|
||||
<dimen name="call_pip_radius">8dp</dimen>
|
||||
|
||||
|
||||
<dimen name="item_form_min_height">76dp</dimen>
|
||||
|
||||
|
|
15
library/ui-styles/src/main/res/values/styles_keyword.xml
Normal file
15
library/ui-styles/src/main/res/values/styles_keyword.xml
Normal file
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<attr name="vctr_keyword_style" format="reference" />
|
||||
|
||||
<style name="Widget.Vector.Keyword" parent="Widget.MaterialComponents.Chip.Action">
|
||||
<item name="android:textAppearance">@style/TextAppearance.Vector.Body</item>
|
||||
<item name="chipBackgroundColor">@color/keyword_background_selector</item>
|
||||
<item name="closeIconTint">@color/keyword_foreground_selector</item>
|
||||
<item name="closeIconVisible">true</item>
|
||||
<item name="android:textColor">@color/keyword_foreground_selector</item>
|
||||
<item name="android:clickable">true</item>
|
||||
<item name="android:checkable">false</item>
|
||||
</style>
|
||||
</resources>
|
|
@ -154,6 +154,8 @@
|
|||
<item name="vctr_social_login_button_gitlab_style">@style/Widget.Vector.Button.Outlined.SocialLogin.Gitlab.Dark</item>
|
||||
|
||||
<item name="vctr_jump_to_unread_style">@style/Widget.Vector.JumpToUnread.Dark</item>
|
||||
<!-- Keywords -->
|
||||
<item name="vctr_keyword_style">@style/Widget.Vector.Keyword</item>
|
||||
|
||||
<!-- Voice Message -->
|
||||
<item name="vctr_voice_message_toast_background">@color/vctr_voice_message_toast_background_dark</item>
|
||||
|
|
|
@ -157,6 +157,9 @@
|
|||
|
||||
<item name="vctr_jump_to_unread_style">@style/Widget.Vector.JumpToUnread.Light</item>
|
||||
|
||||
<!-- Keywords -->
|
||||
<item name="vctr_keyword_style">@style/Widget.Vector.Keyword</item>
|
||||
|
||||
<!-- Voice Message -->
|
||||
<item name="vctr_voice_message_toast_background">@color/vctr_voice_message_toast_background_light</item>
|
||||
</style>
|
||||
|
|
|
@ -24,12 +24,12 @@ android {
|
|||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
jvmTarget = "11"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,5 +44,5 @@ dependencies {
|
|||
implementation "androidx.paging:paging-runtime-ktx:2.1.2"
|
||||
|
||||
// Logging
|
||||
implementation 'com.jakewharton.timber:timber:4.7.1'
|
||||
implementation 'com.jakewharton.timber:timber:5.0.1'
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ buildscript {
|
|||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath "io.realm:realm-gradle-plugin:10.6.1"
|
||||
classpath "io.realm:realm-gradle-plugin:10.8.0"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,17 +67,13 @@ android {
|
|||
installOptions "-g"
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
lintConfig file("lint.xml")
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
jvmTarget = "11"
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
|
@ -112,7 +108,7 @@ dependencies {
|
|||
def lifecycle_version = '2.2.0'
|
||||
def arch_version = '2.1.0'
|
||||
def markwon_version = '3.1.0'
|
||||
def daggerVersion = '2.38'
|
||||
def daggerVersion = '2.38.1'
|
||||
def work_version = '2.5.0'
|
||||
def retrofit_version = '2.9.0'
|
||||
|
||||
|
@ -141,7 +137,7 @@ dependencies {
|
|||
implementation "ru.noties.markwon:core:$markwon_version"
|
||||
|
||||
// Image
|
||||
implementation 'androidx.exifinterface:exifinterface:1.3.2'
|
||||
implementation 'androidx.exifinterface:exifinterface:1.3.3'
|
||||
|
||||
// Database
|
||||
implementation 'com.github.Zhuinden:realm-monarchy:0.7.1'
|
||||
|
@ -162,14 +158,14 @@ dependencies {
|
|||
kapt "com.google.dagger:dagger-compiler:$daggerVersion"
|
||||
|
||||
// Logging
|
||||
implementation 'com.jakewharton.timber:timber:4.7.1'
|
||||
implementation 'com.jakewharton.timber:timber:5.0.1'
|
||||
implementation 'com.facebook.stetho:stetho-okhttp3:1.6.0'
|
||||
|
||||
// Video compression
|
||||
implementation 'com.otaliastudios:transcoder:0.10.3'
|
||||
|
||||
// Phone number https://github.com/google/libphonenumber
|
||||
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.28'
|
||||
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.31'
|
||||
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
testImplementation 'org.robolectric:robolectric:4.5.1'
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<lint>
|
||||
<!-- Modify some severity -->
|
||||
|
||||
<!-- Resource -->
|
||||
<issue id="MissingTranslation" severity="warning" />
|
||||
<issue id="TypographyEllipsis" severity="error" />
|
||||
<issue id="ImpliedQuantity" severity="warning" />
|
||||
|
||||
<!-- UX -->
|
||||
<issue id="ButtonOrder" severity="error" />
|
||||
|
||||
<!-- Layout -->
|
||||
<issue id="UnknownIdInLayout" severity="error" />
|
||||
<issue id="StringFormatCount" severity="error" />
|
||||
<issue id="HardcodedText" severity="error" />
|
||||
<issue id="SpUsage" severity="error" />
|
||||
<issue id="ObsoleteLayoutParam" severity="error" />
|
||||
<issue id="InefficientWeight" severity="error" />
|
||||
<issue id="DisableBaselineAlignment" severity="error" />
|
||||
<issue id="ScrollViewSize" severity="error" />
|
||||
|
||||
<!-- RTL -->
|
||||
<issue id="RtlEnabled" severity="error" />
|
||||
<issue id="RtlHardcoded" severity="error" />
|
||||
<issue id="RtlSymmetry" severity="error" />
|
||||
|
||||
<!-- Code -->
|
||||
<issue id="SetTextI18n" severity="error" />
|
||||
<issue id="ViewConstructor" severity="error" />
|
||||
<issue id="UseValueOf" severity="error" />
|
||||
<issue id="ObsoleteSdkInt" severity="error" />
|
||||
|
||||
</lint>
|
18
matrix-sdk-android/src/androidTest/AndroidManifest.xml
Normal file
18
matrix-sdk-android/src/androidTest/AndroidManifest.xml
Normal file
|
@ -0,0 +1,18 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="org.matrix.android.sdk">
|
||||
|
||||
<application>
|
||||
|
||||
<!--
|
||||
This is mandatory to run integration tests
|
||||
-->
|
||||
<provider
|
||||
android:name="androidx.work.impl.WorkManagerInitializer"
|
||||
android:authorities="${applicationId}.workmanager-init"
|
||||
android:exported="false"
|
||||
tools:node="remove" />
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* 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
|
||||
|
||||
import org.junit.Assert
|
||||
import org.junit.FixMethodOrder
|
||||
import org.junit.Test
|
||||
import org.junit.runners.MethodSorters
|
||||
import org.matrix.android.sdk.api.session.permalinks.PermalinkData
|
||||
import org.matrix.android.sdk.api.session.permalinks.PermalinkParser
|
||||
|
||||
@FixMethodOrder(MethodSorters.JVM)
|
||||
class PermalinkParserTest {
|
||||
|
||||
@Test
|
||||
fun testParseEmailInvite() {
|
||||
val rawInvite = """
|
||||
https://app.element.io/#/room/%21MRBNLPtFnMAazZVPMO%3Amatrix.org?email=bob%2Bspace%40example.com&signurl=https%3A%2F%2Fvector.im%2F_matrix%2Fidentity%2Fapi%2Fv1%2Fsign-ed25519%3Ftoken%3DXmOwRZnSFabCRhTywFbJWKXWVNPysOpXIbroMGaUymqkJSvHeVKRsjHajwjCYdBsvGSvHauxbKfJmOxtXldtyLnyBMLKpBQCMzyYggrdapbVIceWZBtmslOQrXLABRoe%26private_key%3DT2gq0c3kJB_8OroXVxl1pBnzHsN7V6Xn4bEBSeW1ep4&room_name=Team2&room_avatar_url=&inviter_name=hiphop5&guest_access_token=&guest_user_id=
|
||||
""".trimIndent()
|
||||
.replace("https://app.element.io/#/room/", "https://matrix.to/#/")
|
||||
|
||||
val parsedLink = PermalinkParser.parse(rawInvite)
|
||||
Assert.assertTrue("Should be parsed as email invite but was ${parsedLink::class.java}", parsedLink is PermalinkData.RoomEmailInviteLink)
|
||||
parsedLink as PermalinkData.RoomEmailInviteLink
|
||||
Assert.assertEquals("!MRBNLPtFnMAazZVPMO:matrix.org", parsedLink.roomId)
|
||||
Assert.assertEquals("XmOwRZnSFabCRhTywFbJWKXWVNPysOpXIbroMGaUymqkJSvHeVKRsjHajwjCYdBsvGSvHauxbKfJmOxtXldtyLnyBMLKpBQCMzyYggrdapbVIceWZBtmslOQrXLABRoe", parsedLink.token)
|
||||
Assert.assertEquals("vector.im", parsedLink.identityServer)
|
||||
Assert.assertEquals("Team2", parsedLink.roomName)
|
||||
Assert.assertEquals("hiphop5", parsedLink.inviterName)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testParseLinkWIthEvent() {
|
||||
val rawInvite = "https://matrix.to/#/!OGEhHVWSdvArJzumhm:matrix.org/\$xuvJUVDJnwEeVjPx029rAOZ50difpmU_5gZk_T0jGfc?via=matrix.org&via=libera.chat&via=matrix.example.io"
|
||||
|
||||
val parsedLink = PermalinkParser.parse(rawInvite)
|
||||
Assert.assertTrue("Should be parsed as room link", parsedLink is PermalinkData.RoomLink)
|
||||
parsedLink as PermalinkData.RoomLink
|
||||
Assert.assertEquals("!OGEhHVWSdvArJzumhm:matrix.org", parsedLink.roomIdOrAlias)
|
||||
Assert.assertEquals("\$xuvJUVDJnwEeVjPx029rAOZ50difpmU_5gZk_T0jGfc", parsedLink.eventId)
|
||||
Assert.assertEquals(3, parsedLink.viaParameters.size)
|
||||
Assert.assertTrue(parsedLink.viaParameters.contains("matrix.example.io"))
|
||||
Assert.assertTrue(parsedLink.viaParameters.contains("matrix.org"))
|
||||
Assert.assertTrue(parsedLink.viaParameters.contains("matrix.example.io"))
|
||||
}
|
||||
}
|
|
@ -91,6 +91,7 @@ class CommonTestHelper(context: Context) {
|
|||
*
|
||||
* @param session the session to sync
|
||||
*/
|
||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
||||
fun syncSession(session: Session, timeout: Long = TestConstants.timeOutMillis) {
|
||||
val lock = CountDownLatch(1)
|
||||
|
||||
|
@ -327,6 +328,7 @@ class CommonTestHelper(context: Context) {
|
|||
assertTrue(latch.await(timeout ?: TestConstants.timeOutMillis, TimeUnit.MILLISECONDS))
|
||||
}
|
||||
|
||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
||||
fun retryPeriodicallyWithLatch(latch: CountDownLatch, condition: (() -> Boolean)) {
|
||||
GlobalScope.launch {
|
||||
while (true) {
|
||||
|
|
|
@ -84,6 +84,7 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
|
|||
/**
|
||||
* @return alice and bob sessions
|
||||
*/
|
||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
||||
fun doE2ETestWithAliceAndBobInARoom(encryptedRoom: Boolean = true): CryptoTestData {
|
||||
val cryptoTestData = doE2ETestWithAliceInARoom(encryptedRoom)
|
||||
val aliceSession = cryptoTestData.firstSession
|
||||
|
@ -255,6 +256,7 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
|
|||
)
|
||||
}
|
||||
|
||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
||||
fun createDM(alice: Session, bob: Session): String {
|
||||
val roomId = mTestHelper.runBlockingTest {
|
||||
alice.createDirectRoom(bob.myUserId)
|
||||
|
|
|
@ -60,6 +60,7 @@ class QuadSTests : InstrumentedTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
||||
fun test_Generate4SKey() {
|
||||
val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
||||
|
||||
|
@ -275,6 +276,7 @@ class QuadSTests : InstrumentedTest {
|
|||
mTestHelper.signOutAndClose(aliceSession)
|
||||
}
|
||||
|
||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
||||
private fun assertAccountData(session: Session, type: String): UserAccountDataEvent {
|
||||
val accountDataLock = CountDownLatch(1)
|
||||
var accountData: UserAccountDataEvent? = null
|
||||
|
|
|
@ -139,7 +139,7 @@ class TimelineForwardPaginationTest : InstrumentedTest {
|
|||
// Alice can see the first event of the room (so Back pagination has worked)
|
||||
snapshot.lastOrNull()?.root?.getClearType() == EventType.STATE_ROOM_CREATE
|
||||
// 6 for room creation item (backward pagination), 1 for the context, and 50 for the forward pagination
|
||||
&& snapshot.size == 6 + 1 + 50
|
||||
&& snapshot.size == 57 // 6 + 1 + 50
|
||||
}
|
||||
|
||||
aliceTimeline.addListener(aliceEventsListener)
|
||||
|
|
|
@ -189,7 +189,7 @@ class TimelinePreviousLastForwardTest : InstrumentedTest {
|
|||
Timber.w(" event ${it.root}")
|
||||
}
|
||||
|
||||
snapshot.size == 8 + 1 + 35
|
||||
snapshot.size == 44 // 8 + 1 + 35
|
||||
}
|
||||
|
||||
bobTimeline.addListener(eventsListener)
|
||||
|
@ -218,7 +218,7 @@ class TimelinePreviousLastForwardTest : InstrumentedTest {
|
|||
// Bob can see the first event of the room (so Back pagination has worked)
|
||||
snapshot.lastOrNull()?.root?.getClearType() == EventType.STATE_ROOM_CREATE
|
||||
// 8 for room creation item 60 message from Alice
|
||||
&& snapshot.size == 8 + 60
|
||||
&& snapshot.size == 68 // 8 + 60
|
||||
&& snapshot.checkSendOrder(secondMessage, 30, 0)
|
||||
&& snapshot.checkSendOrder(firstMessage, 30, 30)
|
||||
}
|
||||
|
|
|
@ -28,12 +28,9 @@ 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.query.ActiveSpaceFilter
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams
|
||||
import org.matrix.android.sdk.api.session.room.model.GuestAccess
|
||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomGuestAccessContent
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility
|
||||
|
@ -42,7 +39,6 @@ import org.matrix.android.sdk.api.session.room.model.RoomType
|
|||
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
||||
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomPreset
|
||||
import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent
|
||||
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
||||
import org.matrix.android.sdk.api.session.space.JoinSpaceResult
|
||||
import org.matrix.android.sdk.common.CommonTestHelper
|
||||
import org.matrix.android.sdk.common.SessionTestParams
|
||||
|
@ -54,6 +50,7 @@ class SpaceCreationTest : InstrumentedTest {
|
|||
private val commonTestHelper = CommonTestHelper(context())
|
||||
|
||||
@Test
|
||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
||||
fun createSimplePublicSpace() {
|
||||
val session = commonTestHelper.createAccount("Hubble", SessionTestParams(true))
|
||||
val roomName = "My Space"
|
||||
|
@ -137,6 +134,7 @@ class SpaceCreationTest : InstrumentedTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
||||
fun testSimplePublicSpaceWithChildren() {
|
||||
val aliceSession = commonTestHelper.createAccount("alice", SessionTestParams(true))
|
||||
val bobSession = commonTestHelper.createAccount("bob", SessionTestParams(true))
|
||||
|
@ -162,7 +160,7 @@ class SpaceCreationTest : InstrumentedTest {
|
|||
|
||||
commonTestHelper.waitWithLatch {
|
||||
GlobalScope.launch {
|
||||
syncedSpace?.addChildren(firstChild!!, listOf(aliceSession.sessionParams.homeServerHost ?: ""), "a", true, suggested = true)
|
||||
syncedSpace?.addChildren(firstChild!!, listOf(aliceSession.sessionParams.homeServerHost ?: ""), "a", suggested = true)
|
||||
it.countDown()
|
||||
}
|
||||
}
|
||||
|
@ -181,7 +179,7 @@ class SpaceCreationTest : InstrumentedTest {
|
|||
|
||||
commonTestHelper.waitWithLatch {
|
||||
GlobalScope.launch {
|
||||
syncedSpace?.addChildren(secondChild!!, listOf(aliceSession.sessionParams.homeServerHost ?: ""), "b", false, suggested = true)
|
||||
syncedSpace?.addChildren(secondChild!!, listOf(aliceSession.sessionParams.homeServerHost ?: ""), "b", suggested = true)
|
||||
it.countDown()
|
||||
}
|
||||
}
|
||||
|
@ -202,19 +200,20 @@ class SpaceCreationTest : InstrumentedTest {
|
|||
assertEquals("Room name should be set", roomName, spaceBobPov?.asRoom()?.roomSummary()?.name)
|
||||
assertEquals("Room topic should be set", topic, spaceBobPov?.asRoom()?.roomSummary()?.topic)
|
||||
|
||||
// /!\ AUTO_JOIN has been descoped
|
||||
// check if bob has joined automatically the first room
|
||||
|
||||
val bobMembershipFirstRoom = bobSession.getRoomSummary(firstChild!!)?.membership
|
||||
assertEquals("Bob should have joined this room", Membership.JOIN, bobMembershipFirstRoom)
|
||||
RoomSummaryQueryParams.Builder()
|
||||
|
||||
val childCount = bobSession.getRoomSummaries(
|
||||
roomSummaryQueryParams {
|
||||
activeSpaceFilter = ActiveSpaceFilter.ActiveSpace(spaceId)
|
||||
}
|
||||
).size
|
||||
|
||||
assertEquals("Unexpected number of joined children", 1, childCount)
|
||||
// val bobMembershipFirstRoom = bobSession.getRoomSummary(firstChild!!)?.membership
|
||||
// assertEquals("Bob should have joined this room", Membership.JOIN, bobMembershipFirstRoom)
|
||||
// RoomSummaryQueryParams.Builder()
|
||||
//
|
||||
// val childCount = bobSession.getRoomSummaries(
|
||||
// roomSummaryQueryParams {
|
||||
// activeSpaceFilter = ActiveSpaceFilter.ActiveSpace(spaceId)
|
||||
// }
|
||||
// ).size
|
||||
//
|
||||
// assertEquals("Unexpected number of joined children", 1, childCount)
|
||||
|
||||
commonTestHelper.signOutAndClose(aliceSession)
|
||||
commonTestHelper.signOutAndClose(bobSession)
|
||||
|
|
|
@ -47,6 +47,7 @@ class SpaceHierarchyTest : InstrumentedTest {
|
|||
private val commonTestHelper = CommonTestHelper(context())
|
||||
|
||||
@Test
|
||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
||||
fun createCanonicalChildRelation() {
|
||||
val session = commonTestHelper.createAccount("John", SessionTestParams(true))
|
||||
val spaceName = "My Space"
|
||||
|
@ -171,6 +172,7 @@ class SpaceHierarchyTest : InstrumentedTest {
|
|||
// }
|
||||
|
||||
@Test
|
||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
||||
fun testFilteringBySpace() {
|
||||
val session = commonTestHelper.createAccount("John", SessionTestParams(true))
|
||||
|
||||
|
@ -179,7 +181,7 @@ class SpaceHierarchyTest : InstrumentedTest {
|
|||
Triple("A2", true, true)
|
||||
))
|
||||
|
||||
val spaceBInfo = createPublicSpace(session, "SpaceB", listOf(
|
||||
/* val spaceBInfo = */ createPublicSpace(session, "SpaceB", listOf(
|
||||
Triple("B1", true /*auto-join*/, true/*canonical*/),
|
||||
Triple("B2", true, true),
|
||||
Triple("B3", true, true)
|
||||
|
@ -254,6 +256,7 @@ class SpaceHierarchyTest : InstrumentedTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
||||
fun testBreakCycle() {
|
||||
val session = commonTestHelper.createAccount("John", SessionTestParams(true))
|
||||
|
||||
|
@ -301,6 +304,7 @@ class SpaceHierarchyTest : InstrumentedTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
||||
fun testLiveFlatChildren() {
|
||||
val session = commonTestHelper.createAccount("John", SessionTestParams(true))
|
||||
|
||||
|
@ -389,6 +393,7 @@ class SpaceHierarchyTest : InstrumentedTest {
|
|||
val roomIds: List<String>
|
||||
)
|
||||
|
||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
||||
private fun createPublicSpace(session: Session,
|
||||
spaceName: String,
|
||||
childInfo: List<Triple<String, Boolean, Boolean?>>
|
||||
|
@ -433,7 +438,7 @@ class SpaceHierarchyTest : InstrumentedTest {
|
|||
fun testRootSpaces() {
|
||||
val session = commonTestHelper.createAccount("John", SessionTestParams(true))
|
||||
|
||||
val spaceAInfo = createPublicSpace(session, "SpaceA", listOf(
|
||||
/* val spaceAInfo = */ createPublicSpace(session, "SpaceA", listOf(
|
||||
Triple("A1", true /*auto-join*/, true/*canonical*/),
|
||||
Triple("A2", true, true)
|
||||
))
|
||||
|
|
|
@ -10,15 +10,6 @@
|
|||
|
||||
<application android:networkSecurityConfig="@xml/network_security_config">
|
||||
|
||||
<!--
|
||||
This is mandatory to run integration tests
|
||||
-->
|
||||
<provider
|
||||
android:name="androidx.work.impl.WorkManagerInitializer"
|
||||
android:authorities="${applicationId}.workmanager-init"
|
||||
android:exported="false"
|
||||
tools:node="remove" />
|
||||
|
||||
<!--
|
||||
The SDK offers a secured File provider to access downloaded files.
|
||||
Access to these file will be given via the FileService, with a temporary
|
||||
|
|
|
@ -29,7 +29,9 @@ fun Throwable.is401() =
|
|||
|
||||
fun Throwable.isTokenError() =
|
||||
this is Failure.ServerError
|
||||
&& (error.code == MatrixError.M_UNKNOWN_TOKEN || error.code == MatrixError.M_MISSING_TOKEN)
|
||||
&& (error.code == MatrixError.M_UNKNOWN_TOKEN
|
||||
|| error.code == MatrixError.M_MISSING_TOKEN
|
||||
|| error.code == MatrixError.ORG_MATRIX_EXPIRED_ACCOUNT)
|
||||
|
||||
fun Throwable.shouldBeRetried(): Boolean {
|
||||
return this is Failure.NetworkConnection
|
||||
|
|
|
@ -23,4 +23,5 @@ sealed class GlobalError {
|
|||
data class InvalidToken(val softLogout: Boolean) : GlobalError()
|
||||
data class ConsentNotGivenError(val consentUri: String) : GlobalError()
|
||||
data class CertificateError(val fingerprint: Fingerprint) : GlobalError()
|
||||
object ExpiredAccount : GlobalError()
|
||||
}
|
||||
|
|
|
@ -182,6 +182,24 @@ data class MatrixError(
|
|||
/** (Not documented yet) */
|
||||
const val M_WEAK_PASSWORD = "M_WEAK_PASSWORD"
|
||||
|
||||
/** The provided password's length is shorter than the minimum length required by the server. */
|
||||
const val M_PASSWORD_TOO_SHORT = "M_PASSWORD_TOO_SHORT"
|
||||
|
||||
/** The password doesn't contain any digit but the server requires at least one. */
|
||||
const val M_PASSWORD_NO_DIGIT = "M_PASSWORD_NO_DIGIT"
|
||||
|
||||
/** The password doesn't contain any uppercase letter but the server requires at least one. */
|
||||
const val M_PASSWORD_NO_UPPERCASE = "M_PASSWORD_NO_UPPERCASE"
|
||||
|
||||
/** The password doesn't contain any lowercase letter but the server requires at least one. */
|
||||
const val M_PASSWORD_NO_LOWERCASE = "M_PASSWORD_NO_LOWERCASE"
|
||||
|
||||
/** The password doesn't contain any symbol but the server requires at least one. */
|
||||
const val M_PASSWORD_NO_SYMBOL = "M_PASSWORD_NO_SYMBOL"
|
||||
|
||||
/** The password was found in a dictionary, and is not acceptable. */
|
||||
const val M_PASSWORD_IN_DICTIONARY = "M_PASSWORD_IN_DICTIONARY"
|
||||
|
||||
const val M_TERMS_NOT_SIGNED = "M_TERMS_NOT_SIGNED"
|
||||
|
||||
// For identity service
|
||||
|
@ -189,5 +207,12 @@ data class MatrixError(
|
|||
|
||||
// Possible value for "limit_type"
|
||||
const val LIMIT_TYPE_MAU = "monthly_active_user"
|
||||
|
||||
/**
|
||||
* The user account has expired. It has to be renewed by clicking on an email or by sending a renewal token.
|
||||
*
|
||||
* More documentation can be found in the dedicated Synapse plugin module repository: https://github.com/matrix-org/synapse-email-account-validity
|
||||
*/
|
||||
const val ORG_MATRIX_EXPIRED_ACCOUNT = "ORG_MATRIX_EXPIRED_ACCOUNT"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package org.matrix.android.sdk.api.pushrules
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import org.matrix.android.sdk.api.pushrules.rest.PushRule
|
||||
import org.matrix.android.sdk.api.pushrules.rest.RuleSet
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
|
@ -39,7 +40,7 @@ interface PushRuleService {
|
|||
|
||||
suspend fun updatePushRuleActions(kind: RuleKind, ruleId: String, enable: Boolean, actions: List<Action>?)
|
||||
|
||||
suspend fun removePushRule(kind: RuleKind, pushRule: PushRule)
|
||||
suspend fun removePushRule(kind: RuleKind, ruleId: String)
|
||||
|
||||
fun addPushRuleListener(listener: PushRuleListener)
|
||||
|
||||
|
@ -56,4 +57,6 @@ interface PushRuleService {
|
|||
fun onEventRedacted(redactedEventId: String)
|
||||
fun batchFinish()
|
||||
}
|
||||
|
||||
fun getKeywords(): LiveData<Set<String>>
|
||||
}
|
||||
|
|
|
@ -35,6 +35,11 @@ object RuleIds {
|
|||
// Default Content Rules
|
||||
const val RULE_ID_CONTAIN_USER_NAME = ".m.rule.contains_user_name"
|
||||
|
||||
// The keywords rule id is not a "real" id in that it does not exist server-side.
|
||||
// It is used client-side as a placeholder for rendering the keyword push rule setting
|
||||
// alongside the others. A similar approach and naming is used on Web and iOS.
|
||||
const val RULE_ID_KEYWORDS = "_keywords"
|
||||
|
||||
// Default Underride Rules
|
||||
const val RULE_ID_CALL = ".m.rule.call"
|
||||
const val RULE_ID_ONE_TO_ONE_ENCRYPTED_ROOM = ".m.rule.encrypted_room_one_to_one"
|
||||
|
|
|
@ -52,7 +52,7 @@ interface VerificationService {
|
|||
transactionId: String?): String?
|
||||
|
||||
/**
|
||||
* Request a key verification from another user using toDevice events.
|
||||
* Request key verification with another user via room events (instead of the to-device API)
|
||||
*/
|
||||
fun requestKeyVerificationInDMs(methods: List<VerificationMethod>,
|
||||
otherUserId: String,
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package org.matrix.android.sdk.api.session.identity
|
||||
|
||||
import org.matrix.android.sdk.internal.session.identity.model.SignInvitationResult
|
||||
|
||||
/**
|
||||
* Provides access to the identity server configuration and services identity server can provide
|
||||
*/
|
||||
|
@ -121,6 +123,18 @@ interface IdentityService {
|
|||
*/
|
||||
suspend fun getShareStatus(threePids: List<ThreePid>): Map<ThreePid, SharedState>
|
||||
|
||||
/**
|
||||
* When one performs a 3pid invite and the third party identifier is unknown, the home server
|
||||
* will store the invitation in the Identity server and store some information in the room state membership event.
|
||||
* The email invite will contains the token and secret that can be used to claim the stored invitation
|
||||
*
|
||||
* To aid clients who may not be able to perform crypto themselves,
|
||||
* the identity server offers some crypto functionality to help in accepting invitations.
|
||||
* This is less secure than the client doing it itself, but may be useful where this isn't possible.
|
||||
*/
|
||||
suspend fun sign3pidInvitation(identiyServer: String, token: String, secret: String) : SignInvitationResult
|
||||
|
||||
fun addListener(listener: IdentityServiceListener)
|
||||
|
||||
fun removeListener(listener: IdentityServiceListener)
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
package org.matrix.android.sdk.api.session.permalinks
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.Parcelable
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
/**
|
||||
* This sealed class represents all the permalink cases.
|
||||
|
@ -31,6 +33,25 @@ sealed class PermalinkData {
|
|||
val viaParameters: List<String>
|
||||
) : PermalinkData()
|
||||
|
||||
/**
|
||||
* &room_name=Team2
|
||||
&room_avatar_url=mxc:
|
||||
&inviter_name=bob
|
||||
*/
|
||||
@Parcelize
|
||||
data class RoomEmailInviteLink(
|
||||
val roomId: String,
|
||||
val email: String,
|
||||
val signUrl: String,
|
||||
val roomName: String?,
|
||||
val roomAvatarUrl: String?,
|
||||
val inviterName: String?,
|
||||
val identityServer: String,
|
||||
val token: String,
|
||||
val privateKey: String,
|
||||
val roomType: String?
|
||||
) : PermalinkData(), Parcelable
|
||||
|
||||
data class UserLink(val userId: String) : PermalinkData()
|
||||
|
||||
data class GroupLink(val groupId: String) : PermalinkData()
|
||||
|
|
|
@ -19,14 +19,18 @@ package org.matrix.android.sdk.api.session.permalinks
|
|||
import android.net.Uri
|
||||
import android.net.UrlQuerySanitizer
|
||||
import org.matrix.android.sdk.api.MatrixPatterns
|
||||
import timber.log.Timber
|
||||
import java.net.URLDecoder
|
||||
|
||||
/**
|
||||
* This class turns an uri to a [PermalinkData]
|
||||
* This class turns a uri to a [PermalinkData]
|
||||
* element-based domains (e.g. https://app.element.io/#/user/@chagai95:matrix.org) permalinks
|
||||
* or matrix.to permalinks (e.g. https://matrix.to/#/@chagai95:matrix.org)
|
||||
*/
|
||||
object PermalinkParser {
|
||||
|
||||
/**
|
||||
* Turns an uri string to a [PermalinkData]
|
||||
* Turns a uri string to a [PermalinkData]
|
||||
*/
|
||||
fun parse(uriString: String): PermalinkData {
|
||||
val uri = Uri.parse(uriString)
|
||||
|
@ -34,13 +38,16 @@ object PermalinkParser {
|
|||
}
|
||||
|
||||
/**
|
||||
* Turns an uri to a [PermalinkData]
|
||||
* Turns a uri to a [PermalinkData]
|
||||
* https://github.com/matrix-org/matrix-doc/blob/master/proposals/1704-matrix.to-permalinks.md
|
||||
*/
|
||||
fun parse(uri: Uri): PermalinkData {
|
||||
if (!uri.toString().startsWith(PermalinkService.MATRIX_TO_URL_BASE)) {
|
||||
return PermalinkData.FallbackLink(uri)
|
||||
}
|
||||
val fragment = uri.fragment
|
||||
// We can't use uri.fragment as it is decoding to early and it will break the parsing
|
||||
// of parameters that represents url (like signurl)
|
||||
val fragment = uri.toString().substringAfter("#") // uri.fragment
|
||||
if (fragment.isNullOrEmpty()) {
|
||||
return PermalinkData.FallbackLink(uri)
|
||||
}
|
||||
|
@ -51,21 +58,23 @@ object PermalinkParser {
|
|||
val params = safeFragment
|
||||
.split(MatrixPatterns.SEP_REGEX)
|
||||
.filter { it.isNotEmpty() }
|
||||
.map { URLDecoder.decode(it, "UTF-8") }
|
||||
.take(2)
|
||||
|
||||
val identifier = params.getOrNull(0)
|
||||
// the element-based domain permalinks (e.g. https://app.element.io/#/user/@chagai95:matrix.org) don't have the
|
||||
// mxid in the first param (like matrix.to does - https://matrix.to/#/@chagai95:matrix.org) but rather in the second after /user/ so /user/mxid
|
||||
var identifier = params.getOrNull(0)
|
||||
if (identifier.equals("user")) {
|
||||
identifier = params.getOrNull(1)
|
||||
}
|
||||
|
||||
val extraParameter = params.getOrNull(1)
|
||||
return when {
|
||||
identifier.isNullOrEmpty() -> PermalinkData.FallbackLink(uri)
|
||||
MatrixPatterns.isUserId(identifier) -> PermalinkData.UserLink(userId = identifier)
|
||||
MatrixPatterns.isGroupId(identifier) -> PermalinkData.GroupLink(groupId = identifier)
|
||||
MatrixPatterns.isRoomId(identifier) -> {
|
||||
PermalinkData.RoomLink(
|
||||
roomIdOrAlias = identifier,
|
||||
isRoomAlias = false,
|
||||
eventId = extraParameter.takeIf { !it.isNullOrEmpty() && MatrixPatterns.isEventId(it) },
|
||||
viaParameters = viaQueryParameters
|
||||
)
|
||||
handleRoomIdCase(fragment, identifier, uri, extraParameter, viaQueryParameters)
|
||||
}
|
||||
MatrixPatterns.isRoomAlias(identifier) -> {
|
||||
PermalinkData.RoomLink(
|
||||
|
@ -79,13 +88,59 @@ object PermalinkParser {
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleRoomIdCase(fragment: String, identifier: String, uri: Uri, extraParameter: String?, viaQueryParameters: List<String>): PermalinkData {
|
||||
// Can't rely on built in parsing because it's messing around the signurl
|
||||
val paramList = safeExtractParams(fragment)
|
||||
val signUrl = paramList.firstOrNull { it.first == "signurl" }?.second
|
||||
val email = paramList.firstOrNull { it.first == "email" }?.second
|
||||
return if (signUrl.isNullOrEmpty().not() && email.isNullOrEmpty().not()) {
|
||||
try {
|
||||
val signValidUri = Uri.parse(signUrl)
|
||||
val identityServerHost = signValidUri.authority ?: throw IllegalArgumentException()
|
||||
val token = signValidUri.getQueryParameter("token") ?: throw IllegalArgumentException()
|
||||
val privateKey = signValidUri.getQueryParameter("private_key") ?: throw IllegalArgumentException()
|
||||
PermalinkData.RoomEmailInviteLink(
|
||||
roomId = identifier,
|
||||
email = email!!,
|
||||
signUrl = signUrl!!,
|
||||
roomName = paramList.firstOrNull { it.first == "room_name" }?.second,
|
||||
inviterName = paramList.firstOrNull { it.first == "inviter_name" }?.second,
|
||||
roomAvatarUrl = paramList.firstOrNull { it.first == "room_avatar_url" }?.second,
|
||||
roomType = paramList.firstOrNull { it.first == "room_type" }?.second,
|
||||
identityServer = identityServerHost,
|
||||
token = token,
|
||||
privateKey = privateKey
|
||||
)
|
||||
} catch (failure: Throwable) {
|
||||
Timber.i("## Permalink: Failed to parse permalink $signUrl")
|
||||
PermalinkData.FallbackLink(uri)
|
||||
}
|
||||
} else {
|
||||
PermalinkData.RoomLink(
|
||||
roomIdOrAlias = identifier,
|
||||
isRoomAlias = false,
|
||||
eventId = extraParameter.takeIf { !it.isNullOrEmpty() && MatrixPatterns.isEventId(it) },
|
||||
viaParameters = viaQueryParameters
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun safeExtractParams(fragment: String) = fragment.substringAfter("?").split('&').mapNotNull {
|
||||
val splitNameValue = it.split("=")
|
||||
if (splitNameValue.size == 2) {
|
||||
Pair(splitNameValue[0], URLDecoder.decode(splitNameValue[1], "UTF-8"))
|
||||
} else null
|
||||
}
|
||||
|
||||
private fun String.getViaParameters(): List<String> {
|
||||
return UrlQuerySanitizer(this)
|
||||
.parameterList
|
||||
.filter {
|
||||
it.mParameter == "via"
|
||||
}.map {
|
||||
it.mValue
|
||||
it.mValue.let {
|
||||
URLDecoder.decode(it, "UTF-8")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
|||
import org.matrix.android.sdk.api.session.room.peeking.PeekResult
|
||||
import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount
|
||||
import org.matrix.android.sdk.api.util.Optional
|
||||
import org.matrix.android.sdk.internal.session.identity.model.SignInvitationResult
|
||||
import org.matrix.android.sdk.internal.session.room.alias.RoomAliasDescription
|
||||
|
||||
/**
|
||||
|
@ -63,6 +64,18 @@ interface RoomService {
|
|||
reason: String? = null,
|
||||
viaServers: List<String> = emptyList())
|
||||
|
||||
/**
|
||||
* @param roomId the roomId of the room to join
|
||||
* @param reason optional reason for joining the room
|
||||
* @param thirdPartySigned A signature of an m.third_party_invite token to prove that this user owns a third party identity
|
||||
* which has been invited to the room.
|
||||
*/
|
||||
suspend fun joinRoom(
|
||||
roomId: String,
|
||||
reason: String? = null,
|
||||
thirdPartySigned: SignInvitationResult
|
||||
)
|
||||
|
||||
/**
|
||||
* Get a room from a roomId
|
||||
* @param roomId the roomId to look for.
|
||||
|
|
|
@ -19,5 +19,6 @@ package org.matrix.android.sdk.api.session.room
|
|||
enum class RoomSortOrder {
|
||||
NAME,
|
||||
ACTIVITY,
|
||||
PRIORITY_AND_ACTIVITY,
|
||||
NONE
|
||||
}
|
||||
|
|
|
@ -23,11 +23,15 @@ import com.squareup.moshi.JsonClass
|
|||
@JsonClass(generateAdapter = true)
|
||||
data class RoomJoinRulesAllowEntry(
|
||||
/**
|
||||
* space: The room ID of the space to check the membership of.
|
||||
* The room ID to check the membership of.
|
||||
*/
|
||||
@Json(name = "space") val spaceID: String,
|
||||
@Json(name = "room_id") val roomId: String?,
|
||||
/**
|
||||
* via: A list of servers which may be used to peek for membership of the space.
|
||||
* "m.room_membership" to describe that we are allowing access via room membership. Future MSCs may define other types.
|
||||
*/
|
||||
@Json(name = "via") val via: List<String>
|
||||
)
|
||||
@Json(name = "type") val type: String?
|
||||
) {
|
||||
companion object {
|
||||
fun restrictedToRoom(roomId: String) = RoomJoinRulesAllowEntry(roomId, "m.room_membership")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,9 @@ import timber.log.Timber
|
|||
data class RoomJoinRulesContent(
|
||||
@Json(name = "join_rule") val _joinRules: String? = null,
|
||||
/**
|
||||
* If the allow key is an empty list (or not a list at all), then the room reverts to standard public join rules
|
||||
* If the allow key is an empty list (or not a list at all),
|
||||
* then no users are allowed to join without an invite.
|
||||
* Each entry is expected to be an object with the following keys:
|
||||
*/
|
||||
@Json(name = "allow") val allowList: List<RoomJoinRulesAllowEntry>? = null
|
||||
) {
|
||||
|
|
|
@ -27,7 +27,7 @@ data class SpaceChildInfo(
|
|||
val avatarUrl: String?,
|
||||
val order: String?,
|
||||
val activeMemberCount: Int?,
|
||||
val autoJoin: Boolean,
|
||||
// val autoJoin: Boolean,
|
||||
val viaServers: List<String>,
|
||||
val parentRoomId: String?,
|
||||
val suggested: Boolean?,
|
||||
|
|
|
@ -32,5 +32,5 @@ data class AudioWaveformInfo(
|
|||
* List of integers between zero and 1024, inclusive.
|
||||
*/
|
||||
@Json(name = "waveform")
|
||||
val waveform: List<Int>? = null
|
||||
val waveform: List<Int?>? = null
|
||||
)
|
||||
|
|
|
@ -36,7 +36,7 @@ interface Space {
|
|||
suspend fun addChildren(roomId: String,
|
||||
viaServers: List<String>?,
|
||||
order: String?,
|
||||
autoJoin: Boolean = false,
|
||||
// autoJoin: Boolean = false,
|
||||
suggested: Boolean? = false)
|
||||
|
||||
fun getChildInfo(roomId: String): SpaceChildContent?
|
||||
|
@ -46,8 +46,8 @@ interface Space {
|
|||
@Throws
|
||||
suspend fun setChildrenOrder(roomId: String, order: String?)
|
||||
|
||||
@Throws
|
||||
suspend fun setChildrenAutoJoin(roomId: String, autoJoin: Boolean)
|
||||
// @Throws
|
||||
// suspend fun setChildrenAutoJoin(roomId: String, autoJoin: Boolean)
|
||||
|
||||
@Throws
|
||||
suspend fun setChildrenSuggested(roomId: String, suggested: Boolean)
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* 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.space
|
||||
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
|
||||
|
||||
data class SpaceHierarchyData(
|
||||
val rootSummary: RoomSummary,
|
||||
val children: List<SpaceChildInfo>,
|
||||
val childrenState: List<Event>,
|
||||
val nextToken: String? = null
|
||||
)
|
|
@ -18,9 +18,9 @@ package org.matrix.android.sdk.api.session.space
|
|||
|
||||
import android.net.Uri
|
||||
import androidx.lifecycle.LiveData
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
|
||||
import org.matrix.android.sdk.internal.session.space.peeking.SpacePeekResult
|
||||
|
||||
typealias SpaceSummaryQueryParams = RoomSummaryQueryParams
|
||||
|
@ -58,10 +58,17 @@ interface SpaceService {
|
|||
|
||||
/**
|
||||
* Get's information of a space by querying the server
|
||||
* @param suggestedOnly If true, return only child events and rooms where the m.space.child event has suggested: true.
|
||||
* @param limit a client-defined limit to the maximum number of rooms to return per page. Must be a non-negative integer.
|
||||
* @param from: Optional. Pagination token given to retrieve the next set of rooms. Note that if a pagination token is provided,
|
||||
* then the parameters given for suggested_only and max_depth must be the same.
|
||||
*/
|
||||
suspend fun querySpaceChildren(spaceId: String,
|
||||
suggestedOnly: Boolean? = null,
|
||||
autoJoinedOnly: Boolean? = null): Pair<RoomSummary, List<SpaceChildInfo>>
|
||||
limit: Int? = null,
|
||||
from: String? = null,
|
||||
// when paginating, pass back the m.space.child state events
|
||||
knownStateList: List<Event>? = null): SpaceHierarchyData
|
||||
|
||||
/**
|
||||
* Get a live list of space summaries. This list is refreshed as soon as the data changes.
|
||||
|
|
|
@ -40,12 +40,12 @@ data class SpaceChildContent(
|
|||
* or consist of more than 50 characters, are forbidden and should be ignored if received.)
|
||||
*/
|
||||
@Json(name = "order") val order: String? = null,
|
||||
/**
|
||||
* The auto_join flag on a child listing allows a space admin to list the sub-spaces and rooms in that space which should
|
||||
* be automatically joined by members of that space.
|
||||
* (This is not a force-join, which are descoped for a future MSC; the user can subsequently part these room if they desire.)
|
||||
*/
|
||||
@Json(name = "auto_join") val autoJoin: Boolean? = false,
|
||||
// /**
|
||||
// * The auto_join flag on a child listing allows a space admin to list the sub-spaces and rooms in that space which should
|
||||
// * be automatically joined by members of that space.
|
||||
// * (This is not a force-join, which are descoped for a future MSC; the user can subsequently part these room if they desire.)
|
||||
// */
|
||||
// @Json(name = "auto_join") val autoJoin: Boolean? = false,
|
||||
|
||||
/**
|
||||
* If `suggested` is set to `true`, that indicates that the child should be advertised to
|
||||
|
|
|
@ -23,12 +23,12 @@ import org.matrix.android.sdk.api.auth.data.Credentials
|
|||
import org.matrix.android.sdk.api.failure.shouldBeRetried
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.events.model.LocalEcho
|
||||
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.ShareRequestCancellation
|
||||
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
||||
import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask
|
||||
import org.matrix.android.sdk.internal.crypto.tasks.createUniqueTxnId
|
||||
import org.matrix.android.sdk.internal.session.SessionComponent
|
||||
import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
|
||||
import org.matrix.android.sdk.internal.worker.SessionWorkerParams
|
||||
|
@ -43,6 +43,9 @@ internal class CancelGossipRequestWorker(context: Context,
|
|||
override val sessionId: String,
|
||||
val requestId: String,
|
||||
val recipients: Map<String, List<String>>,
|
||||
// The txnId for the sendToDevice request. Nullable for compatibility reasons, but MUST always be provided
|
||||
// to use the same value if this worker is retried.
|
||||
val txnId: String? = null,
|
||||
override val lastFailureMessage: String? = null
|
||||
) : SessionWorkerParams {
|
||||
companion object {
|
||||
|
@ -51,6 +54,7 @@ internal class CancelGossipRequestWorker(context: Context,
|
|||
sessionId = sessionId,
|
||||
requestId = request.requestId,
|
||||
recipients = request.recipients,
|
||||
txnId = createUniqueTxnId(),
|
||||
lastFailureMessage = null
|
||||
)
|
||||
}
|
||||
|
@ -66,7 +70,10 @@ internal class CancelGossipRequestWorker(context: Context,
|
|||
}
|
||||
|
||||
override suspend fun doSafeWork(params: Params): Result {
|
||||
val localId = LocalEcho.createLocalEchoId()
|
||||
// params.txnId should be provided in all cases now. But Params can be deserialized by
|
||||
// the WorkManager from data serialized in a previous version of the application, so without the txnId field.
|
||||
// So if not present, we create a txnId
|
||||
val txnId = params.txnId ?: createUniqueTxnId()
|
||||
val contentMap = MXUsersDevicesMap<Any>()
|
||||
val toDeviceContent = ShareRequestCancellation(
|
||||
requestingDeviceId = credentials.deviceId,
|
||||
|
@ -92,7 +99,7 @@ internal class CancelGossipRequestWorker(context: Context,
|
|||
SendToDeviceTask.Params(
|
||||
eventType = EventType.ROOM_KEY_REQUEST,
|
||||
contentMap = contentMap,
|
||||
transactionId = localId
|
||||
transactionId = txnId
|
||||
)
|
||||
)
|
||||
cryptoStore.updateOutgoingGossipingRequestState(params.requestId, OutgoingGossipingRequestState.CANCELLED)
|
||||
|
|
|
@ -35,6 +35,7 @@ import org.matrix.android.sdk.internal.crypto.model.rest.GossipingDefaultContent
|
|||
import org.matrix.android.sdk.internal.crypto.model.rest.GossipingToDeviceObject
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.RoomKeyRequestBody
|
||||
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
||||
import org.matrix.android.sdk.internal.crypto.tasks.createUniqueTxnId
|
||||
import org.matrix.android.sdk.internal.di.SessionId
|
||||
import org.matrix.android.sdk.internal.session.SessionScope
|
||||
import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
|
||||
|
@ -356,7 +357,8 @@ internal class IncomingGossipingRequestManager @Inject constructor(
|
|||
secretValue = secretValue,
|
||||
requestUserId = request.userId,
|
||||
requestDeviceId = request.deviceId,
|
||||
requestId = request.requestId
|
||||
requestId = request.requestId,
|
||||
txnId = createUniqueTxnId()
|
||||
)
|
||||
|
||||
cryptoStore.updateGossipingRequestState(request, GossipingRequestState.ACCEPTING)
|
||||
|
@ -376,13 +378,13 @@ internal class IncomingGossipingRequestManager @Inject constructor(
|
|||
}
|
||||
|
||||
request.share = { secretValue ->
|
||||
|
||||
val params = SendGossipWorker.Params(
|
||||
sessionId = userId,
|
||||
secretValue = secretValue,
|
||||
requestUserId = request.userId,
|
||||
requestDeviceId = request.deviceId,
|
||||
requestId = request.requestId
|
||||
requestId = request.requestId,
|
||||
txnId = createUniqueTxnId()
|
||||
)
|
||||
|
||||
cryptoStore.updateGossipingRequestState(request, GossipingRequestState.ACCEPTING)
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
package org.matrix.android.sdk.internal.crypto
|
||||
|
||||
import org.matrix.android.sdk.api.session.events.model.LocalEcho
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.RoomKeyRequestBody
|
||||
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
||||
import org.matrix.android.sdk.internal.di.SessionId
|
||||
|
@ -26,6 +25,8 @@ import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
|
|||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import org.matrix.android.sdk.internal.crypto.tasks.createUniqueTxnId
|
||||
import org.matrix.android.sdk.internal.crypto.util.RequestIdHelper
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -131,7 +132,8 @@ internal class OutgoingGossipingRequestManager @Inject constructor(
|
|||
val params = SendGossipRequestWorker.Params(
|
||||
sessionId = sessionId,
|
||||
keyShareRequest = request as? OutgoingRoomKeyRequest,
|
||||
secretShareRequest = request as? OutgoingSecretRequest
|
||||
secretShareRequest = request as? OutgoingSecretRequest,
|
||||
txnId = createUniqueTxnId()
|
||||
)
|
||||
cryptoStore.updateOutgoingGossipingRequestState(request.requestId, OutgoingGossipingRequestState.SENDING)
|
||||
val workRequest = gossipingWorkManager.createWork<SendGossipRequestWorker>(WorkerParamsFactory.toData(params), true)
|
||||
|
@ -154,7 +156,8 @@ internal class OutgoingGossipingRequestManager @Inject constructor(
|
|||
if (resend) {
|
||||
val reSendParams = SendGossipRequestWorker.Params(
|
||||
sessionId = sessionId,
|
||||
keyShareRequest = request.copy(requestId = LocalEcho.createLocalEchoId())
|
||||
keyShareRequest = request.copy(requestId = RequestIdHelper.createUniqueRequestId()),
|
||||
txnId = createUniqueTxnId()
|
||||
)
|
||||
val reSendWorkRequest = gossipingWorkManager.createWork<SendGossipRequestWorker>(WorkerParamsFactory.toData(reSendParams), true)
|
||||
gossipingWorkManager.postWork(reSendWorkRequest)
|
||||
|
|
|
@ -23,7 +23,6 @@ import org.matrix.android.sdk.api.auth.data.Credentials
|
|||
import org.matrix.android.sdk.api.failure.shouldBeRetried
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.events.model.LocalEcho
|
||||
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.GossipingToDeviceObject
|
||||
|
@ -31,6 +30,7 @@ import org.matrix.android.sdk.internal.crypto.model.rest.RoomKeyShareRequest
|
|||
import org.matrix.android.sdk.internal.crypto.model.rest.SecretShareRequest
|
||||
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
||||
import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask
|
||||
import org.matrix.android.sdk.internal.crypto.tasks.createUniqueTxnId
|
||||
import org.matrix.android.sdk.internal.session.SessionComponent
|
||||
import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
|
||||
import org.matrix.android.sdk.internal.worker.SessionWorkerParams
|
||||
|
@ -46,6 +46,9 @@ internal class SendGossipRequestWorker(context: Context,
|
|||
override val sessionId: String,
|
||||
val keyShareRequest: OutgoingRoomKeyRequest? = null,
|
||||
val secretShareRequest: OutgoingSecretRequest? = null,
|
||||
// The txnId for the sendToDevice request. Nullable for compatibility reasons, but MUST always be provided
|
||||
// to use the same value if this worker is retried.
|
||||
val txnId: String? = null,
|
||||
override val lastFailureMessage: String? = null
|
||||
) : SessionWorkerParams
|
||||
|
||||
|
@ -58,7 +61,10 @@ internal class SendGossipRequestWorker(context: Context,
|
|||
}
|
||||
|
||||
override suspend fun doSafeWork(params: Params): Result {
|
||||
val localId = LocalEcho.createLocalEchoId()
|
||||
// params.txnId should be provided in all cases now. But Params can be deserialized by
|
||||
// the WorkManager from data serialized in a previous version of the application, so without the txnId field.
|
||||
// So if not present, we create a txnId
|
||||
val txnId = params.txnId ?: createUniqueTxnId()
|
||||
val contentMap = MXUsersDevicesMap<Any>()
|
||||
val eventType: String
|
||||
val requestId: String
|
||||
|
@ -122,7 +128,7 @@ internal class SendGossipRequestWorker(context: Context,
|
|||
SendToDeviceTask.Params(
|
||||
eventType = eventType,
|
||||
contentMap = contentMap,
|
||||
transactionId = localId
|
||||
transactionId = txnId
|
||||
)
|
||||
)
|
||||
cryptoStore.updateOutgoingGossipingRequestState(requestId, OutgoingGossipingRequestState.SENT)
|
||||
|
|
|
@ -23,7 +23,6 @@ import org.matrix.android.sdk.api.auth.data.Credentials
|
|||
import org.matrix.android.sdk.api.failure.shouldBeRetried
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.events.model.LocalEcho
|
||||
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||
import org.matrix.android.sdk.internal.crypto.actions.EnsureOlmSessionsForDevicesAction
|
||||
import org.matrix.android.sdk.internal.crypto.actions.MessageEncrypter
|
||||
|
@ -31,6 +30,7 @@ import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
|
|||
import org.matrix.android.sdk.internal.crypto.model.event.SecretSendEventContent
|
||||
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
||||
import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask
|
||||
import org.matrix.android.sdk.internal.crypto.tasks.createUniqueTxnId
|
||||
import org.matrix.android.sdk.internal.session.SessionComponent
|
||||
import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
|
||||
import org.matrix.android.sdk.internal.worker.SessionWorkerParams
|
||||
|
@ -48,6 +48,9 @@ internal class SendGossipWorker(context: Context,
|
|||
val requestUserId: String?,
|
||||
val requestDeviceId: String?,
|
||||
val requestId: String?,
|
||||
// The txnId for the sendToDevice request. Nullable for compatibility reasons, but MUST always be provided
|
||||
// to use the same value if this worker is retried.
|
||||
val txnId: String? = null,
|
||||
override val lastFailureMessage: String? = null
|
||||
) : SessionWorkerParams
|
||||
|
||||
|
@ -62,7 +65,10 @@ internal class SendGossipWorker(context: Context,
|
|||
}
|
||||
|
||||
override suspend fun doSafeWork(params: Params): Result {
|
||||
val localId = LocalEcho.createLocalEchoId()
|
||||
// params.txnId should be provided in all cases now. But Params can be deserialized by
|
||||
// the WorkManager from data serialized in a previous version of the application, so without the txnId field.
|
||||
// So if not present, we create a txnId
|
||||
val txnId = params.txnId ?: createUniqueTxnId()
|
||||
val eventType: String = EventType.SEND_SECRET
|
||||
|
||||
val toDeviceContent = SecretSendEventContent(
|
||||
|
@ -127,7 +133,7 @@ internal class SendGossipWorker(context: Context,
|
|||
SendToDeviceTask.Params(
|
||||
eventType = EventType.ENCRYPTED,
|
||||
contentMap = sendToDeviceMap,
|
||||
transactionId = localId
|
||||
transactionId = txnId
|
||||
)
|
||||
)
|
||||
cryptoStore.updateGossipingRequestState(
|
||||
|
|
|
@ -27,7 +27,6 @@ import io.realm.Sort
|
|||
import io.realm.kotlin.where
|
||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.events.model.LocalEcho
|
||||
import org.matrix.android.sdk.api.session.room.send.SendState
|
||||
import org.matrix.android.sdk.api.util.Optional
|
||||
import org.matrix.android.sdk.api.util.toOptional
|
||||
|
@ -88,6 +87,7 @@ import org.matrix.android.sdk.internal.crypto.store.db.query.delete
|
|||
import org.matrix.android.sdk.internal.crypto.store.db.query.get
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.query.getById
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.query.getOrCreate
|
||||
import org.matrix.android.sdk.internal.crypto.util.RequestIdHelper
|
||||
import org.matrix.android.sdk.internal.database.mapper.ContentMapper
|
||||
import org.matrix.android.sdk.internal.database.tools.RealmDebugTools
|
||||
import org.matrix.android.sdk.internal.di.CryptoDatabase
|
||||
|
@ -1123,7 +1123,7 @@ internal class RealmCryptoStore @Inject constructor(
|
|||
|
||||
if (existing == null) {
|
||||
request = realm.createObject(OutgoingGossipingRequestEntity::class.java).apply {
|
||||
this.requestId = LocalEcho.createLocalEchoId()
|
||||
this.requestId = RequestIdHelper.createUniqueRequestId()
|
||||
this.setRecipients(recipients)
|
||||
this.requestState = OutgoingGossipingRequestState.UNSENT
|
||||
this.type = GossipRequestType.KEY
|
||||
|
@ -1153,7 +1153,7 @@ internal class RealmCryptoStore @Inject constructor(
|
|||
this.type = GossipRequestType.SECRET
|
||||
setRecipients(recipients)
|
||||
this.requestState = OutgoingGossipingRequestState.UNSENT
|
||||
this.requestId = LocalEcho.createLocalEchoId()
|
||||
this.requestId = RequestIdHelper.createUniqueRequestId()
|
||||
this.requestedInfoStr = secretName
|
||||
}.toOutgoingGossipingRequest() as? OutgoingSecretRequest
|
||||
} else {
|
||||
|
|
|
@ -22,8 +22,8 @@ import org.matrix.android.sdk.internal.crypto.model.rest.SendToDeviceBody
|
|||
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
|
||||
import org.matrix.android.sdk.internal.network.executeRequest
|
||||
import org.matrix.android.sdk.internal.task.Task
|
||||
import java.util.UUID
|
||||
import javax.inject.Inject
|
||||
import kotlin.random.Random
|
||||
|
||||
internal interface SendToDeviceTask : Task<SendToDeviceTask.Params, Unit> {
|
||||
data class Params(
|
||||
|
@ -31,7 +31,7 @@ internal interface SendToDeviceTask : Task<SendToDeviceTask.Params, Unit> {
|
|||
val eventType: String,
|
||||
// the content to send. Map from user_id to device_id to content dictionary.
|
||||
val contentMap: MXUsersDevicesMap<Any>,
|
||||
// the transactionId
|
||||
// the transactionId. If not provided, a transactionId will be created by the task
|
||||
val transactionId: String? = null
|
||||
)
|
||||
}
|
||||
|
@ -46,16 +46,23 @@ internal class DefaultSendToDeviceTask @Inject constructor(
|
|||
messages = params.contentMap.map
|
||||
)
|
||||
|
||||
// If params.transactionId is not provided, we create a unique txnId.
|
||||
// It's important to do that outside the requestBlock parameter of executeRequest()
|
||||
// to use the same value if the request is retried
|
||||
val txnId = params.transactionId ?: createUniqueTxnId()
|
||||
|
||||
return executeRequest(
|
||||
globalErrorReceiver,
|
||||
canRetry = true,
|
||||
maxRetriesCount = 3
|
||||
) {
|
||||
cryptoApi.sendToDevice(
|
||||
params.eventType,
|
||||
params.transactionId ?: Random.nextInt(Integer.MAX_VALUE).toString(),
|
||||
sendToDeviceBody
|
||||
eventType = params.eventType,
|
||||
transactionId = txnId,
|
||||
body = sendToDeviceBody
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun createUniqueTxnId() = UUID.randomUUID().toString()
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2020 New Vector Ltd
|
||||
* Copyright (c) 2021 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -14,11 +14,10 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.call.conference
|
||||
package org.matrix.android.sdk.internal.crypto.util
|
||||
|
||||
data class JitsiWidgetProperties(
|
||||
val domain: String,
|
||||
val confId: String?,
|
||||
val displayName: String?,
|
||||
val avatarUrl: String?
|
||||
)
|
||||
import java.util.UUID
|
||||
|
||||
internal object RequestIdHelper {
|
||||
fun createUniqueRequestId() = UUID.randomUUID().toString()
|
||||
}
|
|
@ -17,6 +17,9 @@
|
|||
package org.matrix.android.sdk.internal.database
|
||||
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import io.realm.RealmConfiguration
|
||||
import io.realm.RealmResults
|
||||
import kotlinx.coroutines.launch
|
||||
import org.matrix.android.sdk.internal.database.mapper.asDomain
|
||||
import org.matrix.android.sdk.internal.database.model.EventEntity
|
||||
import org.matrix.android.sdk.internal.database.model.EventInsertEntity
|
||||
|
@ -24,20 +27,15 @@ import org.matrix.android.sdk.internal.database.model.EventInsertEntityFields
|
|||
import org.matrix.android.sdk.internal.database.query.where
|
||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||
import org.matrix.android.sdk.internal.session.EventInsertLiveProcessor
|
||||
import io.realm.RealmConfiguration
|
||||
import io.realm.RealmResults
|
||||
import kotlinx.coroutines.launch
|
||||
import org.matrix.android.sdk.internal.crypto.EventDecryptor
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class EventInsertLiveObserver @Inject constructor(@SessionDatabase realmConfiguration: RealmConfiguration,
|
||||
private val processors: Set<@JvmSuppressWildcards EventInsertLiveProcessor>,
|
||||
private val eventDecryptor: EventDecryptor)
|
||||
private val processors: Set<@JvmSuppressWildcards EventInsertLiveProcessor>)
|
||||
: RealmLiveEntityObserver<EventInsertEntity>(realmConfiguration) {
|
||||
|
||||
override val query = Monarchy.Query<EventInsertEntity> {
|
||||
it.where(EventInsertEntity::class.java)
|
||||
override val query = Monarchy.Query {
|
||||
it.where(EventInsertEntity::class.java).equalTo(EventInsertEntityFields.CAN_BE_PROCESSED, true)
|
||||
}
|
||||
|
||||
override fun onChange(results: RealmResults<EventInsertEntity>) {
|
||||
|
@ -86,23 +84,6 @@ internal class EventInsertLiveObserver @Inject constructor(@SessionDatabase real
|
|||
}
|
||||
}
|
||||
|
||||
// private fun decryptIfNeeded(event: Event) {
|
||||
// if (event.isEncrypted() && event.mxDecryptionResult == null) {
|
||||
// try {
|
||||
// val result = eventDecryptor.decryptEvent(event, event.roomId ?: "")
|
||||
// event.mxDecryptionResult = OlmDecryptionResult(
|
||||
// payload = result.clearEvent,
|
||||
// senderKey = result.senderCurve25519Key,
|
||||
// keysClaimed = result.claimedEd25519Key?.let { k -> mapOf("ed25519" to k) },
|
||||
// forwardingCurve25519KeyChain = result.forwardingCurve25519KeyChain
|
||||
// )
|
||||
// } catch (e: MXCryptoError) {
|
||||
// Timber.v("Failed to decrypt event")
|
||||
// // TODO -> we should keep track of this and retry, or some processing will never be handled
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
private fun shouldProcess(eventInsertEntity: EventInsertEntity): Boolean {
|
||||
return processors.any {
|
||||
it.shouldProcess(eventInsertEntity.eventId, eventInsertEntity.eventType, eventInsertEntity.insertType)
|
||||
|
|
|
@ -29,6 +29,7 @@ import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntityFie
|
|||
import org.matrix.android.sdk.internal.database.model.EditAggregatedSummaryEntityFields
|
||||
import org.matrix.android.sdk.internal.database.model.EditionOfEventFields
|
||||
import org.matrix.android.sdk.internal.database.model.EventEntityFields
|
||||
import org.matrix.android.sdk.internal.database.model.EventInsertEntityFields
|
||||
import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEntityFields
|
||||
import org.matrix.android.sdk.internal.database.model.PendingThreePidEntityFields
|
||||
import org.matrix.android.sdk.internal.database.model.PreviewUrlCacheEntityFields
|
||||
|
@ -51,7 +52,7 @@ internal object RealmSessionStoreMigration : RealmMigration {
|
|||
const val SESSION_STORE_SCHEMA_SC_VERSION = 2L
|
||||
const val SESSION_STORE_SCHEMA_SC_VERSION_OFFSET = (1L shl 12)
|
||||
|
||||
const val SESSION_STORE_SCHEMA_VERSION = 16L +
|
||||
const val SESSION_STORE_SCHEMA_VERSION = 17L +
|
||||
SESSION_STORE_SCHEMA_SC_VERSION * SESSION_STORE_SCHEMA_SC_VERSION_OFFSET
|
||||
|
||||
|
||||
|
@ -77,6 +78,7 @@ internal object RealmSessionStoreMigration : RealmMigration {
|
|||
if (oldVersion <= 13) migrateTo14(realm)
|
||||
if (oldVersion <= 14) migrateTo15(realm)
|
||||
if (oldVersion <= 15) migrateTo16(realm)
|
||||
if (oldVersion <= 16) migrateTo17(realm)
|
||||
|
||||
if (oldScVersion <= 0) migrateToSc1(realm)
|
||||
if (oldScVersion <= 1) migrateToSc2(realm)
|
||||
|
@ -359,4 +361,10 @@ internal object RealmSessionStoreMigration : RealmMigration {
|
|||
obj.setLong(HomeServerCapabilitiesEntityFields.LAST_UPDATED_TIMESTAMP, 0)
|
||||
}
|
||||
}
|
||||
|
||||
private fun migrateTo17(realm: DynamicRealm) {
|
||||
Timber.d("Step 16 -> 17")
|
||||
realm.schema.get("EventInsertEntity")
|
||||
?.addField(EventInsertEntityFields.CAN_BE_PROCESSED, Boolean::class.java)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -100,7 +100,7 @@ internal class RoomSummaryMapper @Inject constructor(private val timelineEventMa
|
|||
avatarUrl = it.childSummaryEntity?.avatarUrl,
|
||||
activeMemberCount = it.childSummaryEntity?.joinedMembersCount,
|
||||
order = it.order,
|
||||
autoJoin = it.autoJoin ?: false,
|
||||
// autoJoin = it.autoJoin ?: false,
|
||||
viaServers = it.viaServers.toList(),
|
||||
parentRoomId = roomSummaryEntity.roomId,
|
||||
suggested = it.suggested,
|
||||
|
|
|
@ -22,6 +22,7 @@ import org.matrix.android.sdk.internal.crypto.algorithms.olm.OlmDecryptionResult
|
|||
import org.matrix.android.sdk.internal.di.MoshiProvider
|
||||
import io.realm.RealmObject
|
||||
import io.realm.annotations.Index
|
||||
import org.matrix.android.sdk.internal.extensions.assertIsManaged
|
||||
|
||||
internal open class EventEntity(@Index var eventId: String = "",
|
||||
@Index var roomId: String = "",
|
||||
|
@ -56,15 +57,22 @@ internal open class EventEntity(@Index var eventId: String = "",
|
|||
companion object
|
||||
|
||||
fun setDecryptionResult(result: MXEventDecryptionResult) {
|
||||
assertIsManaged()
|
||||
val decryptionResult = OlmDecryptionResult(
|
||||
payload = result.clearEvent,
|
||||
senderKey = result.senderCurve25519Key,
|
||||
keysClaimed = result.claimedEd25519Key?.let { mapOf("ed25519" to it) },
|
||||
forwardingCurve25519KeyChain = result.forwardingCurve25519KeyChain
|
||||
)
|
||||
val adapter = MoshiProvider.providesMoshi().adapter<OlmDecryptionResult>(OlmDecryptionResult::class.java)
|
||||
val adapter = MoshiProvider.providesMoshi().adapter(OlmDecryptionResult::class.java)
|
||||
decryptionResultJson = adapter.toJson(decryptionResult)
|
||||
decryptionErrorCode = null
|
||||
decryptionErrorReason = null
|
||||
|
||||
// If we have an EventInsertEntity for the eventId we make sures it can be processed now.
|
||||
realm.where(EventInsertEntity::class.java)
|
||||
.equalTo(EventInsertEntityFields.EVENT_ID, eventId)
|
||||
.findFirst()
|
||||
?.canBeProcessed = true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,12 @@ import io.realm.RealmObject
|
|||
* in EventEntity table.
|
||||
*/
|
||||
internal open class EventInsertEntity(var eventId: String = "",
|
||||
var eventType: String = ""
|
||||
var eventType: String = "",
|
||||
/**
|
||||
* This flag will be used to filter EventInsertEntity in EventInsertLiveObserver.
|
||||
* Currently it's set to false when the event content is encrypted.
|
||||
*/
|
||||
var canBeProcessed: Boolean = true
|
||||
) : RealmObject() {
|
||||
|
||||
private var insertTypeStr: String = EventInsertType.INCREMENTAL_SYNC.name
|
||||
|
|
|
@ -24,6 +24,7 @@ import io.realm.Realm
|
|||
import io.realm.RealmList
|
||||
import io.realm.RealmQuery
|
||||
import io.realm.kotlin.where
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
|
||||
internal fun EventEntity.copyToRealmOrIgnore(realm: Realm, insertType: EventInsertType): EventEntity {
|
||||
val eventEntity = realm.where<EventEntity>()
|
||||
|
@ -31,7 +32,8 @@ internal fun EventEntity.copyToRealmOrIgnore(realm: Realm, insertType: EventInse
|
|||
.equalTo(EventEntityFields.ROOM_ID, roomId)
|
||||
.findFirst()
|
||||
return if (eventEntity == null) {
|
||||
val insertEntity = EventInsertEntity(eventId = eventId, eventType = type).apply {
|
||||
val canBeProcessed = type != EventType.ENCRYPTED || decryptionResultJson != null
|
||||
val insertEntity = EventInsertEntity(eventId = eventId, eventType = type, canBeProcessed = canBeProcessed).apply {
|
||||
this.insertType = insertType
|
||||
}
|
||||
realm.insert(insertEntity)
|
||||
|
|
|
@ -30,6 +30,7 @@ internal object NetworkConstants {
|
|||
// Identity server
|
||||
const val URI_IDENTITY_PREFIX_PATH = "_matrix/identity/v2"
|
||||
const val URI_IDENTITY_PATH_V2 = "$URI_IDENTITY_PREFIX_PATH/"
|
||||
const val URI_IDENTITY_PATH_V1 = "_matrix/identity/api/v1/"
|
||||
|
||||
// Push Gateway
|
||||
const val URI_PUSH_GATEWAY_PREFIX_PATH = "_matrix/push/v1/"
|
||||
|
|
|
@ -19,13 +19,13 @@
|
|||
package org.matrix.android.sdk.internal.network
|
||||
|
||||
import com.squareup.moshi.JsonEncodingException
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import okhttp3.ResponseBody
|
||||
import org.matrix.android.sdk.api.extensions.orFalse
|
||||
import org.matrix.android.sdk.api.failure.Failure
|
||||
import org.matrix.android.sdk.api.failure.GlobalError
|
||||
import org.matrix.android.sdk.api.failure.MatrixError
|
||||
import org.matrix.android.sdk.internal.di.MoshiProvider
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import okhttp3.ResponseBody
|
||||
import org.matrix.android.sdk.api.extensions.orFalse
|
||||
import retrofit2.HttpException
|
||||
import retrofit2.Response
|
||||
import timber.log.Timber
|
||||
|
@ -86,13 +86,18 @@ private fun toFailure(errorBody: ResponseBody?, httpCode: Int, globalErrorReceiv
|
|||
val matrixError = matrixErrorAdapter.fromJson(errorBodyStr)
|
||||
|
||||
if (matrixError != null) {
|
||||
if (matrixError.code == MatrixError.M_CONSENT_NOT_GIVEN && !matrixError.consentUri.isNullOrBlank()) {
|
||||
// Also send this error to the globalErrorReceiver, for a global management
|
||||
globalErrorReceiver?.handleGlobalError(GlobalError.ConsentNotGivenError(matrixError.consentUri))
|
||||
} else if (httpCode == HttpURLConnection.HTTP_UNAUTHORIZED /* 401 */
|
||||
&& matrixError.code == MatrixError.M_UNKNOWN_TOKEN) {
|
||||
// Also send this error to the globalErrorReceiver, for a global management
|
||||
globalErrorReceiver?.handleGlobalError(GlobalError.InvalidToken(matrixError.isSoftLogout.orFalse()))
|
||||
// Also send following errors to the globalErrorReceiver, for a global management
|
||||
when {
|
||||
matrixError.code == MatrixError.M_CONSENT_NOT_GIVEN && !matrixError.consentUri.isNullOrBlank() -> {
|
||||
globalErrorReceiver?.handleGlobalError(GlobalError.ConsentNotGivenError(matrixError.consentUri))
|
||||
}
|
||||
httpCode == HttpURLConnection.HTTP_UNAUTHORIZED /* 401 */
|
||||
&& matrixError.code == MatrixError.M_UNKNOWN_TOKEN -> {
|
||||
globalErrorReceiver?.handleGlobalError(GlobalError.InvalidToken(matrixError.isSoftLogout.orFalse()))
|
||||
}
|
||||
matrixError.code == MatrixError.ORG_MATRIX_EXPIRED_ACCOUNT -> {
|
||||
globalErrorReceiver?.handleGlobalError(GlobalError.ExpiredAccount)
|
||||
}
|
||||
}
|
||||
|
||||
return Failure.ServerError(matrixError, httpCode)
|
||||
|
|
|
@ -24,13 +24,21 @@ import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields
|
|||
|
||||
internal fun RealmQuery<RoomSummaryEntity>.process(sortOrder: RoomSortOrder): RealmQuery<RoomSummaryEntity> {
|
||||
when (sortOrder) {
|
||||
RoomSortOrder.NAME -> {
|
||||
RoomSortOrder.NAME -> {
|
||||
sort(RoomSummaryEntityFields.DISPLAY_NAME, Sort.ASCENDING)
|
||||
}
|
||||
RoomSortOrder.ACTIVITY -> {
|
||||
RoomSortOrder.ACTIVITY -> {
|
||||
sort(RoomSummaryEntityFields.LAST_ACTIVITY_TIME, Sort.DESCENDING)
|
||||
}
|
||||
RoomSortOrder.NONE -> {
|
||||
RoomSortOrder.PRIORITY_AND_ACTIVITY -> {
|
||||
sort(
|
||||
arrayOf(
|
||||
RoomSummaryEntityFields.IS_FAVOURITE,
|
||||
RoomSummaryEntityFields.IS_LOW_PRIORITY,
|
||||
RoomSummaryEntityFields.LAST_ACTIVITY_TIME),
|
||||
arrayOf(Sort.DESCENDING, Sort.ASCENDING, Sort.DESCENDING))
|
||||
}
|
||||
RoomSortOrder.NONE -> {
|
||||
}
|
||||
}
|
||||
return this
|
||||
|
|
|
@ -84,7 +84,7 @@ internal data class RoomVersions(
|
|||
* }
|
||||
* }
|
||||
*/
|
||||
@Json(name = "room_capabilities")
|
||||
@Json(name = "org.matrix.msc3244.room_capabilities")
|
||||
val roomCapabilities: JsonDict? = null
|
||||
)
|
||||
|
||||
|
|
|
@ -52,6 +52,7 @@ import kotlinx.coroutines.withContext
|
|||
import okhttp3.OkHttpClient
|
||||
import org.matrix.android.sdk.api.extensions.orFalse
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.internal.session.identity.model.SignInvitationResult
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import javax.net.ssl.HttpsURLConnection
|
||||
|
@ -79,6 +80,7 @@ internal class DefaultIdentityService @Inject constructor(
|
|||
private val identityApiProvider: IdentityApiProvider,
|
||||
private val accountDataDataSource: UserAccountDataDataSource,
|
||||
private val homeServerCapabilitiesService: HomeServerCapabilitiesService,
|
||||
private val sign3pidInvitationTask: DefaultSign3pidInvitationTask,
|
||||
private val sessionParams: SessionParams
|
||||
) : IdentityService, SessionLifecycleObserver {
|
||||
|
||||
|
@ -290,6 +292,14 @@ internal class DefaultIdentityService @Inject constructor(
|
|||
return token.token
|
||||
}
|
||||
|
||||
override suspend fun sign3pidInvitation(identiyServer: String, token: String, secret: String): SignInvitationResult {
|
||||
return sign3pidInvitationTask.execute(Sign3pidInvitationTask.Params(
|
||||
url = identiyServer,
|
||||
token = token,
|
||||
privateKey = secret
|
||||
))
|
||||
}
|
||||
|
||||
override fun addListener(listener: IdentityServiceListener) {
|
||||
listeners.add(listener)
|
||||
}
|
||||
|
|
|
@ -26,10 +26,12 @@ import org.matrix.android.sdk.internal.session.identity.model.IdentityRequestOwn
|
|||
import org.matrix.android.sdk.internal.session.identity.model.IdentityRequestTokenForEmailBody
|
||||
import org.matrix.android.sdk.internal.session.identity.model.IdentityRequestTokenForMsisdnBody
|
||||
import org.matrix.android.sdk.internal.session.identity.model.IdentityRequestTokenResponse
|
||||
import org.matrix.android.sdk.internal.session.identity.model.SignInvitationResult
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.POST
|
||||
import retrofit2.http.Path
|
||||
import retrofit2.http.Query
|
||||
|
||||
/**
|
||||
* Ref: https://matrix.org/docs/spec/identity_service/latest
|
||||
|
@ -95,4 +97,16 @@ internal interface IdentityAPI {
|
|||
@POST(NetworkConstants.URI_IDENTITY_PATH_V2 + "validate/{medium}/submitToken")
|
||||
suspend fun submitToken(@Path("medium") medium: String,
|
||||
@Body body: IdentityRequestOwnershipParams): SuccessResult
|
||||
|
||||
/**
|
||||
* https://matrix.org/docs/spec/identity_service/r0.3.0#post-matrix-identity-v2-sign-ed25519
|
||||
*
|
||||
* Have to rely on V1 for now
|
||||
*/
|
||||
@POST(NetworkConstants.URI_IDENTITY_PATH_V1 + "sign-ed25519")
|
||||
suspend fun signInvitationDetails(
|
||||
@Query("token") token: String,
|
||||
@Query("private_key") privateKey: String,
|
||||
@Query("mxid") mxid: String
|
||||
): SignInvitationResult
|
||||
}
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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.internal.session.identity
|
||||
|
||||
import dagger.Lazy
|
||||
import okhttp3.OkHttpClient
|
||||
import org.matrix.android.sdk.internal.di.Unauthenticated
|
||||
import org.matrix.android.sdk.internal.di.UserId
|
||||
import org.matrix.android.sdk.internal.network.RetrofitFactory
|
||||
import org.matrix.android.sdk.internal.session.identity.model.SignInvitationResult
|
||||
import org.matrix.android.sdk.internal.task.Task
|
||||
import javax.inject.Inject
|
||||
|
||||
internal interface Sign3pidInvitationTask : Task<Sign3pidInvitationTask.Params, SignInvitationResult> {
|
||||
data class Params(
|
||||
val token: String,
|
||||
val url: String,
|
||||
val privateKey: String
|
||||
)
|
||||
}
|
||||
|
||||
internal class DefaultSign3pidInvitationTask @Inject constructor(
|
||||
@Unauthenticated
|
||||
private val okHttpClient: Lazy<OkHttpClient>,
|
||||
private val retrofitFactory: RetrofitFactory,
|
||||
@UserId private val userId: String
|
||||
) : Sign3pidInvitationTask {
|
||||
|
||||
override suspend fun execute(params: Sign3pidInvitationTask.Params): SignInvitationResult {
|
||||
val identityAPI = retrofitFactory
|
||||
.create(okHttpClient, "https://${params.url}")
|
||||
.create(IdentityAPI::class.java)
|
||||
return identityAPI.signInvitationDetails(params.token, params.privateKey, userId)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* 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.internal.session.identity.model
|
||||
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class SignInvitationBody(
|
||||
/**The Matrix user ID of the user accepting the invitation.*/
|
||||
val mxid: String,
|
||||
/**The token from the call to store- invite..*/
|
||||
val token: String,
|
||||
/** The private key, encoded as Unpadded base64. */
|
||||
val private_key: String
|
||||
)
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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.internal.session.identity.model
|
||||
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class SignInvitationResult(
|
||||
/** The Matrix user ID of the user accepting the invitation.*/
|
||||
val mxid: String,
|
||||
/** The Matrix user ID of the user who sent the invitation.*/
|
||||
val sender: String,
|
||||
/**The token from the call to store- invite..*/
|
||||
val signatures: Map<String, *>,
|
||||
/** The token for the invitation */
|
||||
val token: String
|
||||
)
|
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
package org.matrix.android.sdk.internal.session.notification
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.Transformations
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import org.matrix.android.sdk.api.pushrules.Action
|
||||
import org.matrix.android.sdk.api.pushrules.PushRuleService
|
||||
|
@ -26,6 +28,7 @@ import org.matrix.android.sdk.api.pushrules.rest.PushRule
|
|||
import org.matrix.android.sdk.api.pushrules.rest.RuleSet
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.internal.database.mapper.PushRulesMapper
|
||||
import org.matrix.android.sdk.internal.database.model.PushRuleEntity
|
||||
import org.matrix.android.sdk.internal.database.model.PushRulesEntity
|
||||
import org.matrix.android.sdk.internal.database.query.where
|
||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||
|
@ -117,8 +120,8 @@ internal class DefaultPushRuleService @Inject constructor(
|
|||
updatePushRuleActionsTask.execute(UpdatePushRuleActionsTask.Params(kind, ruleId, enable, actions))
|
||||
}
|
||||
|
||||
override suspend fun removePushRule(kind: RuleKind, pushRule: PushRule) {
|
||||
removePushRuleTask.execute(RemovePushRuleTask.Params(kind, pushRule))
|
||||
override suspend fun removePushRule(kind: RuleKind, ruleId: String) {
|
||||
removePushRuleTask.execute(RemovePushRuleTask.Params(kind, ruleId))
|
||||
}
|
||||
|
||||
override fun removePushRuleListener(listener: PushRuleService.PushRuleListener) {
|
||||
|
@ -211,4 +214,19 @@ internal class DefaultPushRuleService @Inject constructor(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getKeywords(): LiveData<Set<String>> {
|
||||
// Keywords are all content rules that don't start with '.'
|
||||
val liveData = monarchy.findAllMappedWithChanges(
|
||||
{ realm ->
|
||||
PushRulesEntity.where(realm, RuleScope.GLOBAL, RuleSetKey.CONTENT)
|
||||
},
|
||||
{ result ->
|
||||
result.pushRules.map(PushRuleEntity::ruleId).filter { !it.startsWith(".") }
|
||||
}
|
||||
)
|
||||
return Transformations.map(liveData) { results ->
|
||||
results.firstOrNull().orEmpty().toSet()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,6 +78,10 @@ internal class ViaParameterFinder @Inject constructor(
|
|||
.toSet()
|
||||
}
|
||||
|
||||
// not used much for now but as per MSC1772
|
||||
// the via parameter of m.space.child must contain a via key which gives a list of candidate servers that can be used to join the room.
|
||||
// It is possible for the list of candidate servers and the list of authorised servers to diverge.
|
||||
// It may not be possible for a user to join a room if there's no overlap between these
|
||||
fun computeViaParamsForRestricted(roomId: String, max: Int): List<String> {
|
||||
val userThatCanInvite = roomGetterProvider.get().getRoom(roomId)
|
||||
?.getRoomMembers(roomMemberQueryParams { memberships = listOf(Membership.JOIN) })
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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.internal.session.pushers
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class EnabledBody(
|
||||
@Json(name = "enabled")
|
||||
val enabled: Boolean
|
||||
)
|
|
@ -19,7 +19,7 @@ import com.squareup.moshi.Json
|
|||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal class GetPushersResponse(
|
||||
internal data class GetPushersResponse(
|
||||
@Json(name = "pushers")
|
||||
val pushers: List<JsonPusher>? = null
|
||||
)
|
||||
|
|
|
@ -41,7 +41,7 @@ internal interface PushRulesApi {
|
|||
@PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "pushrules/global/{kind}/{ruleId}/enabled")
|
||||
suspend fun updateEnableRuleStatus(@Path("kind") kind: String,
|
||||
@Path("ruleId") ruleId: String,
|
||||
@Body enable: Boolean?)
|
||||
@Body enabledBody: EnabledBody)
|
||||
|
||||
/**
|
||||
* Update the ruleID action
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
package org.matrix.android.sdk.internal.session.pushers
|
||||
|
||||
import org.matrix.android.sdk.api.pushrules.RuleKind
|
||||
import org.matrix.android.sdk.api.pushrules.rest.PushRule
|
||||
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
|
||||
import org.matrix.android.sdk.internal.network.executeRequest
|
||||
import org.matrix.android.sdk.internal.task.Task
|
||||
|
@ -25,7 +24,7 @@ import javax.inject.Inject
|
|||
internal interface RemovePushRuleTask : Task<RemovePushRuleTask.Params, Unit> {
|
||||
data class Params(
|
||||
val kind: RuleKind,
|
||||
val pushRule: PushRule
|
||||
val ruleId: String
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -36,7 +35,7 @@ internal class DefaultRemovePushRuleTask @Inject constructor(
|
|||
|
||||
override suspend fun execute(params: RemovePushRuleTask.Params) {
|
||||
return executeRequest(globalErrorReceiver) {
|
||||
pushRulesApi.deleteRule(params.kind.value, params.pushRule.ruleId)
|
||||
pushRulesApi.deleteRule(params.kind.value, params.ruleId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,11 @@ internal class DefaultUpdatePushRuleActionsTask @Inject constructor(
|
|||
|
||||
override suspend fun execute(params: UpdatePushRuleActionsTask.Params) {
|
||||
executeRequest(globalErrorReceiver) {
|
||||
pushRulesApi.updateEnableRuleStatus(params.kind.value, params.ruleId, enable = params.enable)
|
||||
pushRulesApi.updateEnableRuleStatus(
|
||||
params.kind.value,
|
||||
params.ruleId,
|
||||
EnabledBody(params.enable)
|
||||
)
|
||||
}
|
||||
if (params.actions != null) {
|
||||
val body = mapOf("actions" to params.actions.toJson())
|
||||
|
|
|
@ -35,7 +35,11 @@ internal class DefaultUpdatePushRuleEnableStatusTask @Inject constructor(
|
|||
|
||||
override suspend fun execute(params: UpdatePushRuleEnableStatusTask.Params) {
|
||||
return executeRequest(globalErrorReceiver) {
|
||||
pushRulesApi.updateEnableRuleStatus(params.kind.value, params.pushRule.ruleId, params.enabled)
|
||||
pushRulesApi.updateEnableRuleStatus(
|
||||
params.kind.value,
|
||||
params.pushRule.ruleId,
|
||||
EnabledBody(params.enabled)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ import org.matrix.android.sdk.api.util.toOptional
|
|||
import org.matrix.android.sdk.internal.database.mapper.asDomain
|
||||
import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntityFields
|
||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||
import org.matrix.android.sdk.internal.session.identity.model.SignInvitationResult
|
||||
import org.matrix.android.sdk.internal.session.room.alias.DeleteRoomAliasTask
|
||||
import org.matrix.android.sdk.internal.session.room.alias.GetRoomIdByAliasTask
|
||||
import org.matrix.android.sdk.internal.session.room.alias.RoomAliasDescription
|
||||
|
@ -122,6 +123,12 @@ internal class DefaultRoomService @Inject constructor(
|
|||
joinRoomTask.execute(JoinRoomTask.Params(roomIdOrAlias, reason, viaServers))
|
||||
}
|
||||
|
||||
override suspend fun joinRoom(roomId: String,
|
||||
reason: String?,
|
||||
thirdPartySigned: SignInvitationResult) {
|
||||
joinRoomTask.execute(JoinRoomTask.Params(roomId, reason, thirdPartySigned = thirdPartySigned))
|
||||
}
|
||||
|
||||
override suspend fun markAllAsRead(roomIds: List<String>) {
|
||||
markAllRoomsReadTask.execute(MarkAllRoomsReadTask.Params(roomIds))
|
||||
}
|
||||
|
|
|
@ -159,7 +159,8 @@ internal interface RoomAPI {
|
|||
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/receipt/{receiptType}/{eventId}")
|
||||
suspend fun sendReceipt(@Path("roomId") roomId: String,
|
||||
@Path("receiptType") receiptType: String,
|
||||
@Path("eventId") eventId: String)
|
||||
@Path("eventId") eventId: String,
|
||||
@Body body: JsonDict = emptyMap())
|
||||
|
||||
/**
|
||||
* Invite a user to the given room.
|
||||
|
@ -253,7 +254,7 @@ internal interface RoomAPI {
|
|||
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "join/{roomIdOrAlias}")
|
||||
suspend fun join(@Path("roomIdOrAlias") roomIdOrAlias: String,
|
||||
@Query("server_name") viaServers: List<String>,
|
||||
@Body params: Map<String, String?>): JoinRoomResponse
|
||||
@Body params: JsonDict): JoinRoomResponse
|
||||
|
||||
/**
|
||||
* Leave the given room.
|
||||
|
|
|
@ -28,6 +28,8 @@ import org.matrix.android.sdk.api.session.space.SpaceService
|
|||
import org.matrix.android.sdk.internal.session.DefaultFileService
|
||||
import org.matrix.android.sdk.internal.session.SessionScope
|
||||
import org.matrix.android.sdk.internal.session.directory.DirectoryAPI
|
||||
import org.matrix.android.sdk.internal.session.identity.DefaultSign3pidInvitationTask
|
||||
import org.matrix.android.sdk.internal.session.identity.Sign3pidInvitationTask
|
||||
import org.matrix.android.sdk.internal.session.room.accountdata.DefaultUpdateRoomAccountDataTask
|
||||
import org.matrix.android.sdk.internal.session.room.accountdata.UpdateRoomAccountDataTask
|
||||
import org.matrix.android.sdk.internal.session.room.alias.AddRoomAliasTask
|
||||
|
@ -253,4 +255,7 @@ internal abstract class RoomModule {
|
|||
|
||||
@Binds
|
||||
abstract fun bindRoomVersionUpgradeTask(task: DefaultRoomVersionUpgradeTask): RoomVersionUpgradeTask
|
||||
|
||||
@Binds
|
||||
abstract fun bindSign3pidInvitationTask(task: DefaultSign3pidInvitationTask): Sign3pidInvitationTask
|
||||
}
|
||||
|
|
|
@ -123,7 +123,7 @@ internal class DefaultCreateRoomTask @Inject constructor(
|
|||
this.isDirect = true
|
||||
}
|
||||
}
|
||||
val directChats = directChatsHelper.getLocalUserAccount()
|
||||
val directChats = directChatsHelper.getLocalDirectMessages()
|
||||
updateUserAccountDataTask.execute(UpdateUserAccountDataTask.DirectChatParams(directMessages = directChats))
|
||||
}
|
||||
|
||||
|
|
|
@ -20,22 +20,30 @@ import io.realm.Realm
|
|||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomMemberContent
|
||||
import org.matrix.android.sdk.internal.di.UserId
|
||||
import org.matrix.android.sdk.internal.session.events.getFixedRoomMemberContent
|
||||
import org.matrix.android.sdk.internal.session.sync.SyncResponsePostTreatmentAggregator
|
||||
import org.matrix.android.sdk.internal.session.user.UserEntityFactory
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class RoomMemberEventHandler @Inject constructor() {
|
||||
internal class RoomMemberEventHandler @Inject constructor(
|
||||
@UserId private val myUserId: String
|
||||
) {
|
||||
|
||||
fun handle(realm: Realm, roomId: String, event: Event): Boolean {
|
||||
fun handle(realm: Realm, roomId: String, event: Event, aggregator: SyncResponsePostTreatmentAggregator? = null): Boolean {
|
||||
if (event.type != EventType.STATE_ROOM_MEMBER) {
|
||||
return false
|
||||
}
|
||||
val userId = event.stateKey ?: return false
|
||||
val roomMember = event.getFixedRoomMemberContent()
|
||||
return handle(realm, roomId, userId, roomMember)
|
||||
return handle(realm, roomId, userId, roomMember, aggregator)
|
||||
}
|
||||
|
||||
fun handle(realm: Realm, roomId: String, userId: String, roomMember: RoomMemberContent?): Boolean {
|
||||
fun handle(realm: Realm,
|
||||
roomId: String,
|
||||
userId: String,
|
||||
roomMember: RoomMemberContent?,
|
||||
aggregator: SyncResponsePostTreatmentAggregator? = null): Boolean {
|
||||
if (roomMember == null) {
|
||||
return false
|
||||
}
|
||||
|
@ -45,6 +53,14 @@ internal class RoomMemberEventHandler @Inject constructor() {
|
|||
val userEntity = UserEntityFactory.create(userId, roomMember)
|
||||
realm.insertOrUpdate(userEntity)
|
||||
}
|
||||
|
||||
// check whether this new room member event may be used to update the directs dictionary in account data
|
||||
// this is required to handle correctly invite by email in DM
|
||||
val mxId = roomMember.thirdPartyInvite?.signed?.mxid
|
||||
if (mxId != null && mxId != myUserId) {
|
||||
aggregator?.directChatsToCheck?.put(roomId, mxId)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ package org.matrix.android.sdk.internal.session.room.membership.joining
|
|||
import io.realm.Realm
|
||||
import io.realm.RealmConfiguration
|
||||
import kotlinx.coroutines.TimeoutCancellationException
|
||||
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||
import org.matrix.android.sdk.api.session.room.failure.JoinRoomFailure
|
||||
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
|
||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||
|
@ -29,6 +30,7 @@ import org.matrix.android.sdk.internal.database.query.where
|
|||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
|
||||
import org.matrix.android.sdk.internal.network.executeRequest
|
||||
import org.matrix.android.sdk.internal.session.identity.model.SignInvitationResult
|
||||
import org.matrix.android.sdk.internal.session.room.RoomAPI
|
||||
import org.matrix.android.sdk.internal.session.room.membership.RoomChangeMembershipStateDataSource
|
||||
import org.matrix.android.sdk.internal.session.room.read.SetReadMarkersTask
|
||||
|
@ -40,7 +42,8 @@ internal interface JoinRoomTask : Task<JoinRoomTask.Params, Unit> {
|
|||
data class Params(
|
||||
val roomIdOrAlias: String,
|
||||
val reason: String?,
|
||||
val viaServers: List<String> = emptyList()
|
||||
val viaServers: List<String> = emptyList(),
|
||||
val thirdPartySigned : SignInvitationResult? = null
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -59,12 +62,16 @@ internal class DefaultJoinRoomTask @Inject constructor(
|
|||
return
|
||||
}
|
||||
roomChangeMembershipStateDataSource.updateState(params.roomIdOrAlias, ChangeMembershipState.Joining)
|
||||
val extraParams = mutableMapOf<String, Any>().apply {
|
||||
params.reason?.let { this["reason"] = it }
|
||||
params.thirdPartySigned?.let { this["third_party_signed"] = it.toContent() }
|
||||
}
|
||||
val joinRoomResponse = try {
|
||||
executeRequest(globalErrorReceiver) {
|
||||
roomAPI.join(
|
||||
roomIdOrAlias = params.roomIdOrAlias,
|
||||
viaServers = params.viaServers.take(3),
|
||||
params = mapOf("reason" to params.reason)
|
||||
params = extraParams
|
||||
)
|
||||
}
|
||||
} catch (failure: Throwable) {
|
||||
|
|
|
@ -45,7 +45,7 @@ internal class DefaultSetRoomNotificationStateTask @Inject constructor(@SessionD
|
|||
PushRuleEntity.where(it, scope = RuleScope.GLOBAL, ruleId = params.roomId).findFirst()?.toRoomPushRule()
|
||||
}
|
||||
if (currentRoomPushRule != null) {
|
||||
removePushRuleTask.execute(RemovePushRuleTask.Params(currentRoomPushRule.kind, currentRoomPushRule.rule))
|
||||
removePushRuleTask.execute(RemovePushRuleTask.Params(currentRoomPushRule.kind, currentRoomPushRule.rule.ruleId))
|
||||
}
|
||||
val newRoomPushRule = params.roomNotificationState.toRoomPushRule(params.roomId)
|
||||
if (newRoomPushRule != null) {
|
||||
|
|
|
@ -43,7 +43,7 @@ internal class RoomChildRelationInfo(
|
|||
data class SpaceChildInfo(
|
||||
val roomId: String,
|
||||
val order: String?,
|
||||
val autoJoin: Boolean,
|
||||
// val autoJoin: Boolean,
|
||||
val viaServers: List<String>
|
||||
)
|
||||
|
||||
|
@ -71,7 +71,7 @@ internal class RoomChildRelationInfo(
|
|||
SpaceChildInfo(
|
||||
roomId = it.stateKey,
|
||||
order = scc.validOrder(),
|
||||
autoJoin = scc.autoJoin ?: false,
|
||||
// autoJoin = scc.autoJoin ?: false,
|
||||
viaServers = via
|
||||
)
|
||||
}
|
||||
|
|
|
@ -185,7 +185,7 @@ internal class DefaultSendService @AssistedInject constructor(
|
|||
name = messageContent.body,
|
||||
queryUri = Uri.parse(messageContent.url),
|
||||
type = ContentAttachmentData.Type.AUDIO,
|
||||
waveform = messageContent.audioWaveformInfo?.waveform
|
||||
waveform = messageContent.audioWaveformInfo?.waveform?.filterNotNull()
|
||||
)
|
||||
localEchoRepository.updateSendState(localEcho.eventId, roomId, SendState.UNSENT)
|
||||
internalSendMedia(listOf(localEcho.root), attachmentData, true)
|
||||
|
|
|
@ -77,7 +77,7 @@ internal class LocalEchoRepository @Inject constructor(@SessionDatabase private
|
|||
val timelineEvent = timelineEventMapper.map(timelineEventEntity)
|
||||
timelineInput.onLocalEchoCreated(roomId = roomId, timelineEvent = timelineEvent)
|
||||
taskExecutor.executorScope.asyncTransaction(monarchy) { realm ->
|
||||
val eventInsertEntity = EventInsertEntity(event.eventId, event.type).apply {
|
||||
val eventInsertEntity = EventInsertEntity(event.eventId, event.type, canBeProcessed = true).apply {
|
||||
this.insertType = EventInsertType.LOCAL_ECHO
|
||||
}
|
||||
realm.insert(eventInsertEntity)
|
||||
|
|
|
@ -182,7 +182,7 @@ internal class DefaultStateService @AssistedInject constructor(@Assisted private
|
|||
override suspend fun setJoinRuleRestricted(allowList: List<String>) {
|
||||
// we need to compute correct via parameters and check if PL are correct
|
||||
val allowEntries = allowList.map { spaceId ->
|
||||
RoomJoinRulesAllowEntry(spaceId, viaParameterFinder.computeViaParamsForRestricted(spaceId, 3))
|
||||
RoomJoinRulesAllowEntry.restrictedToRoom(spaceId)
|
||||
}
|
||||
updateJoinRule(RoomJoinRules.RESTRICTED, null, allowEntries)
|
||||
}
|
||||
|
|
|
@ -234,7 +234,7 @@ internal class RoomSummaryUpdater @Inject constructor(
|
|||
this.childRoomId = child.roomId
|
||||
this.childSummaryEntity = RoomSummaryEntity.where(realm, child.roomId).findFirst()
|
||||
this.order = child.order
|
||||
this.autoJoin = child.autoJoin
|
||||
// this.autoJoin = child.autoJoin
|
||||
this.viaServers.addAll(child.viaServers)
|
||||
}
|
||||
)
|
||||
|
|
|
@ -106,7 +106,8 @@ internal class TimelineEventDecryptor @Inject constructor(
|
|||
val result = cryptoService.decryptEvent(request.event, timelineId)
|
||||
Timber.v("Successfully decrypted event ${event.eventId}")
|
||||
realm.executeTransaction {
|
||||
EventEntity.where(it, eventId = event.eventId ?: "")
|
||||
val eventId = event.eventId ?: ""
|
||||
EventEntity.where(it, eventId = eventId)
|
||||
.findFirst()
|
||||
?.setDecryptionResult(result)
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import org.matrix.android.sdk.api.session.events.model.EventType
|
|||
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.room.Room
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.session.space.Space
|
||||
import org.matrix.android.sdk.api.session.space.model.SpaceChildContent
|
||||
|
@ -50,16 +51,24 @@ internal class DefaultSpace(
|
|||
override suspend fun addChildren(roomId: String,
|
||||
viaServers: List<String>?,
|
||||
order: String?,
|
||||
autoJoin: Boolean,
|
||||
// autoJoin: Boolean,
|
||||
suggested: Boolean?) {
|
||||
// Find best via
|
||||
val bestVia = viaServers
|
||||
?: (spaceSummaryDataSource.getRoomSummary(roomId)
|
||||
?.takeIf { it.joinRules == RoomJoinRules.RESTRICTED }
|
||||
?.let {
|
||||
// for restricted room, best to take via from users that can invite in the
|
||||
// child room
|
||||
viaParameterFinder.computeViaParamsForRestricted(roomId, 3)
|
||||
}
|
||||
?: viaParameterFinder.computeViaParams(roomId, 3))
|
||||
|
||||
room.sendStateEvent(
|
||||
eventType = EventType.STATE_SPACE_CHILD,
|
||||
stateKey = roomId,
|
||||
body = SpaceChildContent(
|
||||
via = viaServers ?: viaParameterFinder.computeViaParams(roomId, 3),
|
||||
autoJoin = autoJoin,
|
||||
via = bestVia,
|
||||
order = order,
|
||||
suggested = suggested
|
||||
).toContent()
|
||||
|
@ -80,7 +89,7 @@ internal class DefaultSpace(
|
|||
body = SpaceChildContent(
|
||||
order = null,
|
||||
via = null,
|
||||
autoJoin = null,
|
||||
// autoJoin = null,
|
||||
suggested = null
|
||||
).toContent()
|
||||
)
|
||||
|
@ -105,35 +114,35 @@ internal class DefaultSpace(
|
|||
body = SpaceChildContent(
|
||||
order = order,
|
||||
via = existing.via,
|
||||
autoJoin = existing.autoJoin,
|
||||
// autoJoin = existing.autoJoin,
|
||||
suggested = existing.suggested
|
||||
).toContent()
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun setChildrenAutoJoin(roomId: String, autoJoin: Boolean) {
|
||||
val existing = room.getStateEvents(setOf(EventType.STATE_SPACE_CHILD), QueryStringValue.Equals(roomId))
|
||||
.firstOrNull()
|
||||
?.content.toModel<SpaceChildContent>()
|
||||
?: throw IllegalArgumentException("$roomId is not a child of this space")
|
||||
|
||||
if (existing.autoJoin == autoJoin) {
|
||||
// nothing to do?
|
||||
return
|
||||
}
|
||||
|
||||
// edit state event and set via to null
|
||||
room.sendStateEvent(
|
||||
eventType = EventType.STATE_SPACE_CHILD,
|
||||
stateKey = roomId,
|
||||
body = SpaceChildContent(
|
||||
order = existing.order,
|
||||
via = existing.via,
|
||||
autoJoin = autoJoin,
|
||||
suggested = existing.suggested
|
||||
).toContent()
|
||||
)
|
||||
}
|
||||
// override suspend fun setChildrenAutoJoin(roomId: String, autoJoin: Boolean) {
|
||||
// val existing = room.getStateEvents(setOf(EventType.STATE_SPACE_CHILD), QueryStringValue.Equals(roomId))
|
||||
// .firstOrNull()
|
||||
// ?.content.toModel<SpaceChildContent>()
|
||||
// ?: throw IllegalArgumentException("$roomId is not a child of this space")
|
||||
//
|
||||
// if (existing.autoJoin == autoJoin) {
|
||||
// // nothing to do?
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// // edit state event and set via to null
|
||||
// room.sendStateEvent(
|
||||
// eventType = EventType.STATE_SPACE_CHILD,
|
||||
// stateKey = roomId,
|
||||
// body = SpaceChildContent(
|
||||
// order = existing.order,
|
||||
// via = existing.via,
|
||||
// autoJoin = autoJoin,
|
||||
// suggested = existing.suggested
|
||||
// ).toContent()
|
||||
// )
|
||||
// }
|
||||
|
||||
override suspend fun setChildrenSuggested(roomId: String, suggested: Boolean) {
|
||||
val existing = room.getStateEvents(setOf(EventType.STATE_SPACE_CHILD), QueryStringValue.Equals(roomId))
|
||||
|
@ -152,7 +161,7 @@ internal class DefaultSpace(
|
|||
body = SpaceChildContent(
|
||||
order = existing.order,
|
||||
via = existing.via,
|
||||
autoJoin = existing.autoJoin,
|
||||
// autoJoin = existing.autoJoin,
|
||||
suggested = suggested
|
||||
).toContent()
|
||||
)
|
||||
|
|
|
@ -19,6 +19,7 @@ package org.matrix.android.sdk.internal.session.space
|
|||
import android.net.Uri
|
||||
import androidx.lifecycle.LiveData
|
||||
import org.matrix.android.sdk.api.query.QueryStringValue
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
|
@ -27,6 +28,7 @@ import org.matrix.android.sdk.api.session.room.model.Membership
|
|||
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
|
||||
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomPreset
|
||||
|
@ -34,6 +36,7 @@ import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
|
|||
import org.matrix.android.sdk.api.session.space.CreateSpaceParams
|
||||
import org.matrix.android.sdk.api.session.space.JoinSpaceResult
|
||||
import org.matrix.android.sdk.api.session.space.Space
|
||||
import org.matrix.android.sdk.api.session.space.SpaceHierarchyData
|
||||
import org.matrix.android.sdk.api.session.space.SpaceService
|
||||
import org.matrix.android.sdk.api.session.space.SpaceSummaryQueryParams
|
||||
import org.matrix.android.sdk.api.session.space.model.SpaceChildContent
|
||||
|
@ -108,53 +111,65 @@ internal class DefaultSpaceService @Inject constructor(
|
|||
|
||||
override suspend fun querySpaceChildren(spaceId: String,
|
||||
suggestedOnly: Boolean?,
|
||||
autoJoinedOnly: Boolean?): Pair<RoomSummary, List<SpaceChildInfo>> {
|
||||
return resolveSpaceInfoTask.execute(ResolveSpaceInfoTask.Params.withId(spaceId, suggestedOnly, autoJoinedOnly)).let { response ->
|
||||
limit: Int?,
|
||||
from: String?,
|
||||
knownStateList: List<Event>?): SpaceHierarchyData {
|
||||
return resolveSpaceInfoTask.execute(
|
||||
ResolveSpaceInfoTask.Params(
|
||||
spaceId = spaceId, limit = limit, maxDepth = 1, from = from, suggestedOnly = suggestedOnly
|
||||
)
|
||||
).let { response ->
|
||||
val spaceDesc = response.rooms?.firstOrNull { it.roomId == spaceId }
|
||||
Pair(
|
||||
first = RoomSummary(
|
||||
roomId = spaceDesc?.roomId ?: spaceId,
|
||||
roomType = spaceDesc?.roomType,
|
||||
name = spaceDesc?.name ?: "",
|
||||
displayName = spaceDesc?.name ?: "",
|
||||
topic = spaceDesc?.topic ?: "",
|
||||
joinedMembersCount = spaceDesc?.numJoinedMembers,
|
||||
avatarUrl = spaceDesc?.avatarUrl ?: "",
|
||||
encryptionEventTs = null,
|
||||
typingUsers = emptyList(),
|
||||
isEncrypted = false,
|
||||
flattenParentIds = emptyList()
|
||||
),
|
||||
second = response.rooms
|
||||
?.filter { it.roomId != spaceId }
|
||||
?.flatMap { childSummary ->
|
||||
response.events
|
||||
?.filter { it.stateKey == childSummary.roomId && it.type == EventType.STATE_SPACE_CHILD }
|
||||
?.mapNotNull { childStateEv ->
|
||||
// create a child entry for everytime this room is the child of a space
|
||||
// beware that a room could appear then twice in this list
|
||||
childStateEv.content.toModel<SpaceChildContent>()?.let { childStateEvContent ->
|
||||
SpaceChildInfo(
|
||||
childRoomId = childSummary.roomId,
|
||||
isKnown = true,
|
||||
roomType = childSummary.roomType,
|
||||
name = childSummary.name,
|
||||
topic = childSummary.topic,
|
||||
avatarUrl = childSummary.avatarUrl,
|
||||
order = childStateEvContent.order,
|
||||
autoJoin = childStateEvContent.autoJoin ?: false,
|
||||
viaServers = childStateEvContent.via.orEmpty(),
|
||||
activeMemberCount = childSummary.numJoinedMembers,
|
||||
parentRoomId = childStateEv.roomId,
|
||||
suggested = childStateEvContent.suggested,
|
||||
canonicalAlias = childSummary.canonicalAlias,
|
||||
aliases = childSummary.aliases,
|
||||
worldReadable = childSummary.worldReadable
|
||||
)
|
||||
}
|
||||
}.orEmpty()
|
||||
}
|
||||
.orEmpty()
|
||||
val root = RoomSummary(
|
||||
roomId = spaceDesc?.roomId ?: spaceId,
|
||||
roomType = spaceDesc?.roomType,
|
||||
name = spaceDesc?.name ?: "",
|
||||
displayName = spaceDesc?.name ?: "",
|
||||
topic = spaceDesc?.topic ?: "",
|
||||
joinedMembersCount = spaceDesc?.numJoinedMembers,
|
||||
avatarUrl = spaceDesc?.avatarUrl ?: "",
|
||||
encryptionEventTs = null,
|
||||
typingUsers = emptyList(),
|
||||
isEncrypted = false,
|
||||
flattenParentIds = emptyList(),
|
||||
canonicalAlias = spaceDesc?.canonicalAlias,
|
||||
joinRules = RoomJoinRules.PUBLIC.takeIf { spaceDesc?.worldReadable == true }
|
||||
)
|
||||
val children = response.rooms
|
||||
?.filter { it.roomId != spaceId }
|
||||
?.flatMap { childSummary ->
|
||||
(spaceDesc?.childrenState ?: knownStateList)
|
||||
?.filter { it.stateKey == childSummary.roomId && it.type == EventType.STATE_SPACE_CHILD }
|
||||
?.mapNotNull { childStateEv ->
|
||||
// create a child entry for everytime this room is the child of a space
|
||||
// beware that a room could appear then twice in this list
|
||||
childStateEv.content.toModel<SpaceChildContent>()?.let { childStateEvContent ->
|
||||
SpaceChildInfo(
|
||||
childRoomId = childSummary.roomId,
|
||||
isKnown = true,
|
||||
roomType = childSummary.roomType,
|
||||
name = childSummary.name,
|
||||
topic = childSummary.topic,
|
||||
avatarUrl = childSummary.avatarUrl,
|
||||
order = childStateEvContent.order,
|
||||
// autoJoin = childStateEvContent.autoJoin ?: false,
|
||||
viaServers = childStateEvContent.via.orEmpty(),
|
||||
activeMemberCount = childSummary.numJoinedMembers,
|
||||
parentRoomId = childStateEv.roomId,
|
||||
suggested = childStateEvContent.suggested,
|
||||
canonicalAlias = childSummary.canonicalAlias,
|
||||
aliases = childSummary.aliases,
|
||||
worldReadable = childSummary.worldReadable
|
||||
)
|
||||
}
|
||||
}.orEmpty()
|
||||
}
|
||||
.orEmpty()
|
||||
SpaceHierarchyData(
|
||||
rootSummary = root,
|
||||
children = children,
|
||||
childrenState = spaceDesc?.childrenState.orEmpty(),
|
||||
nextToken = response.nextBatch
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@ package org.matrix.android.sdk.internal.session.space
|
|||
import io.realm.RealmConfiguration
|
||||
import kotlinx.coroutines.TimeoutCancellationException
|
||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomType
|
||||
import org.matrix.android.sdk.api.session.space.JoinSpaceResult
|
||||
import org.matrix.android.sdk.internal.database.awaitNotEmptyResult
|
||||
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
|
||||
|
@ -84,39 +83,39 @@ internal class DefaultJoinSpaceTask @Inject constructor(
|
|||
// after that i should have the children (? do I need to paginate to get state)
|
||||
val summary = roomSummaryDataSource.getSpaceSummary(params.roomIdOrAlias)
|
||||
Timber.v("## Space: Found space summary Name:[${summary?.name}] children: ${summary?.spaceChildren?.size}")
|
||||
summary?.spaceChildren?.forEach {
|
||||
// summary?.spaceChildren?.forEach {
|
||||
// val childRoomSummary = it.roomSummary ?: return@forEach
|
||||
Timber.v("## Space: Processing child :[${it.childRoomId}] autoJoin:${it.autoJoin}")
|
||||
if (it.autoJoin) {
|
||||
// I should try to join as well
|
||||
if (it.roomType == RoomType.SPACE) {
|
||||
// recursively join auto-joined child of this space?
|
||||
when (val subspaceJoinResult = execute(JoinSpaceTask.Params(it.childRoomId, null, it.viaServers))) {
|
||||
JoinSpaceResult.Success -> {
|
||||
// nop
|
||||
}
|
||||
is JoinSpaceResult.Fail -> {
|
||||
errors[it.childRoomId] = subspaceJoinResult.error
|
||||
}
|
||||
is JoinSpaceResult.PartialSuccess -> {
|
||||
errors.putAll(subspaceJoinResult.failedRooms)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
Timber.v("## Space: Joining room child ${it.childRoomId}")
|
||||
joinRoomTask.execute(JoinRoomTask.Params(
|
||||
roomIdOrAlias = it.childRoomId,
|
||||
reason = "Auto-join parent space",
|
||||
viaServers = it.viaServers
|
||||
))
|
||||
} catch (failure: Throwable) {
|
||||
errors[it.childRoomId] = failure
|
||||
Timber.e("## Space: Failed to join room child ${it.childRoomId}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Timber.v("## Space: Processing child :[${it.childRoomId}] suggested:${it.suggested}")
|
||||
// if (it.autoJoin) {
|
||||
// // I should try to join as well
|
||||
// if (it.roomType == RoomType.SPACE) {
|
||||
// // recursively join auto-joined child of this space?
|
||||
// when (val subspaceJoinResult = execute(JoinSpaceTask.Params(it.childRoomId, null, it.viaServers))) {
|
||||
// JoinSpaceResult.Success -> {
|
||||
// // nop
|
||||
// }
|
||||
// is JoinSpaceResult.Fail -> {
|
||||
// errors[it.childRoomId] = subspaceJoinResult.error
|
||||
// }
|
||||
// is JoinSpaceResult.PartialSuccess -> {
|
||||
// errors.putAll(subspaceJoinResult.failedRooms)
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// try {
|
||||
// Timber.v("## Space: Joining room child ${it.childRoomId}")
|
||||
// joinRoomTask.execute(JoinRoomTask.Params(
|
||||
// roomIdOrAlias = it.childRoomId,
|
||||
// reason = "Auto-join parent space",
|
||||
// viaServers = it.viaServers
|
||||
// ))
|
||||
// } catch (failure: Throwable) {
|
||||
// errors[it.childRoomId] = failure
|
||||
// Timber.e("## Space: Failed to join room child ${it.childRoomId}")
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
return if (errors.isEmpty()) {
|
||||
JoinSpaceResult.Success
|
||||
|
|
|
@ -24,24 +24,12 @@ import javax.inject.Inject
|
|||
internal interface ResolveSpaceInfoTask : Task<ResolveSpaceInfoTask.Params, SpacesResponse> {
|
||||
data class Params(
|
||||
val spaceId: String,
|
||||
val maxRoomPerSpace: Int?,
|
||||
val limit: Int,
|
||||
val batchToken: String?,
|
||||
val suggestedOnly: Boolean?,
|
||||
val autoJoinOnly: Boolean?
|
||||
) {
|
||||
companion object {
|
||||
fun withId(spaceId: String, suggestedOnly: Boolean?, autoJoinOnly: Boolean?) =
|
||||
Params(
|
||||
spaceId = spaceId,
|
||||
maxRoomPerSpace = 10,
|
||||
limit = 20,
|
||||
batchToken = null,
|
||||
suggestedOnly = suggestedOnly,
|
||||
autoJoinOnly = autoJoinOnly
|
||||
)
|
||||
}
|
||||
}
|
||||
val limit: Int?,
|
||||
val maxDepth: Int?,
|
||||
val from: String?,
|
||||
val suggestedOnly: Boolean?
|
||||
// val autoJoinOnly: Boolean?
|
||||
)
|
||||
}
|
||||
|
||||
internal class DefaultResolveSpaceInfoTask @Inject constructor(
|
||||
|
@ -49,15 +37,13 @@ internal class DefaultResolveSpaceInfoTask @Inject constructor(
|
|||
private val globalErrorReceiver: GlobalErrorReceiver
|
||||
) : ResolveSpaceInfoTask {
|
||||
override suspend fun execute(params: ResolveSpaceInfoTask.Params): SpacesResponse {
|
||||
val body = SpaceSummaryParams(
|
||||
maxRoomPerSpace = params.maxRoomPerSpace,
|
||||
limit = params.limit,
|
||||
batch = params.batchToken ?: "",
|
||||
autoJoinedOnly = params.autoJoinOnly,
|
||||
suggestedOnly = params.suggestedOnly
|
||||
)
|
||||
return executeRequest(globalErrorReceiver) {
|
||||
spaceApi.getSpaces(params.spaceId, body)
|
||||
spaceApi.getSpaceHierarchy(
|
||||
spaceId = params.spaceId,
|
||||
suggestedOnly = params.suggestedOnly,
|
||||
limit = params.limit,
|
||||
maxDepth = params.maxDepth,
|
||||
from = params.from)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue