Merge branch 'develop' into feature/fga/text_composer_extract

This commit is contained in:
ganfra 2021-09-30 12:02:27 +02:00
commit 3df004b035
121 changed files with 1584 additions and 431 deletions

View File

@ -1,7 +1,7 @@
name: Release checklist
description: Checklist for each release. This template is only for the core team.
title: "[Release] Element Android v"
labels: [\U0001F680 Release]
labels: [🚀 Release]
assignees:
- bmarty

View File

@ -1,3 +1,13 @@
Changes in Element v1.3.1 (2021-09-29)
======================================
Bugfixes 🐛
----------
- Verifying exported E2E keys to provide user feedback when the output is malformed ([#4082](https://github.com/vector-im/element-android/issues/4082))
- Fix settings crash when accelerometer not available ([#4103](https://github.com/vector-im/element-android/issues/4103))
- Crash while rendering failed message warning ([#4110](https://github.com/vector-im/element-android/issues/4110))
Changes in Element v1.3.0 (2021-09-27)
======================================

1
changelog.d/240.feature Normal file
View File

@ -0,0 +1 @@
Android Auto notification support

2
changelog.d/3898.bugfix Normal file
View File

@ -0,0 +1,2 @@
Fixes the passphrase screen being incorrectly shown when pressing back on the key verification screen.
When the user doesn't have a passphrase set we don't show the passphrase screen.

1
changelog.d/3935.bugfix Normal file
View File

@ -0,0 +1 @@
Save button for adding rooms to a space is hidden when scrolling through list of rooms

1
changelog.d/4027.feature Normal file
View File

@ -0,0 +1 @@
Add client base url config to customize permalinks

View File

@ -1 +0,0 @@
Verifying exported E2E keys to provide user feedback when the output is malformed

4
changelog.d/4092.bugfix Normal file
View File

@ -0,0 +1,4 @@
Added changes that will make SearchView in search bar focused by default on opening reaction picker.
When tapping close icon of SearchView, the SearchView did not collapse therefore added the on close listener
which will collapse the SearchView on close.

View File

@ -1 +0,0 @@
Fix settings crash when accelerometer not available

1
changelog.d/4113.misc Normal file
View File

@ -0,0 +1 @@
Fix release label in the release issue template

View File

@ -9,8 +9,8 @@ ext.versions = [
def gradle = "7.0.2"
// Ref: https://kotlinlang.org/releases.html
def kotlin = "1.5.30"
def kotlinCoroutines = "1.5.1"
def kotlin = "1.5.31"
def kotlinCoroutines = "1.5.2"
def dagger = "2.38.1"
def retrofit = "2.9.0"
def arrow = "0.8.2"

View File

@ -0,0 +1,2 @@
Hlavní změny v této verzi: Mnohá vylepšení VoIP a prostorů (stále v beta verzi).
Úplný seznam změn: https://github.com/vector-im/element-android/releases/tag/v1.2.1

View File

@ -1,2 +1,2 @@
Hauptänderungen: Sprachnachrichten standardmäßig aktiviert.
Ganze Änderungsliste: https://github.com/vector-im/element-android/releases/tag/v1.1.16
Ganze Änderungsliste: https://github.com/vector-im/element-android/releases/tag/v1.2.0

View File

@ -0,0 +1,2 @@
VoIP und Spaces verbessert
Vollständige Änderungsliste: https://github.com/vector-im/element-android/releases/tag/v1.2.1

View File

@ -0,0 +1,2 @@
Main changes in this version: Organize your rooms using Spaces! v1.3.1 is fixing a crash which can occurs in v1.3.0.
Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.3.1

View File

@ -1,2 +1,2 @@
Põhilised muutused selles versioonis: häälsõnumid on nüüd vaikimisi kasutusel.
Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases/tag/v1.1.16
Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases/tag/v1.2.0

View File

@ -0,0 +1,2 @@
Põhilised muutused selles versioonis: palju täiendusi kõnede ja veel testjärgus olevas kogukonnakeskuste loogikas.
Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases/tag/v1.2.1

View File

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

View File

@ -0,0 +1,2 @@
تغییرات اصلی در این نگارش: چندین بهبود در ویپ و فضاها (همچنان در حالت آزمایشی).
گزارش دگرگونی کامل: https://github.com/vector-im/element-android/releases/tag/v1.2.1

View File

@ -1,2 +1,2 @@
Fő változás ebben a verzióban: Hangüzenetek alapértelmezetten engedélyezettek.
Teljes változásnapló: https://github.com/vector-im/element-android/releases/tag/v1.1.16
Teljes változásnapló: https://github.com/vector-im/element-android/releases/tag/v1.2.0

View File

@ -0,0 +1,2 @@
Fő változás ebben a verzióban: Sok fejlesztés a VoIP és Terek kapcsán (még béta)
Teljes változásnapló: https://github.com/vector-im/element-android/releases/tag/v1.2.1

View File

@ -1,2 +1,2 @@
Modifiche principali in questa versione: i messaggi vocali sono attivi in modo predefinito.
Cronologia completa: https://github.com/vector-im/element-android/releases/tag/v1.1.16
Cronologia completa: https://github.com/vector-im/element-android/releases/tag/v1.2.0

View File

@ -0,0 +1,2 @@
Modifiche principali in questa versione: molti miglioramenti nel VoIP e negli Spazi (ancora in beta).
Cronologia completa: https://github.com/vector-im/element-android/releases/tag/v1.2.1

View File

@ -1,2 +1,2 @@
Principais mudanças nesta versão: Mensagem de Voz está habilitada por default.
Changelog completo: https://github.com/vector-im/element-android/releases/tag/v1.1.16
Changelog completo: https://github.com/vector-im/element-android/releases/tag/v1.2.0

View File

@ -0,0 +1,2 @@
Principais mudanças nesta versão: Muitas melhorias em VoIP e Espaços (ainda em beta).
Changelog completo: https://github.com/vector-im/element-android/releases/tag/v1.2.1

View File

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

View File

@ -0,0 +1,2 @@
Основные изменения в этой версии: Множество улучшений в VoIP и пространствах (все еще в бета-версии).
Полный список изменений: https://github.com/vector-im/element-android/releases/tag/v1.2.1

View File

@ -0,0 +1,2 @@
Ky version i ri përmban kryesisht ndreqje të metash dhe përmirësime. Dërgimi i një mesazhi tani është shumë i shpejtë.
Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.0.10

View File

@ -0,0 +1,2 @@
Ky version i ri përmban kryesisht përmirësime të ndërfaqes dhe punimit të përdoruesit. Tani mund të ftoni shokë, dhe të krijoni MD shumë shpejt, përmes skanimit të kodesh QR.
Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.0.11

View File

@ -0,0 +1,2 @@
Ndryshimet kryesore në këtë version: Paraparje URL-sh, tastierë e re për emoji, aftësi të reja për rregullime dhome, dhe dëborë për Krishtlindje!
Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.0.12

View File

@ -0,0 +1,2 @@
Ndryshimet kryesore në këtë version: Paraparje URL-sh, tastierë e re për emoji, aftësi të reja për rregullime dhome, dhe dëborë për Krishtlindje!
Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.0.13

View File

@ -0,0 +1,2 @@
Ndryshimet kryesore në këtë version: Përpunim lejesh dhome, temë e çelët/e errët e automatizuar, dhe një dorë ndreqjesh të metash.
Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.0.14

View File

@ -0,0 +1,2 @@
Ndryshimet kryesore në këtë version: Mbulim Hyrjesh nga rrjete shoqërorë.
Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.0.15

View File

@ -0,0 +1,2 @@
Ndryshimet kryesore në këtë version: Mbulim Hyrjesh nga rrjete shoqërorë.
Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.0.15 dhe https://github.com/vector-im/element-android/releases/tag/v1.0.16

View File

@ -0,0 +1,2 @@
Ndryshimet kryesore në këtë version: Ndreqje të metash!
Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.0.17

View File

@ -0,0 +1,2 @@
Ndryshimet kryesore në këtë version: Përmirësime për VoIP (thirrje audio dhe video në DM) dhe ndreqje të metash!
Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.1.0

View File

@ -0,0 +1,2 @@
Ndryshimet kryesore në këtë version: përmirësime funksionimi dhe ndreqje të metash!
Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.1.1

View File

@ -0,0 +1,2 @@
Ndryshimet kryesore në këtë version: përmirësime funksionimi dhe ndreqje të metash!
Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.1.2

View File

@ -0,0 +1,2 @@
Ndryshimet kryesore në këtë version: përmirësime funksionimi dhe ndreqje të metash!
Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.1.3

View File

@ -0,0 +1,2 @@
Ndryshimet kryesore në këtë version: përmirësime funksionimi dhe ndreqje të metash!
Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.1.4

View File

@ -0,0 +1,2 @@
Ndryshimet kryesore në këtë version: ndreqje të metash për 1.1.4
Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.1.5

View File

@ -0,0 +1,2 @@
Ndryshimet kryesore në këtë version: ndreqje të metash për 1.1.5
Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.1.6

View File

@ -0,0 +1,2 @@
Ndryshimet kryesore në këtë version: mbulim për Hapësira, në fazë beta. Ngjeshje videosh, përpara dërgimi.
Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.1.7

View File

@ -0,0 +1,2 @@
Ndryshimet kryesore në këtë version: përmirësime për Hapësira.
Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.1.8

View File

@ -0,0 +1,2 @@
Ndryshimet kryesore në këtë version: shtim mbulimi për rrjetin gitter.im.
Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.1.9

View File

@ -0,0 +1,2 @@
Ndryshimet kryesore në këtë version: përditësime teme dhe stili dhe veçori të reja për hapësira.
Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.1.10

View File

@ -0,0 +1,2 @@
Ndryshimet kryesore në këtë version: përditësime teme dhe stili dhe veçori të reja për hapësira (ndreqje të mete për 1.1.10)
Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.1.11

View File

@ -0,0 +1,2 @@
Ndryshimet kryesore në këtë version: përditësime teme dhe stili dhe ndreqje e një vithisjeje pas një thirrjeje video
Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.1.12

View File

@ -0,0 +1,2 @@
Ndryshimet kryesore në këtë version: përditësim kryesisht për qëndrueshmërinë dhe ndreqje të metash.
Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.1.13

View File

@ -0,0 +1,2 @@
Ndryshimet kryesore në këtë version: ndreqje e një problemi rreth mesazhesh të fshehtëzuar.
Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.1.14

View File

@ -0,0 +1,2 @@
Ndryshimet kryesore në këtë version: sendërtim mesazhesh zanore, nën mjedis laboratori.
Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.1.15

View File

@ -0,0 +1,2 @@
Ndryshimet kryesore në këtë version: Ndreqje gabimi, kur dërgohet mesazh i fshehtëzuar, nëse dikush nga dhoma bën dalje prej saj.
Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.1.16

View File

@ -0,0 +1,2 @@
Ndryshimet kryesore në këtë version: Mesazh Zanor është i aktivizuar, si parazgjedhje.
Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.2.0

View File

@ -0,0 +1,2 @@
Ndryshimet kryesore në këtë version: Mjaft përmirësime në VoIP dhe Hapësira (ende në beta).
Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.2.1

View File

@ -0,0 +1,39 @@
Element-i është si aplikacion shkëmbyes i sigurt mesazhesh, ashtu edhe bashkëpunimi prodhimtar ekipi, i cili është ideal për fjalosje në grup, teksa punohet së largët. Ky aplikacion fjalosjeje përdor fshehtëzim skaj-më-skaj për të furnizuar konferencë video, shkëmbim kartelash dhe thirrje me zë të fuqishme.
<b>Në veçoritë e Element-it përfshihen:</b>
- Mjete të thelluara komunikimi internetor
- Mesazhe plotësisht të fshehtëzuar, për të lejuar komunikim në nivel korporate, madje edhe për punonjës së largëti
- Fjalosje e decentralizuar, bazuar në platformën me burim të hapët Matrix
- Shkëmbim i sigurt kartelash, me të dhëna të fshehtëzuara, teksa administrohen projekte
- Fjalosje video të llojit VoIP dhe tregim ekrani
- Integrim i kollajtë me mjetet tuaja të parapëlqyera të bashkëpunimit internetor, mjete administrimi projektesh, shërbime VoIP dhe aplikacione të tjera shkëmbimi mesazhesh në ekip
Element-i është plotësisht i ndryshëm nga aplikacione të tjera shkëmbimi mesazhesh dhe bashkëpunimi. Funksionimi i tij bazohet në Matrix, një rrjet i hapët për mesazhe të siguruar dhe komunikim të decentralizuar. Lejon vetëstrehim, për tu lejuar përdoruesve pronësi dhe kontroll maksimal të të dhënave dhe mesazheve të tyre.
<b>Privatësi dhe shkëmbim mesazhesh të fshehtëzuar</b>
Element-i ju mbron nga reklama të padëshiruara, shfrytëzim të dhënash dhe vatha dixhitale. Ai siguron gjithashtu krejt të dhënat tuaja, komunikime tek-për-tek me video dhe me zë, përmes fshehtëzimi skaj-më-skaj dhe verifikim “cross-signed” pajisjesh.
Element-i ju jep kontrollin e privatësisë tuaj, teksa ju lejon të komunikoni në mënyrë të siguruar me këdo në rrjetin Matrix, ose me mjete të tjera bashkëpunimi në shkallë biznesi, duke u integruar me aplikacione të tillë si Slack.
<b>Element-i mund të vetëstrehohet</b>
Për të lejuar më tepër kontroll mbi të dhënat dhe bisedat tuaja rezervat, Element-i mund të vetëstrehohet, ose mund të zgjidhni cilëndo strehë të bazuar në Matrix - standardi për komunikim me burim të hapët, të decentralizuar. Element-i ju jep privatësi, pajtueshmëri sigurie dhe zhdërvjelltësi integrimesh.
<b>Jini zot i të dhënave tuaja</b>
Ju vendosni ku të mbahen të dhënat dhe mesazhet tuaja. Pa rrezikun e shfrytëzimit të të dhënave apo hyrjes në to nga palë të treta.
Element-i ju vë ju në kontroll përmes rrugësh të ndryshme:
1. Merrni një llogari falas te shërbyesi publik matrix.org strehuar nga zhvillues të Matrix-it, ose zgjidhni prej mijëra shërbyesish publikë të strehuar nga vullnetarë
2. Vetëstrehoni llogarinë tuaj duke xhiruar një shërbyes në infrastrukturën tuaj TI
3. Regjistrohuni për një llogari në një shërbyes vetjak, thjesht duke u pajtuar te platforma Element Matrix Services e strehimeve
<b>Shkëmbim mesazhesh dhe bashkëpunim me burim të hapët</b>
Mund të fjaloseni me këdo në rrjetin Matrix, qoftë kur përdorin Element, një tjetër aplikacion Matrix, apo edhe kur përdorin një tjetër aplikacion shkëmbimi mesazhesh.
<b>Super i sigurt</b>
Fshehtëzim i njëmendtë skaj-më-skaj (vetëm ata te biseda mund të shfshehtëzojnë mesazhe), dhe verifikim “cross-signed” pajisjesh.
<b>Komunikim dhe integrim i plotë</b>
Shkëmbim mesazhesh, thirrje me zë dhe me video, shkëmbim kartelash, tregim ekrani dhe një grup i tërë integrimesh, robotësh dhe widget-esh. Krijoni dhoma, bashkësi, mbani lidhjet dhe mbaroni punë.
<b>Rifillojani atje ku e latë</b>
Jini në dijeni, kudo ku gjendeni, me historik plotësisht të njëkohësuar mesazhesh nëpër krejt pajisjet tuaja dhe në internet te https://app.element.io

View File

@ -0,0 +1 @@
Mesazhe grupi - mesazhe, fjalosje në grup dhe thirrje me video, të fshehtëzuara

View File

@ -1 +1 @@
Element - Shkëmbyes I Sigurt Mesazhesh
Element - Shkëmbyes i Sigurt Mesazhesh

View File

@ -1,2 +1,2 @@
Основні зміни в цій версії: голосові повідомлення типово увімкнено.
Повний журнал змін: https://github.com/vector-im/element-android/releases/tag/v1.1.16
Повний журнал змін: https://github.com/vector-im/element-android/releases/tag/v1.2.0

View File

@ -0,0 +1,2 @@
Основні зміни в цій версії: багато вдосконалень VoIP і просторів (досі бета)
Повний журнал змін: https://github.com/vector-im/element-android/releases/tag/v1.2.1

View File

@ -1,2 +1,2 @@
此版本中的主要更改:默认启用语音消息。
完整更新日志https://github.com/vector-im/element-android/releases/tag/v1.1.16
完整更新日志https://github.com/vector-im/element-android/releases/tag/v1.2.0

View File

@ -0,0 +1,2 @@
这个版本的主要变化VoIP和空间的许多改进(仍在测试中)。
完整更新日志https://github.com/vector-im/element-android/releases/tag/v1.2.1

View File

@ -0,0 +1,2 @@
此版本中的主要變動:對 VoIP 與空間功能的諸多改善(仍在測試中)。
完整的變更紀錄https://github.com/vector-im/element-android/releases/tag/v1.2.1

View File

@ -31,7 +31,7 @@ android {
// that the app's state is completely cleared between tests.
testInstrumentationRunnerArguments clearPackageData: 'true'
buildConfigField "String", "SDK_VERSION", "\"1.3.1\""
buildConfigField "String", "SDK_VERSION", "\"1.3.2\""
buildConfigField "String", "GIT_SDK_REVISION", "\"${gitRevision()}\""
resValue "string", "git_sdk_revision", "\"${gitRevision()}\""

View File

@ -32,8 +32,16 @@ data class MatrixConfiguration(
"https://scalar-staging.riot.im/scalar/api"
),
/**
* Optional proxy to connect to the matrix servers
* You can create one using for instance Proxy(proxyType, InetSocketAddress.createUnresolved(hostname, port)
* Optional base url to create client permalinks (eg. https://www.example.com/#/) instead of Matrix ones (matrix.to links).
* Do not forget to add the "#" which is required by the permalink parser.
*
* Note: this field is only used for permalinks creation, you will also have to edit the string-array `permalink_supported_hosts` in the config file
* and add it to your manifest to handle these links in the application.
*/
val clientPermalinkBaseUrl: String? = null,
/**
* Optional proxy to connect to the matrix servers.
* You can create one using for instance Proxy(proxyType, InetSocketAddress.createUnresolved(hostname, port).
*/
val proxy: Proxy? = null,
/**
@ -47,7 +55,7 @@ data class MatrixConfiguration(
) {
/**
* Can be implemented by your Application class
* Can be implemented by your Application class.
*/
interface Provider {
fun providesMatrixConfiguration(): MatrixConfiguration

View File

@ -0,0 +1,55 @@
/*
* Copyright 2020 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.permalinks
import android.net.Uri
/**
* Mapping of an input URI to a matrix.to compliant URI.
*/
object MatrixToConverter {
/**
* Try to convert a URL from an element web instance or from a client permalink to a matrix.to url.
* To be successfully converted, URL path should contain one of the [SUPPORTED_PATHS].
* Examples:
* - https://riot.im/develop/#/room/#element-android:matrix.org -> https://matrix.to/#/#element-android:matrix.org
* - https://app.element.io/#/room/#element-android:matrix.org -> https://matrix.to/#/#element-android:matrix.org
* - https://www.example.org/#/room/#element-android:matrix.org -> https://matrix.to/#/#element-android:matrix.org
*/
fun convert(uri: Uri): Uri? {
val uriString = uri.toString()
return when {
// URL is already a matrix.to
uriString.startsWith(PermalinkService.MATRIX_TO_URL_BASE) -> uri
// Web or client url
SUPPORTED_PATHS.any { it in uriString } -> {
val path = SUPPORTED_PATHS.first { it in uriString }
Uri.parse(PermalinkService.MATRIX_TO_URL_BASE + uriString.substringAfter(path))
}
// URL is not supported
else -> null
}
}
private val SUPPORTED_PATHS = listOf(
"/#/room/",
"/#/user/",
"/#/group/"
)
}

View File

@ -26,6 +26,7 @@ import java.net.URLDecoder
* This class turns a uri to a [PermalinkData]
* element-based domains (e.g. https://app.element.io/#/user/@chagai95:matrix.org) permalinks
* or matrix.to permalinks (e.g. https://matrix.to/#/@chagai95:matrix.org)
* or client permalinks (e.g. <clientPermalinkBaseUrl>user/@chagai95:matrix.org)
*/
object PermalinkParser {
@ -42,13 +43,15 @@ object PermalinkParser {
* https://github.com/matrix-org/matrix-doc/blob/master/proposals/1704-matrix.to-permalinks.md
*/
fun parse(uri: Uri): PermalinkData {
if (!uri.toString().startsWith(PermalinkService.MATRIX_TO_URL_BASE)) {
return PermalinkData.FallbackLink(uri)
}
// the client or element-based domain permalinks (e.g. https://app.element.io/#/user/@chagai95:matrix.org) don't have the
// mxid in the first param (like matrix.to does - https://matrix.to/#/@chagai95:matrix.org) but rather in the second after /user/ so /user/mxid
// so convert URI to matrix.to to simplify parsing process
val matrixToUri = MatrixToConverter.convert(uri) ?: return PermalinkData.FallbackLink(uri)
// We can't use uri.fragment as it is decoding to early and it will break the parsing
// of parameters that represents url (like signurl)
val fragment = uri.toString().substringAfter("#") // uri.fragment
if (fragment.isNullOrEmpty()) {
val fragment = matrixToUri.toString().substringAfter("#") // uri.fragment
if (fragment.isEmpty()) {
return PermalinkData.FallbackLink(uri)
}
val safeFragment = fragment.substringBefore('?')
@ -61,20 +64,14 @@ object PermalinkParser {
.map { URLDecoder.decode(it, "UTF-8") }
.take(2)
// the element-based domain permalinks (e.g. https://app.element.io/#/user/@chagai95:matrix.org) don't have the
// mxid in the first param (like matrix.to does - https://matrix.to/#/@chagai95:matrix.org) but rather in the second after /user/ so /user/mxid
var identifier = params.getOrNull(0)
if (identifier.equals("user")) {
identifier = params.getOrNull(1)
}
val identifier = params.getOrNull(0)
val extraParameter = params.getOrNull(1)
return when {
identifier.isNullOrEmpty() -> PermalinkData.FallbackLink(uri)
MatrixPatterns.isUserId(identifier) -> PermalinkData.UserLink(userId = identifier)
MatrixPatterns.isGroupId(identifier) -> PermalinkData.GroupLink(groupId = identifier)
MatrixPatterns.isRoomId(identifier) -> {
handleRoomIdCase(fragment, identifier, uri, extraParameter, viaQueryParameters)
handleRoomIdCase(fragment, identifier, matrixToUri, extraParameter, viaQueryParameters)
}
MatrixPatterns.isRoomAlias(identifier) -> {
PermalinkData.RoomLink(
@ -125,7 +122,8 @@ object PermalinkParser {
}
}
private fun safeExtractParams(fragment: String) = fragment.substringAfter("?").split('&').mapNotNull {
private fun safeExtractParams(fragment: String) =
fragment.substringAfter("?").split('&').mapNotNull {
val splitNameValue = it.split("=")
if (splitNameValue.size == 2) {
Pair(splitNameValue[0], URLDecoder.decode(splitNameValue[1], "UTF-8"))
@ -138,9 +136,7 @@ object PermalinkParser {
.filter {
it.mParameter == "via"
}.map {
it.mValue.let {
URLDecoder.decode(it, "UTF-8")
}
URLDecoder.decode(it.mValue, "UTF-8")
}
}
}

View File

@ -19,7 +19,8 @@ package org.matrix.android.sdk.api.session.permalinks
import org.matrix.android.sdk.api.session.events.model.Event
/**
* Useful methods to create Matrix permalink (matrix.to links).
* Useful methods to create permalink (like matrix.to links or client permalinks).
* See [org.matrix.android.sdk.api.MatrixConfiguration.clientPermalinkBaseUrl] to setup a custom permalink base url.
*/
interface PermalinkService {
@ -32,10 +33,11 @@ interface PermalinkService {
* Ex: "https://matrix.to/#/!nbzmcXAqpxBXjAdgoX:matrix.org/$1531497316352799BevdV:matrix.org"
*
* @param event the event
* @param forceMatrixTo whether we should force using matrix.to base URL
*
* @return the permalink, or null in case of error
*/
fun createPermalink(event: Event): String?
fun createPermalink(event: Event, forceMatrixTo: Boolean = false): String?
/**
* Creates a permalink for an id (can be a user Id, etc.).
@ -43,18 +45,21 @@ interface PermalinkService {
* Ex: "https://matrix.to/#/@benoit:matrix.org"
*
* @param id the id
* @param forceMatrixTo whether we should force using matrix.to base URL
*
* @return the permalink, or null in case of error
*/
fun createPermalink(id: String): String?
fun createPermalink(id: String, forceMatrixTo: Boolean = false): String?
/**
* Creates a permalink for a roomId, including the via parameters
*
* @param roomId the room id
* @param forceMatrixTo whether we should force using matrix.to base URL
*
* @return the permalink, or null in case of error
*/
fun createRoomPermalink(roomId: String, viaServers: List<String>? = null): String?
fun createRoomPermalink(roomId: String, viaServers: List<String>? = null, forceMatrixTo: Boolean = false): String?
/**
* Creates a permalink for an event. If you have an event you can use [createPermalink]
@ -62,10 +67,11 @@ interface PermalinkService {
*
* @param roomId the id of the room
* @param eventId the id of the event
* @param forceMatrixTo whether we should force using matrix.to base URL
*
* @return the permalink
*/
fun createPermalink(roomId: String, eventId: String): String
fun createPermalink(roomId: String, eventId: String, forceMatrixTo: Boolean = false): String
/**
* Extract the linked id from the universal link

View File

@ -18,33 +18,29 @@ package org.matrix.android.sdk.internal.session.permalinks
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.permalinks.PermalinkService
import org.matrix.android.sdk.api.session.permalinks.PermalinkService.Companion.MATRIX_TO_URL_BASE
import javax.inject.Inject
internal class DefaultPermalinkService @Inject constructor(
private val permalinkFactory: PermalinkFactory
) : PermalinkService {
override fun createPermalink(event: Event): String? {
return permalinkFactory.createPermalink(event)
override fun createPermalink(event: Event, forceMatrixTo: Boolean): String? {
return permalinkFactory.createPermalink(event, forceMatrixTo)
}
override fun createPermalink(id: String): String? {
return permalinkFactory.createPermalink(id)
override fun createPermalink(id: String, forceMatrixTo: Boolean): String? {
return permalinkFactory.createPermalink(id, forceMatrixTo)
}
override fun createRoomPermalink(roomId: String, viaServers: List<String>?): String? {
return permalinkFactory.createRoomPermalink(roomId, viaServers)
override fun createRoomPermalink(roomId: String, viaServers: List<String>?, forceMatrixTo: Boolean): String? {
return permalinkFactory.createRoomPermalink(roomId, viaServers, forceMatrixTo)
}
override fun createPermalink(roomId: String, eventId: String): String {
return permalinkFactory.createPermalink(roomId, eventId)
override fun createPermalink(roomId: String, eventId: String, forceMatrixTo: Boolean): String {
return permalinkFactory.createPermalink(roomId, eventId, forceMatrixTo)
}
override fun getLinkedId(url: String): String? {
return url
.takeIf { it.startsWith(MATRIX_TO_URL_BASE) }
?.substring(MATRIX_TO_URL_BASE.length)
?.substringBeforeLast("?")
return permalinkFactory.getLinkedId(url)
}
}

View File

@ -16,7 +16,11 @@
package org.matrix.android.sdk.internal.session.permalinks
import org.matrix.android.sdk.api.MatrixConfiguration
import org.matrix.android.sdk.api.MatrixPatterns
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.permalinks.PermalinkData
import org.matrix.android.sdk.api.session.permalinks.PermalinkParser
import org.matrix.android.sdk.api.session.permalinks.PermalinkService.Companion.MATRIX_TO_URL_BASE
import org.matrix.android.sdk.internal.di.UserId
import javax.inject.Inject
@ -24,28 +28,44 @@ import javax.inject.Inject
internal class PermalinkFactory @Inject constructor(
@UserId
private val userId: String,
private val viaParameterFinder: ViaParameterFinder
private val viaParameterFinder: ViaParameterFinder,
private val matrixConfiguration: MatrixConfiguration
) {
fun createPermalink(event: Event): String? {
fun createPermalink(event: Event, forceMatrixTo: Boolean): String? {
if (event.roomId.isNullOrEmpty() || event.eventId.isNullOrEmpty()) {
return null
}
return createPermalink(event.roomId, event.eventId)
return createPermalink(event.roomId, event.eventId, forceMatrixTo)
}
fun createPermalink(id: String): String? {
return if (id.isEmpty()) {
null
} else MATRIX_TO_URL_BASE + escape(id)
fun createPermalink(id: String, forceMatrixTo: Boolean): String? {
return when {
id.isEmpty() -> null
!useClientFormat(forceMatrixTo) -> MATRIX_TO_URL_BASE + escape(id)
else -> {
buildString {
append(matrixConfiguration.clientPermalinkBaseUrl)
when {
MatrixPatterns.isRoomId(id) || MatrixPatterns.isRoomAlias(id) -> append(ROOM_PATH)
MatrixPatterns.isUserId(id) -> append(USER_PATH)
MatrixPatterns.isGroupId(id) -> append(GROUP_PATH)
}
append(escape(id))
}
}
}
}
fun createRoomPermalink(roomId: String, via: List<String>? = null): String? {
fun createRoomPermalink(roomId: String, via: List<String>? = null, forceMatrixTo: Boolean): String? {
return if (roomId.isEmpty()) {
null
} else {
buildString {
append(MATRIX_TO_URL_BASE)
append(baseUrl(forceMatrixTo))
if (useClientFormat(forceMatrixTo)) {
append(ROOM_PATH)
}
append(escape(roomId))
append(
via?.takeIf { it.isNotEmpty() }?.let { viaParameterFinder.asUrlViaParameters(it) }
@ -55,16 +75,34 @@ internal class PermalinkFactory @Inject constructor(
}
}
fun createPermalink(roomId: String, eventId: String): String {
return MATRIX_TO_URL_BASE + escape(roomId) + "/" + escape(eventId) + viaParameterFinder.computeViaParams(userId, roomId)
fun createPermalink(roomId: String, eventId: String, forceMatrixTo: Boolean): String {
return buildString {
append(baseUrl(forceMatrixTo))
if (useClientFormat(forceMatrixTo)) {
append(ROOM_PATH)
}
append(escape(roomId))
append("/")
append(escape(eventId))
append(viaParameterFinder.computeViaParams(userId, roomId))
}
}
fun getLinkedId(url: String): String? {
val isSupported = url.startsWith(MATRIX_TO_URL_BASE)
return if (isSupported) {
url.substring(MATRIX_TO_URL_BASE.length)
} else null
val clientBaseUrl = matrixConfiguration.clientPermalinkBaseUrl
return when {
url.startsWith(MATRIX_TO_URL_BASE) -> url.substring(MATRIX_TO_URL_BASE.length)
clientBaseUrl != null && url.startsWith(clientBaseUrl) -> {
when (PermalinkParser.parse(url)) {
is PermalinkData.GroupLink -> url.substring(clientBaseUrl.length + GROUP_PATH.length)
is PermalinkData.RoomLink -> url.substring(clientBaseUrl.length + ROOM_PATH.length)
is PermalinkData.UserLink -> url.substring(clientBaseUrl.length + USER_PATH.length)
else -> null
}
}
else -> null
}
?.substringBeforeLast("?")
}
/**
@ -86,4 +124,28 @@ internal class PermalinkFactory @Inject constructor(
private fun unescape(id: String): String {
return id.replace("%2F", "/")
}
/**
* Get the permalink base URL according to the potential one in [MatrixConfiguration.clientPermalinkBaseUrl]
* and the [forceMatrixTo] parameter.
*
* @param forceMatrixTo whether we should force using matrix.to base URL.
*
* @return the permalink base URL.
*/
private fun baseUrl(forceMatrixTo: Boolean): String {
return matrixConfiguration.clientPermalinkBaseUrl
?.takeUnless { forceMatrixTo }
?: MATRIX_TO_URL_BASE
}
private fun useClientFormat(forceMatrixTo: Boolean): Boolean {
return !forceMatrixTo && matrixConfiguration.clientPermalinkBaseUrl != null
}
companion object {
private const val ROOM_PATH = "room/"
private const val USER_PATH = "user/"
private const val GROUP_PATH = "group/"
}
}

View File

@ -165,8 +165,8 @@ internal class LocalEchoEventFactory @Inject constructor(
newBodyAutoMarkdown: Boolean,
msgType: String,
compatibilityText: String): Event {
val permalink = permalinkFactory.createPermalink(roomId, originalEvent.root.eventId ?: "")
val userLink = originalEvent.root.senderId?.let { permalinkFactory.createPermalink(it) } ?: ""
val permalink = permalinkFactory.createPermalink(roomId, originalEvent.root.eventId ?: "", false)
val userLink = originalEvent.root.senderId?.let { permalinkFactory.createPermalink(it, false) } ?: ""
val body = bodyForReply(originalEvent.getLastMessageContent(), originalEvent.isReply())
val replyFormatted = REPLY_PATTERN.format(
@ -350,9 +350,9 @@ internal class LocalEchoEventFactory @Inject constructor(
autoMarkdown: Boolean): Event? {
// Fallbacks and event representation
// TODO Add error/warning logs when any of this is null
val permalink = permalinkFactory.createPermalink(eventReplied.root) ?: return null
val permalink = permalinkFactory.createPermalink(eventReplied.root, false) ?: return null
val userId = eventReplied.root.senderId ?: return null
val userLink = permalinkFactory.createPermalink(userId) ?: return null
val userLink = permalinkFactory.createPermalink(userId, false) ?: return null
val body = bodyForReply(eventReplied.getLastMessageContent(), eventReplied.isReply())
val replyFormatted = REPLY_PATTERN.format(

View File

@ -39,7 +39,7 @@ sealed class InitialSyncStrategy {
* Limit to reach to decide to split the init sync response into smaller files
* Empiric value: 1 megabytes
*/
val minSizeToSplit: Long = 1024 * 1024,
val minSizeToSplit: Long = 1_048_576, // 1024 * 1024
/**
* Limit per room to reach to decide to store a join room ephemeral Events into a file
* Empiric value: 1 kilobytes

View File

@ -59,24 +59,6 @@ else
removeShortDes_si=1
fi
if [[ -f "./fastlane/metadata/android/sq/short_description.txt" ]]; then
echo "It appears that file ./fastlane/metadata/android/sq/short_description.txt now exists. This can be removed."
removeShortDes_sq=0
else
echo "Copy default short description to ./fastlane/metadata/android/sq"
cp ./fastlane/metadata/android/en-US/short_description.txt ./fastlane/metadata/android/sq
removeShortDes_sq=1
fi
if [[ -f "./fastlane/metadata/android/sq/full_description.txt" ]]; then
echo "It appears that file ./fastlane/metadata/android/sq/full_description.txt now exists. This can be removed."
removeFullDes_sq=0
else
echo "Copy default full description to ./fastlane/metadata/android/sq"
cp ./fastlane/metadata/android/en-US/full_description.txt ./fastlane/metadata/android/sq
removeFullDes_sq=1
fi
if [[ -f "./fastlane/metadata/android/th/full_description.txt" ]]; then
echo "It appears that file ./fastlane/metadata/android/th/full_description.txt now exists. This can be removed."
removeFullDes_th=0
@ -117,18 +99,10 @@ if [[ ${removeShortDes_si} -eq 1 ]]; then
rm ./fastlane/metadata/android/si-LK/short_description.txt
fi
if [[ ${removeShortDes_sq} -eq 1 ]]; then
rm ./fastlane/metadata/android/sq/short_description.txt
fi
if [[ ${removeFullDes_th} -eq 1 ]]; then
rm ./fastlane/metadata/android/th/full_description.txt
fi
if [[ ${removeFullDes_sq} -eq 1 ]]; then
rm ./fastlane/metadata/android/sq/full_description.txt
fi
if [[ ${removeFullDes_vi} -eq 1 ]]; then
rm ./fastlane/metadata/android/vi/full_description.txt
fi

View File

@ -14,7 +14,7 @@ kapt {
// Note: 2 digits max for each value
ext.versionMajor = 1
ext.versionMinor = 3
ext.versionPatch = 1
ext.versionPatch = 2
static def getGitTimestamp() {
def cmd = 'git show -s --format=%ct'
@ -412,7 +412,7 @@ dependencies {
implementation 'com.nulab-inc:zxcvbn:1.5.2'
// To convert voice message on old platforms
implementation 'com.arthenica:ffmpeg-kit-audio:4.4.LTS'
implementation 'com.arthenica:ffmpeg-kit-audio:4.5.LTS'
// Alerter
implementation 'com.tapadoo.android:alerter:7.0.1'
@ -420,7 +420,7 @@ dependencies {
implementation 'com.otaliastudios:autocomplete:1.1.0'
// Shake detection
implementation 'com.squareup:seismic:1.0.2'
implementation 'com.squareup:seismic:1.0.3'
// Image Loading
implementation libs.github.bigImageViewer

View File

@ -83,6 +83,10 @@
android:name="android.max_aspect"
android:value="9.9" />
<meta-data
android:name="com.google.android.gms.car.application"
android:resource="@xml/automotive_app_desc" />
<activity
android:name=".features.MainActivity"
android:theme="@style/Theme.Vector.Launcher" />
@ -105,8 +109,8 @@
<activity android:name=".features.home.HomeActivity" />
<activity
android:name=".features.login.LoginActivity"
android:launchMode="singleTask"
android:enabled="@bool/useLoginV1"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize">
<!-- Add intent filter to handle redirection URL after SSO login in external browser -->
<intent-filter>
@ -122,8 +126,8 @@
</activity>
<activity
android:name=".features.login2.LoginActivity2"
android:launchMode="singleTask"
android:enabled="@bool/useLoginV2"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize">
<!-- Add intent filter to handle redirection URL after SSO login in external browser -->
<intent-filter>
@ -180,7 +184,12 @@
<activity android:name=".features.createdirect.CreateDirectRoomActivity" />
<activity android:name=".features.invite.InviteUsersToRoomActivity" />
<activity android:name=".features.webview.VectorWebViewActivity" />
<activity android:name=".features.link.LinkHandlerActivity">
<!-- Activity to intercept links coming from a web instance -->
<activity
android:name=".features.link.LinkHandlerActivity"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
@ -196,6 +205,32 @@
</intent-filter>
</activity>
<!-- Activity alias for matrix.to or element permalinks -->
<activity-alias
android:name=".features.permalink.PermalinkHandlerActivity"
android:enabled="true"
android:exported="true"
android:launchMode="singleTask"
android:targetActivity=".features.link.LinkHandlerActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" />
<data android:scheme="https" />
<data android:host="matrix.to" />
<data
android:host="user"
android:scheme="element" />
<data
android:host="room"
android:scheme="element" />
</intent-filter>
</activity-alias>
<activity
android:name=".features.share.IncomingShareActivity"
android:parentActivityName=".features.home.HomeActivity">
@ -230,27 +265,6 @@
<activity
android:name=".features.signout.soft.SoftLogoutActivity"
android:windowSoftInputMode="adjustResize" />
<activity
android:name=".features.permalink.PermalinkHandlerActivity"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" />
<data android:scheme="https" />
<data android:host="matrix.to" />
<data
android:host="user"
android:scheme="element" />
<data
android:host="room"
android:scheme="element" />
</intent-filter>
</activity>
<activity
android:name=".features.roommemberprofile.RoomMemberProfileActivity"
@ -271,12 +285,12 @@
android:name=".features.attachments.preview.AttachmentsPreviewActivity"
android:theme="@style/Theme.Vector.Black.AttachmentsPreview" />
<activity
android:supportsPictureInPicture="true"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
android:name=".features.call.VectorCallActivity"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
android:excludeFromRecents="true"
android:launchMode="singleTask"
android:taskAffinity=".features.call.VectorCallActivity"
android:excludeFromRecents="true" />
android:supportsPictureInPicture="true"
android:taskAffinity=".features.call.VectorCallActivity" />
<!-- PIP Support https://developer.android.com/guide/topics/ui/picture-in-picture -->
<activity
android:name=".features.call.conference.VectorJitsiActivity"

View File

@ -62,7 +62,6 @@ import im.vector.app.features.matrixto.MatrixToBottomSheet
import im.vector.app.features.media.BigImageViewerActivity
import im.vector.app.features.media.VectorAttachmentViewerActivity
import im.vector.app.features.navigation.Navigator
import im.vector.app.features.permalink.PermalinkHandlerActivity
import im.vector.app.features.pin.PinLocker
import im.vector.app.features.qrcode.QrCodeScannerActivity
import im.vector.app.features.rageshake.BugReportActivity
@ -155,7 +154,6 @@ interface ScreenComponent {
fun inject(activity: CreateDirectRoomActivity)
fun inject(activity: IncomingShareActivity)
fun inject(activity: SoftLogoutActivity)
fun inject(activity: PermalinkHandlerActivity)
fun inject(activity: QrCodeScannerActivity)
fun inject(activity: DebugMenuActivity)
fun inject(activity: SharedSecureStorageActivity)

View File

@ -27,6 +27,7 @@ import im.vector.app.core.epoxy.ClickListener
import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.epoxy.onClick
import im.vector.app.core.extensions.setAttributeTintedImageResource
import im.vector.app.core.extensions.setTextOrHide
/**
@ -62,7 +63,7 @@ abstract class BottomSheetRadioActionItem : VectorEpoxyModel<BottomSheetRadioAct
holder.descriptionText.setTextOrHide(description)
if (selected) {
holder.radioImage.setImageDrawable(ContextCompat.getDrawable(holder.view.context, R.drawable.ic_radio_on))
holder.radioImage.setAttributeTintedImageResource(R.drawable.ic_radio_on, R.attr.colorPrimary)
holder.radioImage.contentDescription = holder.view.context.getString(R.string.a11y_checked)
} else {
holder.radioImage.setImageDrawable(ContextCompat.getDrawable(holder.view.context, R.drawable.ic_radio_off))

View File

@ -27,6 +27,7 @@ import im.vector.app.core.epoxy.ClickListener
import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.epoxy.onClick
import im.vector.app.core.extensions.setAttributeTintedImageResource
@EpoxyModelClass(layout = R.layout.item_radio)
abstract class RadioButtonItem : VectorEpoxyModel<RadioButtonItem.Holder>() {
@ -54,7 +55,7 @@ abstract class RadioButtonItem : VectorEpoxyModel<RadioButtonItem.Holder>() {
}
if (selected) {
holder.radioImage.setImageDrawable(ContextCompat.getDrawable(holder.view.context, R.drawable.ic_radio_on))
holder.radioImage.setAttributeTintedImageResource(R.drawable.ic_radio_on, R.attr.colorPrimary)
holder.radioImage.contentDescription = holder.view.context.getString(R.string.a11y_checked)
} else {
holder.radioImage.setImageDrawable(ContextCompat.getDrawable(holder.view.context, R.drawable.ic_radio_off))

View File

@ -22,9 +22,14 @@ import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import android.widget.ImageView
import androidx.annotation.AttrRes
import androidx.annotation.DrawableRes
import androidx.appcompat.widget.SearchView
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.DrawableCompat
import androidx.core.view.isVisible
import im.vector.app.R
import im.vector.app.features.themes.ThemeUtils
/**
* Remove left margin of a SearchView
@ -58,3 +63,20 @@ fun ImageView.setDrawableOrHide(drawableRes: Drawable?) {
setImageDrawable(drawableRes)
isVisible = drawableRes != null
}
fun View.setAttributeTintedBackground(@DrawableRes drawableRes: Int, @AttrRes tint: Int) {
val drawable = ContextCompat.getDrawable(context, drawableRes)!!
DrawableCompat.setTint(drawable, ThemeUtils.getColor(context, tint))
background = drawable
}
fun ImageView.setAttributeTintedImageResource(@DrawableRes drawableRes: Int, @AttrRes tint: Int) {
val drawable = ContextCompat.getDrawable(context, drawableRes)!!
DrawableCompat.setTint(drawable, ThemeUtils.getColor(context, tint))
setImageDrawable(drawable)
}
fun View.setAttributeBackground(@AttrRes attributeId: Int) {
val attribute = ThemeUtils.getAttribute(context, attributeId)!!
setBackgroundResource(attribute.resourceId)
}

View File

@ -76,7 +76,6 @@ class SharedSecureStorageViewModel @AssistedInject constructor(
}
init {
setState {
copy(userId = session.myUserId)
}
@ -167,11 +166,15 @@ class SharedSecureStorageViewModel @AssistedInject constructor(
if (state.checkingSSSSAction is Loading) return@withState // ignore
when (state.step) {
SharedSecureStorageViewState.Step.EnterKey -> {
if (state.hasPassphrase) {
setState {
copy(
step = SharedSecureStorageViewState.Step.EnterPassphrase
)
}
} else {
_viewEvents.post(SharedSecureStorageViewEvent.Dismiss)
}
}
SharedSecureStorageViewState.Step.ResetAll -> {
setState {

View File

@ -51,6 +51,9 @@ import im.vector.app.features.navigation.Navigator
import im.vector.app.features.notifications.NotificationDrawerManager
import im.vector.app.features.permalink.NavigationInterceptor
import im.vector.app.features.permalink.PermalinkHandler
import im.vector.app.features.permalink.PermalinkHandler.Companion.MATRIX_TO_CUSTOM_SCHEME_URL_BASE
import im.vector.app.features.permalink.PermalinkHandler.Companion.ROOM_LINK_PREFIX
import im.vector.app.features.permalink.PermalinkHandler.Companion.USER_LINK_PREFIX
import im.vector.app.features.popup.DefaultVectorAlert
import im.vector.app.features.popup.PopupAlertManager
import im.vector.app.features.popup.VerificationVectorAlert
@ -272,20 +275,19 @@ class HomeActivity :
private fun handleIntent(intent: Intent?) {
intent?.dataString?.let { deepLink ->
val resolvedLink = when {
deepLink.startsWith(PermalinkService.MATRIX_TO_URL_BASE) -> deepLink
// Element custom scheme is not handled by the sdk, convert it to matrix.to link for compatibility
deepLink.startsWith(MATRIX_TO_CUSTOM_SCHEME_URL_BASE) -> {
// This is a bit ugly, but for now just convert to matrix.to link for compatibility
when {
val let = when {
deepLink.startsWith(USER_LINK_PREFIX) -> deepLink.substring(USER_LINK_PREFIX.length)
deepLink.startsWith(ROOM_LINK_PREFIX) -> deepLink.substring(ROOM_LINK_PREFIX.length)
else -> null
}?.let {
activeSessionHolder.getSafeActiveSession()?.permalinkService()?.createPermalink(it)
}?.let { permalinkId ->
activeSessionHolder.getSafeActiveSession()?.permalinkService()?.createPermalink(permalinkId)
}
let
}
else -> return@let
else -> deepLink
}
permalinkHandler.launch(
context = this,
deepLink = resolvedLink,
@ -296,9 +298,11 @@ class HomeActivity :
.observeOn(AndroidSchedulers.mainThread())
.subscribe { isHandled ->
if (!isHandled) {
val isMatrixToLink = deepLink.startsWith(PermalinkService.MATRIX_TO_URL_BASE)
|| deepLink.startsWith(MATRIX_TO_CUSTOM_SCHEME_URL_BASE)
MaterialAlertDialogBuilder(this)
.setTitle(R.string.dialog_title_error)
.setMessage(R.string.permalink_malformed)
.setMessage(if (isMatrixToLink) R.string.permalink_malformed else R.string.universal_link_malformed)
.setPositiveButton(R.string.ok, null)
.show()
}
@ -579,10 +583,6 @@ class HomeActivity :
putExtra(MvRx.KEY_ARG, args)
}
}
private const val MATRIX_TO_CUSTOM_SCHEME_URL_BASE = "element://"
private const val ROOM_LINK_PREFIX = "${MATRIX_TO_CUSTOM_SCHEME_URL_BASE}room/"
private const val USER_LINK_PREFIX = "${MATRIX_TO_CUSTOM_SCHEME_URL_BASE}user/"
}
override fun create(initialState: ActiveSpaceViewState) = promoteRestrictedViewModelFactory.create(initialState)

View File

@ -27,6 +27,9 @@ import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import im.vector.app.BuildConfig
import im.vector.app.R
import im.vector.app.core.extensions.setAttributeBackground
import im.vector.app.core.extensions.setAttributeTintedBackground
import im.vector.app.core.extensions.setAttributeTintedImageResource
import im.vector.app.core.hardware.vibrate
import im.vector.app.core.utils.CountUpTimer
import im.vector.app.core.utils.DimensionConverter
@ -217,7 +220,7 @@ class VoiceMessageRecorderView: ConstraintLayout, VoiceMessagePlaybackTracker.Li
views.voiceMessageLockArrow.translationY = 0F
}
RecordingState.LOCKING -> {
views.voiceMessageLockImage.setImageResource(R.drawable.ic_voice_message_locked)
views.voiceMessageLockImage.setAttributeTintedImageResource(R.drawable.ic_voice_message_locked, R.attr.colorPrimary)
val translationAmount = -distanceY.coerceIn(0F, distanceToLock)
views.voiceMessageMicButton.translationY = translationAmount
views.voiceMessageLockArrow.translationY = translationAmount
@ -366,6 +369,7 @@ class VoiceMessageRecorderView: ConstraintLayout, VoiceMessagePlaybackTracker.Li
private fun showRecordingViews() {
views.voiceMessageMicButton.setImageResource(R.drawable.ic_voice_mic_recording)
views.voiceMessageMicButton.setAttributeTintedBackground(R.drawable.circle_with_halo, R.attr.colorPrimary)
views.voiceMessageMicButton.updateLayoutParams<MarginLayoutParams> {
setMargins(0, 0, 0, 0)
}
@ -443,6 +447,7 @@ class VoiceMessageRecorderView: ConstraintLayout, VoiceMessagePlaybackTracker.Li
private fun resetMicButtonUi() {
views.voiceMessageMicButton.isVisible = true
views.voiceMessageMicButton.setImageResource(R.drawable.ic_voice_mic)
views.voiceMessageMicButton.setAttributeBackground(android.R.attr.selectableItemBackgroundBorderless)
views.voiceMessageMicButton.updateLayoutParams<MarginLayoutParams> {
if (rtlXMultiplier == -1) {
// RTL

View File

@ -146,7 +146,7 @@ class PreviewUrlRetriever(session: Session,
companion object {
// One week in millis
private const val CACHE_VALIDITY: Long = 7 * 24 * 3_600 * 1_000
private const val CACHE_VALIDITY = 604_800_000L // 7 * 24 * 3_600 * 1_000
private val blockedDomains = listOf(
"https://matrix.to",

View File

@ -27,13 +27,12 @@ import im.vector.app.core.error.ErrorFormatter
import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.core.utils.toast
import im.vector.app.databinding.ActivityProgressBinding
import im.vector.app.features.home.HomeActivity
import im.vector.app.features.login.LoginConfig
import im.vector.app.features.permalink.PermalinkHandler
import io.reactivex.android.schedulers.AndroidSchedulers
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.session.permalinks.PermalinkService
import timber.log.Timber
import java.util.concurrent.TimeUnit
import javax.inject.Inject
/**
@ -45,32 +44,40 @@ class LinkHandlerActivity : VectorBaseActivity<ActivityProgressBinding>() {
@Inject lateinit var errorFormatter: ErrorFormatter
@Inject lateinit var permalinkHandler: PermalinkHandler
override fun getBinding() = ActivityProgressBinding.inflate(layoutInflater)
override fun injectWith(injector: ScreenComponent) {
injector.inject(this)
}
override fun getBinding() = ActivityProgressBinding.inflate(layoutInflater)
override fun initUiAndData() {
val uri = intent.data
handleIntent()
}
if (uri == null) {
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
handleIntent()
}
private fun handleIntent() {
val uri = intent.data
when {
uri == null -> {
// Should not happen
Timber.w("Uri is null")
finish()
return
}
if (uri.getQueryParameter(LoginConfig.CONFIG_HS_PARAMETER) != null) {
handleConfigUrl(uri)
} else if (SUPPORTED_HOSTS.contains(uri.host)) {
handleSupportedHostUrl(uri)
} else {
uri.getQueryParameter(LoginConfig.CONFIG_HS_PARAMETER) != null -> handleConfigUrl(uri)
uri.toString().startsWith(PermalinkService.MATRIX_TO_URL_BASE) -> handleSupportedHostUrl()
uri.toString().startsWith(PermalinkHandler.MATRIX_TO_CUSTOM_SCHEME_URL_BASE) -> handleSupportedHostUrl()
resources.getStringArray(R.array.permalink_supported_hosts).contains(uri.host) -> handleSupportedHostUrl()
else -> {
// Other links are not yet handled, but should not come here (manifest configuration error?)
toast(R.string.universal_link_malformed)
finish()
}
}
}
private fun handleConfigUrl(uri: Uri) {
if (sessionHolder.hasActiveSession()) {
@ -81,53 +88,28 @@ class LinkHandlerActivity : VectorBaseActivity<ActivityProgressBinding>() {
}
}
private fun handleSupportedHostUrl(uri: Uri) {
private fun handleSupportedHostUrl() {
// If we are not logged in, open login screen.
// In the future, we might want to relaunch the process after login.
if (!sessionHolder.hasActiveSession()) {
startLoginActivity(uri)
finish()
} else {
convertUriToPermalink(uri)?.let { permalink ->
startPermalinkHandler(permalink)
} ?: run {
// Host is correct but we do not recognize path
Timber.w("Unable to handle this uri: $uri")
finish()
}
startLoginActivity()
return
}
// We forward intent to HomeActivity (singleTask) to avoid the dueling app problem
// https://stackoverflow.com/questions/25884954/deep-linking-and-multiple-app-instances
intent.setClass(this, HomeActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
startActivity(intent)
}
/**
* Convert a URL of element web instance to a matrix.to url
* Examples:
* - https://riot.im/develop/#/room/#element-android:matrix.org -> https://matrix.to/#/#element-android:matrix.org
* - https://app.element.io/#/room/#element-android:matrix.org -> https://matrix.to/#/#element-android:matrix.org
* Start the login screen with identity server and homeserver pre-filled, if any
*/
private fun convertUriToPermalink(uri: Uri): String? {
val uriString = uri.toString()
val path = SUPPORTED_PATHS.find { it in uriString } ?: return null
return PermalinkService.MATRIX_TO_URL_BASE + uriString.substringAfter(path)
}
private fun startPermalinkHandler(permalink: String) {
permalinkHandler.launch(this, permalink, buildTask = true)
.delay(500, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe { isHandled ->
if (!isHandled) {
toast(R.string.universal_link_malformed)
}
finish()
}
.disposeOnDestroy()
}
/**
* Start the login screen with identity server and homeserver pre-filled
*/
private fun startLoginActivity(uri: Uri) {
private fun startLoginActivity(uri: Uri? = null) {
navigator.openLogin(
context = this,
loginConfig = LoginConfig.parse(uri),
loginConfig = uri?.let { LoginConfig.parse(uri) },
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK
)
finish()
@ -173,21 +155,4 @@ class LinkHandlerActivity : VectorBaseActivity<ActivityProgressBinding>() {
.setPositiveButton(R.string.ok) { _, _ -> finish() }
.show()
}
companion object {
private val SUPPORTED_HOSTS = listOf(
// Regular Element Web instance
"app.element.io",
// Other known instances of Element Web
"develop.element.io",
"staging.element.io",
// Previous Web instance, kept for compatibility reason
"riot.im"
)
private val SUPPORTED_PATHS = listOf(
"/#/room/",
"/#/user/",
"/#/group/"
)
}
}

View File

@ -597,10 +597,12 @@ class NotificationUtils @Inject constructor(private val context: Context,
val markRoomReadPendingIntent = PendingIntent.getBroadcast(context, System.currentTimeMillis().toInt(), markRoomReadIntent,
PendingIntent.FLAG_UPDATE_CURRENT)
addAction(NotificationCompat.Action(
R.drawable.ic_material_done_all_white,
stringProvider.getString(R.string.action_mark_room_read),
markRoomReadPendingIntent))
NotificationCompat.Action.Builder(R.drawable.ic_material_done_all_white,
stringProvider.getString(R.string.action_mark_room_read), markRoomReadPendingIntent)
.setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_MARK_AS_READ)
.setShowsUserInterface(false)
.build()
.let { addAction(it) }
// Quick reply
if (!roomInfo.hasSmartReplyError) {
@ -611,6 +613,8 @@ class NotificationUtils @Inject constructor(private val context: Context,
NotificationCompat.Action.Builder(R.drawable.vector_notification_quick_reply,
stringProvider.getString(R.string.action_quick_reply), replyPendingIntent)
.addRemoteInput(remoteInput)
.setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_REPLY)
.setShowsUserInterface(false)
.build()
.let { addAction(it) }
}

View File

@ -29,6 +29,7 @@ import io.reactivex.schedulers.Schedulers
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.permalinks.PermalinkData
import org.matrix.android.sdk.api.session.permalinks.PermalinkParser
import org.matrix.android.sdk.api.session.permalinks.PermalinkService
import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.RoomType
import org.matrix.android.sdk.api.util.Optional
@ -55,7 +56,7 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti
navigationInterceptor: NavigationInterceptor? = null,
buildTask: Boolean = false
): Single<Boolean> {
if (deepLink == null) {
if (deepLink == null || !isPermalinkSupported(context, deepLink.toString())) {
return Single.just(false)
}
return Single
@ -122,6 +123,13 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti
}
}
private fun isPermalinkSupported(context: Context, url: String): Boolean {
return url.startsWith(PermalinkService.MATRIX_TO_URL_BASE)
|| context.resources.getStringArray(R.array.permalink_supported_hosts).any {
url.startsWith(it)
}
}
private fun PermalinkData.RoomLink.getRoomId(): Single<Optional<String>> {
val session = activeSessionHolder.getSafeActiveSession()
return if (isRoomAlias && session != null) {
@ -179,6 +187,12 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti
}
}
}
companion object {
const val MATRIX_TO_CUSTOM_SCHEME_URL_BASE = "element://"
const val ROOM_LINK_PREFIX = "${MATRIX_TO_CUSTOM_SCHEME_URL_BASE}room/"
const val USER_LINK_PREFIX = "${MATRIX_TO_CUSTOM_SCHEME_URL_BASE}user/"
}
}
interface NavigationInterceptor {

View File

@ -1,79 +0,0 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.features.permalink
import android.content.Intent
import android.os.Bundle
import im.vector.app.R
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.di.ScreenComponent
import im.vector.app.core.extensions.replaceFragment
import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.databinding.FragmentProgressBinding
import im.vector.app.features.home.HomeActivity
import im.vector.app.features.home.LoadingFragment
import javax.inject.Inject
class PermalinkHandlerActivity : VectorBaseActivity<FragmentProgressBinding>() {
@Inject lateinit var permalinkHandler: PermalinkHandler
@Inject lateinit var sessionHolder: ActiveSessionHolder
override fun getBinding() = FragmentProgressBinding.inflate(layoutInflater)
override fun injectWith(injector: ScreenComponent) {
injector.inject(this)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_simple)
if (isFirstCreation()) {
replaceFragment(R.id.simpleFragmentContainer, LoadingFragment::class.java)
}
handleIntent()
}
private fun handleIntent() {
// If we are not logged in, open login screen.
// In the future, we might want to relaunch the process after login.
if (!sessionHolder.hasActiveSession()) {
startLoginActivity()
return
}
// We forward intent to HomeActivity (singleTask) to avoid the dueling app problem
// https://stackoverflow.com/questions/25884954/deep-linking-and-multiple-app-instances
intent.setClass(this, HomeActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
startActivity(intent)
finish()
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
handleIntent()
}
private fun startLoginActivity() {
navigator.openLogin(
context = this,
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK
)
finish()
}
}

View File

@ -165,6 +165,12 @@ class EmojiReactionPickerActivity : VectorBaseActivity<ActivityEmojiReactionPick
}
})
searchView.setOnCloseListener {
currentFocus?.clearFocus()
searchItem.collapseActionView()
true
}
searchView.queryTextChanges()
.throttleWithTimeout(600, TimeUnit.MILLISECONDS)
.doOnError { err -> Timber.e(err) }
@ -174,6 +180,7 @@ class EmojiReactionPickerActivity : VectorBaseActivity<ActivityEmojiReactionPick
}
.disposeOnDestroy()
}
searchItem.expandActionView()
return true
}

View File

@ -28,6 +28,7 @@ import im.vector.app.core.epoxy.ClickListener
import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.epoxy.onClick
import im.vector.app.core.extensions.setAttributeTintedImageResource
import im.vector.app.core.utils.DebouncedClickListener
import im.vector.app.features.home.AvatarRenderer
import org.matrix.android.sdk.api.util.MatrixItem
@ -57,7 +58,7 @@ abstract class SpaceJoinRuleItem : VectorEpoxyModel<SpaceJoinRuleItem.Holder>()
holder.upgradeRequiredButton.setOnClickListener(DebouncedClickListener(listener))
if (selected) {
holder.radioImage.setImageDrawable(ContextCompat.getDrawable(holder.view.context, R.drawable.ic_radio_on))
holder.radioImage.setAttributeTintedImageResource(R.drawable.ic_radio_on, R.attr.colorPrimary)
holder.radioImage.contentDescription = holder.view.context.getString(R.string.a11y_checked)
} else {
holder.radioImage.setImageDrawable(ContextCompat.getDrawable(holder.view.context, R.drawable.ic_radio_off))

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="oval">
<solid android:color="#33FF0000" />
</shape>
</item>
<item
android:bottom="4dp"
android:left="4dp"
android:right="4dp"
android:top="4dp">
<shape android:shape="oval">
<solid android:color="#FF0000" />
</shape>
</item>
</layer-list>

View File

@ -5,6 +5,6 @@
android:viewportHeight="24">
<path
android:pathData="M12,2C6.4771,2 2,6.4771 2,12C2,17.5228 6.4771,22 12,22C17.5228,22 22,17.5228 22,12C22,6.4771 17.5228,2 12,2ZM11.2929,6.2929C11.3888,6.197 11.4993,6.1247 11.6172,6.0759L12.7071,6.2929L12.7075,6.2933L16.7071,10.2929C17.0976,10.6834 17.0976,11.3166 16.7071,11.7071C16.3166,12.0976 15.6834,12.0976 15.2929,11.7071L13,9.4142L13,17C13,17.5523 12.5523,18 12,18C11.4477,18 11,17.5523 11,17L11,9.4142L8.7071,11.7071C8.3166,12.0976 7.6834,12.0976 7.2929,11.7071C6.9024,11.3166 6.9024,10.6834 7.2929,10.2929L11.2929,6.2929ZM11.6172,6.0759L12.705,6.2908C12.5242,6.1111 12.2751,6 12,6C11.8644,6 11.7351,6.027 11.6172,6.0759Z"
android:fillColor="#0DBD8B"
android:fillColor="#FF0000"
android:fillType="evenOdd"/>
</vector>

View File

@ -9,6 +9,6 @@
android:fillType="evenOdd"/>
<path
android:pathData="M2,2h20v20h-20z"
android:fillColor="#0DBD8B"/>
android:fillColor="#FF0000"/>
</group>
</vector>

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="4dp"
android:height="32dp"
android:viewportWidth="4"
android:viewportHeight="32">
<path
android:pathData="M0,0C2.2091,0 4,1.7909 4,4V28C4,30.2091 2.2091,32 0,32V0Z"
android:fillColor="#0DBD8B"/>
</vector>

View File

@ -1,5 +1,5 @@
<vector android:autoMirrored="true" android:height="24dp"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#0DBD8B" android:fillType="evenOdd" android:pathData="M11.3333,2C8.3878,2 6,4.3878 6,7.3333V10C4.8954,10 4,10.8954 4,12V20C4,21.1046 4.8954,22 6,22H18C19.1046,22 20,21.1046 20,20V12C20,10.8954 19.1046,10 18,10V7.3333C18,4.3878 15.6122,2 12.6667,2H11.3333ZM15.3333,10V7.3333C15.3333,5.8606 14.1394,4.6667 12.6667,4.6667H11.3333C9.8606,4.6667 8.6667,5.8606 8.6667,7.3333V10H15.3333Z"/>
<path android:fillColor="#FF0000" android:fillType="evenOdd" android:pathData="M11.3333,2C8.3878,2 6,4.3878 6,7.3333V10C4.8954,10 4,10.8954 4,12V20C4,21.1046 4.8954,22 6,22H18C19.1046,22 20,21.1046 20,20V12C20,10.8954 19.1046,10 18,10V7.3333C18,4.3878 15.6122,2 12.6667,2H11.3333ZM15.3333,10V7.3333C15.3333,5.8606 14.1394,4.6667 12.6667,4.6667H11.3333C9.8606,4.6667 8.6667,5.8606 8.6667,7.3333V10H15.3333Z"/>
</vector>

View File

@ -3,14 +3,6 @@
android:height="52dp"
android:viewportWidth="52"
android:viewportHeight="52">
<path
android:pathData="M26.173,26.1729m-22.7631,0a22.7631,22.7631 0,1 1,45.5262 0a22.7631,22.7631 0,1 1,-45.5262 0"
android:fillColor="#0DBD8B"/>
<path
android:pathData="M26,26m-26,0a26,26 0,1 1,52 0a26,26 0,1 1,-52 0"
android:strokeAlpha="0.2"
android:fillColor="#0DBD8B"
android:fillAlpha="0.2"/>
<path
android:pathData="M21.2414,18.7749C21.2414,16.051 23.4496,13.8429 26.1734,13.8429C28.8973,13.8429 31.1054,16.051 31.1054,18.7749V26.1509C31.1054,28.8747 28.8973,31.0829 26.1734,31.0829C23.4496,31.0829 21.2414,28.8747 21.2414,26.1509V18.7749ZM17.542,24.2475C18.5968,24.2475 19.4518,25.1025 19.4518,26.1572C19.4518,29.8561 22.4509,32.8596 26.1586,32.8675C26.1637,32.8674 26.1689,32.8674 26.174,32.8674C26.179,32.8674 26.184,32.8674 26.189,32.8675C29.896,32.8589 32.8944,29.8556 32.8944,26.1572C32.8944,25.1025 33.7494,24.2475 34.8041,24.2475C35.8588,24.2475 36.7138,25.1025 36.7138,26.1572C36.7138,31.3227 32.9916,35.6165 28.0837,36.5143V37.24C28.0837,38.2947 27.2287,39.1497 26.174,39.1497C25.1193,39.1497 24.2643,38.2947 24.2643,37.24V36.5147C19.3555,35.6176 15.6323,31.3233 15.6323,26.1572C15.6323,25.1025 16.4873,24.2475 17.542,24.2475Z"
android:fillColor="#ffffff"

View File

@ -98,7 +98,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="top"
app:constraint_referenced_ids="composerLayout,notificationAreaView,failedMessagesWarningView" />
app:constraint_referenced_ids="composerLayout,notificationAreaView,failedMessagesWarningStub" />
<im.vector.app.features.sync.widget.SyncStateView
android:id="@+id/syncStateView"
@ -139,6 +139,7 @@
android:text="@string/room_jump_to_first_unread"
android:visibility="invisible"
app:chipIcon="@drawable/ic_jump_to_unread"
app:chipIconTint="?colorPrimary"
app:closeIcon="@drawable/ic_close_24dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
@ -159,7 +160,8 @@
android:id="@+id/failedMessagesWarningStub"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inflatedId="@layout/view_stub_failed_message_warning_layout"
android:inflatedId="@+id/failedMessagesWarningStub"
android:layout="@layout/view_stub_failed_message_warning_layout"
app:layout_constraintBottom_toTopOf="@id/composerLayout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />

View File

@ -24,8 +24,7 @@
android:id="@+id/addRoomToSpaceToolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:minHeight="0dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap|enterAlways">
android:minHeight="0dp">
<LinearLayout
android:layout_width="match_parent"

View File

@ -7,5 +7,5 @@
app:iconTint="?vctr_content_primary"
android:title="@string/search"
app:actionViewClass="android.widget.SearchView"
app:showAsAction="collapseActionView|ifRoom" />
app:showAsAction="collapseActionView|always" />
</menu>

View File

@ -53,11 +53,11 @@
<string name="room_displayname_two_members">%1$s وَ %2$s</string>
<string name="room_displayname_room_invite">دَعوة غُرفة</string>
<plurals name="room_displayname_three_and_more_members">
<item quantity="zero"/>
<item quantity="zero">لا أحد</item>
<item quantity="one">%1$s وواحد آخر</item>
<item quantity="two">%1$s و%2$d آخران</item>
<item quantity="few">%1$s و%2$d آخرين</item>
<item quantity="many">%1$s و%2$d آخرون</item>
<item quantity="few">%1$s و%2$d آخرون</item>
<item quantity="many">%1$s و%2$d آخرًا</item>
<item quantity="other">%1$s و%2$d آخرون</item>
</plurals>
<string name="summary_you_sent_image">أرسلتَ صورةً.</string>
@ -122,17 +122,17 @@
<string name="notice_room_server_acl_allow_is_empty">🎉 جميع الخوادِم محظورة مِنَ المُشاركة! لم يعُد من المُمكِن استخدام هذه الغُرفة.</string>
<string name="notice_room_server_acl_updated_no_change">لا تغيير.</string>
<string name="notice_room_server_acl_updated_ip_literals_not_allowed">• خوادِم مُطابقة IP الحرفية محظورة الآن.</string>
<string name="notice_room_server_acl_updated_was_allowed">• الخادِم المُطابق لـ %s قد أُزيل مِن قائمة السماح.</string>
<string name="notice_room_server_acl_updated_allowed">• الخادِم المُطابق لـ %s مسموح الآن.</string>
<string name="notice_room_server_acl_updated_was_banned">• الخادِم المُطابق لـ %s قد أُزيل مِن قائمة الحظر.</string>
<string name="notice_room_server_acl_updated_banned">• الخادِم المُطابق لـ %s محظور الآن.</string>
<string name="notice_room_server_acl_updated_was_allowed">• الخوادِم المُطابقة لـ %s أُزيلت مِن قائمة السماح.</string>
<string name="notice_room_server_acl_updated_allowed">• الخوادِم المُطابقة لـ %s مسموحة الآن.</string>
<string name="notice_room_server_acl_updated_was_banned">• الخوادِم المُطابقة لـ %s أُزيلت مِن قائمة الحظر.</string>
<string name="notice_room_server_acl_updated_banned">• الخوادِم المُطابقة لـ %s محظورة الآن.</string>
<string name="notice_room_server_acl_updated_ip_literals_allowed">• خوادِم مُطابقة IP الحرفية مسموحة الآن.</string>
<string name="notice_room_server_acl_updated_title_by_you">أنتَ قد غيَّرتَ خادِم الـACLs لهذه الغُرفة.</string>
<string name="notice_room_server_acl_updated_title">إنَّ %s قد غيَّرَ خادِم الـACLs لهذه الغُرفة.</string>
<string name="notice_room_server_acl_set_ip_literals_not_allowed">• الخادِم يحظُر مُطابقة القيم الحرفية للـIP.</string>
<string name="notice_room_server_acl_set_allowed">• الخادِم المُطابق لـ %s مسموح.</string>
<string name="notice_room_server_acl_set_banned">• الخادِم المُطابق لـ %s محظور.</string>
<string name="notice_room_server_acl_set_ip_literals_allowed">• الخادِم يسمح بمُطابقة القيم الحرفية للـIP.</string>
<string name="notice_room_server_acl_set_ip_literals_not_allowed">• الخوادِم تحظُر مُطابقة القيم الحرفية للـIP.</string>
<string name="notice_room_server_acl_set_allowed">• الخوادِم المُطابقة لـ %s مسموحة.</string>
<string name="notice_room_server_acl_set_banned">• الخوادِم المُطابقة لـ %s محظورة.</string>
<string name="notice_room_server_acl_set_ip_literals_allowed">• الخوادِم تسمح بمُطابقة القيم الحرفية للـIP.</string>
<string name="notice_room_server_acl_set_title_by_you">أنتَ قد عيَّنتَ خادِم الـACLs لهذه الغُرفة.</string>
<string name="notice_room_server_acl_set_title">إنَّ %s قد عيَّنَ خادِم الـACLs لهذه الغُرفة.</string>
<string name="notice_direct_room_update_by_you">أنتَ قد قمتَ بالترقية هُنا.</string>
@ -216,7 +216,7 @@
<string name="light_theme">السمة الفاتحة</string>
<string name="dark_theme">السمة الداكنة</string>
<string name="black_theme">السمة السوداء</string>
<string name="notification_sync_in_progress">يُزامن</string>
<string name="notification_sync_in_progress">يُزامن</string>
<string name="notification_listening_for_events">يستمع للأحداث</string>
<string name="title_activity_home">الرسائل</string>
<string name="title_activity_settings">الإعدادات</string>
@ -588,7 +588,7 @@
<string name="command_error">خطأ في الأمر</string>
<string name="unrecognized_command">لم يُفهم الأمر: %s</string>
<!-- notification statuses -->
<string name="redact">هذّب</string>
<string name="redact">أزل</string>
<string name="missing_permissions_to_start_conf_call">تحتاج تصريح الدعوة لبدء اجتماع في هذه الغرفة</string>
<string name="or">أو</string>
<string name="action_quick_reply">ردّ سريع</string>
@ -1104,11 +1104,11 @@
<string name="room_sliding_menu_version_x">الإصدار %s</string>
<string name="settings_notification_advanced">الإعدادات المتقدمة للإشعارات</string>
<string name="title_activity_verify_device">توثق من الجهاز</string>
<string name="keys_backup_is_not_finished_please_wait">تأمين المفاتيح لم ينته، يرجى الانتظار</string>
<string name="keys_backup_is_not_finished_please_wait">النسخ الاحتياطي المفاتيح لم ينته، يرجى الانتظار</string>
<string name="sign_out_bottom_sheet_warning_no_backup">عند تسجيل الخروج الآن ستخسر مفاتيحك</string>
<string name="sign_out_bottom_sheet_warning_backing_up">تأمين المفاتيح ما زال جاريا. في حال خروجك الآن لن تتمكن لاحقا من قراءة الرسائل المشفرة</string>
<string name="sign_out_bottom_sheet_warning_backup_not_active">تأكد من تفعيل تأمين المفاتيح على كل أجهزتك كي لا تخسر رسائلك المشفرة</string>
<string name="sign_out_bottom_sheet_backing_up_keys">يتم تأمين المفاتيح</string>
<string name="sign_out_bottom_sheet_warning_backing_up">النسخ الاحتياطي المفاتيح ما زال جاريا. في حال خروجك الآن لن تتمكن لاحقا من قراءة الرسائل المشفرة.</string>
<string name="sign_out_bottom_sheet_warning_backup_not_active">تأكد من تفعيل النسخ الاحتياطي للمفاتيح على كل أجهزتك كي لا تخسر رسائلك المشفرة</string>
<string name="sign_out_bottom_sheet_backing_up_keys">ينسخ احتياطيا المفاتيح…</string>
<string name="no_permissions_to_start_conf_call_in_direct_room">لا يوجد لديك أذن لبدء مكالمة إجتماع</string>
<string name="no_permissions_to_start_conf_call">لا يوجد لديك أذن لبدء إجتماع في هذه الغرفة</string>
<string name="start_chatting">إبدأ بالمحادثة</string>
@ -1123,7 +1123,7 @@
<string name="sign_out_bottom_sheet_will_lose_secure_messages">ستفقد الوصول إلى رسائلك المشفرة إلا إذا أخذت نسخة إحتياطية من مفاتيحك قبل تسجيلك للخروج.</string>
<string name="backup">نسخة إحتياطية</string>
<string name="are_you_sure">هل أنت متأكد؟</string>
<string name="keys_backup_activate">إستخدم المفتاح الإحتياطي</string>
<string name="keys_backup_activate">إستخدم النسخة الإحتياطية للمفتاح</string>
<string name="sign_out_bottom_sheet_dont_want_secure_messages">لا أريد رسائلي المشفرة</string>
<string name="title_activity_keys_backup_setup">نسخ إحتياطي للمفتاح</string>
<string name="notification_sync_init">جاري إعداد الخدمة</string>
@ -1230,4 +1230,26 @@
<item quantity="many">أنت أضفت %1$s كعناوين لهذه الغرفة.</item>
<item quantity="other">أنت أضفت %1$s كعناوين لهذه الغرفة.</item>
</plurals>
<string name="denied_permission_generic">لإكمال الاجراء امنح الصلاحيات الناقصة عبر إعدادات النظام.</string>
<string name="spaces">الفضاءات</string>
<string name="notice_direct_room_guest_access_forbidden_by_you">منعتَ الزوار من دخول الغرفة.</string>
<string name="notice_room_guest_access_forbidden">منع %1$s الزوار من دخول الغرفة.</string>
<string name="notice_direct_room_guest_access_can_join_by_you">سمحت للزوار بدخول هنا.</string>
<string name="notice_direct_room_guest_access_can_join">سمح %1$s للزوار بدخول الغرفة.</string>
<string name="notice_room_guest_access_can_join_by_you">سمحتَ للزوار بدخول الغرفة.</string>
<string name="notice_room_guest_access_can_join">سمح %1$s للزوار بدخول الغرفة.</string>
<string name="notice_room_canonical_alias_no_change_by_you">غيرتَ عناوين الغرفة.</string>
<string name="notice_room_canonical_alias_no_change">غّير %1$s عناوين الغرفة.</string>
<string name="notice_room_canonical_alias_main_and_alternative_changed_by_you">غيرتَ العناوين الرئيسية والبديلة للغرفة.</string>
<string name="notice_room_canonical_alias_alternative_changed_by_you">غيّرت العناوين البديلة للغرفة.</string>
<string name="notice_direct_room_guest_access_forbidden">منع %1$s الزوار من دخول الغرفة.</string>
<string name="notice_room_guest_access_forbidden_by_you">منعتَ الزوار من دخول الغرفة.</string>
<plurals name="notice_room_canonical_alias_alternative_added_by_you">
<item quantity="zero">لم تضف أي رابط بديل.</item>
<item quantity="one">أضفت الرابط البديل %1$s للغرفة.</item>
<item quantity="two">أضفت الرابطين البديلين %1$s للغرفة.</item>
<item quantity="few">أضفت الروابط البديلة %1$s للغرفة.</item>
<item quantity="many">أضفت الروابط البديلة %1$s للغرفة.</item>
<item quantity="other">أضفت الروابط البديلة %1$s للغرفة.</item>
</plurals>
</resources>

View File

@ -2834,8 +2834,8 @@
<string name="spaces_feeling_experimental_subspace">Chcete experimentovat\?
\nDo Spaceu můžete přidat existující Spaces.</string>
<string name="space_add_rooms">Přidat místnosti</string>
<string name="space_leave_prompt_msg_as_admin">Jste administrátorem tohoto prostoru, ujistěte se, že jste před odchodem převedli administrátorská práva na jiného člena.</string>
<string name="space_leave_prompt_msg_private">Tento prostor není veřejný. Bez pozvánky se do něj nebudete moci znovu připojit.</string>
<string name="space_leave_prompt_msg_as_admin">Jste jediným správcem tohoto prostoru. Jeho opuštění bude znamenat, že nad ním nebude mít nikdo kontrolu.</string>
<string name="space_leave_prompt_msg_private">Pokud nebudete znovu pozváni, nebudete se moci připojit.</string>
<string name="space_leave_prompt_msg_only_you">Jste tu jediný člověk. Pokud odejdete, nikdo se k vám v budoucnu nebude moci připojit, včetně vás.</string>
<string name="invite_to_space">Pozvat do %s</string>
<string name="a11y_beta">Tato funkce je ve fázi beta</string>
@ -3003,4 +3003,49 @@
<string name="call_ringing">Vyzvánění…</string>
<string name="spaces">Spaces</string>
<string name="learn_more">Dozvědět se více</string>
<string name="space_add_space_to_any_space_you_manage">Přidejte prostor do jakéhokoli prostoru, který spravujete.</string>
<string name="space_add_existing_spaces">Přidat stávající prostory</string>
<string name="space_add_existing_rooms_only">Přidat stávající místnosti</string>
<string name="pick_tings_to_leave">Vyberte místa, která chcete opustit</string>
<string name="leave_specific_ones">Opustit konkrétní místnosti a prostory…</string>
<string name="dont_leave_any">Neopouštět žádné místnosti a prostory</string>
<string name="you_will_leave_all_in">Opustíte všechny místnosti a prostory v %s.</string>
<string name="leave_all_rooms_and_spaces">Opustit všechny místnosti a prostory</string>
<string name="space_leave_prompt_msg_with_name">Opravdu chcete opustit %s\?</string>
<string name="discovery_section">Objevování (%s)</string>
<string name="finish_setup">Dokončit nastavení</string>
<string name="discovery_invite">Pozvání e-mailem, vyhledávání kontaktů a další…</string>
<string name="finish_setting_up_discovery">Dokončit nastavení objevování.</string>
<string name="create_space_identity_server_info_none">V současné době nepoužíváte server identit. Chcete-li pozvat kolegy a být pro ně zjistitelní, nakonfigurujte jej níže.</string>
<string name="invite_by_mxid_or_mail">Pozvat pomocí uživatelského jména nebo e-mailu</string>
<string name="create_spaces_invite_public_header_desc">Zajistěte, aby měli do společnosti %s přístup ti správní lidé. Další můžete pozvat později.</string>
<string name="create_spaces_invite_public_header">Kdo jsou vaši kolegové\?</string>
<string name="command_description_add_to_space">Přidat do daného prostoru</string>
<string name="create_space_in_progress">Vytváření prostoru…</string>
<string name="settings_developer_mode_show_info_on_screen_summary">Zobrazit užitečné informace, které pomohou při ladění aplikace</string>
<string name="settings_developer_mode_show_info_on_screen_title">Zobrazení ladících informací na obrazovce</string>
<string name="does_not_look_like_valid_email">Nevypadá to jako platná e-mailová adresa</string>
<string name="open_discovery_settings">Otevřít nastavení objevování</string>
<string name="user_directory_search_hint_2">Vyhledávání podle jména, ID nebo emailu</string>
<string name="create_new_space">Vytvořit nový prostor</string>
<string name="room_settings_space_access_public_description">Každý může prostor najít a připojit se k němu</string>
<string name="room_settings_space_access_title">Adresa prostoru</string>
<string name="room_settings_access_rules_pref_dialog_title">Kdo má přístup\?</string>
<string name="settings_notification_emails_enable_for_email">Povolit e-mailová oznámení pro %s</string>
<string name="settings_notification_emails_no_emails">Pro obdržení e-mailu s upozorněním, přiřaďte e-mail ke svému Matrix účtu</string>
<string name="settings_notification_emails_category">Oznámení e-mailem</string>
<string name="room_permissions_upgrade_the_space">Povýšit prostor</string>
<string name="room_permissions_change_space_name">Změnit název prostoru</string>
<string name="room_permissions_enable_space_encryption">Povolit šifrování prostoru</string>
<string name="room_permissions_change_space_avatar">Změnit avatar prostoru</string>
<string name="room_permissions_change_main_address_for_the_space">Změnit hlavní adresu prostoru</string>
<string name="space_permissions_notice_read_only">Nemáte oprávnění k aktualizaci rolí potřebných ke změně různých částí tohoto prostoru</string>
<string name="space_permissions_notice">Vyberte role potřebné ke změně různých částí tohoto prostoru</string>
<string name="space_settings_permissions_subtitle">Zobrazit a aktualizovat role potřebné ke změně různých částí prostoru.</string>
<string name="space_settings_permissions_title">Oprávnění prostoru</string>
<string name="space_participants_unban_prompt_msg">Zrušením vykázání uživateli umožní znovu se připojit do prostoru.</string>
<string name="space_participants_ban_prompt_msg">Vykázáním uživatele z tohoto prostoru vykopnete a zabráníte mu v dalším připojení.</string>
<string name="space_participants_kick_prompt_msg">vykopnutí uživatele je z tohoto prostoru odstraní.
\n
\nAbyste jim zabránili v dalším vstupu, měli byste je raději vykázat.</string>
</resources>

View File

@ -2998,4 +2998,36 @@
<string name="this_invite_to_this_room_was_sent">Die Einladung zu diesem Raum wurde an %s gesendet, welche nicht mit deinem Konto verknüpft ist</string>
<string name="help_space_members">Hilf Space-Mitgliedern private Räume zu finden</string>
<string name="settings_mentions_and_keywords_encryption_notice">Auf deinem Mobilgerät wirst du keine Benachrichtigungen für Erwähnungen und Schlüsselwörter in verschlüsselten Räumen erhalten.</string>
<string name="space_add_existing_spaces">Existierende Spaces hinzufügen</string>
<string name="space_add_existing_rooms_only">Existierende Räume hinzufügen</string>
<string name="leave_specific_ones">Ausgewählte Räume oder Spaces verlassen…</string>
<string name="dont_leave_any">Keine Räume und Spaces verlassen</string>
<string name="you_will_leave_all_in">Du wirst alle Räume und Spaces in %s verlassen.</string>
<string name="leave_all_rooms_and_spaces">Alle Räume und Spaces verlassen</string>
<string name="space_leave_prompt_msg_with_name">Willst du %s wirklich verlassen\?</string>
<string name="invite_by_mxid_or_mail">Mit Benutzername oder E-Mail einladen</string>
<string name="command_description_add_to_space">Zum ausgewählten Space hinzufügen</string>
<string name="create_space_in_progress">Erstelle Space…</string>
<string name="settings_developer_mode_show_info_on_screen_summary">Hilfreiche Informationen zur Fehlersuche anzeigen</string>
<string name="settings_developer_mode_show_info_on_screen_title">Debug-Info anzeigen</string>
<string name="does_not_look_like_valid_email">Das schaut nicht nach einer gültigen E-Mail-Adresse aus</string>
<string name="user_directory_search_hint_2">Nach Name, ID oder E-Mail suchen</string>
<string name="create_new_space">Neuen Space erstellen</string>
<string name="room_settings_space_access_title">Zugriff</string>
<string name="room_settings_access_rules_pref_dialog_title">Wer hat Zugriff\?</string>
<string name="settings_notification_emails_enable_for_email">Benachrichtigungen per Email für %s aktivieren</string>
<string name="settings_notification_emails_no_emails">Um Benachrichtigungen per E-Mail zu empfangen, musst du einen E-Mail-Adresse hinzufügen</string>
<string name="settings_notification_emails_category">Emailbenachrichtigungen</string>
<string name="room_permissions_upgrade_the_space">Space upgraden</string>
<string name="room_permissions_change_space_name">Namen vom Space ändern</string>
<string name="room_permissions_enable_space_encryption">Space verschlüsseln</string>
<string name="room_permissions_change_main_address_for_the_space">Hauptadresse vom Space ändern</string>
<string name="room_permissions_change_space_avatar">Space-Icon ändern</string>
<string name="space_permissions_notice_read_only">Du hast nicht die Berechtigung, Rollenrechte zu bearbeiten</string>
<string name="space_settings_permissions_title">Space-Berechtigungen</string>
<string name="space_participants_unban_prompt_msg">Wenn du die Person entbannst, kann sie wieder beitreten.</string>
<string name="space_participants_ban_prompt_msg">Wenn du eine Person bannst, kann sie nicht erneut beitreten.</string>
<string name="space_participants_kick_prompt_msg">Kicken entfernt die Person aus dem Space
\n
\nUm sie für immer zu entfernen, solltest du sie bannen.</string>
</resources>

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