mirror of
https://github.com/vector-im/riotX-android
synced 2025-10-05 15:52:47 +02:00
Merge branch 'release/1.6.44' into main
This commit is contained in:
2
.github/workflows/post-pr.yml
vendored
2
.github/workflows/post-pr.yml
vendored
@@ -31,7 +31,7 @@ jobs:
|
|||||||
ui-tests:
|
ui-tests:
|
||||||
name: UI Tests (Synapse)
|
name: UI Tests (Synapse)
|
||||||
needs: should-i-run
|
needs: should-i-run
|
||||||
runs-on: buildjet-4vcpu-ubuntu-2204
|
runs-on: ubuntu-22.04
|
||||||
timeout-minutes: 90 # We might need to increase it if the time for tests grows
|
timeout-minutes: 90 # We might need to increase it if the time for tests grows
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
|
8
.github/workflows/tests.yml
vendored
8
.github/workflows/tests.yml
vendored
@@ -1,11 +1,7 @@
|
|||||||
name: Test
|
name: Test
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request: { }
|
workflow_dispatch:
|
||||||
push:
|
|
||||||
branches: [ main, develop ]
|
|
||||||
paths-ignore:
|
|
||||||
- '.github/**'
|
|
||||||
|
|
||||||
# Enrich gradle.properties for CI/CD
|
# Enrich gradle.properties for CI/CD
|
||||||
env:
|
env:
|
||||||
@@ -15,7 +11,7 @@ env:
|
|||||||
jobs:
|
jobs:
|
||||||
tests:
|
tests:
|
||||||
name: Runs all tests
|
name: Runs all tests
|
||||||
runs-on: buildjet-4vcpu-ubuntu-2204
|
runs-on: ubuntu-22.04
|
||||||
timeout-minutes: 90 # We might need to increase it if the time for tests grows
|
timeout-minutes: 90 # We might need to increase it if the time for tests grows
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
|
11
CHANGES.md
11
CHANGES.md
@@ -1,3 +1,14 @@
|
|||||||
|
Changes in Element v1.6.44 (2025-08-06)
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
Other changes
|
||||||
|
-------------
|
||||||
|
- Hide the "Manually Verify by Text" option behind devtool flag. ([#9058](https://github.com/element-hq/element-android/issues/9058))
|
||||||
|
- Change targetSdk to 35. ([#9051](https://github.com/element-hq/element-android/issues/9051))
|
||||||
|
- Support room v12. ([#9065](https://github.com/element-hq/element-android/issues/9065))
|
||||||
|
- Fix window insets. ([#9067](https://github.com/element-hq/element-android/issues/9067))
|
||||||
|
|
||||||
|
|
||||||
Changes in Element v1.6.42 (2025-06-10)
|
Changes in Element v1.6.42 (2025-06-10)
|
||||||
=======================================
|
=======================================
|
||||||
|
|
||||||
|
@@ -7,9 +7,7 @@
|
|||||||
|
|
||||||
# Element Android
|
# Element Android
|
||||||
|
|
||||||
Element Android is an Android Matrix Client provided by [Element](https://element.io/). The app can be run on every Android devices with Android OS Lollipop and more (API 21).
|
Element Classic Android is a previous-generation [Matrix](https://matrix.org/) client provided by [Element](https://element.io/). The app can be run on every Android devices with Android OS Lollipop and more (API 21). This client is still supported and receives security updates but no new features or usability enhancements are made. It is recommended to use [Element X](https://github.com/element-hq/element-x-android) that is the next-generation mobile app.
|
||||||
|
|
||||||
It is a total rewrite of [Riot-Android](https://github.com/element-hq/riot-android) with a new user experience.
|
|
||||||
|
|
||||||
[<img src="resources/img/google-play-badge.png" alt="Get it on Google Play" height="60">](https://play.google.com/store/apps/details?id=im.vector.app)
|
[<img src="resources/img/google-play-badge.png" alt="Get it on Google Play" height="60">](https://play.google.com/store/apps/details?id=im.vector.app)
|
||||||
[<img src="resources/img/f-droid-badge.png" alt="Get it on F-Droid" height="60">](https://f-droid.org/app/im.vector.app)
|
[<img src="resources/img/f-droid-badge.png" alt="Get it on F-Droid" height="60">](https://f-droid.org/app/im.vector.app)
|
||||||
|
@@ -1,13 +1,13 @@
|
|||||||
ext.versions = [
|
ext.versions = [
|
||||||
'minSdk' : 21,
|
'minSdk' : 21,
|
||||||
'compileSdk' : 34,
|
'compileSdk' : 35,
|
||||||
'targetSdk' : 34,
|
'targetSdk' : 35,
|
||||||
'sourceCompat' : JavaVersion.VERSION_21,
|
'sourceCompat' : JavaVersion.VERSION_21,
|
||||||
'targetCompat' : JavaVersion.VERSION_21,
|
'targetCompat' : JavaVersion.VERSION_21,
|
||||||
'jvmTarget' : "21",
|
'jvmTarget' : "21",
|
||||||
]
|
]
|
||||||
|
|
||||||
def gradle = "8.4.2"
|
def gradle = "8.11.0"
|
||||||
// Ref: https://kotlinlang.org/releases.html
|
// Ref: https://kotlinlang.org/releases.html
|
||||||
def kotlin = "1.9.24"
|
def kotlin = "1.9.24"
|
||||||
def kotlinCoroutines = "1.8.1"
|
def kotlinCoroutines = "1.8.1"
|
||||||
@@ -27,7 +27,7 @@ def bigImageViewer = "1.8.1"
|
|||||||
def jjwt = "0.11.5"
|
def jjwt = "0.11.5"
|
||||||
def vanniktechEmoji = "0.16.0"
|
def vanniktechEmoji = "0.16.0"
|
||||||
def sentry = "6.18.1"
|
def sentry = "6.18.1"
|
||||||
def fragment = "1.8.1"
|
def fragment = "1.8.6"
|
||||||
// Testing
|
// Testing
|
||||||
def mockk = "1.13.11"
|
def mockk = "1.13.11"
|
||||||
def espresso = "3.6.1"
|
def espresso = "3.6.1"
|
||||||
@@ -50,7 +50,7 @@ ext.libs = [
|
|||||||
'activity' : "androidx.activity:activity-ktx:1.9.0",
|
'activity' : "androidx.activity:activity-ktx:1.9.0",
|
||||||
'appCompat' : "androidx.appcompat:appcompat:1.7.0",
|
'appCompat' : "androidx.appcompat:appcompat:1.7.0",
|
||||||
'biometric' : "androidx.biometric:biometric:1.1.0",
|
'biometric' : "androidx.biometric:biometric:1.1.0",
|
||||||
'core' : "androidx.core:core-ktx:1.10.1",
|
'core' : "androidx.core:core-ktx:1.16.0",
|
||||||
'recyclerview' : "androidx.recyclerview:recyclerview:1.3.0",
|
'recyclerview' : "androidx.recyclerview:recyclerview:1.3.0",
|
||||||
'exifinterface' : "androidx.exifinterface:exifinterface:1.3.6",
|
'exifinterface' : "androidx.exifinterface:exifinterface:1.3.6",
|
||||||
'fragmentKtx' : "androidx.fragment:fragment-ktx:$fragment",
|
'fragmentKtx' : "androidx.fragment:fragment-ktx:$fragment",
|
||||||
|
@@ -213,6 +213,7 @@ ext.groups = [
|
|||||||
'org.jitsi',
|
'org.jitsi',
|
||||||
'org.json',
|
'org.json',
|
||||||
'org.jsoup',
|
'org.jsoup',
|
||||||
|
'org.jspecify',
|
||||||
'org.junit',
|
'org.junit',
|
||||||
'org.junit.jupiter',
|
'org.junit.jupiter',
|
||||||
'org.junit.platform',
|
'org.junit.platform',
|
||||||
|
2
fastlane/metadata/android/en-US/changelogs/40106440.txt
Normal file
2
fastlane/metadata/android/en-US/changelogs/40106440.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
Main changes in this version: support room v12.
|
||||||
|
Full changelog: https://github.com/element-hq/element-android/releases
|
@@ -42,4 +42,4 @@ signing.element.nightly.keyPassword=Secret
|
|||||||
|
|
||||||
# Customise the Lint version to use a more recent version than the one bundled with AGP
|
# Customise the Lint version to use a more recent version than the one bundled with AGP
|
||||||
# https://googlesamples.github.io/android-custom-lint-rules/usage/newer-lint.md.html
|
# https://googlesamples.github.io/android-custom-lint-rules/usage/newer-lint.md.html
|
||||||
android.experimental.lint.version=8.6.0-alpha08
|
android.experimental.lint.version=8.12.0-alpha08
|
||||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,7 +1,7 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionSha256Sum=f8b4f4772d302c8ff580bc40d0f56e715de69b163546944f787c87abf209c961
|
distributionSha256Sum=bd71102213493060956ec229d946beee57158dbd89d0e62b91bca0fa2c5f3531
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
37
gradlew
vendored
37
gradlew
vendored
@@ -15,6 +15,8 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
#
|
#
|
||||||
@@ -55,7 +57,7 @@
|
|||||||
# Darwin, MinGW, and NonStop.
|
# Darwin, MinGW, and NonStop.
|
||||||
#
|
#
|
||||||
# (3) This script is generated from the Groovy template
|
# (3) This script is generated from the Groovy template
|
||||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
# within the Gradle project.
|
# within the Gradle project.
|
||||||
#
|
#
|
||||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
@@ -83,10 +85,8 @@ done
|
|||||||
# This is normally unused
|
# This is normally unused
|
||||||
# shellcheck disable=SC2034
|
# shellcheck disable=SC2034
|
||||||
APP_BASE_NAME=${0##*/}
|
APP_BASE_NAME=${0##*/}
|
||||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||||
|
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||||
# 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.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD=maximum
|
MAX_FD=maximum
|
||||||
@@ -114,7 +114,7 @@ case "$( uname )" in #(
|
|||||||
NONSTOP* ) nonstop=true ;;
|
NONSTOP* ) nonstop=true ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
CLASSPATH="\\\"\\\""
|
||||||
|
|
||||||
|
|
||||||
# Determine the Java command to use to start the JVM.
|
# Determine the Java command to use to start the JVM.
|
||||||
@@ -133,10 +133,13 @@ location of your Java installation."
|
|||||||
fi
|
fi
|
||||||
else
|
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.
|
if ! command -v java >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
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
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
location of your Java installation."
|
location of your Java installation."
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Increase the maximum file descriptors if we can.
|
# Increase the maximum file descriptors if we can.
|
||||||
@@ -144,7 +147,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
|||||||
case $MAX_FD in #(
|
case $MAX_FD in #(
|
||||||
max*)
|
max*)
|
||||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||||
# shellcheck disable=SC3045
|
# shellcheck disable=SC2039,SC3045
|
||||||
MAX_FD=$( ulimit -H -n ) ||
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
warn "Could not query maximum file descriptor limit"
|
warn "Could not query maximum file descriptor limit"
|
||||||
esac
|
esac
|
||||||
@@ -152,7 +155,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
|||||||
'' | soft) :;; #(
|
'' | soft) :;; #(
|
||||||
*)
|
*)
|
||||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||||
# shellcheck disable=SC3045
|
# shellcheck disable=SC2039,SC3045
|
||||||
ulimit -n "$MAX_FD" ||
|
ulimit -n "$MAX_FD" ||
|
||||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
esac
|
esac
|
||||||
@@ -197,16 +200,20 @@ if "$cygwin" || "$msys" ; then
|
|||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Collect all arguments for the java command;
|
|
||||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
# shell script including quotes and variable substitutions, so put them in
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
# double quotes to make sure that they get re-expanded; and
|
|
||||||
# * put everything else in single quotes, so that it's not re-expanded.
|
# Collect all arguments for the java command:
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||||
|
# and any embedded shellness will be escaped.
|
||||||
|
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||||
|
# treated as '${Hostname}' itself on the command line.
|
||||||
|
|
||||||
set -- \
|
set -- \
|
||||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
-classpath "$CLASSPATH" \
|
-classpath "$CLASSPATH" \
|
||||||
org.gradle.wrapper.GradleWrapperMain \
|
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||||
"$@"
|
"$@"
|
||||||
|
|
||||||
# Stop when "xargs" is not available.
|
# Stop when "xargs" is not available.
|
||||||
|
26
gradlew.bat
vendored
26
gradlew.bat
vendored
@@ -13,6 +13,8 @@
|
|||||||
@rem See the License for the specific language governing permissions and
|
@rem See the License for the specific language governing permissions and
|
||||||
@rem limitations under the License.
|
@rem limitations under the License.
|
||||||
@rem
|
@rem
|
||||||
|
@rem SPDX-License-Identifier: Apache-2.0
|
||||||
|
@rem
|
||||||
|
|
||||||
@if "%DEBUG%"=="" @echo off
|
@if "%DEBUG%"=="" @echo off
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
@@ -43,11 +45,11 @@ set JAVA_EXE=java.exe
|
|||||||
%JAVA_EXE% -version >NUL 2>&1
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
if %ERRORLEVEL% equ 0 goto execute
|
if %ERRORLEVEL% equ 0 goto execute
|
||||||
|
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
echo location of your Java installation.
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
@@ -57,22 +59,22 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
|||||||
|
|
||||||
if exist "%JAVA_EXE%" goto execute
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
echo location of your Java installation.
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
:execute
|
:execute
|
||||||
@rem Setup the command line
|
@rem Setup the command line
|
||||||
|
|
||||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
set CLASSPATH=
|
||||||
|
|
||||||
|
|
||||||
@rem Execute Gradle
|
@rem Execute Gradle
|
||||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||||
|
|
||||||
:end
|
:end
|
||||||
@rem End local scope for the variables with windows NT shell
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
@@ -131,12 +131,15 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
|
|||||||
// the touch coordinates
|
// the touch coordinates
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
// New API instead of SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
// New API instead of SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
window.setDecorFitsSystemWindows(false)
|
window.setDecorFitsSystemWindows(false)
|
||||||
// New API instead of SYSTEM_UI_FLAG_IMMERSIVE
|
// New API instead of SYSTEM_UI_FLAG_IMMERSIVE
|
||||||
window.decorView.windowInsetsController?.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
|
window.decorView.windowInsetsController?.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
|
||||||
// New API instead of FLAG_TRANSLUCENT_STATUS
|
// New API instead of FLAG_TRANSLUCENT_STATUS
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
window.statusBarColor = ContextCompat.getColor(this, R.color.half_transparent_status_bar)
|
window.statusBarColor = ContextCompat.getColor(this, R.color.half_transparent_status_bar)
|
||||||
// new API instead of FLAG_TRANSLUCENT_NAVIGATION
|
// new API instead of FLAG_TRANSLUCENT_NAVIGATION
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
window.navigationBarColor = ContextCompat.getColor(this, R.color.half_transparent_status_bar)
|
window.navigationBarColor = ContextCompat.getColor(this, R.color.half_transparent_status_bar)
|
||||||
} else {
|
} else {
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
@@ -318,6 +321,7 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
|
|||||||
protected open fun shouldAnimateDismiss(): Boolean = true
|
protected open fun shouldAnimateDismiss(): Boolean = true
|
||||||
|
|
||||||
protected open fun animateClose() {
|
protected open fun animateClose() {
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
window.statusBarColor = Color.TRANSPARENT
|
window.statusBarColor = Color.TRANSPARENT
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
@@ -334,14 +338,17 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
|
|||||||
// Or for "sticky immersive," replace it with SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
// Or for "sticky immersive," replace it with SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
// New API instead of SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
// New API instead of SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
window.setDecorFitsSystemWindows(false)
|
window.setDecorFitsSystemWindows(false)
|
||||||
// new API instead of SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
// new API instead of SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||||
window.decorView.windowInsetsController?.hide(WindowInsets.Type.navigationBars())
|
window.decorView.windowInsetsController?.hide(WindowInsets.Type.navigationBars())
|
||||||
// New API instead of SYSTEM_UI_FLAG_IMMERSIVE
|
// New API instead of SYSTEM_UI_FLAG_IMMERSIVE
|
||||||
window.decorView.windowInsetsController?.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
|
window.decorView.windowInsetsController?.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
|
||||||
// New API instead of FLAG_TRANSLUCENT_STATUS
|
// New API instead of FLAG_TRANSLUCENT_STATUS
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
window.statusBarColor = ContextCompat.getColor(this, R.color.half_transparent_status_bar)
|
window.statusBarColor = ContextCompat.getColor(this, R.color.half_transparent_status_bar)
|
||||||
// New API instead of FLAG_TRANSLUCENT_NAVIGATION
|
// New API instead of FLAG_TRANSLUCENT_NAVIGATION
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
window.navigationBarColor = ContextCompat.getColor(this, R.color.half_transparent_status_bar)
|
window.navigationBarColor = ContextCompat.getColor(this, R.color.half_transparent_status_bar)
|
||||||
} else {
|
} else {
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
@@ -363,6 +370,7 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
|
|||||||
systemUiVisibility = true
|
systemUiVisibility = true
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
// New API instead of SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
// New API instead of SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
window.setDecorFitsSystemWindows(false)
|
window.setDecorFitsSystemWindows(false)
|
||||||
} else {
|
} else {
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
|
@@ -120,6 +120,7 @@
|
|||||||
<string name="notice_widget_modified">%1$s modified %2$s widget</string>
|
<string name="notice_widget_modified">%1$s modified %2$s widget</string>
|
||||||
<string name="notice_widget_modified_by_you">You modified %1$s widget</string>
|
<string name="notice_widget_modified_by_you">You modified %1$s widget</string>
|
||||||
|
|
||||||
|
<string name="power_level_owner">Owner</string>
|
||||||
<string name="power_level_admin">Admin</string>
|
<string name="power_level_admin">Admin</string>
|
||||||
<string name="power_level_moderator">Moderator</string>
|
<string name="power_level_moderator">Moderator</string>
|
||||||
<string name="power_level_default">Default</string>
|
<string name="power_level_default">Default</string>
|
||||||
@@ -685,6 +686,7 @@
|
|||||||
<string name="room_participants_leave_prompt_title">Leave room</string>
|
<string name="room_participants_leave_prompt_title">Leave room</string>
|
||||||
<string name="room_participants_leave_prompt_msg">Are you sure you want to leave the room?</string>
|
<string name="room_participants_leave_prompt_msg">Are you sure you want to leave the room?</string>
|
||||||
<string name="room_participants_leave_private_warning">This room is not public. You will not be able to rejoin without an invite.</string>
|
<string name="room_participants_leave_private_warning">This room is not public. You will not be able to rejoin without an invite.</string>
|
||||||
|
<string name="room_participants_leave_last_admin">You\'re the only admin of this room. Leaving it will mean no one has control over it.</string>
|
||||||
|
|
||||||
<string name="room_participants_header_direct_chats">Direct Messages</string>
|
<string name="room_participants_header_direct_chats">Direct Messages</string>
|
||||||
|
|
||||||
@@ -2383,6 +2385,7 @@
|
|||||||
<string name="room_member_power_level_invites">Invites</string>
|
<string name="room_member_power_level_invites">Invites</string>
|
||||||
<string name="room_member_power_level_users">Users</string>
|
<string name="room_member_power_level_users">Users</string>
|
||||||
|
|
||||||
|
<string name="room_member_power_level_owner_in">Owner in %1$s</string>
|
||||||
<string name="room_member_power_level_admin_in">Admin in %1$s</string>
|
<string name="room_member_power_level_admin_in">Admin in %1$s</string>
|
||||||
<string name="room_member_power_level_moderator_in">Moderator in %1$s</string>
|
<string name="room_member_power_level_moderator_in">Moderator in %1$s</string>
|
||||||
<string name="room_member_power_level_default_in">Default in %1$s</string>
|
<string name="room_member_power_level_default_in">Default in %1$s</string>
|
||||||
|
@@ -64,6 +64,9 @@
|
|||||||
|
|
||||||
<!-- Material color -->
|
<!-- Material color -->
|
||||||
<item name="colorPrimary">@color/element_accent_light</item>
|
<item name="colorPrimary">@color/element_accent_light</item>
|
||||||
|
<!-- Fix background color of status bar in home and room detail activity -->
|
||||||
|
<!--item name="colorPrimaryDark">@color/element_background_light</item-->
|
||||||
|
<item name="colorPrimaryDark">?vctr_toolbar_background</item>
|
||||||
<item name="colorPrimaryVariant">@color/element_accent_light</item>
|
<item name="colorPrimaryVariant">@color/element_accent_light</item>
|
||||||
<item name="colorOnPrimary">@android:color/white</item>
|
<item name="colorOnPrimary">@android:color/white</item>
|
||||||
<item name="colorSecondary">@color/element_accent_light</item>
|
<item name="colorSecondary">@color/element_accent_light</item>
|
||||||
|
@@ -30,6 +30,7 @@ import org.matrix.android.sdk.api.session.room.model.ReadReceipt
|
|||||||
import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
|
import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState
|
import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState
|
||||||
|
import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels
|
||||||
import org.matrix.android.sdk.api.session.room.send.UserDraft
|
import org.matrix.android.sdk.api.session.room.send.UserDraft
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||||
import org.matrix.android.sdk.api.util.Optional
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
@@ -95,6 +96,10 @@ class FlowRoom(private val room: Room) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun liveRoomPowerLevels(): Flow<RoomPowerLevels> {
|
||||||
|
return room.stateService().getRoomPowerLevelsLive().asFlow()
|
||||||
|
}
|
||||||
|
|
||||||
fun liveReadMarker(): Flow<Optional<String>> {
|
fun liveReadMarker(): Flow<Optional<String>> {
|
||||||
return room.readService().getReadMarkerLive().asFlow()
|
return room.readService().getReadMarkerLive().asFlow()
|
||||||
}
|
}
|
||||||
|
@@ -62,7 +62,7 @@ android {
|
|||||||
// that the app's state is completely cleared between tests.
|
// that the app's state is completely cleared between tests.
|
||||||
testInstrumentationRunnerArguments clearPackageData: 'true'
|
testInstrumentationRunnerArguments clearPackageData: 'true'
|
||||||
|
|
||||||
buildConfigField "String", "SDK_VERSION", "\"1.6.42\""
|
buildConfigField "String", "SDK_VERSION", "\"1.6.44\""
|
||||||
|
|
||||||
buildConfigField "String", "GIT_SDK_REVISION", "\"${gitRevision()}\""
|
buildConfigField "String", "GIT_SDK_REVISION", "\"${gitRevision()}\""
|
||||||
buildConfigField "String", "GIT_SDK_REVISION_UNIX_DATE", "\"${gitRevisionUnixDate()}\""
|
buildConfigField "String", "GIT_SDK_REVISION_UNIX_DATE", "\"${gitRevisionUnixDate()}\""
|
||||||
|
@@ -40,8 +40,8 @@ import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesAllowEntry
|
|||||||
import org.matrix.android.sdk.api.session.room.model.RoomType
|
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.CreateRoomParams
|
||||||
import org.matrix.android.sdk.api.session.room.model.create.RestrictedRoomPreset
|
import org.matrix.android.sdk.api.session.room.model.create.RestrictedRoomPreset
|
||||||
import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
|
|
||||||
import org.matrix.android.sdk.api.session.room.powerlevels.Role
|
import org.matrix.android.sdk.api.session.room.powerlevels.Role
|
||||||
|
import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels
|
||||||
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
||||||
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runSessionTest
|
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runSessionTest
|
||||||
import org.matrix.android.sdk.common.SessionTestParams
|
import org.matrix.android.sdk.common.SessionTestParams
|
||||||
@@ -500,12 +500,12 @@ class SpaceHierarchyTest : InstrumentedTest {
|
|||||||
room.stateService().sendStateEvent(EventType.STATE_ROOM_POWER_LEVELS, stateKey = "", newPowerLevelsContent!!)
|
room.stateService().sendStateEvent(EventType.STATE_ROOM_POWER_LEVELS, stateKey = "", newPowerLevelsContent!!)
|
||||||
|
|
||||||
commonTestHelper.retryPeriodically {
|
commonTestHelper.retryPeriodically {
|
||||||
val powerLevelsHelper = aliceSession.getRoom(bobRoomId)!!
|
val roomPowerLevels = aliceSession.getRoom(bobRoomId)!!
|
||||||
.getStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty)
|
.getStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty)
|
||||||
?.content
|
?.content
|
||||||
?.toModel<PowerLevelsContent>()
|
?.toModel<PowerLevelsContent>()
|
||||||
?.let { PowerLevelsHelper(it) }
|
?.let { RoomPowerLevels(it) }
|
||||||
powerLevelsHelper!!.isUserAllowedToSend(aliceSession.myUserId, true, EventType.STATE_SPACE_PARENT)
|
roomPowerLevels!!.isUserAllowedToSend(aliceSession.myUserId, true, EventType.STATE_SPACE_PARENT)
|
||||||
}
|
}
|
||||||
|
|
||||||
aliceSession.spaceService().setSpaceParent(bobRoomId, spaceAInfo.spaceId, false, listOf(bobSession.sessionParams.homeServerHost ?: ""))
|
aliceSession.spaceService().setSpaceParent(bobRoomId, spaceAInfo.spaceId, false, listOf(bobSession.sessionParams.homeServerHost ?: ""))
|
||||||
|
@@ -30,15 +30,21 @@ object MatrixPatterns {
|
|||||||
// Note: TLD is not mandatory (localhost, IP address...)
|
// Note: TLD is not mandatory (localhost, IP address...)
|
||||||
private const val DOMAIN_REGEX = ":[A-Z0-9.-]+(:[0-9]{2,5})?"
|
private const val DOMAIN_REGEX = ":[A-Z0-9.-]+(:[0-9]{2,5})?"
|
||||||
|
|
||||||
|
private const val BASE_64_ALPHABET = "[0-9A-Za-z/\\+=]+"
|
||||||
|
private const val BASE_64_URL_SAFE_ALPHABET = "[0-9A-Za-z/\\-_]+"
|
||||||
|
|
||||||
// regex pattern to find matrix user ids in a string.
|
// regex pattern to find matrix user ids in a string.
|
||||||
// See https://matrix.org/docs/spec/appendices#historical-user-ids
|
// See https://matrix.org/docs/spec/appendices#historical-user-ids
|
||||||
private const val MATRIX_USER_IDENTIFIER_REGEX = "@[A-Z0-9\\x21-\\x39\\x3B-\\x7F]+$DOMAIN_REGEX"
|
private const val MATRIX_USER_IDENTIFIER_REGEX = "@[A-Z0-9\\x21-\\x39\\x3B-\\x7F]+$DOMAIN_REGEX"
|
||||||
val PATTERN_CONTAIN_MATRIX_USER_IDENTIFIER = MATRIX_USER_IDENTIFIER_REGEX.toRegex(RegexOption.IGNORE_CASE)
|
val PATTERN_CONTAIN_MATRIX_USER_IDENTIFIER = MATRIX_USER_IDENTIFIER_REGEX.toRegex(RegexOption.IGNORE_CASE)
|
||||||
|
|
||||||
// regex pattern to find room ids in a string.
|
// regex pattern to find room ids in a string.
|
||||||
private const val MATRIX_ROOM_IDENTIFIER_REGEX = "![A-Z0-9]+$DOMAIN_REGEX"
|
private const val MATRIX_ROOM_IDENTIFIER_REGEX = "^!.+$DOMAIN_REGEX$"
|
||||||
private val PATTERN_CONTAIN_MATRIX_ROOM_IDENTIFIER = MATRIX_ROOM_IDENTIFIER_REGEX.toRegex(RegexOption.IGNORE_CASE)
|
private val PATTERN_CONTAIN_MATRIX_ROOM_IDENTIFIER = MATRIX_ROOM_IDENTIFIER_REGEX.toRegex(RegexOption.IGNORE_CASE)
|
||||||
|
|
||||||
|
private const val MATRIX_ROOM_IDENTIFIER_DOMAINLESS_REGEX = "!$BASE_64_URL_SAFE_ALPHABET"
|
||||||
|
private val PATTERN_CONTAIN_MATRIX_ROOM_IDENTIFIER_DOMAINLESS = MATRIX_ROOM_IDENTIFIER_DOMAINLESS_REGEX.toRegex()
|
||||||
|
|
||||||
// regex pattern to find room aliases in a string.
|
// regex pattern to find room aliases in a string.
|
||||||
private const val MATRIX_ROOM_ALIAS_REGEX = "#[A-Z0-9._%#@=+-]+$DOMAIN_REGEX"
|
private const val MATRIX_ROOM_ALIAS_REGEX = "#[A-Z0-9._%#@=+-]+$DOMAIN_REGEX"
|
||||||
private val PATTERN_CONTAIN_MATRIX_ALIAS = MATRIX_ROOM_ALIAS_REGEX.toRegex(RegexOption.IGNORE_CASE)
|
private val PATTERN_CONTAIN_MATRIX_ALIAS = MATRIX_ROOM_ALIAS_REGEX.toRegex(RegexOption.IGNORE_CASE)
|
||||||
@@ -48,11 +54,11 @@ object MatrixPatterns {
|
|||||||
private val PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER = MATRIX_EVENT_IDENTIFIER_REGEX.toRegex(RegexOption.IGNORE_CASE)
|
private val PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER = MATRIX_EVENT_IDENTIFIER_REGEX.toRegex(RegexOption.IGNORE_CASE)
|
||||||
|
|
||||||
// regex pattern to find message ids in a string.
|
// regex pattern to find message ids in a string.
|
||||||
private const val MATRIX_EVENT_IDENTIFIER_V3_REGEX = "\\$[A-Z0-9/+]+"
|
private const val MATRIX_EVENT_IDENTIFIER_V3_REGEX = "\\$$BASE_64_ALPHABET"
|
||||||
private val PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V3 = MATRIX_EVENT_IDENTIFIER_V3_REGEX.toRegex(RegexOption.IGNORE_CASE)
|
private val PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V3 = MATRIX_EVENT_IDENTIFIER_V3_REGEX.toRegex(RegexOption.IGNORE_CASE)
|
||||||
|
|
||||||
// Ref: https://matrix.org/docs/spec/rooms/v4#event-ids
|
// Ref: https://matrix.org/docs/spec/rooms/v4#event-ids
|
||||||
private const val MATRIX_EVENT_IDENTIFIER_V4_REGEX = "\\$[A-Z0-9\\-_]+"
|
private const val MATRIX_EVENT_IDENTIFIER_V4_REGEX = "\\$$BASE_64_URL_SAFE_ALPHABET"
|
||||||
private val PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V4 = MATRIX_EVENT_IDENTIFIER_V4_REGEX.toRegex(RegexOption.IGNORE_CASE)
|
private val PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V4 = MATRIX_EVENT_IDENTIFIER_V4_REGEX.toRegex(RegexOption.IGNORE_CASE)
|
||||||
|
|
||||||
// regex pattern to find group ids in a string.
|
// regex pattern to find group ids in a string.
|
||||||
@@ -76,7 +82,10 @@ object MatrixPatterns {
|
|||||||
PATTERN_CONTAIN_MATRIX_USER_IDENTIFIER,
|
PATTERN_CONTAIN_MATRIX_USER_IDENTIFIER,
|
||||||
PATTERN_CONTAIN_MATRIX_ALIAS,
|
PATTERN_CONTAIN_MATRIX_ALIAS,
|
||||||
PATTERN_CONTAIN_MATRIX_ROOM_IDENTIFIER,
|
PATTERN_CONTAIN_MATRIX_ROOM_IDENTIFIER,
|
||||||
|
PATTERN_CONTAIN_MATRIX_ROOM_IDENTIFIER_DOMAINLESS,
|
||||||
PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER,
|
PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER,
|
||||||
|
PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V3,
|
||||||
|
PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V4,
|
||||||
PATTERN_CONTAIN_MATRIX_GROUP_IDENTIFIER
|
PATTERN_CONTAIN_MATRIX_GROUP_IDENTIFIER
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -97,7 +106,9 @@ object MatrixPatterns {
|
|||||||
* @return true if the string is a valid room Id
|
* @return true if the string is a valid room Id
|
||||||
*/
|
*/
|
||||||
fun isRoomId(str: String?): Boolean {
|
fun isRoomId(str: String?): Boolean {
|
||||||
return str != null && str matches PATTERN_CONTAIN_MATRIX_ROOM_IDENTIFIER
|
return str != null &&
|
||||||
|
(str matches PATTERN_CONTAIN_MATRIX_ROOM_IDENTIFIER ||
|
||||||
|
str matches PATTERN_CONTAIN_MATRIX_ROOM_IDENTIFIER_DOMAINLESS)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -16,8 +16,7 @@
|
|||||||
package org.matrix.android.sdk.api.session.pushrules
|
package org.matrix.android.sdk.api.session.pushrules
|
||||||
|
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
|
import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels
|
||||||
import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
|
|
||||||
|
|
||||||
class SenderNotificationPermissionCondition(
|
class SenderNotificationPermissionCondition(
|
||||||
/**
|
/**
|
||||||
@@ -35,8 +34,7 @@ class SenderNotificationPermissionCondition(
|
|||||||
|
|
||||||
override fun technicalDescription() = "User power level <$key>"
|
override fun technicalDescription() = "User power level <$key>"
|
||||||
|
|
||||||
fun isSatisfied(event: Event, powerLevels: PowerLevelsContent): Boolean {
|
fun isSatisfied(event: Event, roomPowerLevels: RoomPowerLevels): Boolean {
|
||||||
val powerLevelsHelper = PowerLevelsHelper(powerLevels)
|
return event.senderId != null && roomPowerLevels.isUserAbleToTriggerNotification(event.senderId, key)
|
||||||
return event.senderId != null && powerLevelsHelper.getUserPowerLevelValue(event.senderId) >= powerLevels.notificationLevel(key)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -18,6 +18,7 @@ package org.matrix.android.sdk.api.session.room
|
|||||||
|
|
||||||
import org.matrix.android.sdk.api.query.QueryStateEventValue
|
import org.matrix.android.sdk.api.query.QueryStateEventValue
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
|
import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -34,3 +35,10 @@ fun Room.getTimelineEvent(eventId: String): TimelineEvent? =
|
|||||||
*/
|
*/
|
||||||
fun Room.getStateEvent(eventType: String, stateKey: QueryStateEventValue): Event? =
|
fun Room.getStateEvent(eventType: String, stateKey: QueryStateEventValue): Event? =
|
||||||
stateService().getStateEvent(eventType, stateKey)
|
stateService().getStateEvent(eventType, stateKey)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current RoomPowerLevels of the room.
|
||||||
|
*/
|
||||||
|
fun Room.getRoomPowerLevels(): RoomPowerLevels {
|
||||||
|
return stateService().getRoomPowerLevels()
|
||||||
|
}
|
||||||
|
@@ -18,7 +18,8 @@ package org.matrix.android.sdk.api.session.room.model
|
|||||||
|
|
||||||
import com.squareup.moshi.Json
|
import com.squareup.moshi.Json
|
||||||
import com.squareup.moshi.JsonClass
|
import com.squareup.moshi.JsonClass
|
||||||
import org.matrix.android.sdk.api.session.room.powerlevels.Role
|
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent.Companion.NOTIFICATIONS_ROOM_KEY
|
||||||
|
import org.matrix.android.sdk.api.session.room.powerlevels.UserPowerLevel
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class representing the EventType.EVENT_TYPE_STATE_ROOM_POWER_LEVELS state event content.
|
* Class representing the EventType.EVENT_TYPE_STATE_ROOM_POWER_LEVELS state event content.
|
||||||
@@ -34,7 +35,7 @@ data class PowerLevelsContent(
|
|||||||
*/
|
*/
|
||||||
@Json(name = "kick") val kick: Int? = null,
|
@Json(name = "kick") val kick: Int? = null,
|
||||||
/**
|
/**
|
||||||
* The level required to invite a user. Defaults to 50 if unspecified.
|
* The level required to invite a user. Defaults to 0 if unspecified.
|
||||||
*/
|
*/
|
||||||
@Json(name = "invite") val invite: Int? = null,
|
@Json(name = "invite") val invite: Int? = null,
|
||||||
/**
|
/**
|
||||||
@@ -88,7 +89,7 @@ data class PowerLevelsContent(
|
|||||||
* Get the notification level for a dedicated key.
|
* Get the notification level for a dedicated key.
|
||||||
*
|
*
|
||||||
* @param key the notification key
|
* @param key the notification key
|
||||||
* @return the level, default to Moderator if the key is not found
|
* @return the level
|
||||||
*/
|
*/
|
||||||
fun notificationLevel(key: String): Int {
|
fun notificationLevel(key: String): Int {
|
||||||
return when (val value = notifications.orEmpty()[key]) {
|
return when (val value = notifications.orEmpty()[key]) {
|
||||||
@@ -96,10 +97,9 @@ data class PowerLevelsContent(
|
|||||||
is String -> value.toInt()
|
is String -> value.toInt()
|
||||||
is Double -> value.toInt()
|
is Double -> value.toInt()
|
||||||
is Int -> value
|
is Int -> value
|
||||||
else -> Role.Moderator.value
|
else -> defaultNotificationLevel(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
/**
|
||||||
* Key to use for content.notifications and get the level required to trigger an @room notification. Defaults to 50 if unspecified.
|
* Key to use for content.notifications and get the level required to trigger an @room notification. Defaults to 50 if unspecified.
|
||||||
@@ -108,11 +108,20 @@ data class PowerLevelsContent(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun defaultNotificationLevel(key: String): Int {
|
||||||
|
return when (key) {
|
||||||
|
NOTIFICATIONS_ROOM_KEY -> UserPowerLevel.Moderator.value
|
||||||
|
else -> UserPowerLevel.User.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Fallback to default value, defined in the Matrix specification
|
// Fallback to default value, defined in the Matrix specification
|
||||||
fun PowerLevelsContent.banOrDefault() = ban ?: Role.Moderator.value
|
fun PowerLevelsContent?.banOrDefault() = this?.ban ?: UserPowerLevel.Moderator.value
|
||||||
fun PowerLevelsContent.kickOrDefault() = kick ?: Role.Moderator.value
|
fun PowerLevelsContent?.kickOrDefault() = this?.kick ?: UserPowerLevel.Moderator.value
|
||||||
fun PowerLevelsContent.inviteOrDefault() = invite ?: Role.Moderator.value
|
fun PowerLevelsContent?.inviteOrDefault() = this?.invite ?: UserPowerLevel.User.value
|
||||||
fun PowerLevelsContent.redactOrDefault() = redact ?: Role.Moderator.value
|
fun PowerLevelsContent?.redactOrDefault() = this?.redact ?: UserPowerLevel.Moderator.value
|
||||||
fun PowerLevelsContent.eventsDefaultOrDefault() = eventsDefault ?: Role.Default.value
|
fun PowerLevelsContent?.eventsDefaultOrDefault() = this?.eventsDefault ?: UserPowerLevel.User.value
|
||||||
fun PowerLevelsContent.usersDefaultOrDefault() = usersDefault ?: Role.Default.value
|
fun PowerLevelsContent?.usersDefaultOrDefault() = this?.usersDefault ?: UserPowerLevel.User.value
|
||||||
fun PowerLevelsContent.stateDefaultOrDefault() = stateDefault ?: Role.Moderator.value
|
fun PowerLevelsContent?.stateDefaultOrDefault() = this?.stateDefault ?: UserPowerLevel.Moderator.value
|
||||||
|
|
||||||
|
fun PowerLevelsContent?.notificationLevelOrDefault(key: String) = this?.notificationLevel(key) ?: defaultNotificationLevel(key)
|
||||||
|
@@ -18,15 +18,39 @@ package org.matrix.android.sdk.api.session.room.model.create
|
|||||||
|
|
||||||
import com.squareup.moshi.Json
|
import com.squareup.moshi.Json
|
||||||
import com.squareup.moshi.JsonClass
|
import com.squareup.moshi.JsonClass
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Content of a m.room.create type event.
|
* Content of a m.room.create type event.
|
||||||
*/
|
*/
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
data class RoomCreateContent(
|
data class RoomCreateContent(
|
||||||
|
// Creator should be replaced by the sender of the event
|
||||||
@Json(name = "creator") val creator: String? = null,
|
@Json(name = "creator") val creator: String? = null,
|
||||||
@Json(name = "room_version") val roomVersion: String? = null,
|
@Json(name = "room_version") val roomVersion: String? = null,
|
||||||
@Json(name = "predecessor") val predecessor: Predecessor? = null,
|
@Json(name = "predecessor") val predecessor: Predecessor? = null,
|
||||||
// Defines the room type, see #RoomType (user extensible)
|
// Defines the room type, see #RoomType (user extensible)
|
||||||
@Json(name = "type") val type: String? = null
|
@Json(name = "type") val type: String? = null,
|
||||||
|
@Json(name = "additional_creators") val additionalCreators: List<String>? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
data class RoomCreateContentWithSender(
|
||||||
|
val senderId: String,
|
||||||
|
val inner: RoomCreateContent
|
||||||
|
) {
|
||||||
|
val creators = setOf(senderId) + inner.additionalCreators.orEmpty().toSet()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Event.getRoomCreateContentWithSender(): RoomCreateContentWithSender? {
|
||||||
|
if (this.type != EventType.STATE_ROOM_CREATE) return null
|
||||||
|
val innerContent = getClearContent().toModel<RoomCreateContent>() ?: return null
|
||||||
|
val senderId = senderId ?: return null
|
||||||
|
return RoomCreateContentWithSender(senderId, innerContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun RoomCreateContent.explicitlyPrivilegeRoomCreators(): Boolean {
|
||||||
|
val supportedRoomVersions = listOf("org.matrix.hydra.11", "12")
|
||||||
|
return supportedRoomVersions.contains(roomVersion)
|
||||||
|
}
|
||||||
|
@@ -17,26 +17,21 @@
|
|||||||
|
|
||||||
package org.matrix.android.sdk.api.session.room.powerlevels
|
package org.matrix.android.sdk.api.session.room.powerlevels
|
||||||
|
|
||||||
sealed class Role(open val value: Int) : Comparable<Role> {
|
enum class Role {
|
||||||
object Admin : Role(100)
|
Creator,
|
||||||
object Moderator : Role(50)
|
SuperAdmin,
|
||||||
object Default : Role(0)
|
Admin,
|
||||||
data class Custom(override val value: Int) : Role(value)
|
Moderator,
|
||||||
|
User;
|
||||||
override fun compareTo(other: Role): Int {
|
|
||||||
return value.compareTo(other.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
fun getSuggestedRole(userPowerLevel: UserPowerLevel): Role {
|
||||||
// Order matters, default value should be checked after defined roles
|
return when {
|
||||||
fun fromValue(value: Int, default: Int): Role {
|
userPowerLevel == UserPowerLevel.Infinite -> Creator
|
||||||
return when (value) {
|
userPowerLevel >= UserPowerLevel.SuperAdmin -> SuperAdmin
|
||||||
Admin.value -> Admin
|
userPowerLevel >= UserPowerLevel.Admin -> Admin
|
||||||
Moderator.value -> Moderator
|
userPowerLevel >= UserPowerLevel.Moderator -> Moderator
|
||||||
Default.value,
|
else -> User
|
||||||
default -> Default
|
|
||||||
else -> Custom(value)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -19,17 +19,23 @@ package org.matrix.android.sdk.api.session.room.powerlevels
|
|||||||
|
|
||||||
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
|
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.banOrDefault
|
import org.matrix.android.sdk.api.session.room.model.banOrDefault
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContentWithSender
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.create.explicitlyPrivilegeRoomCreators
|
||||||
import org.matrix.android.sdk.api.session.room.model.eventsDefaultOrDefault
|
import org.matrix.android.sdk.api.session.room.model.eventsDefaultOrDefault
|
||||||
import org.matrix.android.sdk.api.session.room.model.inviteOrDefault
|
import org.matrix.android.sdk.api.session.room.model.inviteOrDefault
|
||||||
import org.matrix.android.sdk.api.session.room.model.kickOrDefault
|
import org.matrix.android.sdk.api.session.room.model.kickOrDefault
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.notificationLevelOrDefault
|
||||||
import org.matrix.android.sdk.api.session.room.model.redactOrDefault
|
import org.matrix.android.sdk.api.session.room.model.redactOrDefault
|
||||||
import org.matrix.android.sdk.api.session.room.model.stateDefaultOrDefault
|
import org.matrix.android.sdk.api.session.room.model.stateDefaultOrDefault
|
||||||
import org.matrix.android.sdk.api.session.room.model.usersDefaultOrDefault
|
import org.matrix.android.sdk.api.session.room.model.usersDefaultOrDefault
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is an helper around PowerLevelsContent.
|
* This class is an helper around PowerLevelsContent and RoomCreateContent.
|
||||||
*/
|
*/
|
||||||
class PowerLevelsHelper(private val powerLevelsContent: PowerLevelsContent) {
|
class RoomPowerLevels(
|
||||||
|
val powerLevelsContent: PowerLevelsContent?,
|
||||||
|
private val roomCreateContent: RoomCreateContentWithSender?,
|
||||||
|
) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the user power level of a dedicated user Id.
|
* Returns the user power level of a dedicated user Id.
|
||||||
@@ -37,10 +43,14 @@ class PowerLevelsHelper(private val powerLevelsContent: PowerLevelsContent) {
|
|||||||
* @param userId the user id
|
* @param userId the user id
|
||||||
* @return the power level
|
* @return the power level
|
||||||
*/
|
*/
|
||||||
fun getUserPowerLevelValue(userId: String): Int {
|
fun getUserPowerLevel(userId: String): UserPowerLevel {
|
||||||
return powerLevelsContent.users
|
if (shouldGiveInfinitePowerLevel(userId)) return UserPowerLevel.Infinite
|
||||||
|
if (powerLevelsContent == null) return UserPowerLevel.User
|
||||||
|
val value = powerLevelsContent.users
|
||||||
?.get(userId)
|
?.get(userId)
|
||||||
?: powerLevelsContent.usersDefaultOrDefault()
|
?: powerLevelsContent.usersDefaultOrDefault()
|
||||||
|
|
||||||
|
return UserPowerLevel.Value(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -49,10 +59,9 @@ class PowerLevelsHelper(private val powerLevelsContent: PowerLevelsContent) {
|
|||||||
* @param userId the user id
|
* @param userId the user id
|
||||||
* @return the power level
|
* @return the power level
|
||||||
*/
|
*/
|
||||||
fun getUserRole(userId: String): Role {
|
fun getSuggestedRole(userId: String): Role {
|
||||||
val value = getUserPowerLevelValue(userId)
|
val value = getUserPowerLevel(userId)
|
||||||
// I think we should use powerLevelsContent.usersDefault, but Ganfra told me that it was like that on riot-Web
|
return Role.getSuggestedRole(value)
|
||||||
return Role.fromValue(value, powerLevelsContent.eventsDefaultOrDefault())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -65,14 +74,14 @@ class PowerLevelsHelper(private val powerLevelsContent: PowerLevelsContent) {
|
|||||||
*/
|
*/
|
||||||
fun isUserAllowedToSend(userId: String, isState: Boolean, eventType: String?): Boolean {
|
fun isUserAllowedToSend(userId: String, isState: Boolean, eventType: String?): Boolean {
|
||||||
return if (userId.isNotEmpty()) {
|
return if (userId.isNotEmpty()) {
|
||||||
val powerLevel = getUserPowerLevelValue(userId)
|
val powerLevel = getUserPowerLevel(userId)
|
||||||
val minimumPowerLevel = powerLevelsContent.events?.get(eventType)
|
val minimumPowerLevel = powerLevelsContent?.events?.get(eventType)
|
||||||
?: if (isState) {
|
?: if (isState) {
|
||||||
powerLevelsContent.stateDefaultOrDefault()
|
powerLevelsContent.stateDefaultOrDefault()
|
||||||
} else {
|
} else {
|
||||||
powerLevelsContent.eventsDefaultOrDefault()
|
powerLevelsContent.eventsDefaultOrDefault()
|
||||||
}
|
}
|
||||||
powerLevel >= minimumPowerLevel
|
powerLevel >= UserPowerLevel.Value(minimumPowerLevel)
|
||||||
} else false
|
} else false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,8 +91,8 @@ class PowerLevelsHelper(private val powerLevelsContent: PowerLevelsContent) {
|
|||||||
* @return true if able to invite
|
* @return true if able to invite
|
||||||
*/
|
*/
|
||||||
fun isUserAbleToInvite(userId: String): Boolean {
|
fun isUserAbleToInvite(userId: String): Boolean {
|
||||||
val powerLevel = getUserPowerLevelValue(userId)
|
val powerLevel = getUserPowerLevel(userId)
|
||||||
return powerLevel >= powerLevelsContent.inviteOrDefault()
|
return powerLevel >= UserPowerLevel.Value(powerLevelsContent.inviteOrDefault())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -92,8 +101,8 @@ class PowerLevelsHelper(private val powerLevelsContent: PowerLevelsContent) {
|
|||||||
* @return true if able to ban
|
* @return true if able to ban
|
||||||
*/
|
*/
|
||||||
fun isUserAbleToBan(userId: String): Boolean {
|
fun isUserAbleToBan(userId: String): Boolean {
|
||||||
val powerLevel = getUserPowerLevelValue(userId)
|
val powerLevel = getUserPowerLevel(userId)
|
||||||
return powerLevel >= powerLevelsContent.banOrDefault()
|
return powerLevel >= UserPowerLevel.Value(powerLevelsContent.banOrDefault())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -102,8 +111,8 @@ class PowerLevelsHelper(private val powerLevelsContent: PowerLevelsContent) {
|
|||||||
* @return true if able to kick
|
* @return true if able to kick
|
||||||
*/
|
*/
|
||||||
fun isUserAbleToKick(userId: String): Boolean {
|
fun isUserAbleToKick(userId: String): Boolean {
|
||||||
val powerLevel = getUserPowerLevelValue(userId)
|
val powerLevel = getUserPowerLevel(userId)
|
||||||
return powerLevel >= powerLevelsContent.kickOrDefault()
|
return powerLevel >= UserPowerLevel.Value(powerLevelsContent.kickOrDefault())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -112,7 +121,22 @@ class PowerLevelsHelper(private val powerLevelsContent: PowerLevelsContent) {
|
|||||||
* @return true if able to redact
|
* @return true if able to redact
|
||||||
*/
|
*/
|
||||||
fun isUserAbleToRedact(userId: String): Boolean {
|
fun isUserAbleToRedact(userId: String): Boolean {
|
||||||
val powerLevel = getUserPowerLevelValue(userId)
|
val powerLevel = getUserPowerLevel(userId)
|
||||||
return powerLevel >= powerLevelsContent.redactOrDefault()
|
return powerLevel >= UserPowerLevel.Value(powerLevelsContent.redactOrDefault())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isUserAbleToTriggerNotification(userId: String, notificationKey: String): Boolean {
|
||||||
|
val userPowerLevel = getUserPowerLevel(userId)
|
||||||
|
val notificationPowerLevel = UserPowerLevel.Value(powerLevelsContent.notificationLevelOrDefault(key = notificationKey))
|
||||||
|
return userPowerLevel >= notificationPowerLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun shouldGiveInfinitePowerLevel(userId: String): Boolean {
|
||||||
|
if (roomCreateContent == null) return false
|
||||||
|
return if (roomCreateContent.inner.explicitlyPrivilegeRoomCreators()) {
|
||||||
|
roomCreateContent.creators.contains(userId)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2025 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.matrix.android.sdk.api.session.room.powerlevels
|
||||||
|
|
||||||
|
sealed interface UserPowerLevel : Comparable<UserPowerLevel> {
|
||||||
|
data object Infinite : UserPowerLevel
|
||||||
|
|
||||||
|
@JvmInline
|
||||||
|
value class Value(val value: Int) : UserPowerLevel
|
||||||
|
|
||||||
|
override fun compareTo(other: UserPowerLevel): Int {
|
||||||
|
return when (this) {
|
||||||
|
Infinite -> when (other) {
|
||||||
|
Infinite -> 0
|
||||||
|
is Value -> 1
|
||||||
|
}
|
||||||
|
is Value -> when (other) {
|
||||||
|
Infinite -> -1
|
||||||
|
is Value -> value.compareTo(other.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val User = Value(0)
|
||||||
|
val Moderator = Value(50)
|
||||||
|
val Admin = Value(100)
|
||||||
|
val SuperAdmin = Value(150)
|
||||||
|
}
|
||||||
|
}
|
@@ -24,6 +24,7 @@ import org.matrix.android.sdk.api.session.room.model.GuestAccess
|
|||||||
import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility
|
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.RoomJoinRules
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesAllowEntry
|
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesAllowEntry
|
||||||
|
import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels
|
||||||
import org.matrix.android.sdk.api.util.JsonDict
|
import org.matrix.android.sdk.api.util.JsonDict
|
||||||
import org.matrix.android.sdk.api.util.Optional
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
|
|
||||||
@@ -106,4 +107,6 @@ interface StateService {
|
|||||||
suspend fun setJoinRulePublic()
|
suspend fun setJoinRulePublic()
|
||||||
suspend fun setJoinRuleInviteOnly()
|
suspend fun setJoinRuleInviteOnly()
|
||||||
suspend fun setJoinRuleRestricted(allowList: List<String>)
|
suspend fun setJoinRuleRestricted(allowList: List<String>)
|
||||||
|
fun getRoomPowerLevels(): RoomPowerLevels
|
||||||
|
fun getRoomPowerLevelsLive(): LiveData<RoomPowerLevels>
|
||||||
}
|
}
|
||||||
|
@@ -95,7 +95,7 @@ import org.matrix.rustcomponents.sdk.crypto.ProgressListener as RustProgressList
|
|||||||
|
|
||||||
class CryptoLogger : Logger {
|
class CryptoLogger : Logger {
|
||||||
override fun log(logLine: String) {
|
override fun log(logLine: String) {
|
||||||
Timber.d(logLine)
|
Timber.d(logLine.trimEnd())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -38,7 +38,8 @@ internal class FormattedJsonHttpLogger(
|
|||||||
*/
|
*/
|
||||||
@Synchronized
|
@Synchronized
|
||||||
override fun log(message: String) {
|
override fun log(message: String) {
|
||||||
Timber.v(message)
|
Timber.v(message.take(20_000))
|
||||||
|
if (message.length > 20_000) return
|
||||||
|
|
||||||
// Try to log formatted Json only if there is a chance that [message] contains Json.
|
// Try to log formatted Json only if there is a chance that [message] contains Json.
|
||||||
// It can be only the case if we log the bodies of Http requests.
|
// It can be only the case if we log the bodies of Http requests.
|
||||||
|
@@ -17,15 +17,11 @@
|
|||||||
package org.matrix.android.sdk.internal.session.permalinks
|
package org.matrix.android.sdk.internal.session.permalinks
|
||||||
|
|
||||||
import org.matrix.android.sdk.api.MatrixPatterns.getServerName
|
import org.matrix.android.sdk.api.MatrixPatterns.getServerName
|
||||||
import org.matrix.android.sdk.api.query.QueryStringValue
|
|
||||||
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.members.roomMemberQueryParams
|
import org.matrix.android.sdk.api.session.room.members.roomMemberQueryParams
|
||||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||||
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
|
|
||||||
import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
|
|
||||||
import org.matrix.android.sdk.internal.di.UserId
|
import org.matrix.android.sdk.internal.di.UserId
|
||||||
import org.matrix.android.sdk.internal.session.room.RoomGetter
|
import org.matrix.android.sdk.internal.session.room.RoomGetter
|
||||||
|
import org.matrix.android.sdk.internal.session.room.powerlevels.getRoomPowerLevels
|
||||||
import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource
|
import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource
|
||||||
import java.net.URLEncoder
|
import java.net.URLEncoder
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@@ -101,10 +97,7 @@ internal class ViaParameterFinder @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun userCanInvite(userId: String, roomId: String): Boolean {
|
fun userCanInvite(userId: String, roomId: String): Boolean {
|
||||||
val powerLevelsHelper = stateEventDataSource.getStateEvent(roomId, EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty)
|
val roomPowerLevels = stateEventDataSource.getRoomPowerLevels(roomId)
|
||||||
?.content?.toModel<PowerLevelsContent>()
|
return roomPowerLevels.isUserAbleToInvite(userId)
|
||||||
?.let { PowerLevelsHelper(it) }
|
|
||||||
|
|
||||||
return powerLevelsHelper?.isUserAbleToInvite(userId) ?: false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -15,24 +15,20 @@
|
|||||||
*/
|
*/
|
||||||
package org.matrix.android.sdk.internal.session.pushers
|
package org.matrix.android.sdk.internal.session.pushers
|
||||||
|
|
||||||
import org.matrix.android.sdk.api.query.QueryStringValue
|
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
|
||||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
|
||||||
import org.matrix.android.sdk.api.session.pushrules.ConditionResolver
|
import org.matrix.android.sdk.api.session.pushrules.ConditionResolver
|
||||||
import org.matrix.android.sdk.api.session.pushrules.ContainsDisplayNameCondition
|
import org.matrix.android.sdk.api.session.pushrules.ContainsDisplayNameCondition
|
||||||
import org.matrix.android.sdk.api.session.pushrules.EventMatchCondition
|
import org.matrix.android.sdk.api.session.pushrules.EventMatchCondition
|
||||||
import org.matrix.android.sdk.api.session.pushrules.RoomMemberCountCondition
|
import org.matrix.android.sdk.api.session.pushrules.RoomMemberCountCondition
|
||||||
import org.matrix.android.sdk.api.session.pushrules.SenderNotificationPermissionCondition
|
import org.matrix.android.sdk.api.session.pushrules.SenderNotificationPermissionCondition
|
||||||
import org.matrix.android.sdk.api.session.room.getStateEvent
|
import org.matrix.android.sdk.api.session.room.getRoomPowerLevels
|
||||||
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
|
|
||||||
import org.matrix.android.sdk.internal.di.UserId
|
import org.matrix.android.sdk.internal.di.UserId
|
||||||
import org.matrix.android.sdk.internal.session.room.RoomGetter
|
import org.matrix.android.sdk.internal.session.room.RoomGetter
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class DefaultConditionResolver @Inject constructor(
|
internal class DefaultConditionResolver @Inject constructor(
|
||||||
private val roomGetter: RoomGetter,
|
private val roomGetter: RoomGetter,
|
||||||
@UserId private val userId: String
|
@UserId private val userId: String,
|
||||||
) : ConditionResolver {
|
) : ConditionResolver {
|
||||||
|
|
||||||
override fun resolveEventMatchCondition(
|
override fun resolveEventMatchCondition(
|
||||||
@@ -55,13 +51,8 @@ internal class DefaultConditionResolver @Inject constructor(
|
|||||||
): Boolean {
|
): Boolean {
|
||||||
val roomId = event.roomId ?: return false
|
val roomId = event.roomId ?: return false
|
||||||
val room = roomGetter.getRoom(roomId) ?: return false
|
val room = roomGetter.getRoom(roomId) ?: return false
|
||||||
|
val roomPowerLevels = room.getRoomPowerLevels()
|
||||||
val powerLevelsContent = room.getStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty)
|
return condition.isSatisfied(event, roomPowerLevels)
|
||||||
?.content
|
|
||||||
?.toModel<PowerLevelsContent>()
|
|
||||||
?: PowerLevelsContent()
|
|
||||||
|
|
||||||
return condition.isSatisfied(event, powerLevelsContent)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun resolveContainsDisplayNameCondition(
|
override fun resolveContainsDisplayNameCondition(
|
||||||
|
@@ -16,7 +16,6 @@
|
|||||||
package org.matrix.android.sdk.internal.session.room
|
package org.matrix.android.sdk.internal.session.room
|
||||||
|
|
||||||
import io.realm.Realm
|
import io.realm.Realm
|
||||||
import org.matrix.android.sdk.api.query.QueryStringValue
|
|
||||||
import org.matrix.android.sdk.api.session.crypto.verification.VerificationState
|
import org.matrix.android.sdk.api.session.crypto.verification.VerificationState
|
||||||
import org.matrix.android.sdk.api.session.events.model.AggregatedAnnotation
|
import org.matrix.android.sdk.api.session.events.model.AggregatedAnnotation
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
@@ -27,7 +26,6 @@ import org.matrix.android.sdk.api.session.events.model.content.EncryptedEventCon
|
|||||||
import org.matrix.android.sdk.api.session.events.model.getRelationContent
|
import org.matrix.android.sdk.api.session.events.model.getRelationContent
|
||||||
import org.matrix.android.sdk.api.session.events.model.toContent
|
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||||
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
|
|
||||||
import org.matrix.android.sdk.api.session.room.model.ReferencesAggregatedContent
|
import org.matrix.android.sdk.api.session.room.model.ReferencesAggregatedContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent
|
import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconLocationDataContent
|
import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconLocationDataContent
|
||||||
@@ -36,7 +34,6 @@ import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent
|
|||||||
import org.matrix.android.sdk.api.session.room.model.message.MessagePollResponseContent
|
import org.matrix.android.sdk.api.session.room.model.message.MessagePollResponseContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessageRelationContent
|
import org.matrix.android.sdk.api.session.room.model.message.MessageRelationContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.relation.ReactionContent
|
import org.matrix.android.sdk.api.session.room.model.relation.ReactionContent
|
||||||
import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
|
|
||||||
import org.matrix.android.sdk.internal.SessionManager
|
import org.matrix.android.sdk.internal.SessionManager
|
||||||
import org.matrix.android.sdk.internal.crypto.verification.toState
|
import org.matrix.android.sdk.internal.crypto.verification.toState
|
||||||
import org.matrix.android.sdk.internal.database.helper.findRootThreadEvent
|
import org.matrix.android.sdk.internal.database.helper.findRootThreadEvent
|
||||||
@@ -62,6 +59,7 @@ import org.matrix.android.sdk.internal.session.EventInsertLiveProcessor
|
|||||||
import org.matrix.android.sdk.internal.session.room.aggregation.livelocation.LiveLocationAggregationProcessor
|
import org.matrix.android.sdk.internal.session.room.aggregation.livelocation.LiveLocationAggregationProcessor
|
||||||
import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollAggregationProcessor
|
import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollAggregationProcessor
|
||||||
import org.matrix.android.sdk.internal.session.room.aggregation.utd.EncryptedReferenceAggregationProcessor
|
import org.matrix.android.sdk.internal.session.room.aggregation.utd.EncryptedReferenceAggregationProcessor
|
||||||
|
import org.matrix.android.sdk.internal.session.room.powerlevels.getRoomPowerLevels
|
||||||
import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource
|
import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource
|
||||||
import org.matrix.android.sdk.internal.util.time.Clock
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
@@ -216,9 +214,8 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
|
|||||||
}
|
}
|
||||||
in EventType.POLL_END.values -> {
|
in EventType.POLL_END.values -> {
|
||||||
sessionManager.getSessionComponent(sessionId)?.session()?.let { session ->
|
sessionManager.getSessionComponent(sessionId)?.session()?.let { session ->
|
||||||
getPowerLevelsHelper(event.roomId)?.let {
|
val roomPowerLevels = stateEventDataSource.getRoomPowerLevels(event.roomId)
|
||||||
pollAggregationProcessor.handlePollEndEvent(session, it, realm, event)
|
pollAggregationProcessor.handlePollEndEvent(session, roomPowerLevels, realm, event)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
in EventType.STATE_ROOM_BEACON_INFO.values -> {
|
in EventType.STATE_ROOM_BEACON_INFO.values -> {
|
||||||
@@ -381,12 +378,6 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getPowerLevelsHelper(roomId: String): PowerLevelsHelper? {
|
|
||||||
return stateEventDataSource.getStateEvent(roomId, EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty)
|
|
||||||
?.content?.toModel<PowerLevelsContent>()
|
|
||||||
?.let { PowerLevelsHelper(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleInitialAggregatedRelations(
|
private fun handleInitialAggregatedRelations(
|
||||||
realm: Realm,
|
realm: Realm,
|
||||||
event: Event,
|
event: Event,
|
||||||
|
@@ -32,7 +32,7 @@ import org.matrix.android.sdk.api.session.room.model.VoteInfo
|
|||||||
import org.matrix.android.sdk.api.session.room.model.VoteSummary
|
import org.matrix.android.sdk.api.session.room.model.VoteSummary
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent
|
import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessagePollResponseContent
|
import org.matrix.android.sdk.api.session.room.model.message.MessagePollResponseContent
|
||||||
import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
|
import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent
|
import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent
|
||||||
import org.matrix.android.sdk.internal.database.mapper.ContentMapper
|
import org.matrix.android.sdk.internal.database.mapper.ContentMapper
|
||||||
@@ -160,13 +160,13 @@ internal class DefaultPollAggregationProcessor @Inject constructor(
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun handlePollEndEvent(session: Session, powerLevelsHelper: PowerLevelsHelper, realm: Realm, event: Event): Boolean {
|
override fun handlePollEndEvent(session: Session, roomPowerLevels: RoomPowerLevels, realm: Realm, event: Event): Boolean {
|
||||||
val roomId = event.roomId ?: return false
|
val roomId = event.roomId ?: return false
|
||||||
val pollEventId = event.getRelationContent()?.eventId ?: return false
|
val pollEventId = event.getRelationContent()?.eventId ?: return false
|
||||||
val pollOwnerId = getPollEvent(session, roomId, pollEventId)?.root?.senderId
|
val pollOwnerId = getPollEvent(session, roomId, pollEventId)?.root?.senderId
|
||||||
val isPollOwner = pollOwnerId == event.senderId
|
val isPollOwner = pollOwnerId == event.senderId
|
||||||
|
|
||||||
if (!isPollOwner && !powerLevelsHelper.isUserAbleToRedact(event.senderId ?: "")) {
|
if (!isPollOwner && !roomPowerLevels.isUserAbleToRedact(event.senderId ?: "")) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -19,7 +19,7 @@ package org.matrix.android.sdk.internal.session.room.aggregation.poll
|
|||||||
import io.realm.Realm
|
import io.realm.Realm
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
|
import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels
|
||||||
|
|
||||||
internal interface PollAggregationProcessor {
|
internal interface PollAggregationProcessor {
|
||||||
/**
|
/**
|
||||||
@@ -48,7 +48,7 @@ internal interface PollAggregationProcessor {
|
|||||||
*/
|
*/
|
||||||
fun handlePollEndEvent(
|
fun handlePollEndEvent(
|
||||||
session: Session,
|
session: Session,
|
||||||
powerLevelsHelper: PowerLevelsHelper,
|
roomPowerLevels: RoomPowerLevels,
|
||||||
realm: Realm,
|
realm: Realm,
|
||||||
event: Event
|
event: Event
|
||||||
): Boolean
|
): Boolean
|
||||||
|
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2025 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.matrix.android.sdk.internal.session.room.powerlevels
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MediatorLiveData
|
||||||
|
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.toModel
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.create.getRoomCreateContentWithSender
|
||||||
|
import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels
|
||||||
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
|
import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource
|
||||||
|
|
||||||
|
internal fun StateEventDataSource.getRoomPowerLevels(roomId: String): RoomPowerLevels {
|
||||||
|
val powerLevelsEvent = getStateEvent(roomId, EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty)
|
||||||
|
val roomCreateEvent = getStateEvent(roomId, EventType.STATE_ROOM_CREATE, QueryStringValue.IsEmpty)
|
||||||
|
return createRoomPowerLevels(
|
||||||
|
powerLevelsEvent = powerLevelsEvent,
|
||||||
|
roomCreateEvent = roomCreateEvent
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun StateEventDataSource.getRoomPowerLevelsLive(roomId: String): LiveData<RoomPowerLevels> {
|
||||||
|
val powerLevelsEventLive = getStateEventLive(roomId, EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty)
|
||||||
|
val roomCreateEventLive = getStateEventLive(roomId, EventType.STATE_ROOM_CREATE, QueryStringValue.IsEmpty)
|
||||||
|
val resultLiveData = MediatorLiveData<RoomPowerLevels>()
|
||||||
|
|
||||||
|
fun emitIfReady(powerLevelEvent: Optional<Event>?, roomCreateEvent: Optional<Event>?) {
|
||||||
|
if (powerLevelEvent != null && roomCreateEvent != null) {
|
||||||
|
val roomPowerLevels = createRoomPowerLevels(
|
||||||
|
powerLevelsEvent = powerLevelEvent.getOrNull(),
|
||||||
|
roomCreateEvent = roomCreateEvent.getOrNull()
|
||||||
|
)
|
||||||
|
resultLiveData.postValue(roomPowerLevels)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resultLiveData.apply {
|
||||||
|
var powerLevelEvent: Optional<Event>? = null
|
||||||
|
var roomCreateEvent: Optional<Event>? = null
|
||||||
|
|
||||||
|
addSource(powerLevelsEventLive) {
|
||||||
|
powerLevelEvent = it
|
||||||
|
emitIfReady(powerLevelEvent, roomCreateEvent)
|
||||||
|
}
|
||||||
|
addSource(roomCreateEventLive) {
|
||||||
|
roomCreateEvent = it
|
||||||
|
emitIfReady(powerLevelEvent, roomCreateEvent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resultLiveData
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createRoomPowerLevels(powerLevelsEvent: Event?, roomCreateEvent: Event?): RoomPowerLevels {
|
||||||
|
val powerLevelsContent = powerLevelsEvent?.content?.toModel<PowerLevelsContent>()
|
||||||
|
val roomCreateContent = roomCreateEvent?.getRoomCreateContentWithSender()
|
||||||
|
return RoomPowerLevels(powerLevelsContent, roomCreateContent)
|
||||||
|
}
|
@@ -31,11 +31,14 @@ 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.RoomJoinRules
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesAllowEntry
|
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesAllowEntry
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesContent
|
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesContent
|
||||||
|
import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels
|
||||||
import org.matrix.android.sdk.api.session.room.state.StateService
|
import org.matrix.android.sdk.api.session.room.state.StateService
|
||||||
import org.matrix.android.sdk.api.util.JsonDict
|
import org.matrix.android.sdk.api.util.JsonDict
|
||||||
import org.matrix.android.sdk.api.util.MimeTypes
|
import org.matrix.android.sdk.api.util.MimeTypes
|
||||||
import org.matrix.android.sdk.api.util.Optional
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
import org.matrix.android.sdk.internal.session.content.FileUploader
|
import org.matrix.android.sdk.internal.session.content.FileUploader
|
||||||
|
import org.matrix.android.sdk.internal.session.room.powerlevels.getRoomPowerLevels
|
||||||
|
import org.matrix.android.sdk.internal.session.room.powerlevels.getRoomPowerLevelsLive
|
||||||
|
|
||||||
internal class DefaultStateService @AssistedInject constructor(
|
internal class DefaultStateService @AssistedInject constructor(
|
||||||
@Assisted private val roomId: String,
|
@Assisted private val roomId: String,
|
||||||
@@ -65,6 +68,14 @@ internal class DefaultStateService @AssistedInject constructor(
|
|||||||
return stateEventDataSource.getStateEventsLive(roomId, eventTypes, stateKey)
|
return stateEventDataSource.getStateEventsLive(roomId, eventTypes, stateKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getRoomPowerLevels(): RoomPowerLevels {
|
||||||
|
return stateEventDataSource.getRoomPowerLevels(roomId)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getRoomPowerLevelsLive(): LiveData<RoomPowerLevels> {
|
||||||
|
return stateEventDataSource.getRoomPowerLevelsLive(roomId)
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun sendStateEvent(
|
override suspend fun sendStateEvent(
|
||||||
eventType: String,
|
eventType: String,
|
||||||
stateKey: String,
|
stateKey: String,
|
||||||
|
@@ -34,7 +34,8 @@ import org.matrix.android.sdk.api.session.room.model.RoomTopicContent
|
|||||||
import org.matrix.android.sdk.api.session.room.model.RoomType
|
import org.matrix.android.sdk.api.session.room.model.RoomType
|
||||||
import org.matrix.android.sdk.api.session.room.model.VersioningState
|
import org.matrix.android.sdk.api.session.room.model.VersioningState
|
||||||
import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent
|
import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent
|
||||||
import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
|
import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContentWithSender
|
||||||
|
import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels
|
||||||
import org.matrix.android.sdk.api.session.room.send.SendState
|
import org.matrix.android.sdk.api.session.room.send.SendState
|
||||||
import org.matrix.android.sdk.api.session.sync.model.RoomSyncSummary
|
import org.matrix.android.sdk.api.session.sync.model.RoomSyncSummary
|
||||||
import org.matrix.android.sdk.api.session.sync.model.RoomSyncUnreadNotifications
|
import org.matrix.android.sdk.api.session.sync.model.RoomSyncUnreadNotifications
|
||||||
@@ -313,13 +314,25 @@ internal class RoomSummaryUpdater @Inject constructor(
|
|||||||
// check if sender can post child relation in parent?
|
// check if sender can post child relation in parent?
|
||||||
val senderId = parentInfo.stateEventSender
|
val senderId = parentInfo.stateEventSender
|
||||||
val parentRoomId = parentInfo.roomId
|
val parentRoomId = parentInfo.roomId
|
||||||
val powerLevelsHelper = CurrentStateEventEntity
|
val powerLevelsContent = CurrentStateEventEntity
|
||||||
.getOrNull(realm, parentRoomId, "", EventType.STATE_ROOM_POWER_LEVELS)
|
.getOrNull(realm, parentRoomId, "", EventType.STATE_ROOM_POWER_LEVELS)
|
||||||
?.root
|
?.root
|
||||||
?.let { ContentMapper.map(it.content).toModel<PowerLevelsContent>() }
|
?.let { ContentMapper.map(it.content).toModel<PowerLevelsContent>() }
|
||||||
?.let { PowerLevelsHelper(it) }
|
|
||||||
|
|
||||||
isValidRelation = powerLevelsHelper?.isUserAllowedToSend(senderId, true, EventType.STATE_SPACE_CHILD) ?: false
|
val roomCreateContent = CurrentStateEventEntity
|
||||||
|
.getOrNull(realm, parentRoomId, "", EventType.STATE_ROOM_CREATE)
|
||||||
|
?.root
|
||||||
|
?.let {
|
||||||
|
val content = ContentMapper.map(it.content).toModel<RoomCreateContent>()
|
||||||
|
val sender = it.sender
|
||||||
|
if (content != null && sender != null) {
|
||||||
|
RoomCreateContentWithSender(sender, content)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val roomPowerLevels = RoomPowerLevels(powerLevelsContent, roomCreateContent)
|
||||||
|
isValidRelation = roomPowerLevels.isUserAllowedToSend(senderId, true, EventType.STATE_SPACE_CHILD)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isValidRelation) {
|
if (isValidRelation) {
|
||||||
|
@@ -23,11 +23,10 @@ import org.matrix.android.sdk.api.query.QueryStringValue
|
|||||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||||
import org.matrix.android.sdk.api.session.homeserver.RoomVersionStatus
|
import org.matrix.android.sdk.api.session.homeserver.RoomVersionStatus
|
||||||
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
|
|
||||||
import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent
|
import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent
|
||||||
import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
|
|
||||||
import org.matrix.android.sdk.api.session.room.version.RoomVersionService
|
import org.matrix.android.sdk.api.session.room.version.RoomVersionService
|
||||||
import org.matrix.android.sdk.internal.session.homeserver.HomeServerCapabilitiesDataSource
|
import org.matrix.android.sdk.internal.session.homeserver.HomeServerCapabilitiesDataSource
|
||||||
|
import org.matrix.android.sdk.internal.session.room.powerlevels.getRoomPowerLevels
|
||||||
import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource
|
import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource
|
||||||
|
|
||||||
internal class DefaultRoomVersionService @AssistedInject constructor(
|
internal class DefaultRoomVersionService @AssistedInject constructor(
|
||||||
@@ -71,11 +70,8 @@ internal class DefaultRoomVersionService @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun userMayUpgradeRoom(userId: String): Boolean {
|
override fun userMayUpgradeRoom(userId: String): Boolean {
|
||||||
val powerLevelsHelper = stateEventDataSource.getStateEvent(roomId, EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty)
|
val roomPowerLevels = stateEventDataSource.getRoomPowerLevels(roomId)
|
||||||
?.content?.toModel<PowerLevelsContent>()
|
return roomPowerLevels.isUserAllowedToSend(userId, true, EventType.STATE_ROOM_TOMBSTONE)
|
||||||
?.let { PowerLevelsHelper(it) }
|
|
||||||
|
|
||||||
return powerLevelsHelper?.isUserAllowedToSend(userId, true, EventType.STATE_ROOM_TOMBSTONE) ?: false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@@ -35,8 +35,7 @@ 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.RoomSummary
|
||||||
import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
|
import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
|
||||||
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomPreset
|
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomPreset
|
||||||
import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
|
import org.matrix.android.sdk.api.session.room.powerlevels.UserPowerLevel
|
||||||
import org.matrix.android.sdk.api.session.room.powerlevels.Role
|
|
||||||
import org.matrix.android.sdk.api.session.space.CreateSpaceParams
|
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.JoinSpaceResult
|
||||||
import org.matrix.android.sdk.api.session.space.Space
|
import org.matrix.android.sdk.api.session.space.Space
|
||||||
@@ -47,11 +46,13 @@ import org.matrix.android.sdk.api.session.space.model.SpaceChildContent
|
|||||||
import org.matrix.android.sdk.api.session.space.model.SpaceChildSummaryEvent
|
import org.matrix.android.sdk.api.session.space.model.SpaceChildSummaryEvent
|
||||||
import org.matrix.android.sdk.api.session.space.model.SpaceParentContent
|
import org.matrix.android.sdk.api.session.space.model.SpaceParentContent
|
||||||
import org.matrix.android.sdk.api.session.space.peeking.SpacePeekResult
|
import org.matrix.android.sdk.api.session.space.peeking.SpacePeekResult
|
||||||
|
import org.matrix.android.sdk.api.session.user.model.User
|
||||||
import org.matrix.android.sdk.internal.di.UserId
|
import org.matrix.android.sdk.internal.di.UserId
|
||||||
import org.matrix.android.sdk.internal.session.room.RoomGetter
|
import org.matrix.android.sdk.internal.session.room.RoomGetter
|
||||||
import org.matrix.android.sdk.internal.session.room.SpaceGetter
|
import org.matrix.android.sdk.internal.session.room.SpaceGetter
|
||||||
import org.matrix.android.sdk.internal.session.room.create.CreateRoomTask
|
import org.matrix.android.sdk.internal.session.room.create.CreateRoomTask
|
||||||
import org.matrix.android.sdk.internal.session.room.membership.leaving.LeaveRoomTask
|
import org.matrix.android.sdk.internal.session.room.membership.leaving.LeaveRoomTask
|
||||||
|
import org.matrix.android.sdk.internal.session.room.powerlevels.getRoomPowerLevels
|
||||||
import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource
|
import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource
|
||||||
import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryDataSource
|
import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryDataSource
|
||||||
import org.matrix.android.sdk.internal.session.space.peeking.PeekSpaceTask
|
import org.matrix.android.sdk.internal.session.space.peeking.PeekSpaceTask
|
||||||
@@ -83,7 +84,7 @@ internal class DefaultSpaceService @Inject constructor(
|
|||||||
if (isPublic) {
|
if (isPublic) {
|
||||||
this.roomAliasName = roomAliasLocalPart
|
this.roomAliasName = roomAliasLocalPart
|
||||||
this.powerLevelContentOverride = (powerLevelContentOverride ?: PowerLevelsContent()).copy(
|
this.powerLevelContentOverride = (powerLevelContentOverride ?: PowerLevelsContent()).copy(
|
||||||
invite = if (isPublic) Role.Default.value else Role.Moderator.value
|
invite = UserPowerLevel.User.value
|
||||||
)
|
)
|
||||||
this.preset = CreateRoomPreset.PRESET_PUBLIC_CHAT
|
this.preset = CreateRoomPreset.PRESET_PUBLIC_CHAT
|
||||||
this.historyVisibility = RoomHistoryVisibility.WORLD_READABLE
|
this.historyVisibility = RoomHistoryVisibility.WORLD_READABLE
|
||||||
@@ -253,15 +254,8 @@ internal class DefaultSpaceService @Inject constructor(
|
|||||||
if (roomSummaryDataSource.getRoomSummary(parentSpaceId)?.membership != Membership.JOIN) {
|
if (roomSummaryDataSource.getRoomSummary(parentSpaceId)?.membership != Membership.JOIN) {
|
||||||
throw UnsupportedOperationException("Cannot add canonical child if not member of parent")
|
throw UnsupportedOperationException("Cannot add canonical child if not member of parent")
|
||||||
}
|
}
|
||||||
val powerLevelsEvent = stateEventDataSource.getStateEvent(
|
val roomPowerLevels = stateEventDataSource.getRoomPowerLevels(parentSpaceId)
|
||||||
roomId = parentSpaceId,
|
if (!roomPowerLevels.isUserAllowedToSend(userId, true, EventType.STATE_SPACE_CHILD)) {
|
||||||
eventType = EventType.STATE_ROOM_POWER_LEVELS,
|
|
||||||
stateKey = QueryStringValue.IsEmpty
|
|
||||||
)
|
|
||||||
val powerLevelsContent = powerLevelsEvent?.content?.toModel<PowerLevelsContent>()
|
|
||||||
?: throw UnsupportedOperationException("Cannot add canonical child, missing power level")
|
|
||||||
val powerLevelsHelper = PowerLevelsHelper(powerLevelsContent)
|
|
||||||
if (!powerLevelsHelper.isUserAllowedToSend(userId, true, EventType.STATE_SPACE_CHILD)) {
|
|
||||||
throw UnsupportedOperationException("Cannot add canonical child, not enough power level")
|
throw UnsupportedOperationException("Cannot add canonical child, not enough power level")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -30,15 +30,13 @@ import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes
|
|||||||
import org.matrix.android.sdk.api.session.events.model.Content
|
import org.matrix.android.sdk.api.session.events.model.Content
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
|
||||||
import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerService
|
import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerService
|
||||||
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
|
|
||||||
import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
|
|
||||||
import org.matrix.android.sdk.api.session.widgets.WidgetManagementFailure
|
import org.matrix.android.sdk.api.session.widgets.WidgetManagementFailure
|
||||||
import org.matrix.android.sdk.api.session.widgets.model.Widget
|
import org.matrix.android.sdk.api.session.widgets.model.Widget
|
||||||
import org.matrix.android.sdk.internal.di.UserId
|
import org.matrix.android.sdk.internal.di.UserId
|
||||||
import org.matrix.android.sdk.internal.session.SessionScope
|
import org.matrix.android.sdk.internal.session.SessionScope
|
||||||
import org.matrix.android.sdk.internal.session.integrationmanager.IntegrationManager
|
import org.matrix.android.sdk.internal.session.integrationmanager.IntegrationManager
|
||||||
|
import org.matrix.android.sdk.internal.session.room.powerlevels.getRoomPowerLevels
|
||||||
import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource
|
import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource
|
||||||
import org.matrix.android.sdk.internal.session.user.accountdata.UserAccountDataDataSource
|
import org.matrix.android.sdk.internal.session.user.accountdata.UserAccountDataDataSource
|
||||||
import org.matrix.android.sdk.internal.session.widgets.helper.WidgetFactory
|
import org.matrix.android.sdk.internal.session.widgets.helper.WidgetFactory
|
||||||
@@ -200,12 +198,7 @@ internal class WidgetManager @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun hasPermissionsToHandleWidgets(roomId: String): Boolean {
|
fun hasPermissionsToHandleWidgets(roomId: String): Boolean {
|
||||||
val powerLevelsEvent = stateEventDataSource.getStateEvent(
|
val roomPowerLevels = stateEventDataSource.getRoomPowerLevels(roomId)
|
||||||
roomId = roomId,
|
return roomPowerLevels.isUserAllowedToSend(userId, true, EventType.STATE_ROOM_WIDGET_LEGACY)
|
||||||
eventType = EventType.STATE_ROOM_POWER_LEVELS,
|
|
||||||
stateKey = QueryStringValue.IsEmpty
|
|
||||||
)
|
|
||||||
val powerLevelsContent = powerLevelsEvent?.content?.toModel<PowerLevelsContent>() ?: return false
|
|
||||||
return PowerLevelsHelper(powerLevelsContent).isUserAllowedToSend(userId, true, EventType.STATE_ROOM_WIDGET_LEGACY)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -33,7 +33,7 @@ import org.matrix.android.sdk.api.session.Session
|
|||||||
import org.matrix.android.sdk.api.session.getRoom
|
import org.matrix.android.sdk.api.session.getRoom
|
||||||
import org.matrix.android.sdk.api.session.room.Room
|
import org.matrix.android.sdk.api.session.room.Room
|
||||||
import org.matrix.android.sdk.api.session.room.getTimelineEvent
|
import org.matrix.android.sdk.api.session.room.getTimelineEvent
|
||||||
import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
|
import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels
|
||||||
import org.matrix.android.sdk.internal.database.model.EventAnnotationsSummaryEntity
|
import org.matrix.android.sdk.internal.database.model.EventAnnotationsSummaryEntity
|
||||||
import org.matrix.android.sdk.internal.database.model.EventAnnotationsSummaryEntityFields
|
import org.matrix.android.sdk.internal.database.model.EventAnnotationsSummaryEntityFields
|
||||||
import org.matrix.android.sdk.internal.database.model.PollResponseAggregatedSummaryEntity
|
import org.matrix.android.sdk.internal.database.model.PollResponseAggregatedSummaryEntity
|
||||||
@@ -255,9 +255,9 @@ class DefaultPollAggregationProcessorTest {
|
|||||||
every { room.getTimelineEvent(eventId) } returns if (hasExistingTimelineEvent) A_TIMELINE_EVENT else null
|
every { room.getTimelineEvent(eventId) } returns if (hasExistingTimelineEvent) A_TIMELINE_EVENT else null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun mockRedactionPowerLevels(userId: String, isAbleToRedact: Boolean): PowerLevelsHelper {
|
private fun mockRedactionPowerLevels(userId: String, isAbleToRedact: Boolean): RoomPowerLevels {
|
||||||
val powerLevelsHelper = mockk<PowerLevelsHelper>()
|
val roomPowerLevels = mockk<RoomPowerLevels>()
|
||||||
every { powerLevelsHelper.isUserAbleToRedact(userId) } returns isAbleToRedact
|
every { roomPowerLevels.isUserAbleToRedact(userId) } returns isAbleToRedact
|
||||||
return powerLevelsHelper
|
return roomPowerLevels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -51,7 +51,7 @@ import org.matrix.android.sdk.api.session.room.model.RoomThirdPartyInviteContent
|
|||||||
import org.matrix.android.sdk.api.session.room.model.RoomTopicContent
|
import org.matrix.android.sdk.api.session.room.model.RoomTopicContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomPreset
|
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.model.create.RoomCreateContent
|
||||||
import org.matrix.android.sdk.api.session.room.powerlevels.Role
|
import org.matrix.android.sdk.api.session.room.powerlevels.UserPowerLevel
|
||||||
import org.matrix.android.sdk.api.session.user.UserService
|
import org.matrix.android.sdk.api.session.user.UserService
|
||||||
import org.matrix.android.sdk.api.session.user.model.User
|
import org.matrix.android.sdk.api.session.user.model.User
|
||||||
import org.matrix.android.sdk.internal.session.profile.ThirdPartyIdentifier.Companion.MEDIUM_EMAIL
|
import org.matrix.android.sdk.internal.session.profile.ThirdPartyIdentifier.Companion.MEDIUM_EMAIL
|
||||||
@@ -372,13 +372,13 @@ internal class DefaultCreateLocalRoomStateEventsTaskTest {
|
|||||||
// Power levels
|
// Power levels
|
||||||
val powerLevelsContent = result.find { it.type == EventType.STATE_ROOM_POWER_LEVELS }?.content.toModel<PowerLevelsContent>()
|
val powerLevelsContent = result.find { it.type == EventType.STATE_ROOM_POWER_LEVELS }?.content.toModel<PowerLevelsContent>()
|
||||||
powerLevelsContent.shouldNotBeNull()
|
powerLevelsContent.shouldNotBeNull()
|
||||||
powerLevelsContent.ban shouldBeEqualTo Role.Moderator.value
|
powerLevelsContent.ban shouldBeEqualTo UserPowerLevel.Moderator.value
|
||||||
powerLevelsContent.kick shouldBeEqualTo Role.Moderator.value
|
powerLevelsContent.kick shouldBeEqualTo UserPowerLevel.Moderator.value
|
||||||
powerLevelsContent.invite shouldBeEqualTo Role.Moderator.value
|
powerLevelsContent.invite shouldBeEqualTo UserPowerLevel.User.value
|
||||||
powerLevelsContent.redact shouldBeEqualTo Role.Moderator.value
|
powerLevelsContent.redact shouldBeEqualTo UserPowerLevel.Moderator.value
|
||||||
powerLevelsContent.eventsDefault shouldBeEqualTo Role.Default.value
|
powerLevelsContent.eventsDefault shouldBeEqualTo UserPowerLevel.User.value
|
||||||
powerLevelsContent.usersDefault shouldBeEqualTo Role.Default.value
|
powerLevelsContent.usersDefault shouldBeEqualTo UserPowerLevel.User.value
|
||||||
powerLevelsContent.stateDefault shouldBeEqualTo Role.Moderator.value
|
powerLevelsContent.stateDefault shouldBeEqualTo UserPowerLevel.Moderator.value
|
||||||
// Guest access
|
// Guest access
|
||||||
result.find { it.type == EventType.STATE_ROOM_GUEST_ACCESS }
|
result.find { it.type == EventType.STATE_ROOM_GUEST_ACCESS }
|
||||||
?.content.toModel<RoomGuestAccessContent>()?.guestAccess shouldBeEqualTo GuestAccess.Forbidden
|
?.content.toModel<RoomGuestAccessContent>()?.guestAccess shouldBeEqualTo GuestAccess.Forbidden
|
||||||
|
@@ -37,7 +37,7 @@ ext.versionMinor = 6
|
|||||||
// Note: even values are reserved for regular release, odd values for hotfix release.
|
// Note: even values are reserved for regular release, odd values for hotfix release.
|
||||||
// When creating a hotfix, you should decrease the value, since the current value
|
// When creating a hotfix, you should decrease the value, since the current value
|
||||||
// is the value for the next regular release.
|
// is the value for the next regular release.
|
||||||
ext.versionPatch = 42
|
ext.versionPatch = 44
|
||||||
|
|
||||||
static def getGitTimestamp() {
|
static def getGitTimestamp() {
|
||||||
def cmd = 'git show -s --format=%ct'
|
def cmd = 'git show -s --format=%ct'
|
||||||
|
@@ -12,6 +12,7 @@ import android.app.NotificationChannel
|
|||||||
import android.app.NotificationManager
|
import android.app.NotificationManager
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import android.view.View
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.app.Person
|
import androidx.core.app.Person
|
||||||
import androidx.core.content.getSystemService
|
import androidx.core.content.getSystemService
|
||||||
@@ -49,7 +50,9 @@ import javax.inject.Inject
|
|||||||
class DebugMenuActivity : VectorBaseActivity<ActivityDebugMenuBinding>() {
|
class DebugMenuActivity : VectorBaseActivity<ActivityDebugMenuBinding>() {
|
||||||
|
|
||||||
override fun getBinding() = ActivityDebugMenuBinding.inflate(layoutInflater)
|
override fun getBinding() = ActivityDebugMenuBinding.inflate(layoutInflater)
|
||||||
|
override fun getCoordinatorLayout() = views.coordinatorLayout
|
||||||
|
override val rootView: View
|
||||||
|
get() = views.coordinatorLayout
|
||||||
@Inject lateinit var clock: Clock
|
@Inject lateinit var clock: Clock
|
||||||
|
|
||||||
private lateinit var buffer: ByteArray
|
private lateinit var buffer: ByteArray
|
||||||
|
@@ -10,6 +10,7 @@ package im.vector.app.features.debug
|
|||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import android.view.View
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.core.app.ActivityCompat
|
import androidx.core.app.ActivityCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
@@ -32,6 +33,9 @@ class DebugPermissionActivity : VectorBaseActivity<ActivityDebugPermissionBindin
|
|||||||
|
|
||||||
override fun getCoordinatorLayout() = views.coordinatorLayout
|
override fun getCoordinatorLayout() = views.coordinatorLayout
|
||||||
|
|
||||||
|
override val rootView: View
|
||||||
|
get() = views.coordinatorLayout
|
||||||
|
|
||||||
// For debug
|
// For debug
|
||||||
private val allPermissions = listOf(
|
private val allPermissions = listOf(
|
||||||
Manifest.permission.CAMERA,
|
Manifest.permission.CAMERA,
|
||||||
|
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
package im.vector.app.features.debug.analytics
|
package im.vector.app.features.debug.analytics
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import im.vector.app.core.extensions.addFragment
|
import im.vector.app.core.extensions.addFragment
|
||||||
import im.vector.app.core.platform.VectorBaseActivity
|
import im.vector.app.core.platform.VectorBaseActivity
|
||||||
@@ -17,6 +18,10 @@ class DebugAnalyticsActivity : VectorBaseActivity<ActivitySimpleBinding>() {
|
|||||||
|
|
||||||
override fun getBinding() = ActivitySimpleBinding.inflate(layoutInflater)
|
override fun getBinding() = ActivitySimpleBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
|
override fun getCoordinatorLayout() = views.coordinatorLayout
|
||||||
|
override val rootView: View
|
||||||
|
get() = views.coordinatorLayout
|
||||||
|
|
||||||
override fun initUiAndData() {
|
override fun initUiAndData() {
|
||||||
if (isFirstCreation()) {
|
if (isFirstCreation()) {
|
||||||
addFragment(
|
addFragment(
|
||||||
|
@@ -8,6 +8,7 @@
|
|||||||
package im.vector.app.features.debug.features
|
package im.vector.app.features.debug.features
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import im.vector.app.core.extensions.cleanup
|
import im.vector.app.core.extensions.cleanup
|
||||||
import im.vector.app.core.extensions.configureWith
|
import im.vector.app.core.extensions.configureWith
|
||||||
@@ -24,6 +25,9 @@ class DebugFeaturesSettingsActivity : VectorBaseActivity<FragmentGenericRecycler
|
|||||||
|
|
||||||
override fun getBinding() = FragmentGenericRecyclerBinding.inflate(layoutInflater)
|
override fun getBinding() = FragmentGenericRecyclerBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
|
override val rootView: View
|
||||||
|
get() = views.mainRoot
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
controller.listener = object : FeaturesController.Listener {
|
controller.listener = object : FeaturesController.Listener {
|
||||||
|
@@ -8,6 +8,7 @@
|
|||||||
package im.vector.app.features.debug.jitsi
|
package im.vector.app.features.debug.jitsi
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
|
import android.view.View
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import im.vector.app.core.platform.VectorBaseActivity
|
import im.vector.app.core.platform.VectorBaseActivity
|
||||||
import im.vector.application.databinding.ActivityDebugJitsiBinding
|
import im.vector.application.databinding.ActivityDebugJitsiBinding
|
||||||
@@ -19,6 +20,8 @@ class DebugJitsiActivity : VectorBaseActivity<ActivityDebugJitsiBinding>() {
|
|||||||
override fun getBinding() = ActivityDebugJitsiBinding.inflate(layoutInflater)
|
override fun getBinding() = ActivityDebugJitsiBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
override fun getCoordinatorLayout() = views.coordinatorLayout
|
override fun getCoordinatorLayout() = views.coordinatorLayout
|
||||||
|
override val rootView: View
|
||||||
|
get() = views.coordinatorLayout
|
||||||
|
|
||||||
@SuppressLint("SetTextI18n")
|
@SuppressLint("SetTextI18n")
|
||||||
override fun initUiAndData() {
|
override fun initUiAndData() {
|
||||||
|
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
package im.vector.app.features.debug.leak
|
package im.vector.app.features.debug.leak
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import im.vector.app.core.extensions.addFragment
|
import im.vector.app.core.extensions.addFragment
|
||||||
import im.vector.app.core.platform.VectorBaseActivity
|
import im.vector.app.core.platform.VectorBaseActivity
|
||||||
@@ -17,6 +18,10 @@ class DebugMemoryLeaksActivity : VectorBaseActivity<ActivitySimpleBinding>() {
|
|||||||
|
|
||||||
override fun getBinding() = ActivitySimpleBinding.inflate(layoutInflater)
|
override fun getBinding() = ActivitySimpleBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
|
override fun getCoordinatorLayout() = views.coordinatorLayout
|
||||||
|
override val rootView: View
|
||||||
|
get() = views.coordinatorLayout
|
||||||
|
|
||||||
override fun initUiAndData() {
|
override fun initUiAndData() {
|
||||||
if (isFirstCreation()) {
|
if (isFirstCreation()) {
|
||||||
addFragment(
|
addFragment(
|
||||||
|
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
package im.vector.app.features.debug.settings
|
package im.vector.app.features.debug.settings
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import im.vector.app.core.extensions.addFragment
|
import im.vector.app.core.extensions.addFragment
|
||||||
import im.vector.app.core.platform.VectorBaseActivity
|
import im.vector.app.core.platform.VectorBaseActivity
|
||||||
@@ -17,6 +18,10 @@ class DebugPrivateSettingsActivity : VectorBaseActivity<ActivitySimpleBinding>()
|
|||||||
|
|
||||||
override fun getBinding() = ActivitySimpleBinding.inflate(layoutInflater)
|
override fun getBinding() = ActivitySimpleBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
|
override fun getCoordinatorLayout() = views.coordinatorLayout
|
||||||
|
override val rootView: View
|
||||||
|
get() = views.coordinatorLayout
|
||||||
|
|
||||||
override fun initUiAndData() {
|
override fun initUiAndData() {
|
||||||
if (isFirstCreation()) {
|
if (isFirstCreation()) {
|
||||||
addFragment(
|
addFragment(
|
||||||
|
@@ -161,6 +161,7 @@ class VectorApplication :
|
|||||||
"Noto Color Emoji Compat",
|
"Noto Color Emoji Compat",
|
||||||
R.array.com_google_android_gms_fonts_certs
|
R.array.com_google_android_gms_fonts_certs
|
||||||
)
|
)
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
FontsContractCompat.requestFont(this, fontRequest, emojiCompatFontProvider, getFontThreadHandler())
|
FontsContractCompat.requestFont(this, fontRequest, emojiCompatFontProvider, getFontThreadHandler())
|
||||||
vectorLocale.init()
|
vectorLocale.init()
|
||||||
ThemeUtils.init(this)
|
ThemeUtils.init(this)
|
||||||
|
@@ -167,8 +167,7 @@
|
|||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".features.home.room.detail.RoomDetailActivity"
|
android:name=".features.home.room.detail.RoomDetailActivity"
|
||||||
android:parentActivityName=".features.home.HomeActivity"
|
android:parentActivityName=".features.home.HomeActivity">
|
||||||
android:windowSoftInputMode="adjustResize">
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value=".features.home.HomeActivity" />
|
android:value=".features.home.HomeActivity" />
|
||||||
@@ -369,8 +368,8 @@
|
|||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".core.services.CallAndroidService"
|
android:name=".core.services.CallAndroidService"
|
||||||
android:foregroundServiceType="phoneCall"
|
android:exported="false"
|
||||||
android:exported="false">
|
android:foregroundServiceType="phoneCall">
|
||||||
<!-- in order to get headset button events -->
|
<!-- in order to get headset button events -->
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MEDIA_BUTTON" />
|
<action android:name="android.intent.action.MEDIA_BUTTON" />
|
||||||
@@ -387,7 +386,8 @@
|
|||||||
<service
|
<service
|
||||||
android:name=".features.call.telecom.VectorConnectionAndroidService"
|
android:name=".features.call.telecom.VectorConnectionAndroidService"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE">
|
android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE"
|
||||||
|
tools:targetApi="M">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.telecom.ConnectionService" />
|
<action android:name="android.telecom.ConnectionService" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
@@ -413,8 +413,7 @@
|
|||||||
android:name=".features.call.audio.MicrophoneAccessService"
|
android:name=".features.call.audio.MicrophoneAccessService"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:foregroundServiceType="microphone"
|
android:foregroundServiceType="microphone"
|
||||||
android:permission="android.permission.FOREGROUND_SERVICE_MICROPHONE">
|
android:permission="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
|
||||||
</service>
|
|
||||||
|
|
||||||
<!-- Receivers -->
|
<!-- Receivers -->
|
||||||
|
|
||||||
|
@@ -137,7 +137,7 @@ class SpaceStateHandlerImpl @Inject constructor(
|
|||||||
|
|
||||||
override fun popSpaceBackstack(): String? {
|
override fun popSpaceBackstack(): String? {
|
||||||
vectorPreferences.getSpaceBackstack().toMutableList().apply {
|
vectorPreferences.getSpaceBackstack().toMutableList().apply {
|
||||||
val poppedSpaceId = removeLast()
|
val poppedSpaceId = removeAt(lastIndex)
|
||||||
vectorPreferences.setSpaceBackstack(this)
|
vectorPreferences.setSpaceBackstack(this)
|
||||||
return poppedSpaceId
|
return poppedSpaceId
|
||||||
}
|
}
|
||||||
|
@@ -6,6 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
package im.vector.app.core.platform
|
package im.vector.app.core.platform
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
import androidx.core.view.isGone
|
import androidx.core.view.isGone
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import im.vector.app.core.extensions.hideKeyboard
|
import im.vector.app.core.extensions.hideKeyboard
|
||||||
@@ -20,6 +21,9 @@ abstract class SimpleFragmentActivity : VectorBaseActivity<ActivityBinding>() {
|
|||||||
|
|
||||||
final override fun getCoordinatorLayout() = views.coordinatorLayout
|
final override fun getCoordinatorLayout() = views.coordinatorLayout
|
||||||
|
|
||||||
|
override val rootView: View
|
||||||
|
get() = views.coordinatorLayout
|
||||||
|
|
||||||
override fun initUiAndData() {
|
override fun initUiAndData() {
|
||||||
setupToolbar(views.toolbar)
|
setupToolbar(views.toolbar)
|
||||||
.allowBack(true)
|
.allowBack(true)
|
||||||
|
@@ -10,25 +10,27 @@ package im.vector.app.core.platform
|
|||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Build
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuInflater
|
import android.view.MenuInflater
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.WindowInsetsController
|
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import androidx.activity.enableEdgeToEdge
|
||||||
import androidx.annotation.CallSuper
|
import androidx.annotation.CallSuper
|
||||||
import androidx.annotation.MainThread
|
import androidx.annotation.MainThread
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
import androidx.core.app.MultiWindowModeChangedInfo
|
import androidx.core.app.MultiWindowModeChangedInfo
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import androidx.core.util.Consumer
|
import androidx.core.util.Consumer
|
||||||
import androidx.core.view.MenuProvider
|
import androidx.core.view.MenuProvider
|
||||||
|
import androidx.core.view.ViewCompat
|
||||||
|
import androidx.core.view.ViewGroupCompat
|
||||||
|
import androidx.core.view.WindowInsetsCompat
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.core.view.updatePadding
|
||||||
import androidx.fragment.app.FragmentManager
|
import androidx.fragment.app.FragmentManager
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
@@ -208,6 +210,8 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
|
|||||||
val activityEntryPoint = EntryPointAccessors.fromActivity(this, ActivityEntryPoint::class.java)
|
val activityEntryPoint = EntryPointAccessors.fromActivity(this, ActivityEntryPoint::class.java)
|
||||||
ThemeUtils.setActivityTheme(this, getOtherThemes())
|
ThemeUtils.setActivityTheme(this, getOtherThemes())
|
||||||
viewModelFactory = activityEntryPoint.viewModelFactory()
|
viewModelFactory = activityEntryPoint.viewModelFactory()
|
||||||
|
enableEdgeToEdge()
|
||||||
|
ViewGroupCompat.installCompatInsetsDispatch(window.decorView)
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
addOnMultiWindowModeChangedListener(onMultiWindowModeChangedListener)
|
addOnMultiWindowModeChangedListener(onMultiWindowModeChangedListener)
|
||||||
setupMenu()
|
setupMenu()
|
||||||
@@ -247,7 +251,9 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
|
|||||||
if (vectorPreferences.isNewAppLayoutEnabled()) {
|
if (vectorPreferences.isNewAppLayoutEnabled()) {
|
||||||
tryOrNull { // Add to XML theme when feature flag is removed
|
tryOrNull { // Add to XML theme when feature flag is removed
|
||||||
val toolbarBackground = MaterialColors.getColor(views.root, im.vector.lib.ui.styles.R.attr.vctr_toolbar_background)
|
val toolbarBackground = MaterialColors.getColor(views.root, im.vector.lib.ui.styles.R.attr.vctr_toolbar_background)
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
window.statusBarColor = toolbarBackground
|
window.statusBarColor = toolbarBackground
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
window.navigationBarColor = toolbarBackground
|
window.navigationBarColor = toolbarBackground
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -334,7 +340,8 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
|
|||||||
private fun handleCertificateError(certificateError: GlobalError.CertificateError) {
|
private fun handleCertificateError(certificateError: GlobalError.CertificateError) {
|
||||||
singletonEntryPoint()
|
singletonEntryPoint()
|
||||||
.unrecognizedCertificateDialog()
|
.unrecognizedCertificateDialog()
|
||||||
.show(this,
|
.show(
|
||||||
|
this,
|
||||||
certificateError.fingerprint,
|
certificateError.fingerprint,
|
||||||
object : UnrecognizedCertificateDialog.Callback {
|
object : UnrecognizedCertificateDialog.Callback {
|
||||||
override fun onAccept() {
|
override fun onAccept() {
|
||||||
@@ -411,6 +418,21 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
|
|||||||
// Just log that a change occurred.
|
// Just log that a change occurred.
|
||||||
Timber.w("MDM data has been updated")
|
Timber.w("MDM data has been updated")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(rootView) { v, insets ->
|
||||||
|
val systemBars = insets.getInsets(
|
||||||
|
WindowInsetsCompat.Type.systemBars() or
|
||||||
|
WindowInsetsCompat.Type.displayCutout() or
|
||||||
|
WindowInsetsCompat.Type.ime()
|
||||||
|
)
|
||||||
|
v.updatePadding(
|
||||||
|
systemBars.left,
|
||||||
|
systemBars.top,
|
||||||
|
systemBars.right,
|
||||||
|
systemBars.bottom,
|
||||||
|
)
|
||||||
|
WindowInsetsCompat.CONSUMED
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val postResumeScheduledActions = mutableListOf<() -> Unit>()
|
private val postResumeScheduledActions = mutableListOf<() -> Unit>()
|
||||||
@@ -444,14 +466,6 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
|
|||||||
mdmService.unregisterListener(this)
|
mdmService.unregisterListener(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onWindowFocusChanged(hasFocus: Boolean) {
|
|
||||||
super.onWindowFocusChanged(hasFocus)
|
|
||||||
|
|
||||||
if (hasFocus && displayInFullscreen()) {
|
|
||||||
setFullScreen()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val onMultiWindowModeChangedListener = Consumer<MultiWindowModeChangedInfo> {
|
private val onMultiWindowModeChangedListener = Consumer<MultiWindowModeChangedInfo> {
|
||||||
Timber.w("onMultiWindowModeChanged. isInMultiWindowMode: ${it.isInMultiWindowMode}")
|
Timber.w("onMultiWindowModeChanged. isInMultiWindowMode: ${it.isInMultiWindowMode}")
|
||||||
bugReporter.inMultiWindowMode = it.isInMultiWindowMode
|
bugReporter.inMultiWindowMode = it.isInMultiWindowMode
|
||||||
@@ -461,30 +475,6 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
|
|||||||
* PRIVATE METHODS
|
* PRIVATE METHODS
|
||||||
* ========================================================================================== */
|
* ========================================================================================== */
|
||||||
|
|
||||||
/**
|
|
||||||
* Force to render the activity in fullscreen.
|
|
||||||
*/
|
|
||||||
private fun setFullScreen() {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
||||||
// New API instead of SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
|
||||||
window.setDecorFitsSystemWindows(false)
|
|
||||||
// New API instead of SYSTEM_UI_FLAG_IMMERSIVE
|
|
||||||
window.decorView.windowInsetsController?.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
|
|
||||||
// New API instead of FLAG_TRANSLUCENT_STATUS
|
|
||||||
window.statusBarColor = ContextCompat.getColor(this, im.vector.lib.ui.styles.R.color.half_transparent_status_bar)
|
|
||||||
// New API instead of FLAG_TRANSLUCENT_NAVIGATION
|
|
||||||
window.navigationBarColor = ContextCompat.getColor(this, im.vector.lib.ui.styles.R.color.half_transparent_status_bar)
|
|
||||||
} else {
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
|
||||||
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
|
||||||
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
|
||||||
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
|
||||||
or View.SYSTEM_UI_FLAG_FULLSCREEN
|
|
||||||
or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleMenuItemHome(item: MenuItem): Boolean {
|
private fun handleMenuItemHome(item: MenuItem): Boolean {
|
||||||
return when (item.itemId) {
|
return when (item.itemId) {
|
||||||
android.R.id.home -> {
|
android.R.id.home -> {
|
||||||
@@ -586,8 +576,6 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
|
|||||||
|
|
||||||
abstract fun getBinding(): VB
|
abstract fun getBinding(): VB
|
||||||
|
|
||||||
open fun displayInFullscreen() = false
|
|
||||||
|
|
||||||
open fun doBeforeSetContentView() = Unit
|
open fun doBeforeSetContentView() = Unit
|
||||||
|
|
||||||
open fun initUiAndData() = Unit
|
open fun initUiAndData() = Unit
|
||||||
@@ -626,6 +614,8 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
|
|||||||
|
|
||||||
open fun getCoordinatorLayout(): CoordinatorLayout? = null
|
open fun getCoordinatorLayout(): CoordinatorLayout? = null
|
||||||
|
|
||||||
|
abstract val rootView: View
|
||||||
|
|
||||||
/* ==========================================================================================
|
/* ==========================================================================================
|
||||||
* User Consent
|
* User Consent
|
||||||
* ========================================================================================== */
|
* ========================================================================================== */
|
||||||
|
@@ -18,6 +18,10 @@ import androidx.core.content.ContextCompat
|
|||||||
import im.vector.lib.core.utils.compat.getParcelableExtraCompat
|
import im.vector.lib.core.utils.compat.getParcelableExtraCompat
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
|
|
||||||
|
/**
|
||||||
|
* It's only used in API 21 and 22 so we will not have security exception on these OS,
|
||||||
|
* so it's safe to use @Suppress("MissingPermission").
|
||||||
|
*/
|
||||||
class BluetoothHeadsetReceiver : BroadcastReceiver() {
|
class BluetoothHeadsetReceiver : BroadcastReceiver() {
|
||||||
|
|
||||||
interface EventListener {
|
interface EventListener {
|
||||||
@@ -53,12 +57,15 @@ class BluetoothHeadsetReceiver : BroadcastReceiver() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val device = intent.getParcelableExtraCompat<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE)
|
val device = intent.getParcelableExtraCompat<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE)
|
||||||
|
@Suppress("MissingPermission")
|
||||||
val deviceName = device?.name
|
val deviceName = device?.name
|
||||||
|
@Suppress("MissingPermission")
|
||||||
when (device?.bluetoothClass?.deviceClass) {
|
when (device?.bluetoothClass?.deviceClass) {
|
||||||
BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE,
|
BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE,
|
||||||
BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO,
|
BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO,
|
||||||
BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET -> {
|
BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET -> {
|
||||||
// filter only device that we care about for
|
// filter only device that we care about for
|
||||||
|
@Suppress("MissingPermission")
|
||||||
delegate?.get()?.onBTHeadsetEvent(
|
delegate?.get()?.onBTHeadsetEvent(
|
||||||
BTHeadsetPlugEvent(
|
BTHeadsetPlugEvent(
|
||||||
plugged = headsetConnected,
|
plugged = headsetConnected,
|
||||||
|
@@ -8,11 +8,14 @@
|
|||||||
|
|
||||||
package im.vector.app.core.services
|
package im.vector.app.core.services
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager
|
||||||
import android.os.Binder
|
import android.os.Binder
|
||||||
import android.support.v4.media.session.MediaSessionCompat
|
import android.support.v4.media.session.MediaSessionCompat
|
||||||
import android.view.KeyEvent
|
import android.view.KeyEvent
|
||||||
|
import androidx.core.app.ActivityCompat
|
||||||
import androidx.core.app.NotificationManagerCompat
|
import androidx.core.app.NotificationManagerCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.media.session.MediaButtonReceiver
|
import androidx.media.session.MediaButtonReceiver
|
||||||
@@ -150,7 +153,8 @@ class CallAndroidService : VectorAndroidService() {
|
|||||||
val isVideoCall = call.mxCall.isVideoCall
|
val isVideoCall = call.mxCall.isVideoCall
|
||||||
val fromBg = intent.getBooleanExtra(EXTRA_IS_IN_BG, false)
|
val fromBg = intent.getBooleanExtra(EXTRA_IS_IN_BG, false)
|
||||||
Timber.tag(loggerTag.value).v("displayIncomingCallNotification : display the dedicated notification")
|
Timber.tag(loggerTag.value).v("displayIncomingCallNotification : display the dedicated notification")
|
||||||
val incomingCallAlert = IncomingCallAlert(callId,
|
val incomingCallAlert = IncomingCallAlert(
|
||||||
|
callId,
|
||||||
shouldBeDisplayedIn = { activity ->
|
shouldBeDisplayedIn = { activity ->
|
||||||
if (activity is VectorCallActivity) {
|
if (activity is VectorCallActivity) {
|
||||||
activity.intent.getParcelableExtraCompat<CallArgs>(Mavericks.KEY_ARG)?.callId != call.callId
|
activity.intent.getParcelableExtraCompat<CallArgs>(Mavericks.KEY_ARG)?.callId != call.callId
|
||||||
@@ -176,7 +180,11 @@ class CallAndroidService : VectorAndroidService() {
|
|||||||
if (knownCalls.isEmpty()) {
|
if (knownCalls.isEmpty()) {
|
||||||
startForegroundCompat(callId.hashCode(), notification)
|
startForegroundCompat(callId.hashCode(), notification)
|
||||||
} else {
|
} else {
|
||||||
notificationManager.notify(callId.hashCode(), notification)
|
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
Timber.w("Not allowed to notify.")
|
||||||
|
} else {
|
||||||
|
notificationManager.notify(callId.hashCode(), notification)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
knownCalls[callId] = callInformation
|
knownCalls[callId] = callInformation
|
||||||
}
|
}
|
||||||
@@ -234,7 +242,11 @@ class CallAndroidService : VectorAndroidService() {
|
|||||||
if (knownCalls.isEmpty()) {
|
if (knownCalls.isEmpty()) {
|
||||||
startForegroundCompat(callId.hashCode(), notification)
|
startForegroundCompat(callId.hashCode(), notification)
|
||||||
} else {
|
} else {
|
||||||
notificationManager.notify(callId.hashCode(), notification)
|
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
Timber.w("Not allowed to notify.")
|
||||||
|
} else {
|
||||||
|
notificationManager.notify(callId.hashCode(), notification)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
knownCalls[callId] = callInformation
|
knownCalls[callId] = callInformation
|
||||||
}
|
}
|
||||||
@@ -258,7 +270,11 @@ class CallAndroidService : VectorAndroidService() {
|
|||||||
if (knownCalls.isEmpty()) {
|
if (knownCalls.isEmpty()) {
|
||||||
startForegroundCompat(callId.hashCode(), notification)
|
startForegroundCompat(callId.hashCode(), notification)
|
||||||
} else {
|
} else {
|
||||||
notificationManager.notify(callId.hashCode(), notification)
|
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
Timber.w("Not allowed to notify.")
|
||||||
|
} else {
|
||||||
|
notificationManager.notify(callId.hashCode(), notification)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
knownCalls[callId] = callInformation
|
knownCalls[callId] = callInformation
|
||||||
}
|
}
|
||||||
|
@@ -658,7 +658,8 @@ class ExpandingBottomSheetBehavior<V : View> : CoordinatorLayout.Behavior<V> {
|
|||||||
val insetsType = WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.ime()
|
val insetsType = WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.ime()
|
||||||
val imeInsets = insets.getInsets(insetsType)
|
val imeInsets = insets.getInsets(insetsType)
|
||||||
insetTop = imeInsets.top
|
insetTop = imeInsets.top
|
||||||
insetBottom = imeInsets.bottom
|
// Now that edgeToEdge is enabled, disable the bottom padding.
|
||||||
|
insetBottom = 0
|
||||||
insetLeft = imeInsets.left
|
insetLeft = imeInsets.left
|
||||||
insetRight = imeInsets.right
|
insetRight = imeInsets.right
|
||||||
|
|
||||||
|
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2025 New Vector Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.core.utils
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import androidx.core.app.ActivityCompat
|
||||||
|
import dagger.Binds
|
||||||
|
import dagger.hilt.InstallIn
|
||||||
|
import dagger.hilt.components.SingletonComponent
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
interface PermissionChecker {
|
||||||
|
|
||||||
|
@InstallIn(SingletonComponent::class)
|
||||||
|
@dagger.Module
|
||||||
|
interface Module {
|
||||||
|
@Binds
|
||||||
|
fun bindPermissionChecker(permissionChecker: AndroidPermissionChecker): PermissionChecker
|
||||||
|
}
|
||||||
|
|
||||||
|
fun checkPermission(vararg permissions: String): Boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
class AndroidPermissionChecker @Inject constructor(
|
||||||
|
private val applicationContext: Context,
|
||||||
|
) : PermissionChecker {
|
||||||
|
override fun checkPermission(vararg permissions: String): Boolean {
|
||||||
|
return permissions.any { permission ->
|
||||||
|
ActivityCompat.checkSelfPermission(applicationContext, permission) != PackageManager.PERMISSION_GRANTED
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -12,6 +12,7 @@ import android.content.Context
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
|
import android.view.View
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
@@ -392,4 +393,7 @@ class MainActivity : VectorBaseActivity<ActivityMainBinding>(), UnlockedActivity
|
|||||||
val className = componentName.className
|
val className = componentName.className
|
||||||
return packageName == buildMeta.applicationId && className in allowList
|
return packageName == buildMeta.applicationId && className in allowList
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val rootView: View
|
||||||
|
get() = views.mainRoot
|
||||||
}
|
}
|
||||||
|
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
package im.vector.app.features.analytics.ui.consent
|
package im.vector.app.features.analytics.ui.consent
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
import com.airbnb.mvrx.viewModel
|
import com.airbnb.mvrx.viewModel
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import im.vector.app.core.extensions.addFragment
|
import im.vector.app.core.extensions.addFragment
|
||||||
@@ -29,6 +30,9 @@ class AnalyticsOptInActivity : VectorBaseActivity<ActivitySimpleBinding>() {
|
|||||||
|
|
||||||
override fun getCoordinatorLayout() = views.coordinatorLayout
|
override fun getCoordinatorLayout() = views.coordinatorLayout
|
||||||
|
|
||||||
|
override val rootView: View
|
||||||
|
get() = views.coordinatorLayout
|
||||||
|
|
||||||
override fun initUiAndData() {
|
override fun initUiAndData() {
|
||||||
orientationLocker.lockPhonesToPortrait(this)
|
orientationLocker.lockPhonesToPortrait(this)
|
||||||
if (isFirstCreation()) {
|
if (isFirstCreation()) {
|
||||||
|
@@ -9,6 +9,7 @@ package im.vector.app.features.attachments.preview
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.view.View
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import im.vector.app.core.extensions.addFragment
|
import im.vector.app.core.extensions.addFragment
|
||||||
import im.vector.app.core.platform.VectorBaseActivity
|
import im.vector.app.core.platform.VectorBaseActivity
|
||||||
@@ -47,6 +48,9 @@ class AttachmentsPreviewActivity : VectorBaseActivity<ActivitySimpleBinding>() {
|
|||||||
|
|
||||||
override fun getCoordinatorLayout() = views.coordinatorLayout
|
override fun getCoordinatorLayout() = views.coordinatorLayout
|
||||||
|
|
||||||
|
override val rootView: View
|
||||||
|
get() = views.coordinatorLayout
|
||||||
|
|
||||||
override fun initUiAndData() {
|
override fun initUiAndData() {
|
||||||
if (isFirstCreation()) {
|
if (isFirstCreation()) {
|
||||||
val fragmentArgs: AttachmentsPreviewArgs = intent?.extras?.getParcelableCompat(EXTRA_FRAGMENT_ARGS) ?: return
|
val fragmentArgs: AttachmentsPreviewArgs = intent?.extras?.getParcelableCompat(EXTRA_FRAGMENT_ARGS) ?: return
|
||||||
|
@@ -163,6 +163,7 @@ class AttachmentsPreviewFragment :
|
|||||||
|
|
||||||
private fun applyInsets() {
|
private fun applyInsets() {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
activity?.window?.setDecorFitsSystemWindows(false)
|
activity?.window?.setDecorFitsSystemWindows(false)
|
||||||
} else {
|
} else {
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
|
@@ -128,7 +128,9 @@ class VectorCallActivity :
|
|||||||
window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)
|
window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)
|
||||||
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
|
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
|
||||||
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
window.statusBarColor = Color.TRANSPARENT
|
window.statusBarColor = Color.TRANSPARENT
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
window.navigationBarColor = Color.BLACK
|
window.navigationBarColor = Color.BLACK
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
addOnPictureInPictureModeChangedListener(pictureInPictureModeChangedInfoConsumer)
|
addOnPictureInPictureModeChangedListener(pictureInPictureModeChangedInfoConsumer)
|
||||||
@@ -185,6 +187,9 @@ class VectorCallActivity :
|
|||||||
|
|
||||||
override fun getMenuRes() = R.menu.vector_call
|
override fun getMenuRes() = R.menu.vector_call
|
||||||
|
|
||||||
|
override val rootView: View
|
||||||
|
get() = views.constraintLayout
|
||||||
|
|
||||||
override fun onUserLeaveHint() {
|
override fun onUserLeaveHint() {
|
||||||
super.onUserLeaveHint()
|
super.onUserLeaveHint()
|
||||||
enterPictureInPictureIfRequired()
|
enterPictureInPictureIfRequired()
|
||||||
|
@@ -43,6 +43,11 @@ internal class API21AudioDeviceDetector(
|
|||||||
return HashSet<CallAudioManager.Device>().apply {
|
return HashSet<CallAudioManager.Device>().apply {
|
||||||
if (isBluetoothHeadsetOn()) {
|
if (isBluetoothHeadsetOn()) {
|
||||||
connectedBlueToothHeadset?.connectedDevices?.forEach {
|
connectedBlueToothHeadset?.connectedDevices?.forEach {
|
||||||
|
// Call requires permission which may be rejected by user: code should explicitly
|
||||||
|
// check to see if permission is available (with checkPermission) or explicitly
|
||||||
|
// handle a potential SecurityException
|
||||||
|
// But it should not happen on API 21/22.
|
||||||
|
@Suppress("MissingPermission")
|
||||||
add(CallAudioManager.Device.WirelessHeadset(it.name))
|
add(CallAudioManager.Device.WirelessHeadset(it.name))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -15,6 +15,7 @@ import android.content.res.Configuration
|
|||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
|
import android.view.View
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.core.app.PictureInPictureModeChangedInfo
|
import androidx.core.app.PictureInPictureModeChangedInfo
|
||||||
@@ -58,6 +59,9 @@ class VectorJitsiActivity : VectorBaseActivity<ActivityJitsiBinding>(), JitsiMee
|
|||||||
|
|
||||||
override fun getBinding() = ActivityJitsiBinding.inflate(layoutInflater)
|
override fun getBinding() = ActivityJitsiBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
|
override val rootView: View
|
||||||
|
get() = views.jitsiLayout
|
||||||
|
|
||||||
private var jitsiMeetView: JitsiMeetView? = null
|
private var jitsiMeetView: JitsiMeetView? = null
|
||||||
|
|
||||||
private val jitsiViewModel: JitsiCallViewModel by viewModel()
|
private val jitsiViewModel: JitsiCallViewModel by viewModel()
|
||||||
|
@@ -12,7 +12,6 @@ import android.content.ClipboardManager
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.res.ColorStateList
|
import android.content.res.ColorStateList
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.telephony.PhoneNumberFormattingTextWatcher
|
|
||||||
import android.telephony.PhoneNumberUtils
|
import android.telephony.PhoneNumberUtils
|
||||||
import android.text.Editable
|
import android.text.Editable
|
||||||
import android.text.InputType
|
import android.text.InputType
|
||||||
@@ -78,7 +77,8 @@ class DialPadFragment : Fragment(), TextWatcher {
|
|||||||
digits.inputType = InputType.TYPE_CLASS_PHONE
|
digits.inputType = InputType.TYPE_CLASS_PHONE
|
||||||
digits.keyListener = DialerKeyListener.getInstance()
|
digits.keyListener = DialerKeyListener.getInstance()
|
||||||
digits.setTextColor(ThemeUtils.getColor(requireContext(), im.vector.lib.ui.styles.R.attr.vctr_content_primary))
|
digits.setTextColor(ThemeUtils.getColor(requireContext(), im.vector.lib.ui.styles.R.attr.vctr_content_primary))
|
||||||
digits.addTextChangedListener(PhoneNumberFormattingTextWatcher(if (formatAsYouType) regionCode else ""))
|
@Suppress("DEPRECATION")
|
||||||
|
digits.addTextChangedListener(android.telephony.PhoneNumberFormattingTextWatcher(if (formatAsYouType) regionCode else ""))
|
||||||
digits.addTextChangedListener(this)
|
digits.addTextChangedListener(this)
|
||||||
dialpadView.findViewById<View>(R.id.zero).setOnClickListener { keyPressed(KeyEvent.KEYCODE_0, "0") }
|
dialpadView.findViewById<View>(R.id.zero).setOnClickListener { keyPressed(KeyEvent.KEYCODE_0, "0") }
|
||||||
dialpadView.findViewById<View>(R.id.one).setOnClickListener { keyPressed(KeyEvent.KEYCODE_1, "1") }
|
dialpadView.findViewById<View>(R.id.one).setOnClickListener { keyPressed(KeyEvent.KEYCODE_1, "1") }
|
||||||
|
@@ -11,6 +11,7 @@ import android.content.Context
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
|
import android.view.View
|
||||||
import com.airbnb.mvrx.Mavericks
|
import com.airbnb.mvrx.Mavericks
|
||||||
import com.airbnb.mvrx.viewModel
|
import com.airbnb.mvrx.viewModel
|
||||||
import com.google.android.material.tabs.TabLayoutMediator
|
import com.google.android.material.tabs.TabLayoutMediator
|
||||||
@@ -37,6 +38,9 @@ class CallTransferActivity : VectorBaseActivity<ActivityCallTransferBinding>() {
|
|||||||
|
|
||||||
override fun getCoordinatorLayout() = views.vectorCoordinatorLayout
|
override fun getCoordinatorLayout() = views.vectorCoordinatorLayout
|
||||||
|
|
||||||
|
override val rootView: View
|
||||||
|
get() = views.vectorCoordinatorLayout
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
waitingView = views.waitingView.waitingView
|
waitingView = views.waitingView.waitingView
|
||||||
|
@@ -122,6 +122,7 @@ class BootstrapBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetBoot
|
|||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
val rootView = super.onCreateView(inflater, container, savedInstanceState)
|
val rootView = super.onCreateView(inflater, container, savedInstanceState)
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
dialog?.window?.setDecorFitsSystemWindows(false)
|
dialog?.window?.setDecorFitsSystemWindows(false)
|
||||||
} else {
|
} else {
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
|
@@ -192,6 +192,9 @@ class HomeActivity :
|
|||||||
|
|
||||||
override fun getCoordinatorLayout() = views.coordinatorLayout
|
override fun getCoordinatorLayout() = views.coordinatorLayout
|
||||||
|
|
||||||
|
override val rootView: View
|
||||||
|
get() = views.coordinatorLayout
|
||||||
|
|
||||||
override fun getBinding() = ActivityHomeBinding.inflate(layoutInflater)
|
override fun getBinding() = ActivityHomeBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
@@ -13,7 +13,9 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.core.app.ActivityOptionsCompat
|
import androidx.core.app.ActivityOptionsCompat
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
|
import androidx.core.view.WindowInsetsCompat
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.core.view.updatePadding
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.extensions.observeK
|
import im.vector.app.core.extensions.observeK
|
||||||
@@ -109,6 +111,20 @@ class HomeDrawerFragment :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(view) { v, insets ->
|
||||||
|
val systemBars = insets.getInsets(
|
||||||
|
WindowInsetsCompat.Type.systemBars() or
|
||||||
|
WindowInsetsCompat.Type.displayCutout()
|
||||||
|
)
|
||||||
|
v.updatePadding(
|
||||||
|
systemBars.left,
|
||||||
|
systemBars.top,
|
||||||
|
systemBars.right,
|
||||||
|
systemBars.bottom,
|
||||||
|
)
|
||||||
|
WindowInsetsCompat.CONSUMED
|
||||||
|
}
|
||||||
|
|
||||||
// Debug menu
|
// Debug menu
|
||||||
views.homeDrawerHeaderDebugView.debouncedClicks {
|
views.homeDrawerHeaderDebugView.debouncedClicks {
|
||||||
sharedActionViewModel.post(HomeActivitySharedAction.CloseDrawer)
|
sharedActionViewModel.post(HomeActivitySharedAction.CloseDrawer)
|
||||||
|
@@ -11,6 +11,9 @@ import android.os.Bundle
|
|||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import androidx.core.view.ViewCompat
|
||||||
|
import androidx.core.view.WindowInsetsCompat
|
||||||
|
import androidx.core.view.updatePadding
|
||||||
import com.airbnb.mvrx.fragmentViewModel
|
import com.airbnb.mvrx.fragmentViewModel
|
||||||
import com.airbnb.mvrx.withState
|
import com.airbnb.mvrx.withState
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
@@ -38,6 +41,19 @@ class BreadcrumbsFragment :
|
|||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(views.breadcrumbsRecyclerView) { v, insets ->
|
||||||
|
val systemBars = insets.getInsets(
|
||||||
|
WindowInsetsCompat.Type.systemBars() or
|
||||||
|
WindowInsetsCompat.Type.displayCutout()
|
||||||
|
)
|
||||||
|
v.updatePadding(
|
||||||
|
systemBars.left,
|
||||||
|
systemBars.top,
|
||||||
|
systemBars.right,
|
||||||
|
systemBars.bottom,
|
||||||
|
)
|
||||||
|
WindowInsetsCompat.CONSUMED
|
||||||
|
}
|
||||||
setupRecyclerView()
|
setupRecyclerView()
|
||||||
sharedActionViewModel = activityViewModelProvider.get(RoomDetailSharedActionViewModel::class.java)
|
sharedActionViewModel = activityViewModelProvider.get(RoomDetailSharedActionViewModel::class.java)
|
||||||
}
|
}
|
||||||
|
@@ -14,7 +14,6 @@ import android.os.Bundle
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.core.view.GravityCompat
|
import androidx.core.view.GravityCompat
|
||||||
import androidx.core.view.WindowCompat
|
|
||||||
import androidx.drawerlayout.widget.DrawerLayout
|
import androidx.drawerlayout.widget.DrawerLayout
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.FragmentManager
|
import androidx.fragment.app.FragmentManager
|
||||||
@@ -82,6 +81,9 @@ class RoomDetailActivity :
|
|||||||
|
|
||||||
override fun getCoordinatorLayout() = views.coordinatorLayout
|
override fun getCoordinatorLayout() = views.coordinatorLayout
|
||||||
|
|
||||||
|
override val rootView: View
|
||||||
|
get() = views.coordinatorLayout
|
||||||
|
|
||||||
@Inject lateinit var playbackTracker: AudioMessagePlaybackTracker
|
@Inject lateinit var playbackTracker: AudioMessagePlaybackTracker
|
||||||
private lateinit var sharedActionViewModel: RoomDetailSharedActionViewModel
|
private lateinit var sharedActionViewModel: RoomDetailSharedActionViewModel
|
||||||
private val requireActiveMembershipViewModel: RequireActiveMembershipViewModel by viewModel()
|
private val requireActiveMembershipViewModel: RequireActiveMembershipViewModel by viewModel()
|
||||||
@@ -93,7 +95,7 @@ class RoomDetailActivity :
|
|||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
// For dealing with insets and status bar background color
|
// For dealing with insets and status bar background color
|
||||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
@Suppress("DEPRECATION")
|
||||||
window.statusBarColor = Color.TRANSPARENT
|
window.statusBarColor = Color.TRANSPARENT
|
||||||
|
|
||||||
supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, false)
|
supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, false)
|
||||||
|
@@ -32,11 +32,9 @@ import androidx.core.net.toUri
|
|||||||
import androidx.core.text.toSpannable
|
import androidx.core.text.toSpannable
|
||||||
import androidx.core.util.Pair
|
import androidx.core.util.Pair
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
import androidx.core.view.WindowInsetsCompat
|
|
||||||
import androidx.core.view.forEach
|
import androidx.core.view.forEach
|
||||||
import androidx.core.view.isInvisible
|
import androidx.core.view.isInvisible
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.core.view.updatePadding
|
|
||||||
import androidx.fragment.app.setFragmentResultListener
|
import androidx.fragment.app.setFragmentResultListener
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.lifecycle.withResumed
|
import androidx.lifecycle.withResumed
|
||||||
@@ -409,13 +407,6 @@ class TimelineFragment :
|
|||||||
is RoomDetailViewEvents.RevokeFilePermission -> revokeFilePermission(it)
|
is RoomDetailViewEvents.RevokeFilePermission -> revokeFilePermission(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ViewCompat.setOnApplyWindowInsetsListener(views.coordinatorLayout) { _, insets ->
|
|
||||||
val imeInsets = insets.getInsets(WindowInsetsCompat.Type.ime() or WindowInsetsCompat.Type.systemBars())
|
|
||||||
views.appBarLayout.updatePadding(top = imeInsets.top)
|
|
||||||
views.voiceMessageRecorderContainer.updatePadding(bottom = imeInsets.bottom)
|
|
||||||
insets
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupBackPressHandling() {
|
private fun setupBackPressHandling() {
|
||||||
|
@@ -54,7 +54,6 @@ import im.vector.app.features.home.room.typing.TypingHelper
|
|||||||
import im.vector.app.features.location.live.StopLiveLocationShareUseCase
|
import im.vector.app.features.location.live.StopLiveLocationShareUseCase
|
||||||
import im.vector.app.features.location.live.tracking.LocationSharingServiceConnection
|
import im.vector.app.features.location.live.tracking.LocationSharingServiceConnection
|
||||||
import im.vector.app.features.notifications.NotificationDrawerManager
|
import im.vector.app.features.notifications.NotificationDrawerManager
|
||||||
import im.vector.app.features.powerlevel.PowerLevelsFlowFactory
|
|
||||||
import im.vector.app.features.raw.wellknown.CryptoConfig
|
import im.vector.app.features.raw.wellknown.CryptoConfig
|
||||||
import im.vector.app.features.raw.wellknown.getOutboundSessionKeySharingStrategyOrDefault
|
import im.vector.app.features.raw.wellknown.getOutboundSessionKeySharingStrategyOrDefault
|
||||||
import im.vector.app.features.raw.wellknown.withElementWellKnown
|
import im.vector.app.features.raw.wellknown.withElementWellKnown
|
||||||
@@ -110,7 +109,6 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageWithAttachme
|
|||||||
import org.matrix.android.sdk.api.session.room.model.message.getFileUrl
|
import org.matrix.android.sdk.api.session.room.model.message.getFileUrl
|
||||||
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
|
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.tombstone.RoomTombstoneContent
|
import org.matrix.android.sdk.api.session.room.model.tombstone.RoomTombstoneContent
|
||||||
import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
|
|
||||||
import org.matrix.android.sdk.api.session.room.read.ReadService
|
import org.matrix.android.sdk.api.session.room.read.ReadService
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.Timeline
|
import org.matrix.android.sdk.api.session.room.timeline.Timeline
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||||
@@ -303,12 +301,12 @@ class TimelineViewModel @AssistedInject constructor(
|
|||||||
|
|
||||||
private fun observePowerLevel() {
|
private fun observePowerLevel() {
|
||||||
if (room == null) return
|
if (room == null) return
|
||||||
PowerLevelsFlowFactory(room).createFlow()
|
room.flow().liveRoomPowerLevels()
|
||||||
.onEach {
|
.onEach { powerLevels ->
|
||||||
val canInvite = PowerLevelsHelper(it).isUserAbleToInvite(session.myUserId)
|
val canInvite = powerLevels.isUserAbleToInvite(session.myUserId)
|
||||||
val isAllowedToManageWidgets = session.widgetService().hasPermissionsToHandleWidgets(room.roomId)
|
val isAllowedToManageWidgets = session.widgetService().hasPermissionsToHandleWidgets(room.roomId)
|
||||||
val isAllowedToStartWebRTCCall = PowerLevelsHelper(it).isUserAllowedToSend(session.myUserId, false, EventType.CALL_INVITE)
|
val isAllowedToStartWebRTCCall = powerLevels.isUserAllowedToSend(session.myUserId, false, EventType.CALL_INVITE)
|
||||||
val isAllowedToSetupEncryption = PowerLevelsHelper(it).isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_ENCRYPTION)
|
val isAllowedToSetupEncryption = powerLevels.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_ENCRYPTION)
|
||||||
setState {
|
setState {
|
||||||
copy(
|
copy(
|
||||||
canInvite = canInvite,
|
canInvite = canInvite,
|
||||||
|
@@ -30,7 +30,6 @@ import im.vector.app.features.home.room.detail.ChatEffect
|
|||||||
import im.vector.app.features.home.room.detail.composer.rainbow.RainbowGenerator
|
import im.vector.app.features.home.room.detail.composer.rainbow.RainbowGenerator
|
||||||
import im.vector.app.features.home.room.detail.composer.voice.VoiceMessageRecorderView
|
import im.vector.app.features.home.room.detail.composer.voice.VoiceMessageRecorderView
|
||||||
import im.vector.app.features.home.room.detail.toMessageType
|
import im.vector.app.features.home.room.detail.toMessageType
|
||||||
import im.vector.app.features.powerlevel.PowerLevelsFlowFactory
|
|
||||||
import im.vector.app.features.session.coroutineScope
|
import im.vector.app.features.session.coroutineScope
|
||||||
import im.vector.app.features.settings.VectorPreferences
|
import im.vector.app.features.settings.VectorPreferences
|
||||||
import im.vector.app.features.voice.VoiceFailure
|
import im.vector.app.features.voice.VoiceFailure
|
||||||
@@ -69,7 +68,6 @@ import org.matrix.android.sdk.api.session.room.model.RoomMemberContent
|
|||||||
import org.matrix.android.sdk.api.session.room.model.message.MessageContentWithFormattedBody
|
import org.matrix.android.sdk.api.session.room.model.message.MessageContentWithFormattedBody
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessageType
|
import org.matrix.android.sdk.api.session.room.model.message.MessageType
|
||||||
import org.matrix.android.sdk.api.session.room.model.relation.shouldRenderInThread
|
import org.matrix.android.sdk.api.session.room.model.relation.shouldRenderInThread
|
||||||
import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
|
|
||||||
import org.matrix.android.sdk.api.session.room.send.UserDraft
|
import org.matrix.android.sdk.api.session.room.send.UserDraft
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.getRelationContent
|
import org.matrix.android.sdk.api.session.room.timeline.getRelationContent
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.getTextEditableContent
|
import org.matrix.android.sdk.api.session.room.timeline.getTextEditableContent
|
||||||
@@ -180,10 +178,10 @@ class MessageComposerViewModel @AssistedInject constructor(
|
|||||||
|
|
||||||
private fun observePowerLevelAndEncryption(room: Room) {
|
private fun observePowerLevelAndEncryption(room: Room) {
|
||||||
combine(
|
combine(
|
||||||
PowerLevelsFlowFactory(room).createFlow(),
|
room.flow().liveRoomPowerLevels(),
|
||||||
room.flow().liveRoomSummary().unwrap()
|
room.flow().liveRoomSummary().unwrap()
|
||||||
) { pl, sum ->
|
) { pl, sum ->
|
||||||
val canSendMessage = PowerLevelsHelper(pl).isUserAllowedToSend(session.myUserId, false, EventType.MESSAGE)
|
val canSendMessage = pl.isUserAllowedToSend(session.myUserId, false, EventType.MESSAGE)
|
||||||
if (canSendMessage) {
|
if (canSendMessage) {
|
||||||
val isE2E = sum.isEncrypted
|
val isE2E = sum.isEncrypted
|
||||||
if (isE2E) {
|
if (isE2E) {
|
||||||
|
@@ -10,6 +10,7 @@ package im.vector.app.features.home.room.detail.search
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
import androidx.appcompat.widget.SearchView
|
import androidx.appcompat.widget.SearchView
|
||||||
import com.airbnb.mvrx.Mavericks
|
import com.airbnb.mvrx.Mavericks
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
@@ -30,6 +31,9 @@ class SearchActivity : VectorBaseActivity<ActivitySearchBinding>() {
|
|||||||
|
|
||||||
override fun getCoordinatorLayout() = views.coordinatorLayout
|
override fun getCoordinatorLayout() = views.coordinatorLayout
|
||||||
|
|
||||||
|
override val rootView: View
|
||||||
|
get() = views.coordinatorLayout
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setupToolbar(views.searchToolbar)
|
setupToolbar(views.searchToolbar)
|
||||||
|
@@ -23,7 +23,6 @@ import im.vector.app.features.home.room.detail.timeline.format.NoticeEventFormat
|
|||||||
import im.vector.app.features.html.EventHtmlRenderer
|
import im.vector.app.features.html.EventHtmlRenderer
|
||||||
import im.vector.app.features.html.PillsPostProcessor
|
import im.vector.app.features.html.PillsPostProcessor
|
||||||
import im.vector.app.features.html.VectorHtmlCompressor
|
import im.vector.app.features.html.VectorHtmlCompressor
|
||||||
import im.vector.app.features.powerlevel.PowerLevelsFlowFactory
|
|
||||||
import im.vector.app.features.reactions.data.EmojiDataSource
|
import im.vector.app.features.reactions.data.EmojiDataSource
|
||||||
import im.vector.app.features.settings.VectorPreferences
|
import im.vector.app.features.settings.VectorPreferences
|
||||||
import im.vector.lib.strings.CommonStrings
|
import im.vector.lib.strings.CommonStrings
|
||||||
@@ -49,7 +48,6 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageTextContent
|
|||||||
import org.matrix.android.sdk.api.session.room.model.message.MessageType
|
import org.matrix.android.sdk.api.session.room.model.message.MessageType
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationRequestContent
|
import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationRequestContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessageWithAttachmentContent
|
import org.matrix.android.sdk.api.session.room.model.message.MessageWithAttachmentContent
|
||||||
import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
|
|
||||||
import org.matrix.android.sdk.api.session.room.send.SendState
|
import org.matrix.android.sdk.api.session.room.send.SendState
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.hasBeenEdited
|
import org.matrix.android.sdk.api.session.room.timeline.hasBeenEdited
|
||||||
@@ -116,12 +114,11 @@ class MessageActionsViewModel @AssistedInject constructor(
|
|||||||
if (room == null) {
|
if (room == null) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
PowerLevelsFlowFactory(room).createFlow()
|
room.flow().liveRoomPowerLevels()
|
||||||
.onEach {
|
.onEach { roomPowerLevels ->
|
||||||
val powerLevelsHelper = PowerLevelsHelper(it)
|
val canReact = roomPowerLevels.isUserAllowedToSend(session.myUserId, false, EventType.REACTION)
|
||||||
val canReact = powerLevelsHelper.isUserAllowedToSend(session.myUserId, false, EventType.REACTION)
|
val canRedact = roomPowerLevels.isUserAbleToRedact(session.myUserId)
|
||||||
val canRedact = powerLevelsHelper.isUserAbleToRedact(session.myUserId)
|
val canSendMessage = roomPowerLevels.isUserAllowedToSend(session.myUserId, false, EventType.MESSAGE)
|
||||||
val canSendMessage = powerLevelsHelper.isUserAllowedToSend(session.myUserId, false, EventType.MESSAGE)
|
|
||||||
val permissions = ActionPermissions(canSendMessage = canSendMessage, canRedact = canRedact, canReact = canReact)
|
val permissions = ActionPermissions(canSendMessage = canSendMessage, canRedact = canRedact, canReact = canReact)
|
||||||
setState {
|
setState {
|
||||||
copy(actionPermissions = permissions)
|
copy(actionPermissions = permissions)
|
||||||
|
@@ -24,16 +24,13 @@ import im.vector.app.features.home.room.detail.timeline.tools.createLinkMovement
|
|||||||
import im.vector.lib.strings.CommonPlurals
|
import im.vector.lib.strings.CommonPlurals
|
||||||
import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_MEGOLM
|
import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_MEGOLM
|
||||||
import org.matrix.android.sdk.api.extensions.orFalse
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
import org.matrix.android.sdk.api.query.QueryStringValue
|
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||||
import org.matrix.android.sdk.api.session.events.model.content.EncryptionEventContent
|
import org.matrix.android.sdk.api.session.events.model.content.EncryptionEventContent
|
||||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||||
import org.matrix.android.sdk.api.session.getRoom
|
import org.matrix.android.sdk.api.session.getRoom
|
||||||
import org.matrix.android.sdk.api.session.room.getStateEvent
|
import org.matrix.android.sdk.api.session.room.getRoomPowerLevels
|
||||||
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
|
|
||||||
import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent
|
import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent
|
||||||
import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
|
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@@ -303,9 +300,7 @@ class MergedHeaderItemFactory @Inject constructor(
|
|||||||
collapsedEventIds.removeAll(mergedEventIds)
|
collapsedEventIds.removeAll(mergedEventIds)
|
||||||
}
|
}
|
||||||
val mergeId = mergedEventIds.joinToString(separator = "_") { it.toString() }
|
val mergeId = mergedEventIds.joinToString(separator = "_") { it.toString() }
|
||||||
val powerLevelsHelper = activeSessionHolder.getSafeActiveSession()?.getRoom(event.roomId)
|
val roomPowerLevels = activeSessionHolder.getSafeActiveSession()?.getRoom(event.roomId)?.getRoomPowerLevels()
|
||||||
?.let { it.getStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty)?.content?.toModel<PowerLevelsContent>() }
|
|
||||||
?.let { PowerLevelsHelper(it) }
|
|
||||||
val currentUserId = activeSessionHolder.getSafeActiveSession()?.myUserId ?: ""
|
val currentUserId = activeSessionHolder.getSafeActiveSession()?.myUserId ?: ""
|
||||||
val attributes = MergedRoomCreationItem.Attributes(
|
val attributes = MergedRoomCreationItem.Attributes(
|
||||||
isCollapsed = isCollapsed,
|
isCollapsed = isCollapsed,
|
||||||
@@ -320,10 +315,10 @@ class MergedHeaderItemFactory @Inject constructor(
|
|||||||
callback = callback,
|
callback = callback,
|
||||||
currentUserId = currentUserId,
|
currentUserId = currentUserId,
|
||||||
roomSummary = partialState.roomSummary,
|
roomSummary = partialState.roomSummary,
|
||||||
canInvite = powerLevelsHelper?.isUserAbleToInvite(currentUserId) ?: false,
|
canInvite = roomPowerLevels?.isUserAbleToInvite(currentUserId) ?: false,
|
||||||
canChangeAvatar = powerLevelsHelper?.isUserAllowedToSend(currentUserId, true, EventType.STATE_ROOM_AVATAR) ?: false,
|
canChangeAvatar = roomPowerLevels?.isUserAllowedToSend(currentUserId, true, EventType.STATE_ROOM_AVATAR) ?: false,
|
||||||
canChangeTopic = powerLevelsHelper?.isUserAllowedToSend(currentUserId, true, EventType.STATE_ROOM_TOPIC) ?: false,
|
canChangeTopic = roomPowerLevels?.isUserAllowedToSend(currentUserId, true, EventType.STATE_ROOM_TOPIC) ?: false,
|
||||||
canChangeName = powerLevelsHelper?.isUserAllowedToSend(currentUserId, true, EventType.STATE_ROOM_NAME) ?: false
|
canChangeName = roomPowerLevels?.isUserAllowedToSend(currentUserId, true, EventType.STATE_ROOM_NAME) ?: false
|
||||||
)
|
)
|
||||||
MergedRoomCreationItem_()
|
MergedRoomCreationItem_()
|
||||||
.id(mergeId)
|
.id(mergeId)
|
||||||
|
@@ -41,7 +41,7 @@ import org.matrix.android.sdk.api.session.room.model.RoomThirdPartyInviteContent
|
|||||||
import org.matrix.android.sdk.api.session.room.model.RoomTopicContent
|
import org.matrix.android.sdk.api.session.room.model.RoomTopicContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.call.CallInviteContent
|
import org.matrix.android.sdk.api.session.room.model.call.CallInviteContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent
|
import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent
|
||||||
import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
|
import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||||
import org.matrix.android.sdk.api.session.widgets.model.WidgetContent
|
import org.matrix.android.sdk.api.session.widgets.model.WidgetContent
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
@@ -122,8 +122,8 @@ class NoticeEventFormatter @Inject constructor(
|
|||||||
userIds.addAll(previousPowerLevelsContent.users.orEmpty().keys)
|
userIds.addAll(previousPowerLevelsContent.users.orEmpty().keys)
|
||||||
val diffs = ArrayList<String>()
|
val diffs = ArrayList<String>()
|
||||||
userIds.forEach { userId ->
|
userIds.forEach { userId ->
|
||||||
val from = PowerLevelsHelper(previousPowerLevelsContent).getUserRole(userId)
|
val from = RoomPowerLevels(previousPowerLevelsContent, null).getSuggestedRole(userId)
|
||||||
val to = PowerLevelsHelper(powerLevelsContent).getUserRole(userId)
|
val to = RoomPowerLevels(powerLevelsContent, null).getSuggestedRole(userId)
|
||||||
if (from != to) {
|
if (from != to) {
|
||||||
val fromStr = roleFormatter.format(from)
|
val fromStr = roleFormatter.format(from)
|
||||||
val toStr = roleFormatter.format(to)
|
val toStr = roleFormatter.format(to)
|
||||||
|
@@ -10,6 +10,7 @@ package im.vector.app.features.home.room.filtered
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
import androidx.appcompat.widget.SearchView
|
import androidx.appcompat.widget.SearchView
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import im.vector.app.core.extensions.replaceFragment
|
import im.vector.app.core.extensions.replaceFragment
|
||||||
@@ -32,6 +33,9 @@ class FilteredRoomsActivity : VectorBaseActivity<ActivityFilteredRoomsBinding>()
|
|||||||
|
|
||||||
override fun getCoordinatorLayout() = views.coordinatorLayout
|
override fun getCoordinatorLayout() = views.coordinatorLayout
|
||||||
|
|
||||||
|
override val rootView: View
|
||||||
|
get() = views.coordinatorLayout
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
analyticsScreenName = MobileScreen.ScreenName.RoomFilter
|
analyticsScreenName = MobileScreen.ScreenName.RoomFilter
|
||||||
|
@@ -25,7 +25,6 @@ import com.airbnb.epoxy.OnModelBuildFinishedListener
|
|||||||
import com.airbnb.mvrx.args
|
import com.airbnb.mvrx.args
|
||||||
import com.airbnb.mvrx.fragmentViewModel
|
import com.airbnb.mvrx.fragmentViewModel
|
||||||
import com.airbnb.mvrx.withState
|
import com.airbnb.mvrx.withState
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.epoxy.LayoutManagerStateRestorer
|
import im.vector.app.core.epoxy.LayoutManagerStateRestorer
|
||||||
@@ -45,6 +44,7 @@ import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedA
|
|||||||
import im.vector.app.features.home.room.list.widget.NotifsFabMenuView
|
import im.vector.app.features.home.room.list.widget.NotifsFabMenuView
|
||||||
import im.vector.app.features.matrixto.OriginOfMatrixTo
|
import im.vector.app.features.matrixto.OriginOfMatrixTo
|
||||||
import im.vector.app.features.notifications.NotificationDrawerManager
|
import im.vector.app.features.notifications.NotificationDrawerManager
|
||||||
|
import im.vector.app.features.room.LeaveRoomPrompt
|
||||||
import im.vector.lib.strings.CommonStrings
|
import im.vector.lib.strings.CommonStrings
|
||||||
import kotlinx.coroutines.flow.filter
|
import kotlinx.coroutines.flow.filter
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
@@ -422,7 +422,7 @@ class RoomListFragment :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleQuickActions(quickAction: RoomListQuickActionsSharedAction) {
|
private suspend fun handleQuickActions(quickAction: RoomListQuickActionsSharedAction) {
|
||||||
when (quickAction) {
|
when (quickAction) {
|
||||||
is RoomListQuickActionsSharedAction.NotificationsAllNoisy -> {
|
is RoomListQuickActionsSharedAction.NotificationsAllNoisy -> {
|
||||||
roomListViewModel.handle(RoomListAction.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.ALL_MESSAGES_NOISY))
|
roomListViewModel.handle(RoomListAction.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.ALL_MESSAGES_NOISY))
|
||||||
@@ -451,26 +451,11 @@ class RoomListFragment :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun promptLeaveRoom(roomId: String) {
|
private suspend fun promptLeaveRoom(roomId: String) {
|
||||||
val isPublicRoom = roomListViewModel.isPublicRoom(roomId)
|
val warning = roomListViewModel.getLeaveRoomWarning(roomId)
|
||||||
val message = buildString {
|
LeaveRoomPrompt.show(requireContext(), warning) {
|
||||||
append(getString(CommonStrings.room_participants_leave_prompt_msg))
|
roomListViewModel.handle(RoomListAction.LeaveRoom(roomId))
|
||||||
if (!isPublicRoom) {
|
|
||||||
append("\n\n")
|
|
||||||
append(getString(CommonStrings.room_participants_leave_private_warning))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
MaterialAlertDialogBuilder(
|
|
||||||
requireContext(),
|
|
||||||
if (isPublicRoom) 0 else im.vector.lib.ui.styles.R.style.ThemeOverlay_Vector_MaterialAlertDialog_Destructive
|
|
||||||
)
|
|
||||||
.setTitle(CommonStrings.room_participants_leave_prompt_title)
|
|
||||||
.setMessage(message)
|
|
||||||
.setPositiveButton(CommonStrings.action_leave) { _, _ ->
|
|
||||||
roomListViewModel.handle(RoomListAction.LeaveRoom(roomId))
|
|
||||||
}
|
|
||||||
.setNegativeButton(CommonStrings.action_cancel, null)
|
|
||||||
.show()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun invalidate() = withState(roomListViewModel) { state ->
|
override fun invalidate() = withState(roomListViewModel) { state ->
|
||||||
|
@@ -26,6 +26,8 @@ import im.vector.app.features.analytics.extensions.toAnalyticsJoinedRoom
|
|||||||
import im.vector.app.features.analytics.plan.JoinedRoom
|
import im.vector.app.features.analytics.plan.JoinedRoom
|
||||||
import im.vector.app.features.displayname.getBestName
|
import im.vector.app.features.displayname.getBestName
|
||||||
import im.vector.app.features.invite.AutoAcceptInvites
|
import im.vector.app.features.invite.AutoAcceptInvites
|
||||||
|
import im.vector.app.features.room.LeaveRoomPrompt
|
||||||
|
import im.vector.app.features.room.getLeaveRoomWarning
|
||||||
import im.vector.app.features.settings.VectorPreferences
|
import im.vector.app.features.settings.VectorPreferences
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
@@ -41,7 +43,6 @@ import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
|
|||||||
import org.matrix.android.sdk.api.session.room.model.localecho.RoomLocalEcho
|
import org.matrix.android.sdk.api.session.room.model.localecho.RoomLocalEcho
|
||||||
import org.matrix.android.sdk.api.session.room.model.tag.RoomTag
|
import org.matrix.android.sdk.api.session.room.model.tag.RoomTag
|
||||||
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
||||||
import org.matrix.android.sdk.api.session.room.state.isPublic
|
|
||||||
import org.matrix.android.sdk.api.util.toMatrixItem
|
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||||
import org.matrix.android.sdk.flow.flow
|
import org.matrix.android.sdk.flow.flow
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
@@ -150,8 +151,8 @@ class RoomListViewModel @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isPublicRoom(roomId: String): Boolean {
|
suspend fun getLeaveRoomWarning(roomId: String): LeaveRoomPrompt.Warning {
|
||||||
return session.getRoom(roomId)?.stateService()?.isPublic().orFalse()
|
return session.getLeaveRoomWarning(roomId)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PRIVATE METHODS *****************************************************************************
|
// PRIVATE METHODS *****************************************************************************
|
||||||
|
@@ -18,7 +18,6 @@ import androidx.recyclerview.widget.ConcatAdapter.Config.StableIdMode
|
|||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.airbnb.epoxy.OnModelBuildFinishedListener
|
import com.airbnb.epoxy.OnModelBuildFinishedListener
|
||||||
import com.airbnb.mvrx.fragmentViewModel
|
import com.airbnb.mvrx.fragmentViewModel
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import im.vector.app.core.epoxy.LayoutManagerStateRestorer
|
import im.vector.app.core.epoxy.LayoutManagerStateRestorer
|
||||||
import im.vector.app.core.extensions.cleanup
|
import im.vector.app.core.extensions.cleanup
|
||||||
@@ -36,7 +35,7 @@ import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedA
|
|||||||
import im.vector.app.features.home.room.list.home.header.HomeRoomFilter
|
import im.vector.app.features.home.room.list.home.header.HomeRoomFilter
|
||||||
import im.vector.app.features.home.room.list.home.header.HomeRoomsHeadersController
|
import im.vector.app.features.home.room.list.home.header.HomeRoomsHeadersController
|
||||||
import im.vector.app.features.home.room.list.home.invites.InvitesActivity
|
import im.vector.app.features.home.room.list.home.invites.InvitesActivity
|
||||||
import im.vector.lib.strings.CommonStrings
|
import im.vector.app.features.room.LeaveRoomPrompt
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
@@ -103,7 +102,7 @@ class HomeRoomListFragment :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleQuickActions(quickAction: RoomListQuickActionsSharedAction) {
|
private suspend fun handleQuickActions(quickAction: RoomListQuickActionsSharedAction) {
|
||||||
when (quickAction) {
|
when (quickAction) {
|
||||||
is RoomListQuickActionsSharedAction.NotificationsAllNoisy -> {
|
is RoomListQuickActionsSharedAction.NotificationsAllNoisy -> {
|
||||||
roomListViewModel.handle(HomeRoomListAction.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.ALL_MESSAGES_NOISY))
|
roomListViewModel.handle(HomeRoomListAction.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.ALL_MESSAGES_NOISY))
|
||||||
@@ -185,26 +184,11 @@ class HomeRoomListFragment :
|
|||||||
concatAdapter.addAdapter(roomsAdapter)
|
concatAdapter.addAdapter(roomsAdapter)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun promptLeaveRoom(roomId: String) {
|
private suspend fun promptLeaveRoom(roomId: String) {
|
||||||
val isPublicRoom = roomListViewModel.isPublicRoom(roomId)
|
val warning = roomListViewModel.getLeaveRoomWarning(roomId)
|
||||||
val message = buildString {
|
LeaveRoomPrompt.show(requireContext(), warning) {
|
||||||
append(getString(CommonStrings.room_participants_leave_prompt_msg))
|
roomListViewModel.handle(HomeRoomListAction.LeaveRoom(roomId))
|
||||||
if (!isPublicRoom) {
|
|
||||||
append("\n\n")
|
|
||||||
append(getString(CommonStrings.room_participants_leave_private_warning))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
MaterialAlertDialogBuilder(
|
|
||||||
requireContext(),
|
|
||||||
if (isPublicRoom) 0 else im.vector.lib.ui.styles.R.style.ThemeOverlay_Vector_MaterialAlertDialog_Destructive
|
|
||||||
)
|
|
||||||
.setTitle(CommonStrings.room_participants_leave_prompt_title)
|
|
||||||
.setMessage(message)
|
|
||||||
.setPositiveButton(CommonStrings.action_leave) { _, _ ->
|
|
||||||
roomListViewModel.handle(HomeRoomListAction.LeaveRoom(roomId))
|
|
||||||
}
|
|
||||||
.setNegativeButton(CommonStrings.action_cancel, null)
|
|
||||||
.show()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onInvitesCounterClicked() {
|
private fun onInvitesCounterClicked() {
|
||||||
|
@@ -26,6 +26,8 @@ import im.vector.app.features.analytics.extensions.toTrackingValue
|
|||||||
import im.vector.app.features.analytics.plan.UserProperties
|
import im.vector.app.features.analytics.plan.UserProperties
|
||||||
import im.vector.app.features.displayname.getBestName
|
import im.vector.app.features.displayname.getBestName
|
||||||
import im.vector.app.features.home.room.list.home.header.HomeRoomFilter
|
import im.vector.app.features.home.room.list.home.header.HomeRoomFilter
|
||||||
|
import im.vector.app.features.room.LeaveRoomPrompt
|
||||||
|
import im.vector.app.features.room.getLeaveRoomWarning
|
||||||
import im.vector.lib.strings.CommonStrings
|
import im.vector.lib.strings.CommonStrings
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
@@ -53,7 +55,6 @@ import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
|||||||
import org.matrix.android.sdk.api.session.room.model.localecho.RoomLocalEcho
|
import org.matrix.android.sdk.api.session.room.model.localecho.RoomLocalEcho
|
||||||
import org.matrix.android.sdk.api.session.room.model.tag.RoomTag
|
import org.matrix.android.sdk.api.session.room.model.tag.RoomTag
|
||||||
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
||||||
import org.matrix.android.sdk.api.session.room.state.isPublic
|
|
||||||
import org.matrix.android.sdk.api.util.Optional
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
import org.matrix.android.sdk.api.util.toMatrixItem
|
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||||
import org.matrix.android.sdk.api.util.toOption
|
import org.matrix.android.sdk.api.util.toOption
|
||||||
@@ -331,8 +332,8 @@ class HomeRoomListViewModel @AssistedInject constructor(
|
|||||||
filteredPagedRoomSummariesLive.queryParams = getFilteredQueryParams(newFilter, filteredPagedRoomSummariesLive.queryParams)
|
filteredPagedRoomSummariesLive.queryParams = getFilteredQueryParams(newFilter, filteredPagedRoomSummariesLive.queryParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isPublicRoom(roomId: String): Boolean {
|
suspend fun getLeaveRoomWarning(roomId: String): LeaveRoomPrompt.Warning {
|
||||||
return session.getRoom(roomId)?.stateService()?.isPublic().orFalse()
|
return session.getLeaveRoomWarning(roomId)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleSelectRoom(action: HomeRoomListAction.SelectRoom) = withState {
|
private fun handleSelectRoom(action: HomeRoomListAction.SelectRoom) = withState {
|
||||||
|
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
package im.vector.app.features.home.room.list.home.invites
|
package im.vector.app.features.home.room.list.home.invites
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import im.vector.app.core.extensions.addFragment
|
import im.vector.app.core.extensions.addFragment
|
||||||
import im.vector.app.core.platform.VectorBaseActivity
|
import im.vector.app.core.platform.VectorBaseActivity
|
||||||
@@ -22,4 +23,8 @@ class InvitesActivity : VectorBaseActivity<ActivitySimpleBinding>() {
|
|||||||
addFragment(views.simpleFragmentContainer, InvitesFragment::class.java)
|
addFragment(views.simpleFragmentContainer, InvitesFragment::class.java)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getCoordinatorLayout() = views.coordinatorLayout
|
||||||
|
override val rootView: View
|
||||||
|
get() = views.coordinatorLayout
|
||||||
}
|
}
|
||||||
|
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
package im.vector.app.features.home.room.list.home.release
|
package im.vector.app.features.home.room.list.home.release
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import im.vector.app.core.extensions.addFragment
|
import im.vector.app.core.extensions.addFragment
|
||||||
@@ -26,6 +27,9 @@ class ReleaseNotesActivity : VectorBaseActivity<ActivitySimpleBinding>() {
|
|||||||
|
|
||||||
override fun getCoordinatorLayout() = views.coordinatorLayout
|
override fun getCoordinatorLayout() = views.coordinatorLayout
|
||||||
|
|
||||||
|
override val rootView: View
|
||||||
|
get() = views.coordinatorLayout
|
||||||
|
|
||||||
override fun initUiAndData() {
|
override fun initUiAndData() {
|
||||||
orientationLocker.lockPhonesToPortrait(this)
|
orientationLocker.lockPhonesToPortrait(this)
|
||||||
if (isFirstCreation()) {
|
if (isFirstCreation()) {
|
||||||
|
@@ -10,6 +10,7 @@ package im.vector.app.features.home.room.threads
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
import androidx.fragment.app.FragmentTransaction
|
import androidx.fragment.app.FragmentTransaction
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import im.vector.app.core.extensions.addFragmentToBackstack
|
import im.vector.app.core.extensions.addFragmentToBackstack
|
||||||
@@ -42,6 +43,9 @@ class ThreadsActivity : VectorBaseActivity<ActivityThreadsBinding>() {
|
|||||||
|
|
||||||
override fun getCoordinatorLayout() = views.coordinatorLayout
|
override fun getCoordinatorLayout() = views.coordinatorLayout
|
||||||
|
|
||||||
|
override val rootView: View
|
||||||
|
get() = views.coordinatorLayout
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
initFragment()
|
initFragment()
|
||||||
|
@@ -32,7 +32,7 @@ class VectorActivityLifecycleCallbacks constructor(private val popupAlertManager
|
|||||||
/**
|
/**
|
||||||
* The activities information collected from the app manifest.
|
* The activities information collected from the app manifest.
|
||||||
*/
|
*/
|
||||||
private var activitiesInfo: Array<ActivityInfo> = emptyArray()
|
private var activitiesInfo: List<ActivityInfo>? = null
|
||||||
|
|
||||||
private val coroutineScope = CoroutineScope(SupervisorJob())
|
private val coroutineScope = CoroutineScope(SupervisorJob())
|
||||||
|
|
||||||
@@ -51,24 +51,32 @@ class VectorActivityLifecycleCallbacks constructor(private val popupAlertManager
|
|||||||
override fun onActivityStopped(activity: Activity) {}
|
override fun onActivityStopped(activity: Activity) {}
|
||||||
|
|
||||||
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
|
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
|
||||||
if (activitiesInfo.isEmpty()) {
|
if (activitiesInfo == null) {
|
||||||
val context = activity.applicationContext
|
val context = activity.applicationContext
|
||||||
val packageManager: PackageManager = context.packageManager
|
val packageManager: PackageManager = context.packageManager
|
||||||
|
|
||||||
// Get all activities from element android
|
// Get all activities from element android
|
||||||
activitiesInfo = packageManager.getPackageInfoCompat(context.packageName, PackageManager.GET_ACTIVITIES).activities
|
val activities = packageManager
|
||||||
|
.getPackageInfoCompat(context.packageName, PackageManager.GET_ACTIVITIES)
|
||||||
|
.activities
|
||||||
|
.orEmpty()
|
||||||
|
.toList()
|
||||||
// Get all activities from PermissionController module
|
// Get all activities from PermissionController module
|
||||||
// See https://source.android.com/docs/core/architecture/modular-system/permissioncontroller#package-format
|
// See https://source.android.com/docs/core/architecture/modular-system/permissioncontroller#package-format
|
||||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.S_V2) {
|
val otherActivities = if (Build.VERSION.SDK_INT > Build.VERSION_CODES.S_V2) {
|
||||||
activitiesInfo += tryOrNull {
|
(tryOrNull {
|
||||||
packageManager.getPackageInfoCompat("com.google.android.permissioncontroller", PackageManager.GET_ACTIVITIES).activities
|
packageManager.getPackageInfoCompat("com.google.android.permissioncontroller", PackageManager.GET_ACTIVITIES).activities
|
||||||
} ?: tryOrNull {
|
} ?: tryOrNull {
|
||||||
packageManager.getModuleInfo("com.google.android.permission", 1).packageName?.let {
|
packageManager.getModuleInfo("com.google.android.permission", 1).packageName?.let {
|
||||||
packageManager.getPackageInfoCompat(it, PackageManager.GET_ACTIVITIES or PackageManager.MATCH_APEX).activities
|
packageManager.getPackageInfoCompat(it, PackageManager.GET_ACTIVITIES or PackageManager.MATCH_APEX).activities
|
||||||
}
|
}
|
||||||
}.orEmpty()
|
})
|
||||||
|
.orEmpty()
|
||||||
|
.toList()
|
||||||
|
} else {
|
||||||
|
emptyList()
|
||||||
}
|
}
|
||||||
|
activitiesInfo = activities + otherActivities
|
||||||
}
|
}
|
||||||
|
|
||||||
// restart the app if the task contains an unknown activity
|
// restart the app if the task contains an unknown activity
|
||||||
@@ -144,5 +152,5 @@ class VectorActivityLifecycleCallbacks constructor(private val popupAlertManager
|
|||||||
* @param activity the activity of the task
|
* @param activity the activity of the task
|
||||||
* @return true if the activity is potentially malicious
|
* @return true if the activity is potentially malicious
|
||||||
*/
|
*/
|
||||||
private fun isPotentialMaliciousActivity(activity: ComponentName): Boolean = activitiesInfo.none { it.name == activity.className }
|
private fun isPotentialMaliciousActivity(activity: ComponentName): Boolean = activitiesInfo.orEmpty().none { it.name == activity.className }
|
||||||
}
|
}
|
||||||
|
@@ -10,6 +10,7 @@ package im.vector.app.features.link
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.airbnb.mvrx.viewModel
|
import com.airbnb.mvrx.viewModel
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
@@ -41,6 +42,9 @@ class LinkHandlerActivity : VectorBaseActivity<ActivityProgressBinding>() {
|
|||||||
|
|
||||||
override fun getBinding() = ActivityProgressBinding.inflate(layoutInflater)
|
override fun getBinding() = ActivityProgressBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
|
override val rootView: View
|
||||||
|
get() = views.mainRoot
|
||||||
|
|
||||||
override fun initUiAndData() {
|
override fun initUiAndData() {
|
||||||
handleIntent()
|
handleIntent()
|
||||||
}
|
}
|
||||||
|
@@ -10,6 +10,7 @@ package im.vector.app.features.location
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
|
import android.view.View
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import im.vector.app.core.extensions.addFragment
|
import im.vector.app.core.extensions.addFragment
|
||||||
import im.vector.app.core.platform.VectorBaseActivity
|
import im.vector.app.core.platform.VectorBaseActivity
|
||||||
@@ -31,6 +32,9 @@ class LocationSharingActivity : VectorBaseActivity<ActivityLocationSharingBindin
|
|||||||
|
|
||||||
override fun getBinding() = ActivityLocationSharingBinding.inflate(layoutInflater)
|
override fun getBinding() = ActivityLocationSharingBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
|
override val rootView: View
|
||||||
|
get() = views.mainRoot
|
||||||
|
|
||||||
override fun initUiAndData() {
|
override fun initUiAndData() {
|
||||||
val locationSharingArgs: LocationSharingArgs? = intent?.extras?.getParcelableCompat(EXTRA_LOCATION_SHARING_ARGS)
|
val locationSharingArgs: LocationSharingArgs? = intent?.extras?.getParcelableCompat(EXTRA_LOCATION_SHARING_ARGS)
|
||||||
if (locationSharingArgs == null) {
|
if (locationSharingArgs == null) {
|
||||||
|
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
package im.vector.app.features.location
|
package im.vector.app.features.location
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import com.airbnb.mvrx.MavericksViewModelFactory
|
import com.airbnb.mvrx.MavericksViewModelFactory
|
||||||
import dagger.assisted.Assisted
|
import dagger.assisted.Assisted
|
||||||
@@ -15,9 +16,9 @@ import dagger.assisted.AssistedInject
|
|||||||
import im.vector.app.core.di.MavericksAssistedViewModelFactory
|
import im.vector.app.core.di.MavericksAssistedViewModelFactory
|
||||||
import im.vector.app.core.di.hiltMavericksViewModelFactory
|
import im.vector.app.core.di.hiltMavericksViewModelFactory
|
||||||
import im.vector.app.core.platform.VectorViewModel
|
import im.vector.app.core.platform.VectorViewModel
|
||||||
|
import im.vector.app.core.utils.PermissionChecker
|
||||||
import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider
|
import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider
|
||||||
import im.vector.app.features.location.domain.usecase.CompareLocationsUseCase
|
import im.vector.app.features.location.domain.usecase.CompareLocationsUseCase
|
||||||
import im.vector.app.features.powerlevel.PowerLevelsFlowFactory
|
|
||||||
import im.vector.app.features.settings.VectorPreferences
|
import im.vector.app.features.settings.VectorPreferences
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
@@ -32,8 +33,8 @@ import org.matrix.android.sdk.api.session.Session
|
|||||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||||
import org.matrix.android.sdk.api.session.getRoom
|
import org.matrix.android.sdk.api.session.getRoom
|
||||||
import org.matrix.android.sdk.api.session.getUserOrDefault
|
import org.matrix.android.sdk.api.session.getUserOrDefault
|
||||||
import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
|
|
||||||
import org.matrix.android.sdk.api.util.toMatrixItem
|
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||||
|
import org.matrix.android.sdk.flow.flow
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -48,6 +49,7 @@ class LocationSharingViewModel @AssistedInject constructor(
|
|||||||
private val session: Session,
|
private val session: Session,
|
||||||
private val compareLocationsUseCase: CompareLocationsUseCase,
|
private val compareLocationsUseCase: CompareLocationsUseCase,
|
||||||
private val vectorPreferences: VectorPreferences,
|
private val vectorPreferences: VectorPreferences,
|
||||||
|
private val permissionChecker: PermissionChecker,
|
||||||
) : VectorViewModel<LocationSharingViewState, LocationSharingAction, LocationSharingViewEvents>(initialState), LocationTracker.Callback {
|
) : VectorViewModel<LocationSharingViewState, LocationSharingAction, LocationSharingViewEvents>(initialState), LocationTracker.Callback {
|
||||||
|
|
||||||
private val room = session.getRoom(initialState.roomId)!!
|
private val room = session.getRoom(initialState.roomId)!!
|
||||||
@@ -70,13 +72,12 @@ class LocationSharingViewModel @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun observePowerLevelsForLiveLocationSharing() {
|
private fun observePowerLevelsForLiveLocationSharing() {
|
||||||
PowerLevelsFlowFactory(room).createFlow()
|
room.flow().liveRoomPowerLevels()
|
||||||
.distinctUntilChanged()
|
.distinctUntilChanged()
|
||||||
.setOnEach {
|
.setOnEach { roomPowerLevels ->
|
||||||
val powerLevelsHelper = PowerLevelsHelper(it)
|
|
||||||
val canShareLiveLocation = EventType.STATE_ROOM_BEACON_INFO.values
|
val canShareLiveLocation = EventType.STATE_ROOM_BEACON_INFO.values
|
||||||
.all { beaconInfoType ->
|
.all { beaconInfoType ->
|
||||||
powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, beaconInfoType)
|
roomPowerLevels.isUserAllowedToSend(session.myUserId, true, beaconInfoType)
|
||||||
}
|
}
|
||||||
|
|
||||||
copy(canShareLiveLocation = canShareLiveLocation)
|
copy(canShareLiveLocation = canShareLiveLocation)
|
||||||
@@ -88,7 +89,15 @@ class LocationSharingViewModel @AssistedInject constructor(
|
|||||||
locationTracker.locations
|
locationTracker.locations
|
||||||
.onEach(::onLocationUpdate)
|
.onEach(::onLocationUpdate)
|
||||||
.launchIn(viewModelScope)
|
.launchIn(viewModelScope)
|
||||||
locationTracker.start()
|
if (permissionChecker.checkPermission(
|
||||||
|
Manifest.permission.ACCESS_COARSE_LOCATION,
|
||||||
|
Manifest.permission.ACCESS_FINE_LOCATION,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
locationTracker.start()
|
||||||
|
} else {
|
||||||
|
Timber.w("Not allowed to use location api.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setUserItem() {
|
private fun setUserItem() {
|
||||||
|
@@ -17,6 +17,7 @@ import androidx.core.content.getSystemService
|
|||||||
import androidx.core.location.LocationListenerCompat
|
import androidx.core.location.LocationListenerCompat
|
||||||
import im.vector.app.core.di.ActiveSessionHolder
|
import im.vector.app.core.di.ActiveSessionHolder
|
||||||
import im.vector.app.core.resources.BuildMeta
|
import im.vector.app.core.resources.BuildMeta
|
||||||
|
import im.vector.app.core.utils.PermissionChecker
|
||||||
import im.vector.app.features.session.coroutineScope
|
import im.vector.app.features.session.coroutineScope
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.asSharedFlow
|
import kotlinx.coroutines.flow.asSharedFlow
|
||||||
@@ -37,6 +38,7 @@ class LocationTracker @Inject constructor(
|
|||||||
context: Context,
|
context: Context,
|
||||||
private val activeSessionHolder: ActiveSessionHolder,
|
private val activeSessionHolder: ActiveSessionHolder,
|
||||||
private val buildMeta: BuildMeta,
|
private val buildMeta: BuildMeta,
|
||||||
|
private val permissionChecker: PermissionChecker,
|
||||||
) : LocationListenerCompat {
|
) : LocationListenerCompat {
|
||||||
|
|
||||||
private val locationManager = context.getSystemService<LocationManager>()
|
private val locationManager = context.getSystemService<LocationManager>()
|
||||||
@@ -173,7 +175,15 @@ class LocationTracker @Inject constructor(
|
|||||||
fun removeCallback(callback: Callback) {
|
fun removeCallback(callback: Callback) {
|
||||||
callbacks.remove(callback)
|
callbacks.remove(callback)
|
||||||
if (callbacks.size == 0) {
|
if (callbacks.size == 0) {
|
||||||
stop()
|
if (permissionChecker.checkPermission(
|
||||||
|
Manifest.permission.ACCESS_COARSE_LOCATION,
|
||||||
|
Manifest.permission.ACCESS_FINE_LOCATION,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
stop()
|
||||||
|
} else {
|
||||||
|
Timber.w("Not allowed to use location api.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -10,6 +10,7 @@ package im.vector.app.features.location.live.map
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
|
import android.view.View
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import im.vector.app.core.extensions.addFragment
|
import im.vector.app.core.extensions.addFragment
|
||||||
import im.vector.app.core.platform.VectorBaseActivity
|
import im.vector.app.core.platform.VectorBaseActivity
|
||||||
@@ -29,6 +30,9 @@ class LiveLocationMapViewActivity : VectorBaseActivity<ActivityLocationSharingBi
|
|||||||
|
|
||||||
override fun getBinding() = ActivityLocationSharingBinding.inflate(layoutInflater)
|
override fun getBinding() = ActivityLocationSharingBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
|
override val rootView: View
|
||||||
|
get() = views.mainRoot
|
||||||
|
|
||||||
override fun initUiAndData() {
|
override fun initUiAndData() {
|
||||||
val mapViewArgs: LiveLocationMapViewArgs? = intent?.extras?.getParcelableCompat(EXTRA_LIVE_LOCATION_MAP_VIEW_ARGS)
|
val mapViewArgs: LiveLocationMapViewArgs? = intent?.extras?.getParcelableCompat(EXTRA_LIVE_LOCATION_MAP_VIEW_ARGS)
|
||||||
if (mapViewArgs == null) {
|
if (mapViewArgs == null) {
|
||||||
|
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
package im.vector.app.features.location.live.map
|
package im.vector.app.features.location.live.map
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
import com.airbnb.mvrx.MavericksViewModelFactory
|
import com.airbnb.mvrx.MavericksViewModelFactory
|
||||||
import dagger.assisted.Assisted
|
import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedFactory
|
import dagger.assisted.AssistedFactory
|
||||||
@@ -14,6 +15,7 @@ import dagger.assisted.AssistedInject
|
|||||||
import im.vector.app.core.di.MavericksAssistedViewModelFactory
|
import im.vector.app.core.di.MavericksAssistedViewModelFactory
|
||||||
import im.vector.app.core.di.hiltMavericksViewModelFactory
|
import im.vector.app.core.di.hiltMavericksViewModelFactory
|
||||||
import im.vector.app.core.platform.VectorViewModel
|
import im.vector.app.core.platform.VectorViewModel
|
||||||
|
import im.vector.app.core.utils.PermissionChecker
|
||||||
import im.vector.app.features.location.LocationData
|
import im.vector.app.features.location.LocationData
|
||||||
import im.vector.app.features.location.LocationTracker
|
import im.vector.app.features.location.LocationTracker
|
||||||
import im.vector.app.features.location.live.StopLiveLocationShareUseCase
|
import im.vector.app.features.location.live.StopLiveLocationShareUseCase
|
||||||
@@ -23,6 +25,7 @@ import kotlinx.coroutines.flow.onEach
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import org.matrix.android.sdk.api.session.room.location.UpdateLiveLocationShareResult
|
import org.matrix.android.sdk.api.session.room.location.UpdateLiveLocationShareResult
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
class LiveLocationMapViewModel @AssistedInject constructor(
|
class LiveLocationMapViewModel @AssistedInject constructor(
|
||||||
@Assisted private val initialState: LiveLocationMapViewState,
|
@Assisted private val initialState: LiveLocationMapViewState,
|
||||||
@@ -31,6 +34,7 @@ class LiveLocationMapViewModel @AssistedInject constructor(
|
|||||||
private val locationSharingServiceConnection: LocationSharingServiceConnection,
|
private val locationSharingServiceConnection: LocationSharingServiceConnection,
|
||||||
private val stopLiveLocationShareUseCase: StopLiveLocationShareUseCase,
|
private val stopLiveLocationShareUseCase: StopLiveLocationShareUseCase,
|
||||||
private val locationTracker: LocationTracker,
|
private val locationTracker: LocationTracker,
|
||||||
|
private val permissionChecker: PermissionChecker,
|
||||||
) :
|
) :
|
||||||
VectorViewModel<LiveLocationMapViewState, LiveLocationMapAction, LiveLocationMapViewEvents>(initialState),
|
VectorViewModel<LiveLocationMapViewState, LiveLocationMapAction, LiveLocationMapViewEvents>(initialState),
|
||||||
LocationSharingServiceConnection.Callback,
|
LocationSharingServiceConnection.Callback,
|
||||||
@@ -123,7 +127,15 @@ class LiveLocationMapViewModel @AssistedInject constructor(
|
|||||||
copy(isLoadingUserLocation = true)
|
copy(isLoadingUserLocation = true)
|
||||||
}
|
}
|
||||||
viewModelScope.launch(session.coroutineDispatchers.main) {
|
viewModelScope.launch(session.coroutineDispatchers.main) {
|
||||||
locationTracker.start()
|
if (permissionChecker.checkPermission(
|
||||||
|
Manifest.permission.ACCESS_COARSE_LOCATION,
|
||||||
|
Manifest.permission.ACCESS_FINE_LOCATION,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
locationTracker.start()
|
||||||
|
} else {
|
||||||
|
Timber.w("Not allowed to use location api.")
|
||||||
|
}
|
||||||
locationTracker.requestLastKnownLocation()
|
locationTracker.requestLastKnownLocation()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -7,14 +7,18 @@
|
|||||||
|
|
||||||
package im.vector.app.features.location.live.tracking
|
package im.vector.app.features.location.live.tracking
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
|
import androidx.core.app.ActivityCompat
|
||||||
import androidx.core.app.NotificationManagerCompat
|
import androidx.core.app.NotificationManagerCompat
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import im.vector.app.core.di.ActiveSessionHolder
|
import im.vector.app.core.di.ActiveSessionHolder
|
||||||
import im.vector.app.core.extensions.startForegroundCompat
|
import im.vector.app.core.extensions.startForegroundCompat
|
||||||
import im.vector.app.core.services.VectorAndroidService
|
import im.vector.app.core.services.VectorAndroidService
|
||||||
|
import im.vector.app.core.utils.PermissionChecker
|
||||||
import im.vector.app.features.location.LocationData
|
import im.vector.app.features.location.LocationData
|
||||||
import im.vector.app.features.location.LocationTracker
|
import im.vector.app.features.location.LocationTracker
|
||||||
import im.vector.app.features.location.live.GetLiveLocationShareSummaryUseCase
|
import im.vector.app.features.location.live.GetLiveLocationShareSummaryUseCase
|
||||||
@@ -52,6 +56,7 @@ class LocationSharingAndroidService : VectorAndroidService(), LocationTracker.Ca
|
|||||||
@Inject lateinit var activeSessionHolder: ActiveSessionHolder
|
@Inject lateinit var activeSessionHolder: ActiveSessionHolder
|
||||||
@Inject lateinit var getLiveLocationShareSummaryUseCase: GetLiveLocationShareSummaryUseCase
|
@Inject lateinit var getLiveLocationShareSummaryUseCase: GetLiveLocationShareSummaryUseCase
|
||||||
@Inject lateinit var checkIfEventIsRedactedUseCase: CheckIfEventIsRedactedUseCase
|
@Inject lateinit var checkIfEventIsRedactedUseCase: CheckIfEventIsRedactedUseCase
|
||||||
|
@Inject lateinit var permissionChecker: PermissionChecker
|
||||||
|
|
||||||
private var binder: LocationSharingAndroidServiceBinder? = null
|
private var binder: LocationSharingAndroidServiceBinder? = null
|
||||||
|
|
||||||
@@ -74,7 +79,15 @@ class LocationSharingAndroidService : VectorAndroidService(), LocationTracker.Ca
|
|||||||
private fun initLocationTracking() {
|
private fun initLocationTracking() {
|
||||||
// Start tracking location
|
// Start tracking location
|
||||||
locationTracker.addCallback(this)
|
locationTracker.addCallback(this)
|
||||||
locationTracker.start()
|
if (permissionChecker.checkPermission(
|
||||||
|
Manifest.permission.ACCESS_COARSE_LOCATION,
|
||||||
|
Manifest.permission.ACCESS_FINE_LOCATION,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
locationTracker.start()
|
||||||
|
} else {
|
||||||
|
Timber.w("Not allowed to use location api.")
|
||||||
|
}
|
||||||
|
|
||||||
launchWithActiveSession { session ->
|
launchWithActiveSession { session ->
|
||||||
val job = locationTracker.locations
|
val job = locationTracker.locations
|
||||||
@@ -95,7 +108,11 @@ class LocationSharingAndroidService : VectorAndroidService(), LocationTracker.Ca
|
|||||||
// Show a sticky notification
|
// Show a sticky notification
|
||||||
val notification = liveLocationNotificationBuilder.buildLiveLocationSharingNotification(roomArgs.roomId)
|
val notification = liveLocationNotificationBuilder.buildLiveLocationSharingNotification(roomArgs.roomId)
|
||||||
if (foregroundModeStarted) {
|
if (foregroundModeStarted) {
|
||||||
NotificationManagerCompat.from(this).notify(FOREGROUND_SERVICE_NOTIFICATION_ID, notification)
|
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
Timber.w("Not allowed to notify.")
|
||||||
|
} else {
|
||||||
|
NotificationManagerCompat.from(this).notify(FOREGROUND_SERVICE_NOTIFICATION_ID, notification)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
startForegroundCompat(FOREGROUND_SERVICE_NOTIFICATION_ID, notification)
|
startForegroundCompat(FOREGROUND_SERVICE_NOTIFICATION_ID, notification)
|
||||||
foregroundModeStarted = true
|
foregroundModeStarted = true
|
||||||
@@ -146,10 +163,14 @@ class LocationSharingAndroidService : VectorAndroidService(), LocationTracker.Ca
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun updateNotification() {
|
private fun updateNotification() {
|
||||||
if (liveInfoSet.isNotEmpty()) {
|
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
|
||||||
val roomId = liveInfoSet.last().roomArgs.roomId
|
Timber.w("Not allowed to notify.")
|
||||||
val notification = liveLocationNotificationBuilder.buildLiveLocationSharingNotification(roomId)
|
} else {
|
||||||
NotificationManagerCompat.from(this).notify(FOREGROUND_SERVICE_NOTIFICATION_ID, notification)
|
if (liveInfoSet.isNotEmpty()) {
|
||||||
|
val roomId = liveInfoSet.last().roomArgs.roomId
|
||||||
|
val notification = liveLocationNotificationBuilder.buildLiveLocationSharingNotification(roomId)
|
||||||
|
NotificationManagerCompat.from(this).notify(FOREGROUND_SERVICE_NOTIFICATION_ID, notification)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
package im.vector.app.features.location.preview
|
package im.vector.app.features.location.preview
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
import com.airbnb.mvrx.MavericksViewModelFactory
|
import com.airbnb.mvrx.MavericksViewModelFactory
|
||||||
import dagger.assisted.Assisted
|
import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedFactory
|
import dagger.assisted.AssistedFactory
|
||||||
@@ -14,6 +15,7 @@ import dagger.assisted.AssistedInject
|
|||||||
import im.vector.app.core.di.MavericksAssistedViewModelFactory
|
import im.vector.app.core.di.MavericksAssistedViewModelFactory
|
||||||
import im.vector.app.core.di.hiltMavericksViewModelFactory
|
import im.vector.app.core.di.hiltMavericksViewModelFactory
|
||||||
import im.vector.app.core.platform.VectorViewModel
|
import im.vector.app.core.platform.VectorViewModel
|
||||||
|
import im.vector.app.core.utils.PermissionChecker
|
||||||
import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider
|
import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider
|
||||||
import im.vector.app.features.location.LocationData
|
import im.vector.app.features.location.LocationData
|
||||||
import im.vector.app.features.location.LocationTracker
|
import im.vector.app.features.location.LocationTracker
|
||||||
@@ -23,12 +25,14 @@ import kotlinx.coroutines.launch
|
|||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import org.matrix.android.sdk.api.util.MatrixItem
|
import org.matrix.android.sdk.api.util.MatrixItem
|
||||||
import org.matrix.android.sdk.api.util.toMatrixItem
|
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
class LocationPreviewViewModel @AssistedInject constructor(
|
class LocationPreviewViewModel @AssistedInject constructor(
|
||||||
@Assisted private val initialState: LocationPreviewViewState,
|
@Assisted private val initialState: LocationPreviewViewState,
|
||||||
private val session: Session,
|
private val session: Session,
|
||||||
private val locationPinProvider: LocationPinProvider,
|
private val locationPinProvider: LocationPinProvider,
|
||||||
private val locationTracker: LocationTracker,
|
private val locationTracker: LocationTracker,
|
||||||
|
private val permissionChecker: PermissionChecker,
|
||||||
) : VectorViewModel<LocationPreviewViewState, LocationPreviewAction, LocationPreviewViewEvents>(initialState), LocationTracker.Callback {
|
) : VectorViewModel<LocationPreviewViewState, LocationPreviewAction, LocationPreviewViewEvents>(initialState), LocationTracker.Callback {
|
||||||
|
|
||||||
@AssistedFactory
|
@AssistedFactory
|
||||||
@@ -89,7 +93,15 @@ class LocationPreviewViewModel @AssistedInject constructor(
|
|||||||
copy(isLoadingUserLocation = true)
|
copy(isLoadingUserLocation = true)
|
||||||
}
|
}
|
||||||
viewModelScope.launch(session.coroutineDispatchers.main) {
|
viewModelScope.launch(session.coroutineDispatchers.main) {
|
||||||
locationTracker.start()
|
if (permissionChecker.checkPermission(
|
||||||
|
Manifest.permission.ACCESS_COARSE_LOCATION,
|
||||||
|
Manifest.permission.ACCESS_FINE_LOCATION,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
locationTracker.start()
|
||||||
|
} else {
|
||||||
|
Timber.w("Not allowed to use location api.")
|
||||||
|
}
|
||||||
locationTracker.requestLastKnownLocation()
|
locationTracker.requestLastKnownLocation()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -76,6 +76,9 @@ open class LoginActivity : VectorBaseActivity<ActivityLoginBinding>(), UnlockedA
|
|||||||
|
|
||||||
override fun getCoordinatorLayout() = views.coordinatorLayout
|
override fun getCoordinatorLayout() = views.coordinatorLayout
|
||||||
|
|
||||||
|
override val rootView: View
|
||||||
|
get() = views.coordinatorLayout
|
||||||
|
|
||||||
override fun initUiAndData() {
|
override fun initUiAndData() {
|
||||||
analyticsScreenName = MobileScreen.ScreenName.Login
|
analyticsScreenName = MobileScreen.ScreenName.Login
|
||||||
|
|
||||||
|
@@ -10,6 +10,7 @@ package im.vector.app.features.media
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import im.vector.app.core.di.ActiveSessionHolder
|
import im.vector.app.core.di.ActiveSessionHolder
|
||||||
@@ -26,6 +27,9 @@ class BigImageViewerActivity : VectorBaseActivity<ActivityBigImageViewerBinding>
|
|||||||
|
|
||||||
override fun getBinding() = ActivityBigImageViewerBinding.inflate(layoutInflater)
|
override fun getBinding() = ActivityBigImageViewerBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
|
override val rootView: View
|
||||||
|
get() = views.mainRoot
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
@@ -138,7 +138,9 @@ class VectorAttachmentViewerActivity : AttachmentViewerActivity(), AttachmentInt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
window.statusBarColor = ContextCompat.getColor(this, im.vector.lib.ui.styles.R.color.black_alpha)
|
window.statusBarColor = ContextCompat.getColor(this, im.vector.lib.ui.styles.R.color.black_alpha)
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
window.navigationBarColor = ContextCompat.getColor(this, im.vector.lib.ui.styles.R.color.black_alpha)
|
window.navigationBarColor = ContextCompat.getColor(this, im.vector.lib.ui.styles.R.color.black_alpha)
|
||||||
|
|
||||||
observeViewEvents()
|
observeViewEvents()
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user