mirror of
https://github.com/element-hq/element-android
synced 2024-10-23 03:06:33 +03:00
Merge branch 'develop' into feature/fix_call_sound_device
This commit is contained in:
commit
7d76264b25
41 changed files with 546 additions and 306 deletions
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
|
@ -1,6 +1,6 @@
|
||||||
### Pull Request Checklist
|
### Pull Request Checklist
|
||||||
|
|
||||||
<!-- Please read [CONTRIBUTING.md](https://github.com/vector-im/riotX-android/blob/develop/CONTRIBUTING.md) before submitting your pull request -->
|
<!-- Please read [CONTRIBUTING.md](https://github.com/vector-im/element-android/blob/develop/CONTRIBUTING.md) before submitting your pull request -->
|
||||||
|
|
||||||
- [ ] Changes has been tested on an Android device or Android emulator with API 21
|
- [ ] Changes has been tested on an Android device or Android emulator with API 21
|
||||||
- [ ] UI change has been tested on both light and dark themes
|
- [ ] UI change has been tested on both light and dark themes
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
A full developer contributors list can be found [here](https://github.com/vector-im/riotX-android/graphs/contributors).
|
A full developer contributors list can be found [here](https://github.com/vector-im/element-android/graphs/contributors).
|
||||||
|
|
||||||
# Core team:
|
# Core team:
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ Features ✨:
|
||||||
-
|
-
|
||||||
|
|
||||||
Improvements 🙌:
|
Improvements 🙌:
|
||||||
|
- You can now join room through permalink and within room directory search
|
||||||
- Add long click gesture to copy userId, user display name, room name, room topic and room alias (#1774)
|
- Add long click gesture to copy userId, user display name, room name, room topic and room alias (#1774)
|
||||||
|
|
||||||
Bugfix 🐛:
|
Bugfix 🐛:
|
||||||
|
@ -15,6 +16,8 @@ Bugfix 🐛:
|
||||||
- Fix bad color for settings icon on Android < 24 (#1786)
|
- Fix bad color for settings icon on Android < 24 (#1786)
|
||||||
- Change user or room avatar: when selecting Gallery, I'm not proposed to crop the selected image (#1590)
|
- Change user or room avatar: when selecting Gallery, I'm not proposed to crop the selected image (#1590)
|
||||||
- Loudspeaker is always used (#1685)
|
- Loudspeaker is always used (#1685)
|
||||||
|
- Fix uploads still don't work with room v6 (#1879)
|
||||||
|
- Can't handle ongoing call events in background (#1992)
|
||||||
|
|
||||||
Translations 🗣:
|
Translations 🗣:
|
||||||
-
|
-
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
[![Buildkite](https://badge.buildkite.com/657d3db27364448d69d54f66c690f7788bc6aa80a7628e37f3.svg?branch=develop)](https://buildkite.com/matrix-dot-org/riotx-android/builds?branch=develop)
|
[![Buildkite](https://badge.buildkite.com/657d3db27364448d69d54f66c690f7788bc6aa80a7628e37f3.svg?branch=develop)](https://buildkite.com/matrix-dot-org/riotx-android/builds?branch=develop)
|
||||||
[![Weblate](https://translate.riot.im/widgets/element-android/-/svg-badge.svg)](https://translate.riot.im/engage/element-android/?utm_source=widget)
|
[![Weblate](https://translate.riot.im/widgets/element-android/-/svg-badge.svg)](https://translate.riot.im/engage/element-android/?utm_source=widget)
|
||||||
[![Element Android Matrix room #element-android:matrix.org](https://img.shields.io/matrix/element-android:matrix.org.svg?label=%23element-android:matrix.org&logo=matrix&server_fqdn=matrix.org)](https://matrix.to/#/#element-android:matrix.org)
|
[![Element Android Matrix room #element-android:matrix.org](https://img.shields.io/matrix/element-android:matrix.org.svg?label=%23element-android:matrix.org&logo=matrix&server_fqdn=matrix.org)](https://matrix.to/#/#element-android:matrix.org)
|
||||||
[![Quality Gate](https://sonarcloud.io/api/project_badges/measure?project=vector.android.riotx&metric=alert_status)](https://sonarcloud.io/dashboard?id=vector.android.riotx)
|
[![Quality Gate](https://sonarcloud.io/api/project_badges/measure?project=im.vector.app.android&metric=alert_status)](https://sonarcloud.io/dashboard?id=im.vector.app.android)
|
||||||
[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=vector.android.riotx&metric=vulnerabilities)](https://sonarcloud.io/dashboard?id=vector.android.riotx)
|
[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=im.vector.app.android&metric=vulnerabilities)](https://sonarcloud.io/dashboard?id=im.vector.app.android)
|
||||||
[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=vector.android.riotx&metric=bugs)](https://sonarcloud.io/dashboard?id=vector.android.riotx)
|
[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=im.vector.app.android&metric=bugs)](https://sonarcloud.io/dashboard?id=im.vector.app.android)
|
||||||
|
|
||||||
# Element Android
|
# Element Android
|
||||||
|
|
||||||
|
@ -27,6 +27,6 @@ The team will work to add them on a regular basis.
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Please refer to [CONTRIBUTING.md](https://github.com/vector-im/riotX-android/blob/develop/CONTRIBUTING.md) if you want to contribute on Matrix Android projects!
|
Please refer to [CONTRIBUTING.md](https://github.com/vector-im/element-android/blob/develop/CONTRIBUTING.md) if you want to contribute on Matrix Android projects!
|
||||||
|
|
||||||
Come chat with the community in the dedicated Matrix [room](https://matrix.to/#/#element-android:matrix.org).
|
Come chat with the community in the dedicated Matrix [room](https://matrix.to/#/#element-android:matrix.org).
|
||||||
|
|
19
build.gradle
19
build.gradle
|
@ -81,15 +81,15 @@ apply plugin: 'org.sonarqube'
|
||||||
|
|
||||||
sonarqube {
|
sonarqube {
|
||||||
properties {
|
properties {
|
||||||
property "sonar.projectName", "RiotX-Android"
|
property "sonar.projectName", "Element-Android"
|
||||||
property "sonar.projectKey", "vector.android.riotx"
|
property "sonar.projectKey", "im.vector.app.android"
|
||||||
property "sonar.host.url", "https://sonarcloud.io"
|
property "sonar.host.url", "https://sonarcloud.io"
|
||||||
property "sonar.projectVersion", project(":vector").android.defaultConfig.versionName
|
property "sonar.projectVersion", project(":vector").android.defaultConfig.versionName
|
||||||
property "sonar.sourceEncoding", "UTF-8"
|
property "sonar.sourceEncoding", "UTF-8"
|
||||||
property "sonar.links.homepage", "https://github.com/vector-im/riotX-android/"
|
property "sonar.links.homepage", "https://github.com/vector-im/element-android/"
|
||||||
property "sonar.links.ci", "https://buildkite.com/matrix-dot-org/riotx-android"
|
property "sonar.links.ci", "https://buildkite.com/matrix-dot-org/riotx-android"
|
||||||
property "sonar.links.scm", "https://github.com/vector-im/riotX-android/"
|
property "sonar.links.scm", "https://github.com/vector-im/element-android/"
|
||||||
property "sonar.links.issue", "https://github.com/vector-im/riotX-android/issues"
|
property "sonar.links.issue", "https://github.com/vector-im/element-android/issues"
|
||||||
property "sonar.organization", "new_vector_ltd_organization"
|
property "sonar.organization", "new_vector_ltd_organization"
|
||||||
property "sonar.login", project.hasProperty("SONAR_LOGIN") ? SONAR_LOGIN : "invalid"
|
property "sonar.login", project.hasProperty("SONAR_LOGIN") ? SONAR_LOGIN : "invalid"
|
||||||
}
|
}
|
||||||
|
@ -100,11 +100,18 @@ project(":vector") {
|
||||||
properties {
|
properties {
|
||||||
property "sonar.sources", project(":vector").android.sourceSets.main.java.srcDirs
|
property "sonar.sources", project(":vector").android.sourceSets.main.java.srcDirs
|
||||||
// exclude source code from analyses separated by a colon (:)
|
// exclude source code from analyses separated by a colon (:)
|
||||||
// property "sonar.exclusions", "**/*.*"
|
// Exclude Java source
|
||||||
|
property "sonar.exclusions", "**/BugReporterMultipartBody.java"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
project(":diff-match-patch") {
|
||||||
|
sonarqube {
|
||||||
|
skipProject = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//project(":matrix-sdk-android") {
|
//project(":matrix-sdk-android") {
|
||||||
// sonarqube {
|
// sonarqube {
|
||||||
// properties {
|
// properties {
|
||||||
|
|
|
@ -131,8 +131,6 @@ dependencies {
|
||||||
// Network
|
// Network
|
||||||
implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
|
implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
|
||||||
implementation "com.squareup.retrofit2:converter-moshi:$retrofit_version"
|
implementation "com.squareup.retrofit2:converter-moshi:$retrofit_version"
|
||||||
implementation "com.squareup.retrofit2:converter-scalars:$retrofit_version"
|
|
||||||
|
|
||||||
|
|
||||||
implementation(platform("com.squareup.okhttp3:okhttp-bom:4.8.1"))
|
implementation(platform("com.squareup.okhttp3:okhttp-bom:4.8.1"))
|
||||||
implementation 'com.squareup.okhttp3:okhttp'
|
implementation 'com.squareup.okhttp3:okhttp'
|
||||||
|
|
|
@ -34,4 +34,6 @@ interface CallSignalingService {
|
||||||
fun removeCallListener(listener: CallsListener)
|
fun removeCallListener(listener: CallsListener)
|
||||||
|
|
||||||
fun getCallWithId(callId: String) : MxCall?
|
fun getCallWithId(callId: String) : MxCall?
|
||||||
|
|
||||||
|
fun isThereAnyActiveCall(): Boolean
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,12 @@ import android.net.Uri
|
||||||
*/
|
*/
|
||||||
sealed class PermalinkData {
|
sealed class PermalinkData {
|
||||||
|
|
||||||
data class RoomLink(val roomIdOrAlias: String, val isRoomAlias: Boolean, val eventId: String?) : PermalinkData()
|
data class RoomLink(
|
||||||
|
val roomIdOrAlias: String,
|
||||||
|
val isRoomAlias: Boolean,
|
||||||
|
val eventId: String?,
|
||||||
|
val viaParameters: List<String>
|
||||||
|
) : PermalinkData()
|
||||||
|
|
||||||
data class UserLink(val userId: String) : PermalinkData()
|
data class UserLink(val userId: String) : PermalinkData()
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
package org.matrix.android.sdk.api.session.permalinks
|
package org.matrix.android.sdk.api.session.permalinks
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.net.UrlQuerySanitizer
|
||||||
import org.matrix.android.sdk.api.MatrixPatterns
|
import org.matrix.android.sdk.api.MatrixPatterns
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -40,14 +41,13 @@ object PermalinkParser {
|
||||||
if (!uri.toString().startsWith(PermalinkService.MATRIX_TO_URL_BASE)) {
|
if (!uri.toString().startsWith(PermalinkService.MATRIX_TO_URL_BASE)) {
|
||||||
return PermalinkData.FallbackLink(uri)
|
return PermalinkData.FallbackLink(uri)
|
||||||
}
|
}
|
||||||
|
|
||||||
val fragment = uri.fragment
|
val fragment = uri.fragment
|
||||||
if (fragment.isNullOrEmpty()) {
|
if (fragment.isNullOrEmpty()) {
|
||||||
return PermalinkData.FallbackLink(uri)
|
return PermalinkData.FallbackLink(uri)
|
||||||
}
|
}
|
||||||
|
|
||||||
val indexOfQuery = fragment.indexOf("?")
|
val indexOfQuery = fragment.indexOf("?")
|
||||||
val safeFragment = if (indexOfQuery != -1) fragment.substring(0, indexOfQuery) else fragment
|
val safeFragment = if (indexOfQuery != -1) fragment.substring(0, indexOfQuery) else fragment
|
||||||
|
val viaQueryParameters = fragment.getViaParameters()
|
||||||
|
|
||||||
// we are limiting to 2 params
|
// we are limiting to 2 params
|
||||||
val params = safeFragment
|
val params = safeFragment
|
||||||
|
@ -65,17 +65,29 @@ object PermalinkParser {
|
||||||
PermalinkData.RoomLink(
|
PermalinkData.RoomLink(
|
||||||
roomIdOrAlias = identifier,
|
roomIdOrAlias = identifier,
|
||||||
isRoomAlias = false,
|
isRoomAlias = false,
|
||||||
eventId = extraParameter.takeIf { !it.isNullOrEmpty() && MatrixPatterns.isEventId(it) }
|
eventId = extraParameter.takeIf { !it.isNullOrEmpty() && MatrixPatterns.isEventId(it) },
|
||||||
|
viaParameters = viaQueryParameters
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
MatrixPatterns.isRoomAlias(identifier) -> {
|
MatrixPatterns.isRoomAlias(identifier) -> {
|
||||||
PermalinkData.RoomLink(
|
PermalinkData.RoomLink(
|
||||||
roomIdOrAlias = identifier,
|
roomIdOrAlias = identifier,
|
||||||
isRoomAlias = true,
|
isRoomAlias = true,
|
||||||
eventId = extraParameter.takeIf { !it.isNullOrEmpty() && MatrixPatterns.isEventId(it) }
|
eventId = extraParameter.takeIf { !it.isNullOrEmpty() && MatrixPatterns.isEventId(it) },
|
||||||
|
viaParameters = viaQueryParameters
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
else -> PermalinkData.FallbackLink(uri)
|
else -> PermalinkData.FallbackLink(uri)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun String.getViaParameters(): List<String> {
|
||||||
|
return UrlQuerySanitizer(this)
|
||||||
|
.parameterList
|
||||||
|
.filter {
|
||||||
|
it.mParameter == "via"
|
||||||
|
}.map {
|
||||||
|
it.mValue
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,13 +19,12 @@ package org.matrix.android.sdk.internal.network
|
||||||
|
|
||||||
import com.squareup.moshi.Moshi
|
import com.squareup.moshi.Moshi
|
||||||
import dagger.Lazy
|
import dagger.Lazy
|
||||||
import org.matrix.android.sdk.internal.util.ensureTrailingSlash
|
|
||||||
import okhttp3.Call
|
import okhttp3.Call
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
|
import org.matrix.android.sdk.internal.util.ensureTrailingSlash
|
||||||
import retrofit2.Retrofit
|
import retrofit2.Retrofit
|
||||||
import retrofit2.converter.moshi.MoshiConverterFactory
|
import retrofit2.converter.moshi.MoshiConverterFactory
|
||||||
import retrofit2.converter.scalars.ScalarsConverterFactory
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class RetrofitFactory @Inject constructor(private val moshi: Moshi) {
|
internal class RetrofitFactory @Inject constructor(private val moshi: Moshi) {
|
||||||
|
@ -50,7 +49,6 @@ internal class RetrofitFactory @Inject constructor(private val moshi: Moshi) {
|
||||||
return okHttpClient.get().newCall(request)
|
return okHttpClient.get().newCall(request)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.addConverterFactory(ScalarsConverterFactory.create())
|
|
||||||
.addConverterFactory(UnitConverterFactory)
|
.addConverterFactory(UnitConverterFactory)
|
||||||
.addConverterFactory(MoshiConverterFactory.create(moshi))
|
.addConverterFactory(MoshiConverterFactory.create(moshi))
|
||||||
.build()
|
.build()
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 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 org.matrix.android.sdk.internal.network.parsing
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable
|
||||||
|
import com.squareup.moshi.JsonAdapter
|
||||||
|
import com.squareup.moshi.JsonReader
|
||||||
|
import com.squareup.moshi.JsonWriter
|
||||||
|
|
||||||
|
import com.squareup.moshi.Moshi
|
||||||
|
import java.io.IOException
|
||||||
|
import java.lang.reflect.Type
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is used to check if NUMBER in json is integer or double, so we can preserve typing when serializing/deserializing in a row.
|
||||||
|
*/
|
||||||
|
interface CheckNumberType {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val JSON_ADAPTER_FACTORY = object : JsonAdapter.Factory {
|
||||||
|
@Nullable
|
||||||
|
override fun create(type: Type, annotations: Set<Annotation?>?, moshi: Moshi): JsonAdapter<*>? {
|
||||||
|
if (type !== Any::class.java) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
val delegate: JsonAdapter<Any> = moshi.nextAdapter(this, Any::class.java, emptySet())
|
||||||
|
return object : JsonAdapter<Any?>() {
|
||||||
|
@Nullable
|
||||||
|
@Throws(IOException::class)
|
||||||
|
override fun fromJson(reader: JsonReader): Any? {
|
||||||
|
return if (reader.peek() !== JsonReader.Token.NUMBER) {
|
||||||
|
delegate.fromJson(reader)
|
||||||
|
} else {
|
||||||
|
val numberAsString = reader.nextString()
|
||||||
|
val decimal = BigDecimal(numberAsString)
|
||||||
|
if (decimal.scale() <= 0) {
|
||||||
|
decimal.intValueExact()
|
||||||
|
} else {
|
||||||
|
decimal.toDouble()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toJson(writer: JsonWriter, value: Any?) {
|
||||||
|
delegate.toJson(writer, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 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 org.matrix.android.sdk.internal.session.call
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import org.matrix.android.sdk.api.session.call.MxCall
|
||||||
|
import org.matrix.android.sdk.internal.session.SessionScope
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@SessionScope
|
||||||
|
internal class ActiveCallHandler @Inject constructor() {
|
||||||
|
|
||||||
|
private val activeCallListLiveData: MutableLiveData<MutableList<MxCall>> by lazy {
|
||||||
|
MutableLiveData<MutableList<MxCall>>(mutableListOf())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addCall(call: MxCall) {
|
||||||
|
activeCallListLiveData.postValue(activeCallListLiveData.value?.apply { add(call) })
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeCall(callId: String) {
|
||||||
|
activeCallListLiveData.postValue(activeCallListLiveData.value?.apply { removeAll { it.callId == callId } })
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCallWithId(callId: String): MxCall? {
|
||||||
|
return activeCallListLiveData.value?.find { it.callId == callId }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getActiveCallsLiveData(): LiveData<MutableList<MxCall>> = activeCallListLiveData
|
||||||
|
}
|
|
@ -49,6 +49,7 @@ import javax.inject.Inject
|
||||||
internal class DefaultCallSignalingService @Inject constructor(
|
internal class DefaultCallSignalingService @Inject constructor(
|
||||||
@UserId
|
@UserId
|
||||||
private val userId: String,
|
private val userId: String,
|
||||||
|
private val activeCallHandler: ActiveCallHandler,
|
||||||
private val localEchoEventFactory: LocalEchoEventFactory,
|
private val localEchoEventFactory: LocalEchoEventFactory,
|
||||||
private val roomEventSender: RoomEventSender,
|
private val roomEventSender: RoomEventSender,
|
||||||
private val taskExecutor: TaskExecutor,
|
private val taskExecutor: TaskExecutor,
|
||||||
|
@ -57,8 +58,6 @@ internal class DefaultCallSignalingService @Inject constructor(
|
||||||
|
|
||||||
private val callListeners = mutableSetOf<CallsListener>()
|
private val callListeners = mutableSetOf<CallsListener>()
|
||||||
|
|
||||||
private val activeCalls = mutableListOf<MxCall>()
|
|
||||||
|
|
||||||
private val cachedTurnServerResponse = object {
|
private val cachedTurnServerResponse = object {
|
||||||
// Keep one minute safe to avoid considering the data is valid and then actually it is not when effectively using it.
|
// Keep one minute safe to avoid considering the data is valid and then actually it is not when effectively using it.
|
||||||
private val MIN_TTL = 60
|
private val MIN_TTL = 60
|
||||||
|
@ -97,7 +96,7 @@ internal class DefaultCallSignalingService @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createOutgoingCall(roomId: String, otherUserId: String, isVideoCall: Boolean): MxCall {
|
override fun createOutgoingCall(roomId: String, otherUserId: String, isVideoCall: Boolean): MxCall {
|
||||||
return MxCallImpl(
|
val call = MxCallImpl(
|
||||||
callId = UUID.randomUUID().toString(),
|
callId = UUID.randomUUID().toString(),
|
||||||
isOutgoing = true,
|
isOutgoing = true,
|
||||||
roomId = roomId,
|
roomId = roomId,
|
||||||
|
@ -106,8 +105,9 @@ internal class DefaultCallSignalingService @Inject constructor(
|
||||||
isVideoCall = isVideoCall,
|
isVideoCall = isVideoCall,
|
||||||
localEchoEventFactory = localEchoEventFactory,
|
localEchoEventFactory = localEchoEventFactory,
|
||||||
roomEventSender = roomEventSender
|
roomEventSender = roomEventSender
|
||||||
).also {
|
)
|
||||||
activeCalls.add(it)
|
activeCallHandler.addCall(call).also {
|
||||||
|
return call
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,8 +120,12 @@ internal class DefaultCallSignalingService @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getCallWithId(callId: String): MxCall? {
|
override fun getCallWithId(callId: String): MxCall? {
|
||||||
Timber.v("## VOIP getCallWithId $callId all calls ${activeCalls.map { it.callId }}")
|
Timber.v("## VOIP getCallWithId $callId all calls ${activeCallHandler.getActiveCallsLiveData().value?.map { it.callId }}")
|
||||||
return activeCalls.find { it.callId == callId }
|
return activeCallHandler.getCallWithId(callId)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isThereAnyActiveCall(): Boolean {
|
||||||
|
return activeCallHandler.getActiveCallsLiveData().value?.isNotEmpty() == true
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun onCallEvent(event: Event) {
|
internal fun onCallEvent(event: Event) {
|
||||||
|
@ -152,6 +156,7 @@ internal class DefaultCallSignalingService @Inject constructor(
|
||||||
// Always ignore local echos of invite
|
// Always ignore local echos of invite
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
event.getClearContent().toModel<CallInviteContent>()?.let { content ->
|
event.getClearContent().toModel<CallInviteContent>()?.let { content ->
|
||||||
val incomingCall = MxCallImpl(
|
val incomingCall = MxCallImpl(
|
||||||
callId = content.callId ?: return@let,
|
callId = content.callId ?: return@let,
|
||||||
|
@ -163,7 +168,7 @@ internal class DefaultCallSignalingService @Inject constructor(
|
||||||
localEchoEventFactory = localEchoEventFactory,
|
localEchoEventFactory = localEchoEventFactory,
|
||||||
roomEventSender = roomEventSender
|
roomEventSender = roomEventSender
|
||||||
)
|
)
|
||||||
activeCalls.add(incomingCall)
|
activeCallHandler.addCall(incomingCall)
|
||||||
onCallInvite(incomingCall, content)
|
onCallInvite(incomingCall, content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -185,8 +190,8 @@ internal class DefaultCallSignalingService @Inject constructor(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
activeCallHandler.removeCall(content.callId)
|
||||||
onCallHangup(content)
|
onCallHangup(content)
|
||||||
activeCalls.removeAll { it.callId == content.callId }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EventType.CALL_CANDIDATES -> {
|
EventType.CALL_CANDIDATES -> {
|
||||||
|
@ -195,7 +200,7 @@ internal class DefaultCallSignalingService @Inject constructor(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
event.getClearContent().toModel<CallCandidatesContent>()?.let { content ->
|
event.getClearContent().toModel<CallCandidatesContent>()?.let { content ->
|
||||||
activeCalls.firstOrNull { it.callId == content.callId }?.let {
|
activeCallHandler.getCallWithId(content.callId)?.let {
|
||||||
onCallIceCandidate(it, content)
|
onCallIceCandidate(it, content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -130,21 +130,6 @@ internal interface RoomAPI {
|
||||||
@Body content: Content?
|
@Body content: Content?
|
||||||
): Call<SendResponse>
|
): Call<SendResponse>
|
||||||
|
|
||||||
/**
|
|
||||||
* Send an event to a room.
|
|
||||||
*
|
|
||||||
* @param txId the transaction Id
|
|
||||||
* @param roomId the room id
|
|
||||||
* @param eventType the event type
|
|
||||||
* @param content the event content as string
|
|
||||||
*/
|
|
||||||
@PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/send/{eventType}/{txId}")
|
|
||||||
fun send(@Path("txId") txId: String,
|
|
||||||
@Path("roomId") roomId: String,
|
|
||||||
@Path("eventType") eventType: String,
|
|
||||||
@Body content: String?
|
|
||||||
): Call<SendResponse>
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the context surrounding an event.
|
* Get the context surrounding an event.
|
||||||
*
|
*
|
||||||
|
|
|
@ -208,7 +208,7 @@ internal class DefaultRelationService @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createSendEventWork(event: Event, startChain: Boolean): OneTimeWorkRequest {
|
private fun createSendEventWork(event: Event, startChain: Boolean): OneTimeWorkRequest {
|
||||||
val sendContentWorkerParams = SendEventWorker.Params(sessionId, event)
|
val sendContentWorkerParams = SendEventWorker.Params(sessionId = sessionId, event = event)
|
||||||
val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams)
|
val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams)
|
||||||
return timeLineSendEventWorkCommon.createWork<SendEventWorker>(sendWorkData, startChain)
|
return timeLineSendEventWorkCommon.createWork<SendEventWorker>(sendWorkData, startChain)
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,7 +120,7 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters)
|
||||||
localEchoRepository.updateEncryptedEcho(localEvent.eventId, safeResult.eventContent, decryptionLocalEcho)
|
localEchoRepository.updateEncryptedEcho(localEvent.eventId, safeResult.eventContent, decryptionLocalEcho)
|
||||||
}
|
}
|
||||||
|
|
||||||
val nextWorkerParams = SendEventWorker.Params(params.sessionId, encryptedEvent)
|
val nextWorkerParams = SendEventWorker.Params(sessionId = params.sessionId, event = encryptedEvent)
|
||||||
return Result.success(WorkerParamsFactory.toData(nextWorkerParams))
|
return Result.success(WorkerParamsFactory.toData(nextWorkerParams))
|
||||||
} else {
|
} else {
|
||||||
val sendState = when (error) {
|
val sendState = when (error) {
|
||||||
|
@ -129,8 +129,11 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters)
|
||||||
}
|
}
|
||||||
localEchoRepository.updateSendState(localEvent.eventId, sendState)
|
localEchoRepository.updateSendState(localEvent.eventId, sendState)
|
||||||
// always return success, or the chain will be stuck for ever!
|
// always return success, or the chain will be stuck for ever!
|
||||||
val nextWorkerParams = SendEventWorker.Params(params.sessionId, localEvent, error?.localizedMessage
|
val nextWorkerParams = SendEventWorker.Params(
|
||||||
?: "Error")
|
sessionId = params.sessionId,
|
||||||
|
event = localEvent,
|
||||||
|
lastFailureMessage = error?.localizedMessage ?: "Error"
|
||||||
|
)
|
||||||
return Result.success(WorkerParamsFactory.toData(nextWorkerParams))
|
return Result.success(WorkerParamsFactory.toData(nextWorkerParams))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,7 +105,7 @@ internal class MultipleEventSendingDispatcherWorker(context: Context, params: Wo
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createSendEventWork(sessionId: String, event: Event, startChain: Boolean): OneTimeWorkRequest {
|
private fun createSendEventWork(sessionId: String, event: Event, startChain: Boolean): OneTimeWorkRequest {
|
||||||
val sendContentWorkerParams = SendEventWorker.Params(sessionId, event)
|
val sendContentWorkerParams = SendEventWorker.Params(sessionId = sessionId, event = event)
|
||||||
val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams)
|
val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams)
|
||||||
|
|
||||||
return timelineSendEventWorkCommon.createWork<SendEventWorker>(sendWorkData, startChain)
|
return timelineSendEventWorkCommon.createWork<SendEventWorker>(sendWorkData, startChain)
|
||||||
|
|
|
@ -65,7 +65,7 @@ internal class RoomEventSender @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createSendEventWork(event: Event, startChain: Boolean): OneTimeWorkRequest {
|
private fun createSendEventWork(event: Event, startChain: Boolean): OneTimeWorkRequest {
|
||||||
val sendContentWorkerParams = SendEventWorker.Params(sessionId, event)
|
val sendContentWorkerParams = SendEventWorker.Params(sessionId = sessionId, event = event)
|
||||||
val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams)
|
val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams)
|
||||||
|
|
||||||
return timelineSendEventWorkCommon.createWork<SendEventWorker>(sendWorkData, startChain)
|
return timelineSendEventWorkCommon.createWork<SendEventWorker>(sendWorkData, startChain)
|
||||||
|
|
|
@ -21,16 +21,16 @@ import android.content.Context
|
||||||
import androidx.work.CoroutineWorker
|
import androidx.work.CoroutineWorker
|
||||||
import androidx.work.WorkerParameters
|
import androidx.work.WorkerParameters
|
||||||
import com.squareup.moshi.JsonClass
|
import com.squareup.moshi.JsonClass
|
||||||
|
import org.greenrobot.eventbus.EventBus
|
||||||
import org.matrix.android.sdk.api.failure.shouldBeRetried
|
import org.matrix.android.sdk.api.failure.shouldBeRetried
|
||||||
|
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.room.send.SendState
|
import org.matrix.android.sdk.api.session.room.send.SendState
|
||||||
import org.matrix.android.sdk.internal.database.mapper.ContentMapper
|
|
||||||
import org.matrix.android.sdk.internal.network.executeRequest
|
import org.matrix.android.sdk.internal.network.executeRequest
|
||||||
import org.matrix.android.sdk.internal.session.room.RoomAPI
|
import org.matrix.android.sdk.internal.session.room.RoomAPI
|
||||||
import org.matrix.android.sdk.internal.worker.SessionWorkerParams
|
import org.matrix.android.sdk.internal.worker.SessionWorkerParams
|
||||||
import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
|
import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
|
||||||
import org.matrix.android.sdk.internal.worker.getSessionComponent
|
import org.matrix.android.sdk.internal.worker.getSessionComponent
|
||||||
import org.greenrobot.eventbus.EventBus
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -47,24 +47,11 @@ internal class SendEventWorker(context: Context,
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
internal data class Params(
|
internal data class Params(
|
||||||
override val sessionId: String,
|
override val sessionId: String,
|
||||||
// TODO remove after some time, it's used for compat
|
override val lastFailureMessage: String? = null,
|
||||||
val event: Event? = null,
|
val event: Event? = null,
|
||||||
val eventId: String? = null,
|
// Keep for compat at the moment, will be removed later
|
||||||
val roomId: String? = null,
|
val eventId: String? = null
|
||||||
val type: String? = null,
|
) : SessionWorkerParams
|
||||||
val contentStr: String? = null,
|
|
||||||
override val lastFailureMessage: String? = null
|
|
||||||
) : SessionWorkerParams {
|
|
||||||
|
|
||||||
constructor(sessionId: String, event: Event, lastFailureMessage: String? = null) : this(
|
|
||||||
sessionId = sessionId,
|
|
||||||
eventId = event.eventId,
|
|
||||||
roomId = event.roomId,
|
|
||||||
type = event.type,
|
|
||||||
contentStr = ContentMapper.map(event.content),
|
|
||||||
lastFailureMessage = lastFailureMessage
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Inject lateinit var localEchoRepository: LocalEchoRepository
|
@Inject lateinit var localEchoRepository: LocalEchoRepository
|
||||||
@Inject lateinit var roomAPI: RoomAPI
|
@Inject lateinit var roomAPI: RoomAPI
|
||||||
|
@ -77,27 +64,31 @@ internal class SendEventWorker(context: Context,
|
||||||
|
|
||||||
val sessionComponent = getSessionComponent(params.sessionId) ?: return Result.success()
|
val sessionComponent = getSessionComponent(params.sessionId) ?: return Result.success()
|
||||||
sessionComponent.inject(this)
|
sessionComponent.inject(this)
|
||||||
if (params.eventId == null || params.roomId == null || params.type == null) {
|
|
||||||
// compat with old params, make it fail if any
|
val event = params.event
|
||||||
if (params.event?.eventId != null) {
|
if (event?.eventId == null || event.roomId == null) {
|
||||||
localEchoRepository.updateSendState(params.event.eventId, SendState.UNDELIVERED)
|
// Old way of sending
|
||||||
|
if (params.eventId != null) {
|
||||||
|
localEchoRepository.updateSendState(params.eventId, SendState.UNDELIVERED)
|
||||||
}
|
}
|
||||||
return Result.success()
|
return Result.success()
|
||||||
|
.also { Timber.e("Work cancelled due to bad input data") }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.lastFailureMessage != null) {
|
if (params.lastFailureMessage != null) {
|
||||||
localEchoRepository.updateSendState(params.eventId, SendState.UNDELIVERED)
|
localEchoRepository.updateSendState(event.eventId, SendState.UNDELIVERED)
|
||||||
// Transmit the error
|
// Transmit the error
|
||||||
return Result.success(inputData)
|
return Result.success(inputData)
|
||||||
.also { Timber.e("Work cancelled due to input error from parent") }
|
.also { Timber.e("Work cancelled due to input error from parent") }
|
||||||
}
|
}
|
||||||
return try {
|
return try {
|
||||||
sendEvent(params.eventId, params.roomId, params.type, params.contentStr)
|
sendEvent(event.eventId, event.roomId, event.type, event.content)
|
||||||
Result.success()
|
Result.success()
|
||||||
} catch (exception: Throwable) {
|
} catch (exception: Throwable) {
|
||||||
// It does start from 0, we want it to stop if it fails the third time
|
// It does start from 0, we want it to stop if it fails the third time
|
||||||
val currentAttemptCount = runAttemptCount + 1
|
val currentAttemptCount = runAttemptCount + 1
|
||||||
if (currentAttemptCount >= MAX_NUMBER_OF_RETRY_BEFORE_FAILING || !exception.shouldBeRetried()) {
|
if (currentAttemptCount >= MAX_NUMBER_OF_RETRY_BEFORE_FAILING || !exception.shouldBeRetried()) {
|
||||||
localEchoRepository.updateSendState(params.eventId, SendState.UNDELIVERED)
|
localEchoRepository.updateSendState(event.eventId, SendState.UNDELIVERED)
|
||||||
return Result.success()
|
return Result.success()
|
||||||
} else {
|
} else {
|
||||||
Result.retry()
|
Result.retry()
|
||||||
|
@ -105,10 +96,10 @@ internal class SendEventWorker(context: Context,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun sendEvent(eventId: String, roomId: String, type: String, contentStr: String?) {
|
private suspend fun sendEvent(eventId: String, roomId: String, type: String, content: Content?) {
|
||||||
localEchoRepository.updateSendState(eventId, SendState.SENDING)
|
localEchoRepository.updateSendState(eventId, SendState.SENDING)
|
||||||
executeRequest<SendResponse>(eventBus) {
|
executeRequest<SendResponse>(eventBus) {
|
||||||
apiCall = roomAPI.send(eventId, roomId, type, contentStr)
|
apiCall = roomAPI.send(eventId, roomId, type, content)
|
||||||
}
|
}
|
||||||
localEchoRepository.updateSendState(eventId, SendState.SENT)
|
localEchoRepository.updateSendState(eventId, SendState.SENT)
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ package org.matrix.android.sdk.internal.session.sync.job
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.Observer
|
||||||
import com.squareup.moshi.JsonEncodingException
|
import com.squareup.moshi.JsonEncodingException
|
||||||
import org.matrix.android.sdk.api.failure.Failure
|
import org.matrix.android.sdk.api.failure.Failure
|
||||||
import org.matrix.android.sdk.api.failure.isTokenError
|
import org.matrix.android.sdk.api.failure.isTokenError
|
||||||
|
@ -30,11 +31,14 @@ import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver
|
||||||
import org.matrix.android.sdk.internal.util.Debouncer
|
import org.matrix.android.sdk.internal.util.Debouncer
|
||||||
import org.matrix.android.sdk.internal.util.createUIHandler
|
import org.matrix.android.sdk.internal.util.createUIHandler
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.SupervisorJob
|
import kotlinx.coroutines.SupervisorJob
|
||||||
import kotlinx.coroutines.cancelChildren
|
import kotlinx.coroutines.cancelChildren
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.matrix.android.sdk.api.session.call.MxCall
|
||||||
|
import org.matrix.android.sdk.internal.session.call.ActiveCallHandler
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.net.SocketTimeoutException
|
import java.net.SocketTimeoutException
|
||||||
import java.util.Timer
|
import java.util.Timer
|
||||||
|
@ -48,8 +52,9 @@ private const val DEFAULT_LONG_POOL_TIMEOUT = 30_000L
|
||||||
internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
|
internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
|
||||||
private val typingUsersTracker: DefaultTypingUsersTracker,
|
private val typingUsersTracker: DefaultTypingUsersTracker,
|
||||||
private val networkConnectivityChecker: NetworkConnectivityChecker,
|
private val networkConnectivityChecker: NetworkConnectivityChecker,
|
||||||
private val backgroundDetectionObserver: BackgroundDetectionObserver)
|
private val backgroundDetectionObserver: BackgroundDetectionObserver,
|
||||||
: Thread(), NetworkConnectivityChecker.Listener, BackgroundDetectionObserver.Listener {
|
private val activeCallHandler: ActiveCallHandler
|
||||||
|
) : Thread(), NetworkConnectivityChecker.Listener, BackgroundDetectionObserver.Listener {
|
||||||
|
|
||||||
private var state: SyncState = SyncState.Idle
|
private var state: SyncState = SyncState.Idle
|
||||||
private var liveState = MutableLiveData<SyncState>(state)
|
private var liveState = MutableLiveData<SyncState>(state)
|
||||||
|
@ -62,6 +67,12 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
|
||||||
private var isTokenValid = true
|
private var isTokenValid = true
|
||||||
private var retryNoNetworkTask: TimerTask? = null
|
private var retryNoNetworkTask: TimerTask? = null
|
||||||
|
|
||||||
|
private val activeCallListObserver = Observer<MutableList<MxCall>> { activeCalls ->
|
||||||
|
if (activeCalls.isEmpty() && backgroundDetectionObserver.isInBackground) {
|
||||||
|
pause()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
updateStateTo(SyncState.Idle)
|
updateStateTo(SyncState.Idle)
|
||||||
}
|
}
|
||||||
|
@ -115,9 +126,11 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
|
||||||
|
|
||||||
override fun run() {
|
override fun run() {
|
||||||
Timber.v("Start syncing...")
|
Timber.v("Start syncing...")
|
||||||
|
|
||||||
isStarted = true
|
isStarted = true
|
||||||
networkConnectivityChecker.register(this)
|
networkConnectivityChecker.register(this)
|
||||||
backgroundDetectionObserver.register(this)
|
backgroundDetectionObserver.register(this)
|
||||||
|
registerActiveCallsObserver()
|
||||||
while (state != SyncState.Killing) {
|
while (state != SyncState.Killing) {
|
||||||
Timber.v("Entering loop, state: $state")
|
Timber.v("Entering loop, state: $state")
|
||||||
if (!isStarted) {
|
if (!isStarted) {
|
||||||
|
@ -163,6 +176,19 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
|
||||||
updateStateTo(SyncState.Killed)
|
updateStateTo(SyncState.Killed)
|
||||||
backgroundDetectionObserver.unregister(this)
|
backgroundDetectionObserver.unregister(this)
|
||||||
networkConnectivityChecker.unregister(this)
|
networkConnectivityChecker.unregister(this)
|
||||||
|
unregisterActiveCallsObserver()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun registerActiveCallsObserver() {
|
||||||
|
syncScope.launch(Dispatchers.Main) {
|
||||||
|
activeCallHandler.getActiveCallsLiveData().observeForever(activeCallListObserver)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun unregisterActiveCallsObserver() {
|
||||||
|
syncScope.launch(Dispatchers.Main) {
|
||||||
|
activeCallHandler.getActiveCallsLiveData().removeObserver(activeCallListObserver)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun doSync(params: SyncTask.Params) {
|
private suspend fun doSync(params: SyncTask.Params) {
|
||||||
|
@ -215,6 +241,8 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMoveToBackground() {
|
override fun onMoveToBackground() {
|
||||||
pause()
|
if (activeCallHandler.getActiveCallsLiveData().value.isNullOrEmpty()) {
|
||||||
|
pause()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,13 +19,23 @@ package org.matrix.android.sdk.internal.worker
|
||||||
|
|
||||||
import androidx.work.Data
|
import androidx.work.Data
|
||||||
import org.matrix.android.sdk.internal.di.MoshiProvider
|
import org.matrix.android.sdk.internal.di.MoshiProvider
|
||||||
|
import org.matrix.android.sdk.internal.network.parsing.CheckNumberType
|
||||||
|
|
||||||
object WorkerParamsFactory {
|
internal object WorkerParamsFactory {
|
||||||
|
|
||||||
|
val moshi by lazy {
|
||||||
|
// We are adding the CheckNumberType as we are serializing/deserializing multiple time in a row
|
||||||
|
// and we lost typing information doing so.
|
||||||
|
// We don't want this check to be done on all adapters, so we just add it here.
|
||||||
|
MoshiProvider.providesMoshi()
|
||||||
|
.newBuilder()
|
||||||
|
.add(CheckNumberType.JSON_ADAPTER_FACTORY)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
const val KEY = "WORKER_PARAMS_JSON"
|
const val KEY = "WORKER_PARAMS_JSON"
|
||||||
|
|
||||||
inline fun <reified T> toData(params: T): Data {
|
inline fun <reified T> toData(params: T): Data {
|
||||||
val moshi = MoshiProvider.providesMoshi()
|
|
||||||
val adapter = moshi.adapter(T::class.java)
|
val adapter = moshi.adapter(T::class.java)
|
||||||
val json = adapter.toJson(params)
|
val json = adapter.toJson(params)
|
||||||
return Data.Builder().putString(KEY, json).build()
|
return Data.Builder().putString(KEY, json).build()
|
||||||
|
@ -36,7 +46,6 @@ object WorkerParamsFactory {
|
||||||
return if (json == null) {
|
return if (json == null) {
|
||||||
null
|
null
|
||||||
} else {
|
} else {
|
||||||
val moshi = MoshiProvider.providesMoshi()
|
|
||||||
val adapter = moshi.adapter(T::class.java)
|
val adapter = moshi.adapter(T::class.java)
|
||||||
adapter.fromJson(json)
|
adapter.fromJson(json)
|
||||||
}
|
}
|
||||||
|
|
|
@ -122,6 +122,9 @@ android {
|
||||||
// Other branches (master, features, etc.) will have version code based on application version.
|
// Other branches (master, features, etc.) will have version code based on application version.
|
||||||
versionCode project.getVersionCode()
|
versionCode project.getVersionCode()
|
||||||
|
|
||||||
|
// Required for sonar analysis
|
||||||
|
versionName "${versionMajor}.${versionMinor}.${versionPatch}-sonar"
|
||||||
|
|
||||||
buildConfigField "String", "GIT_REVISION", "\"${gitRevision()}\""
|
buildConfigField "String", "GIT_REVISION", "\"${gitRevision()}\""
|
||||||
resValue "string", "git_revision", "\"${gitRevision()}\""
|
resValue "string", "git_revision", "\"${gitRevision()}\""
|
||||||
|
|
||||||
|
|
|
@ -68,7 +68,7 @@ class DebugMenuActivity : VectorBaseActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun renderQrCode(text: String) {
|
private fun renderQrCode(text: String) {
|
||||||
debug_qr_code.setData(text, true)
|
debug_qr_code.setData(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnClick(R.id.debug_test_text_view_link)
|
@OnClick(R.id.debug_test_text_view_link)
|
||||||
|
|
|
@ -18,28 +18,23 @@ package im.vector.app.core.ui.views
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.graphics.drawable.AnimationDrawable
|
|
||||||
import android.graphics.drawable.BitmapDrawable
|
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import androidx.appcompat.widget.AppCompatImageView
|
import androidx.appcompat.widget.AppCompatImageView
|
||||||
import im.vector.app.core.qrcode.toBitMatrix
|
import im.vector.app.core.qrcode.toBitMatrix
|
||||||
import im.vector.app.core.qrcode.toBitmap
|
import im.vector.app.core.qrcode.toBitmap
|
||||||
import kotlin.random.Random
|
|
||||||
|
|
||||||
class QrCodeImageView @JvmOverloads constructor(
|
class QrCodeImageView @JvmOverloads constructor(
|
||||||
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
|
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
|
||||||
) : AppCompatImageView(context, attrs, defStyleAttr) {
|
) : AppCompatImageView(context, attrs, defStyleAttr) {
|
||||||
|
|
||||||
private var data: String? = null
|
private var data: String? = null
|
||||||
private var animate = false
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
setBackgroundColor(Color.WHITE)
|
setBackgroundColor(Color.WHITE)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setData(data: String, animate: Boolean) {
|
fun setData(data: String) {
|
||||||
this.data = data
|
this.data = data
|
||||||
this.animate = animate
|
|
||||||
|
|
||||||
render()
|
render()
|
||||||
}
|
}
|
||||||
|
@ -53,47 +48,8 @@ class QrCodeImageView @JvmOverloads constructor(
|
||||||
data
|
data
|
||||||
?.takeIf { height > 0 }
|
?.takeIf { height > 0 }
|
||||||
?.let {
|
?.let {
|
||||||
if (animate) {
|
val bitmap = it.toBitMatrix(height).toBitmap()
|
||||||
// NOT SUPPORTED YET val anim = createAnimation(it)
|
post { setImageBitmap(bitmap) }
|
||||||
// NOT SUPPORTED YET setImageDrawable(anim)
|
|
||||||
// NOT SUPPORTED YET anim.start()
|
|
||||||
// NOT SUPPORTED YET setImageDrawable(BitmapDrawable(resources, it.toBitMatrix(height).toBitmap()))
|
|
||||||
val bitmap = it.toBitMatrix(height).toBitmap()
|
|
||||||
post { setImageBitmap(bitmap) }
|
|
||||||
} else {
|
|
||||||
val bitmap = it.toBitMatrix(height).toBitmap()
|
|
||||||
post { setImageBitmap(bitmap) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createAnimation(data: String): AnimationDrawable {
|
|
||||||
val finalQr = data.toBitMatrix(height)
|
|
||||||
|
|
||||||
val list = mutableListOf(finalQr)
|
|
||||||
|
|
||||||
val random = Random(System.currentTimeMillis())
|
|
||||||
val repeatTime = 8
|
|
||||||
repeat(repeatTime) { index ->
|
|
||||||
val alteredQr = finalQr.clone()
|
|
||||||
for (x in 0 until alteredQr.width) {
|
|
||||||
for (y in 0 until alteredQr.height) {
|
|
||||||
if (random.nextInt(repeatTime - index) == 0) {
|
|
||||||
// Pb is that it does not toggle a whole black square, but only a pixel
|
|
||||||
alteredQr.unset(x, y)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
list.add(alteredQr)
|
|
||||||
}
|
|
||||||
|
|
||||||
val animDrawable = AnimationDrawable()
|
|
||||||
|
|
||||||
list.asReversed()
|
|
||||||
.forEach {
|
|
||||||
animDrawable.addFrame(BitmapDrawable(resources, it.toBitmap()), 150)
|
|
||||||
}
|
|
||||||
|
|
||||||
return animDrawable
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,6 @@ class VerificationChooseMethodController @Inject constructor(
|
||||||
bottomSheetVerificationQrCodeItem {
|
bottomSheetVerificationQrCodeItem {
|
||||||
id("qr")
|
id("qr")
|
||||||
data(state.qrCodeText)
|
data(state.qrCodeText)
|
||||||
animate(false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dividerItem {
|
dividerItem {
|
||||||
|
|
|
@ -32,12 +32,9 @@ abstract class BottomSheetVerificationQrCodeItem : VectorEpoxyModel<BottomSheetV
|
||||||
@EpoxyAttribute
|
@EpoxyAttribute
|
||||||
lateinit var data: String
|
lateinit var data: String
|
||||||
|
|
||||||
@EpoxyAttribute
|
|
||||||
var animate = false
|
|
||||||
|
|
||||||
override fun bind(holder: Holder) {
|
override fun bind(holder: Holder) {
|
||||||
super.bind(holder)
|
super.bind(holder)
|
||||||
holder.qsrCodeImage.setData(data, animate)
|
holder.qsrCodeImage.setData(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
class Holder : VectorEpoxyHolder() {
|
class Holder : VectorEpoxyHolder() {
|
||||||
|
|
|
@ -77,16 +77,15 @@ class RoomDetailActivity :
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
waitingView = waiting_view
|
waitingView = waiting_view
|
||||||
|
val roomDetailArgs: RoomDetailArgs? = if (intent?.action == ACTION_ROOM_DETAILS_FROM_SHORTCUT) {
|
||||||
|
RoomDetailArgs(roomId = intent?.extras?.getString(EXTRA_ROOM_ID)!!)
|
||||||
|
} else {
|
||||||
|
intent?.extras?.getParcelable(EXTRA_ROOM_DETAIL_ARGS)
|
||||||
|
}
|
||||||
|
if (roomDetailArgs == null) return
|
||||||
|
currentRoomId = roomDetailArgs.roomId
|
||||||
|
|
||||||
if (isFirstCreation()) {
|
if (isFirstCreation()) {
|
||||||
val roomDetailArgs: RoomDetailArgs? = if (intent?.action == ACTION_ROOM_DETAILS_FROM_SHORTCUT) {
|
|
||||||
RoomDetailArgs(roomId = intent?.extras?.getString(EXTRA_ROOM_ID)!!)
|
|
||||||
} else {
|
|
||||||
intent?.extras?.getParcelable(EXTRA_ROOM_DETAIL_ARGS)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (roomDetailArgs == null) return
|
|
||||||
|
|
||||||
currentRoomId = roomDetailArgs.roomId
|
|
||||||
replaceFragment(R.id.roomDetailContainer, RoomDetailFragment::class.java, roomDetailArgs)
|
replaceFragment(R.id.roomDetailContainer, RoomDetailFragment::class.java, roomDetailArgs)
|
||||||
replaceFragment(R.id.roomDetailDrawerContainer, BreadcrumbsFragment::class.java)
|
replaceFragment(R.id.roomDetailDrawerContainer, BreadcrumbsFragment::class.java)
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,6 +54,7 @@ import im.vector.app.features.pin.PinMode
|
||||||
import im.vector.app.features.roomdirectory.RoomDirectoryActivity
|
import im.vector.app.features.roomdirectory.RoomDirectoryActivity
|
||||||
import im.vector.app.features.roomdirectory.createroom.CreateRoomActivity
|
import im.vector.app.features.roomdirectory.createroom.CreateRoomActivity
|
||||||
import im.vector.app.features.roomdirectory.roompreview.RoomPreviewActivity
|
import im.vector.app.features.roomdirectory.roompreview.RoomPreviewActivity
|
||||||
|
import im.vector.app.features.roomdirectory.roompreview.RoomPreviewData
|
||||||
import im.vector.app.features.roommemberprofile.RoomMemberProfileActivity
|
import im.vector.app.features.roommemberprofile.RoomMemberProfileActivity
|
||||||
import im.vector.app.features.roommemberprofile.RoomMemberProfileArgs
|
import im.vector.app.features.roommemberprofile.RoomMemberProfileArgs
|
||||||
import im.vector.app.features.roomprofile.RoomProfileActivity
|
import im.vector.app.features.roomprofile.RoomProfileActivity
|
||||||
|
@ -156,14 +157,6 @@ class DefaultNavigator @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun openNotJoinedRoom(context: Context, roomIdOrAlias: String?, eventId: String?, buildTask: Boolean) {
|
|
||||||
if (context is VectorBaseActivity) {
|
|
||||||
context.notImplemented("Open not joined room")
|
|
||||||
} else {
|
|
||||||
context.toast(R.string.not_implemented)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun openGroupDetail(groupId: String, context: Context, buildTask: Boolean) {
|
override fun openGroupDetail(groupId: String, context: Context, buildTask: Boolean) {
|
||||||
if (context is VectorBaseActivity) {
|
if (context is VectorBaseActivity) {
|
||||||
context.notImplemented("Open group detail")
|
context.notImplemented("Open group detail")
|
||||||
|
@ -186,7 +179,12 @@ class DefaultNavigator @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun openRoomPreview(context: Context, publicRoom: PublicRoom, roomDirectoryData: RoomDirectoryData) {
|
override fun openRoomPreview(context: Context, publicRoom: PublicRoom, roomDirectoryData: RoomDirectoryData) {
|
||||||
val intent = RoomPreviewActivity.getIntent(context, publicRoom, roomDirectoryData)
|
val intent = RoomPreviewActivity.newIntent(context, publicRoom, roomDirectoryData)
|
||||||
|
context.startActivity(intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun openRoomPreview(context: Context, roomPreviewData: RoomPreviewData) {
|
||||||
|
val intent = RoomPreviewActivity.newIntent(context, roomPreviewData)
|
||||||
context.startActivity(intent)
|
context.startActivity(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ import im.vector.app.features.home.room.detail.widget.WidgetRequestCodes
|
||||||
import im.vector.app.features.media.AttachmentData
|
import im.vector.app.features.media.AttachmentData
|
||||||
import im.vector.app.features.pin.PinActivity
|
import im.vector.app.features.pin.PinActivity
|
||||||
import im.vector.app.features.pin.PinMode
|
import im.vector.app.features.pin.PinMode
|
||||||
|
import im.vector.app.features.roomdirectory.roompreview.RoomPreviewData
|
||||||
import im.vector.app.features.settings.VectorSettingsActivity
|
import im.vector.app.features.settings.VectorSettingsActivity
|
||||||
import im.vector.app.features.share.SharedData
|
import im.vector.app.features.share.SharedData
|
||||||
import im.vector.app.features.terms.ReviewTermsActivity
|
import im.vector.app.features.terms.ReviewTermsActivity
|
||||||
|
@ -50,10 +51,10 @@ interface Navigator {
|
||||||
|
|
||||||
fun openRoomForSharingAndFinish(activity: Activity, roomId: String, sharedData: SharedData)
|
fun openRoomForSharingAndFinish(activity: Activity, roomId: String, sharedData: SharedData)
|
||||||
|
|
||||||
fun openNotJoinedRoom(context: Context, roomIdOrAlias: String?, eventId: String? = null, buildTask: Boolean = false)
|
|
||||||
|
|
||||||
fun openRoomPreview(context: Context, publicRoom: PublicRoom, roomDirectoryData: RoomDirectoryData)
|
fun openRoomPreview(context: Context, publicRoom: PublicRoom, roomDirectoryData: RoomDirectoryData)
|
||||||
|
|
||||||
|
fun openRoomPreview(context: Context, roomPreviewData: RoomPreviewData)
|
||||||
|
|
||||||
fun openCreateRoom(context: Context, initialName: String = "")
|
fun openCreateRoom(context: Context, initialName: String = "")
|
||||||
|
|
||||||
fun openCreateDirectRoom(context: Context)
|
fun openCreateDirectRoom(context: Context)
|
||||||
|
|
|
@ -18,13 +18,18 @@ package im.vector.app.features.permalink
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import im.vector.app.R
|
||||||
import im.vector.app.core.di.ActiveSessionHolder
|
import im.vector.app.core.di.ActiveSessionHolder
|
||||||
|
import im.vector.app.core.utils.toast
|
||||||
import im.vector.app.features.navigation.Navigator
|
import im.vector.app.features.navigation.Navigator
|
||||||
|
import im.vector.app.features.roomdirectory.roompreview.RoomPreviewData
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
import io.reactivex.schedulers.Schedulers
|
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.PermalinkData
|
||||||
import org.matrix.android.sdk.api.session.permalinks.PermalinkParser
|
import org.matrix.android.sdk.api.session.permalinks.PermalinkParser
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||||
import org.matrix.android.sdk.api.util.Optional
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
import org.matrix.android.sdk.rx.rx
|
import org.matrix.android.sdk.rx.rx
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -51,14 +56,37 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti
|
||||||
if (deepLink == null) {
|
if (deepLink == null) {
|
||||||
return Single.just(false)
|
return Single.just(false)
|
||||||
}
|
}
|
||||||
return when (val permalinkData = PermalinkParser.parse(deepLink)) {
|
return Single
|
||||||
|
.fromCallable {
|
||||||
|
PermalinkParser.parse(deepLink)
|
||||||
|
}
|
||||||
|
.subscribeOn(Schedulers.computation())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.flatMap { permalinkData ->
|
||||||
|
handlePermalink(permalinkData, context, navigationInterceptor, buildTask)
|
||||||
|
}
|
||||||
|
.onErrorReturnItem(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handlePermalink(
|
||||||
|
permalinkData: PermalinkData,
|
||||||
|
context: Context,
|
||||||
|
navigationInterceptor: NavigationInterceptor?,
|
||||||
|
buildTask: Boolean
|
||||||
|
): Single<Boolean> {
|
||||||
|
return when (permalinkData) {
|
||||||
is PermalinkData.RoomLink -> {
|
is PermalinkData.RoomLink -> {
|
||||||
permalinkData.getRoomId()
|
permalinkData.getRoomId()
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.map {
|
.map {
|
||||||
val roomId = it.getOrNull()
|
val roomId = it.getOrNull()
|
||||||
if (navigationInterceptor?.navToRoom(roomId, permalinkData.eventId) != true) {
|
if (navigationInterceptor?.navToRoom(roomId, permalinkData.eventId) != true) {
|
||||||
openRoom(context, roomId, permalinkData.eventId, buildTask)
|
openRoom(
|
||||||
|
context = context,
|
||||||
|
roomId = roomId,
|
||||||
|
permalinkData = permalinkData,
|
||||||
|
buildTask = buildTask
|
||||||
|
)
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -82,22 +110,55 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti
|
||||||
private fun PermalinkData.RoomLink.getRoomId(): Single<Optional<String>> {
|
private fun PermalinkData.RoomLink.getRoomId(): Single<Optional<String>> {
|
||||||
val session = activeSessionHolder.getSafeActiveSession()
|
val session = activeSessionHolder.getSafeActiveSession()
|
||||||
return if (isRoomAlias && session != null) {
|
return if (isRoomAlias && session != null) {
|
||||||
// At the moment we are not fetching on the server as we don't handle not join room
|
session.rx().getRoomIdByAlias(roomIdOrAlias, true).subscribeOn(Schedulers.io())
|
||||||
session.rx().getRoomIdByAlias(roomIdOrAlias, false).subscribeOn(Schedulers.io())
|
|
||||||
} else {
|
} else {
|
||||||
Single.just(Optional.from(roomIdOrAlias))
|
Single.just(Optional.from(roomIdOrAlias))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun PermalinkData.RoomLink.getRoomAliasOrNull(): String? {
|
||||||
|
return if (isRoomAlias) {
|
||||||
|
roomIdOrAlias
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open room either joined, or not
|
* Open room either joined, or not
|
||||||
*/
|
*/
|
||||||
private fun openRoom(context: Context, roomId: String?, eventId: String?, buildTask: Boolean) {
|
private fun openRoom(
|
||||||
|
context: Context,
|
||||||
|
roomId: String?,
|
||||||
|
permalinkData: PermalinkData.RoomLink,
|
||||||
|
buildTask: Boolean
|
||||||
|
) {
|
||||||
val session = activeSessionHolder.getSafeActiveSession() ?: return
|
val session = activeSessionHolder.getSafeActiveSession() ?: return
|
||||||
return if (roomId != null && session.getRoom(roomId) != null) {
|
if (roomId == null) {
|
||||||
navigator.openRoom(context, roomId, eventId, buildTask)
|
context.toast(R.string.room_error_not_found)
|
||||||
} else {
|
return
|
||||||
navigator.openNotJoinedRoom(context, roomId, eventId, buildTask)
|
}
|
||||||
|
val roomSummary = session.getRoomSummary(roomId)
|
||||||
|
val membership = roomSummary?.membership
|
||||||
|
val eventId = permalinkData.eventId
|
||||||
|
val roomAlias = permalinkData.getRoomAliasOrNull()
|
||||||
|
return when {
|
||||||
|
membership == Membership.BAN -> context.toast(R.string.error_opening_banned_room)
|
||||||
|
membership?.isActive().orFalse() -> {
|
||||||
|
navigator.openRoom(context, roomId, eventId, buildTask)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
val roomPreviewData = RoomPreviewData(
|
||||||
|
roomId = roomId,
|
||||||
|
eventId = eventId,
|
||||||
|
roomAlias = roomAlias ?: roomSummary?.canonicalAlias,
|
||||||
|
roomName = roomSummary?.displayName,
|
||||||
|
avatarUrl = roomSummary?.avatarUrl,
|
||||||
|
buildTask = buildTask,
|
||||||
|
homeServers = permalinkData.viaParameters
|
||||||
|
)
|
||||||
|
navigator.openRoomPreview(context, roomPreviewData)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,8 +28,10 @@ import im.vector.app.core.epoxy.noResultItem
|
||||||
import im.vector.app.core.error.ErrorFormatter
|
import im.vector.app.core.error.ErrorFormatter
|
||||||
import im.vector.app.core.resources.StringProvider
|
import im.vector.app.core.resources.StringProvider
|
||||||
import im.vector.app.features.home.AvatarRenderer
|
import im.vector.app.features.home.AvatarRenderer
|
||||||
|
import org.matrix.android.sdk.api.MatrixPatterns
|
||||||
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
|
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
|
||||||
import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoom
|
import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoom
|
||||||
|
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 javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -42,8 +44,10 @@ class PublicRoomsController @Inject constructor(private val stringProvider: Stri
|
||||||
override fun buildModels(viewState: PublicRoomsViewState) {
|
override fun buildModels(viewState: PublicRoomsViewState) {
|
||||||
val publicRooms = viewState.publicRooms
|
val publicRooms = viewState.publicRooms
|
||||||
|
|
||||||
if (publicRooms.isEmpty()
|
val unknownRoomItem = viewState.buildUnknownRoomIfNeeded()
|
||||||
&& viewState.asyncPublicRoomsRequest is Success) {
|
|
||||||
|
val noResult = publicRooms.isEmpty() && viewState.asyncPublicRoomsRequest is Success && unknownRoomItem == null
|
||||||
|
if (noResult) {
|
||||||
// No result
|
// No result
|
||||||
noResultItem {
|
noResultItem {
|
||||||
id("noResult")
|
id("noResult")
|
||||||
|
@ -54,6 +58,8 @@ class PublicRoomsController @Inject constructor(private val stringProvider: Stri
|
||||||
buildPublicRoom(it, viewState)
|
buildPublicRoom(it, viewState)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unknownRoomItem?.addTo(this)
|
||||||
|
|
||||||
if ((viewState.hasMore && viewState.asyncPublicRoomsRequest is Success)
|
if ((viewState.hasMore && viewState.asyncPublicRoomsRequest is Success)
|
||||||
|| viewState.asyncPublicRoomsRequest is Incomplete) {
|
|| viewState.asyncPublicRoomsRequest is Incomplete) {
|
||||||
loadingItem {
|
loadingItem {
|
||||||
|
@ -109,7 +115,29 @@ class PublicRoomsController @Inject constructor(private val stringProvider: Stri
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun PublicRoomsViewState.buildUnknownRoomIfNeeded(): UnknownRoomItem? {
|
||||||
|
val roomIdOrAlias = currentFilter.trim()
|
||||||
|
val isAlias = MatrixPatterns.isRoomAlias(roomIdOrAlias) && !publicRooms.any { it.canonicalAlias == roomIdOrAlias }
|
||||||
|
val isRoomId = !isAlias && MatrixPatterns.isRoomId(roomIdOrAlias) && !publicRooms.any { it.roomId == roomIdOrAlias }
|
||||||
|
val roomItem = when {
|
||||||
|
isAlias -> MatrixItem.RoomAliasItem(roomIdOrAlias, roomIdOrAlias)
|
||||||
|
isRoomId -> MatrixItem.RoomItem(roomIdOrAlias)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
return roomItem?.let {
|
||||||
|
UnknownRoomItem_().apply {
|
||||||
|
id(roomIdOrAlias)
|
||||||
|
matrixItem(it)
|
||||||
|
avatarRenderer(this@PublicRoomsController.avatarRenderer)
|
||||||
|
globalListener {
|
||||||
|
callback?.onUnknownRoomClicked(roomIdOrAlias)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
interface Callback {
|
interface Callback {
|
||||||
|
fun onUnknownRoomClicked(roomIdOrAlias: String)
|
||||||
fun onPublicRoomClicked(publicRoom: PublicRoom, joinState: JoinState)
|
fun onPublicRoomClicked(publicRoom: PublicRoom, joinState: JoinState)
|
||||||
fun onPublicRoomJoin(publicRoom: PublicRoom)
|
fun onPublicRoomJoin(publicRoom: PublicRoom)
|
||||||
fun loadMore()
|
fun loadMore()
|
||||||
|
|
|
@ -29,9 +29,13 @@ import im.vector.app.core.extensions.configureWith
|
||||||
import im.vector.app.core.extensions.exhaustive
|
import im.vector.app.core.extensions.exhaustive
|
||||||
import im.vector.app.core.extensions.trackItemsVisibilityChange
|
import im.vector.app.core.extensions.trackItemsVisibilityChange
|
||||||
import im.vector.app.core.platform.VectorBaseFragment
|
import im.vector.app.core.platform.VectorBaseFragment
|
||||||
import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoom
|
import im.vector.app.core.utils.toast
|
||||||
|
import im.vector.app.features.permalink.NavigationInterceptor
|
||||||
|
import im.vector.app.features.permalink.PermalinkHandler
|
||||||
import io.reactivex.rxkotlin.subscribeBy
|
import io.reactivex.rxkotlin.subscribeBy
|
||||||
import kotlinx.android.synthetic.main.fragment_public_rooms.*
|
import kotlinx.android.synthetic.main.fragment_public_rooms.*
|
||||||
|
import org.matrix.android.sdk.api.session.Session
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoom
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -41,7 +45,9 @@ import javax.inject.Inject
|
||||||
* - When filtering more (when entering new chars), we could filter on result we already have, during the new server request, to avoid empty screen effect
|
* - When filtering more (when entering new chars), we could filter on result we already have, during the new server request, to avoid empty screen effect
|
||||||
*/
|
*/
|
||||||
class PublicRoomsFragment @Inject constructor(
|
class PublicRoomsFragment @Inject constructor(
|
||||||
private val publicRoomsController: PublicRoomsController
|
private val publicRoomsController: PublicRoomsController,
|
||||||
|
private val permalinkHandler: PermalinkHandler,
|
||||||
|
private val session: Session
|
||||||
) : VectorBaseFragment(), PublicRoomsController.Callback {
|
) : VectorBaseFragment(), PublicRoomsController.Callback {
|
||||||
|
|
||||||
private val viewModel: RoomDirectoryViewModel by activityViewModel()
|
private val viewModel: RoomDirectoryViewModel by activityViewModel()
|
||||||
|
@ -112,6 +118,23 @@ class PublicRoomsFragment @Inject constructor(
|
||||||
publicRoomsController.callback = this
|
publicRoomsController.callback = this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onUnknownRoomClicked(roomIdOrAlias: String) {
|
||||||
|
val permalink = session.permalinkService().createPermalink(roomIdOrAlias)
|
||||||
|
permalinkHandler
|
||||||
|
.launch(requireContext(), permalink, object : NavigationInterceptor {
|
||||||
|
override fun navToRoom(roomId: String?, eventId: String?): Boolean {
|
||||||
|
requireActivity().finish()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.subscribe { isSuccessful ->
|
||||||
|
if (!isSuccessful) {
|
||||||
|
requireContext().toast(R.string.room_error_not_found)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.disposeOnDestroyView()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onPublicRoomClicked(publicRoom: PublicRoom, joinState: JoinState) {
|
override fun onPublicRoomClicked(publicRoom: PublicRoom, joinState: JoinState) {
|
||||||
Timber.v("PublicRoomClicked: $publicRoom")
|
Timber.v("PublicRoomClicked: $publicRoom")
|
||||||
withState(viewModel) { state ->
|
withState(viewModel) { state ->
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* 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.roomdirectory
|
||||||
|
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.TextView
|
||||||
|
import com.airbnb.epoxy.EpoxyAttribute
|
||||||
|
import com.airbnb.epoxy.EpoxyModelClass
|
||||||
|
import im.vector.app.R
|
||||||
|
import im.vector.app.core.epoxy.VectorEpoxyHolder
|
||||||
|
import im.vector.app.core.epoxy.VectorEpoxyModel
|
||||||
|
import im.vector.app.features.home.AvatarRenderer
|
||||||
|
import org.matrix.android.sdk.api.util.MatrixItem
|
||||||
|
|
||||||
|
@EpoxyModelClass(layout = R.layout.item_unknown_room)
|
||||||
|
abstract class UnknownRoomItem : VectorEpoxyModel<UnknownRoomItem.Holder>() {
|
||||||
|
|
||||||
|
@EpoxyAttribute
|
||||||
|
lateinit var avatarRenderer: AvatarRenderer
|
||||||
|
|
||||||
|
@EpoxyAttribute
|
||||||
|
lateinit var matrixItem: MatrixItem
|
||||||
|
|
||||||
|
@EpoxyAttribute
|
||||||
|
var globalListener: (() -> Unit)? = null
|
||||||
|
|
||||||
|
override fun bind(holder: Holder) {
|
||||||
|
super.bind(holder)
|
||||||
|
holder.rootView.setOnClickListener { globalListener?.invoke() }
|
||||||
|
avatarRenderer.render(matrixItem, holder.avatarView)
|
||||||
|
holder.nameView.text = matrixItem.displayName
|
||||||
|
}
|
||||||
|
|
||||||
|
class Holder : VectorEpoxyHolder() {
|
||||||
|
val rootView by bind<ViewGroup>(R.id.itemUnknownRoomLayout)
|
||||||
|
val avatarView by bind<ImageView>(R.id.itemUnknownRoomAvatar)
|
||||||
|
val nameView by bind<TextView>(R.id.itemUnknownRoomName)
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,20 +24,23 @@ import im.vector.app.R
|
||||||
import im.vector.app.core.extensions.addFragment
|
import im.vector.app.core.extensions.addFragment
|
||||||
import im.vector.app.core.platform.ToolbarConfigurable
|
import im.vector.app.core.platform.ToolbarConfigurable
|
||||||
import im.vector.app.core.platform.VectorBaseActivity
|
import im.vector.app.core.platform.VectorBaseActivity
|
||||||
|
import kotlinx.android.parcel.Parcelize
|
||||||
import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoom
|
import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoom
|
||||||
import org.matrix.android.sdk.api.session.room.model.thirdparty.RoomDirectoryData
|
import org.matrix.android.sdk.api.session.room.model.thirdparty.RoomDirectoryData
|
||||||
import org.matrix.android.sdk.api.util.MatrixItem
|
import org.matrix.android.sdk.api.util.MatrixItem
|
||||||
import kotlinx.android.parcel.Parcelize
|
import timber.log.Timber
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class RoomPreviewData(
|
data class RoomPreviewData(
|
||||||
val roomId: String,
|
val roomId: String,
|
||||||
val roomName: String?,
|
val eventId: String? = null,
|
||||||
val roomAlias: String?,
|
val roomName: String? = null,
|
||||||
val topic: String?,
|
val roomAlias: String? = null,
|
||||||
val worldReadable: Boolean,
|
val topic: String? = null,
|
||||||
val avatarUrl: String?,
|
val worldReadable: Boolean = false,
|
||||||
val homeServer: String?
|
val avatarUrl: String? = null,
|
||||||
|
val homeServers: List<String> = emptyList(),
|
||||||
|
val buildTask: Boolean = false
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
val matrixItem: MatrixItem
|
val matrixItem: MatrixItem
|
||||||
get() = MatrixItem.RoomItem(roomId, roomName ?: roomAlias, avatarUrl)
|
get() = MatrixItem.RoomItem(roomId, roomName ?: roomAlias, avatarUrl)
|
||||||
|
@ -48,19 +51,24 @@ class RoomPreviewActivity : VectorBaseActivity(), ToolbarConfigurable {
|
||||||
companion object {
|
companion object {
|
||||||
private const val ARG = "ARG"
|
private const val ARG = "ARG"
|
||||||
|
|
||||||
fun getIntent(context: Context, publicRoom: PublicRoom, roomDirectoryData: RoomDirectoryData): Intent {
|
fun newIntent(context: Context, roomPreviewData: RoomPreviewData): Intent {
|
||||||
return Intent(context, RoomPreviewActivity::class.java).apply {
|
return Intent(context, RoomPreviewActivity::class.java).apply {
|
||||||
putExtra(ARG, RoomPreviewData(
|
putExtra(ARG, roomPreviewData)
|
||||||
roomId = publicRoom.roomId,
|
|
||||||
roomName = publicRoom.name,
|
|
||||||
roomAlias = publicRoom.getPrimaryAlias(),
|
|
||||||
topic = publicRoom.topic,
|
|
||||||
worldReadable = publicRoom.worldReadable,
|
|
||||||
avatarUrl = publicRoom.avatarUrl,
|
|
||||||
homeServer = roomDirectoryData.homeServer
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun newIntent(context: Context, publicRoom: PublicRoom, roomDirectoryData: RoomDirectoryData): Intent {
|
||||||
|
val roomPreviewData = RoomPreviewData(
|
||||||
|
roomId = publicRoom.roomId,
|
||||||
|
roomName = publicRoom.name,
|
||||||
|
roomAlias = publicRoom.getPrimaryAlias(),
|
||||||
|
topic = publicRoom.topic,
|
||||||
|
worldReadable = publicRoom.worldReadable,
|
||||||
|
avatarUrl = publicRoom.avatarUrl,
|
||||||
|
homeServers = listOfNotNull(roomDirectoryData.homeServer)
|
||||||
|
)
|
||||||
|
return newIntent(context, roomPreviewData)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getLayoutRes() = R.layout.activity_simple
|
override fun getLayoutRes() = R.layout.activity_simple
|
||||||
|
@ -76,6 +84,7 @@ class RoomPreviewActivity : VectorBaseActivity(), ToolbarConfigurable {
|
||||||
if (args?.worldReadable == true) {
|
if (args?.worldReadable == true) {
|
||||||
// TODO Room preview: Note: M does not recommend to use /events anymore, so for now we just display the room preview
|
// TODO Room preview: Note: M does not recommend to use /events anymore, so for now we just display the room preview
|
||||||
// TODO the same way if it was not world readable
|
// TODO the same way if it was not world readable
|
||||||
|
Timber.d("just display the room preview the same way if it was not world readable")
|
||||||
addFragment(R.id.simpleFragmentContainer, RoomPreviewNoPreviewFragment::class.java, args)
|
addFragment(R.id.simpleFragmentContainer, RoomPreviewNoPreviewFragment::class.java, args)
|
||||||
} else {
|
} else {
|
||||||
addFragment(R.id.simpleFragmentContainer, RoomPreviewNoPreviewFragment::class.java, args)
|
addFragment(R.id.simpleFragmentContainer, RoomPreviewNoPreviewFragment::class.java, args)
|
||||||
|
|
|
@ -48,13 +48,15 @@ class RoomPreviewNoPreviewFragment @Inject constructor(
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
setupToolbar(roomPreviewNoPreviewToolbar)
|
setupToolbar(roomPreviewNoPreviewToolbar)
|
||||||
|
val titleText = roomPreviewData.roomName ?: roomPreviewData.roomAlias ?: roomPreviewData.roomId
|
||||||
|
|
||||||
// Toolbar
|
// Toolbar
|
||||||
avatarRenderer.render(roomPreviewData.matrixItem, roomPreviewNoPreviewToolbarAvatar)
|
avatarRenderer.render(roomPreviewData.matrixItem, roomPreviewNoPreviewToolbarAvatar)
|
||||||
roomPreviewNoPreviewToolbarTitle.text = roomPreviewData.roomName ?: roomPreviewData.roomAlias
|
roomPreviewNoPreviewToolbarTitle.text = titleText
|
||||||
|
|
||||||
// Screen
|
// Screen
|
||||||
avatarRenderer.render(roomPreviewData.matrixItem, roomPreviewNoPreviewAvatar)
|
avatarRenderer.render(roomPreviewData.matrixItem, roomPreviewNoPreviewAvatar)
|
||||||
roomPreviewNoPreviewName.text = roomPreviewData.roomName ?: roomPreviewData.roomAlias
|
roomPreviewNoPreviewName.text = titleText
|
||||||
roomPreviewNoPreviewTopic.setTextOrHide(roomPreviewData.topic)
|
roomPreviewNoPreviewTopic.setTextOrHide(roomPreviewData.topic)
|
||||||
|
|
||||||
if (roomPreviewData.worldReadable) {
|
if (roomPreviewData.worldReadable) {
|
||||||
|
@ -98,7 +100,7 @@ class RoomPreviewNoPreviewFragment @Inject constructor(
|
||||||
// Quit this screen
|
// Quit this screen
|
||||||
requireActivity().finish()
|
requireActivity().finish()
|
||||||
// Open room
|
// Open room
|
||||||
navigator.openRoom(requireActivity(), roomPreviewData.roomId)
|
navigator.openRoom(requireActivity(), roomPreviewData.roomId, roomPreviewData.eventId, roomPreviewData.buildTask)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,10 +106,7 @@ class RoomPreviewViewModel @AssistedInject constructor(@Assisted private val ini
|
||||||
Timber.w("Try to join an already joining room. Should not happen")
|
Timber.w("Try to join an already joining room. Should not happen")
|
||||||
return@withState
|
return@withState
|
||||||
}
|
}
|
||||||
val viaServers = state.homeServer?.let {
|
session.joinRoom(state.roomId, viaServers = state.homeServers, callback = object : MatrixCallback<Unit> {
|
||||||
listOf(it)
|
|
||||||
} ?: emptyList()
|
|
||||||
session.joinRoom(state.roomId, viaServers = viaServers, callback = object : MatrixCallback<Unit> {
|
|
||||||
override fun onSuccess(data: Unit) {
|
override fun onSuccess(data: Unit) {
|
||||||
// We do not update the joiningRoomsIds here, because, the room is not joined yet regarding the sync data.
|
// We do not update the joiningRoomsIds here, because, the room is not joined yet regarding the sync data.
|
||||||
// Instead, we wait for the room to be joined
|
// Instead, we wait for the room to be joined
|
||||||
|
|
|
@ -24,10 +24,9 @@ data class RoomPreviewViewState(
|
||||||
val roomId: String = "",
|
val roomId: String = "",
|
||||||
val roomAlias: String? = null,
|
val roomAlias: String? = null,
|
||||||
/**
|
/**
|
||||||
* The server name (might be null)
|
* Can be empty when the server is the current user's home server.
|
||||||
* Set null when the server is the current user's home server.
|
|
||||||
*/
|
*/
|
||||||
val homeServer: String? = null,
|
val homeServers: List<String> = emptyList(),
|
||||||
// Current state of the room in preview
|
// Current state of the room in preview
|
||||||
val roomJoinState: JoinState = JoinState.NOT_JOINED,
|
val roomJoinState: JoinState = JoinState.NOT_JOINED,
|
||||||
// Last error of join room request
|
// Last error of join room request
|
||||||
|
@ -37,6 +36,6 @@ data class RoomPreviewViewState(
|
||||||
constructor(args: RoomPreviewData) : this(
|
constructor(args: RoomPreviewData) : this(
|
||||||
roomId = args.roomId,
|
roomId = args.roomId,
|
||||||
roomAlias = args.roomAlias,
|
roomAlias = args.roomAlias,
|
||||||
homeServer = args.homeServer
|
homeServers = args.homeServers
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
package im.vector.app.features.settings
|
package im.vector.app.features.settings
|
||||||
|
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.preference.VectorSwitchPreference
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class VectorSettingsLabsFragment @Inject constructor(
|
class VectorSettingsLabsFragment @Inject constructor(
|
||||||
|
@ -28,103 +27,6 @@ class VectorSettingsLabsFragment @Inject constructor(
|
||||||
override val preferenceXmlRes = R.xml.vector_settings_labs
|
override val preferenceXmlRes = R.xml.vector_settings_labs
|
||||||
|
|
||||||
override fun bindPref() {
|
override fun bindPref() {
|
||||||
// Lab
|
// Nothing to do
|
||||||
|
|
||||||
findPreference<VectorSwitchPreference>(VectorPreferences.SETTINGS_LABS_ALLOW_EXTENDED_LOGS)?.let {
|
|
||||||
it.isChecked = vectorPreferences.labAllowedExtendedLogging()
|
|
||||||
}
|
|
||||||
|
|
||||||
findPreference<VectorSwitchPreference>(VectorPreferences.SETTINGS_LABS_UNREAD_NOTIFICATIONS_AS_TAB)?.let {
|
|
||||||
it.isChecked = vectorPreferences.labAddNotificationTab()
|
|
||||||
}
|
|
||||||
|
|
||||||
// val useCryptoPref = findPreference(VectorPreferences.SETTINGS_ROOM_SETTINGS_LABS_END_TO_END_PREFERENCE_KEY) as SwitchPreference
|
|
||||||
// val cryptoIsEnabledPref = findPreference(VectorPreferences.SETTINGS_ROOM_SETTINGS_LABS_END_TO_END_IS_ACTIVE_PREFERENCE_KEY)
|
|
||||||
|
|
||||||
if (session.cryptoService().isCryptoEnabled()) {
|
|
||||||
// mLabsCategory.removePreference(useCryptoPref)
|
|
||||||
//
|
|
||||||
// cryptoIsEnabledPref.isEnabled = false
|
|
||||||
} else {
|
|
||||||
// mLabsCategory.removePreference(cryptoIsEnabledPref)
|
|
||||||
//
|
|
||||||
// useCryptoPref.isChecked = false
|
|
||||||
//
|
|
||||||
// useCryptoPref.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValueAsVoid ->
|
|
||||||
// if (TextUtils.isEmpty(mSession.sessionParams.deviceId)) {
|
|
||||||
// activity?.let { activity ->
|
|
||||||
// AlertDialog.Builder(activity)
|
|
||||||
// .setMessage(R.string.room_settings_labs_end_to_end_warnings)
|
|
||||||
// .setPositiveButton(R.string.logout) { _, _ ->
|
|
||||||
// notImplemented()
|
|
||||||
// // TODO CommonActivityUtils.logout(activity)
|
|
||||||
// }
|
|
||||||
// .setNegativeButton(R.string.cancel) { _, _ ->
|
|
||||||
// useCryptoPref.isChecked = false
|
|
||||||
// }
|
|
||||||
// .setOnCancelListener {
|
|
||||||
// useCryptoPref.isChecked = false
|
|
||||||
// }
|
|
||||||
// .show()
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// val newValue = newValueAsVoid as Boolean
|
|
||||||
//
|
|
||||||
// if (mSession.isCryptoEnabled() != newValue) {
|
|
||||||
// notImplemented()
|
|
||||||
// /* TODO
|
|
||||||
// displayLoadingView()
|
|
||||||
//
|
|
||||||
// session.enableCrypto(newValue, object : MatrixCallback<Unit> {
|
|
||||||
// private fun refresh() {
|
|
||||||
// activity?.runOnUiThread {
|
|
||||||
// hideLoadingView()
|
|
||||||
// useCryptoPref.isChecked = session.isCryptoEnabled
|
|
||||||
//
|
|
||||||
// if (session.isCryptoEnabled) {
|
|
||||||
// mLabsCategory.removePreference(useCryptoPref)
|
|
||||||
// mLabsCategory.addPreference(cryptoIsEnabledPref)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// override fun onSuccess(info: Void?) {
|
|
||||||
// useCryptoPref.isEnabled = false
|
|
||||||
// refresh()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// override fun onNetworkError(e: Exception) {
|
|
||||||
// useCryptoPref.isChecked = false
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// override fun onMatrixError(e: MatrixError) {
|
|
||||||
// useCryptoPref.isChecked = false
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// override fun onUnexpectedError(e: Exception) {
|
|
||||||
// useCryptoPref.isChecked = false
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// */
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// true
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
// SaveMode Management
|
|
||||||
// findPreference(VectorPreferences.SETTINGS_DATA_SAVE_MODE_PREFERENCE_KEY)
|
|
||||||
// .onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
|
|
||||||
// notImplemented()
|
|
||||||
// /* TODO
|
|
||||||
// val sessions = Matrix.getMXSessions(activity)
|
|
||||||
// for (session in sessions) {
|
|
||||||
// session.setUseDataSaveMode(newValue as Boolean)
|
|
||||||
// }
|
|
||||||
// */
|
|
||||||
//
|
|
||||||
// true
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,8 @@ class VectorSettingsDevicesFragment @Inject constructor(
|
||||||
) : VectorBaseFragment(), DevicesController.Callback {
|
) : VectorBaseFragment(), DevicesController.Callback {
|
||||||
|
|
||||||
// used to avoid requesting to enter the password for each deletion
|
// used to avoid requesting to enter the password for each deletion
|
||||||
private var mAccountPassword: String = ""
|
// Note: Sonar does not like to use password for member name.
|
||||||
|
private var mAccountPass: String = ""
|
||||||
|
|
||||||
override fun getLayoutResId() = R.layout.fragment_generic_recycler
|
override fun getLayoutResId() = R.layout.fragment_generic_recycler
|
||||||
|
|
||||||
|
@ -91,7 +92,7 @@ class VectorSettingsDevicesFragment @Inject constructor(
|
||||||
super.showFailure(throwable)
|
super.showFailure(throwable)
|
||||||
|
|
||||||
// Password is maybe not good, for safety measure, reset it here
|
// Password is maybe not good, for safety measure, reset it here
|
||||||
mAccountPassword = ""
|
mAccountPass = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
|
@ -153,12 +154,12 @@ class VectorSettingsDevicesFragment @Inject constructor(
|
||||||
* Show a dialog to ask for user password, or use a previously entered password.
|
* Show a dialog to ask for user password, or use a previously entered password.
|
||||||
*/
|
*/
|
||||||
private fun maybeShowDeleteDeviceWithPasswordDialog() {
|
private fun maybeShowDeleteDeviceWithPasswordDialog() {
|
||||||
if (mAccountPassword.isNotEmpty()) {
|
if (mAccountPass.isNotEmpty()) {
|
||||||
viewModel.handle(DevicesAction.Password(mAccountPassword))
|
viewModel.handle(DevicesAction.Password(mAccountPass))
|
||||||
} else {
|
} else {
|
||||||
PromptPasswordDialog().show(requireActivity()) { password ->
|
PromptPasswordDialog().show(requireActivity()) { password ->
|
||||||
mAccountPassword = password
|
mAccountPass = password
|
||||||
viewModel.handle(DevicesAction.Password(mAccountPassword))
|
viewModel.handle(DevicesAction.Password(mAccountPass))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
52
vector/src/main/res/layout/item_unknown_room.xml
Normal file
52
vector/src/main/res/layout/item_unknown_room.xml
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/itemUnknownRoomLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?riotx_background"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:foreground="?attr/selectableItemBackground"
|
||||||
|
android:minHeight="64dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/itemUnknownRoomAvatar"
|
||||||
|
android:layout_width="56dp"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/itemPublicRoomBottomSeparator"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:src="@tools:sample/avatars" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/itemUnknownRoomName"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="17dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="2"
|
||||||
|
android:textColor="?riotx_text_primary"
|
||||||
|
android:textSize="15sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/itemUnknownRoomAvatar"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintVertical_chainStyle="packed"
|
||||||
|
tools:text="@sample/matrix.json/data/roomName" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/itemPublicRoomBottomSeparator"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:background="?riotx_header_panel_border_mobile"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -2539,4 +2539,6 @@
|
||||||
<string name="settings_security_pin_code_title">Enable PIN</string>
|
<string name="settings_security_pin_code_title">Enable PIN</string>
|
||||||
<string name="settings_security_pin_code_summary">If you want to reset your PIN, tap Forgot PIN to logout and reset.</string>
|
<string name="settings_security_pin_code_summary">If you want to reset your PIN, tap Forgot PIN to logout and reset.</string>
|
||||||
<string name="auth_pin_confirm_to_disable_title">Confirm PIN to disable PIN</string>
|
<string name="auth_pin_confirm_to_disable_title">Confirm PIN to disable PIN</string>
|
||||||
|
<string name="error_opening_banned_room">Can\'t open a room where you are banned from.</string>
|
||||||
|
<string name="room_error_not_found">Can\'t find this room. Make sure it exists.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Reference in a new issue