mirror of
https://github.com/element-hq/element-android
synced 2024-11-24 02:15:35 +03:00
Merge branch 'develop' into feature/bma/fix_cancel
This commit is contained in:
commit
5237eb0638
154 changed files with 3636 additions and 758 deletions
|
@ -31,6 +31,7 @@
|
|||
<w>ssss</w>
|
||||
<w>sygnal</w>
|
||||
<w>threepid</w>
|
||||
<w>unpublish</w>
|
||||
<w>unwedging</w>
|
||||
</words>
|
||||
</dictionary>
|
||||
|
|
13
CHANGES.md
13
CHANGES.md
|
@ -2,13 +2,17 @@ Changes in Element 1.0.12 (2020-XX-XX)
|
|||
===================================================
|
||||
|
||||
Features ✨:
|
||||
-
|
||||
- Add room aliases management, and room directory visibility management in a dedicated screen (#1579, #2428)
|
||||
- Room setting: update join rules and guest access (#2442)
|
||||
|
||||
Improvements 🙌:
|
||||
-
|
||||
- Add Setting Item to Change PIN (#2462)
|
||||
- Improve room history visibility setting UX (#1579)
|
||||
|
||||
Bugfix 🐛:
|
||||
- Fix cancellation of sending event (#2438)
|
||||
- Double bottomsheet effect after verify with passphrase
|
||||
- EditText cursor jumps to the start while typing fast (#2469)
|
||||
|
||||
Translations 🗣:
|
||||
-
|
||||
|
@ -17,13 +21,14 @@ SDK API changes ⚠️:
|
|||
-
|
||||
|
||||
Build 🧱:
|
||||
-
|
||||
- Upgrade some dependencies and Kotlin version
|
||||
- Use fragment-ktx and preference-ktx dependencies (fix lint issue KtxExtensionAvailable)
|
||||
|
||||
Test:
|
||||
-
|
||||
|
||||
Other changes:
|
||||
-
|
||||
- Remove "Status.im" theme #2424
|
||||
|
||||
Changes in Element 1.0.11 (2020-11-27)
|
||||
===================================================
|
||||
|
|
|
@ -66,7 +66,6 @@ dependencies {
|
|||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||
implementation 'androidx.core:core-ktx:1.3.2'
|
||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||
implementation "androidx.fragment:fragment:1.3.0-beta01"
|
||||
implementation "androidx.recyclerview:recyclerview:1.1.0"
|
||||
|
||||
implementation 'com.google.android.material:material:1.2.1'
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
buildscript {
|
||||
// Ref: https://kotlinlang.org/releases.html
|
||||
ext.kotlin_version = '1.4.10'
|
||||
ext.kotlin_coroutines_version = "1.3.9"
|
||||
ext.kotlin_version = '1.4.20'
|
||||
ext.kotlin_coroutines_version = "1.4.1"
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
|
@ -12,7 +12,7 @@ buildscript {
|
|||
}
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.1.0'
|
||||
classpath 'com.android.tools.build:gradle:4.1.1'
|
||||
classpath 'com.google.gms:google-services:4.3.4'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.7.1'
|
||||
|
|
|
@ -36,9 +36,9 @@ android {
|
|||
dependencies {
|
||||
implementation project(":matrix-sdk-android")
|
||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||
implementation "androidx.fragment:fragment:1.3.0-beta01"
|
||||
implementation 'io.reactivex.rxjava2:rxkotlin:2.3.0'
|
||||
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
|
||||
|
||||
// Paging
|
||||
implementation "androidx.paging:paging-runtime-ktx:2.1.2"
|
||||
|
||||
|
|
|
@ -21,34 +21,36 @@ import org.matrix.android.sdk.api.util.Cancelable
|
|||
import io.reactivex.Completable
|
||||
import io.reactivex.Single
|
||||
|
||||
fun <T> singleBuilder(builder: (callback: MatrixCallback<T>) -> Cancelable): Single<T> = Single.create {
|
||||
val callback: MatrixCallback<T> = object : MatrixCallback<T> {
|
||||
fun <T> singleBuilder(builder: (MatrixCallback<T>) -> Cancelable): Single<T> = Single.create { emitter ->
|
||||
val callback = object : MatrixCallback<T> {
|
||||
override fun onSuccess(data: T) {
|
||||
it.onSuccess(data)
|
||||
// Add `!!` to fix the warning:
|
||||
// "Type mismatch: type parameter with nullable bounds is used T is used where T was expected. This warning will become an error soon"
|
||||
emitter.onSuccess(data!!)
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
it.tryOnError(failure)
|
||||
emitter.tryOnError(failure)
|
||||
}
|
||||
}
|
||||
val cancelable = builder(callback)
|
||||
it.setCancellable {
|
||||
emitter.setCancellable {
|
||||
cancelable.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> completableBuilder(builder: (callback: MatrixCallback<T>) -> Cancelable): Completable = Completable.create {
|
||||
val callback: MatrixCallback<T> = object : MatrixCallback<T> {
|
||||
fun <T> completableBuilder(builder: (MatrixCallback<T>) -> Cancelable): Completable = Completable.create { emitter ->
|
||||
val callback = object : MatrixCallback<T> {
|
||||
override fun onSuccess(data: T) {
|
||||
it.onComplete()
|
||||
emitter.onComplete()
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
it.tryOnError(failure)
|
||||
emitter.tryOnError(failure)
|
||||
}
|
||||
}
|
||||
val cancelable = builder(callback)
|
||||
it.setCancellable {
|
||||
emitter.setCancellable {
|
||||
cancelable.cancel()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,6 +35,8 @@ import org.matrix.android.sdk.api.util.toOptional
|
|||
import io.reactivex.Completable
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.Single
|
||||
import org.matrix.android.sdk.api.session.room.model.GuestAccess
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
|
||||
|
||||
class RxRoom(private val room: Room) {
|
||||
|
||||
|
@ -127,18 +129,14 @@ class RxRoom(private val room: Room) {
|
|||
room.updateName(name, it)
|
||||
}
|
||||
|
||||
fun addRoomAlias(alias: String): Completable = completableBuilder<Unit> {
|
||||
room.addRoomAlias(alias, it)
|
||||
}
|
||||
|
||||
fun updateCanonicalAlias(alias: String): Completable = completableBuilder<Unit> {
|
||||
room.updateCanonicalAlias(alias, it)
|
||||
}
|
||||
|
||||
fun updateHistoryReadability(readability: RoomHistoryVisibility): Completable = completableBuilder<Unit> {
|
||||
room.updateHistoryReadability(readability, it)
|
||||
}
|
||||
|
||||
fun updateJoinRule(joinRules: RoomJoinRules?, guestAccess: GuestAccess?): Completable = completableBuilder<Unit> {
|
||||
room.updateJoinRule(joinRules, guestAccess, it)
|
||||
}
|
||||
|
||||
fun updateAvatar(avatarUri: Uri, fileName: String): Completable = completableBuilder<Unit> {
|
||||
room.updateAvatar(avatarUri, fileName, it)
|
||||
}
|
||||
|
|
|
@ -125,7 +125,6 @@ dependencies {
|
|||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version"
|
||||
|
||||
implementation "androidx.appcompat:appcompat:1.2.0"
|
||||
implementation "androidx.fragment:fragment:1.3.0-beta01"
|
||||
implementation "androidx.core:core-ktx:1.3.2"
|
||||
|
||||
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
|
||||
|
@ -146,7 +145,7 @@ dependencies {
|
|||
implementation "ru.noties.markwon:core:$markwon_version"
|
||||
|
||||
// Image
|
||||
implementation 'androidx.exifinterface:exifinterface:1.3.0'
|
||||
implementation 'androidx.exifinterface:exifinterface:1.3.1'
|
||||
|
||||
// Database
|
||||
implementation 'com.github.Zhuinden:realm-monarchy:0.7.1'
|
||||
|
|
|
@ -49,6 +49,12 @@ object EventType {
|
|||
const val STATE_ROOM_JOIN_RULES = "m.room.join_rules"
|
||||
const val STATE_ROOM_GUEST_ACCESS = "m.room.guest_access"
|
||||
const val STATE_ROOM_POWER_LEVELS = "m.room.power_levels"
|
||||
|
||||
/**
|
||||
* Note that this Event has been deprecated, see
|
||||
* - https://matrix.org/docs/spec/client_server/r0.6.1#historical-events
|
||||
* - https://github.com/matrix-org/matrix-doc/pull/2432
|
||||
*/
|
||||
const val STATE_ROOM_ALIASES = "m.room.aliases"
|
||||
const val STATE_ROOM_TOMBSTONE = "m.room.tombstone"
|
||||
const val STATE_ROOM_CANONICAL_ALIAS = "m.room.canonical_alias"
|
||||
|
|
|
@ -16,9 +16,6 @@
|
|||
|
||||
package org.matrix.android.sdk.api.session.integrationmanager
|
||||
|
||||
import org.matrix.android.sdk.api.MatrixCallback
|
||||
import org.matrix.android.sdk.api.util.Cancelable
|
||||
|
||||
/**
|
||||
* This is the entry point to manage integration. You can grab an instance of this service through an active session.
|
||||
*/
|
||||
|
@ -80,19 +77,17 @@ interface IntegrationManagerService {
|
|||
/**
|
||||
* Offers to enable or disable the integration.
|
||||
* @param enable the param to change
|
||||
* @param callback the matrix callback to listen for result.
|
||||
* @return Cancelable
|
||||
*/
|
||||
fun setIntegrationEnabled(enable: Boolean, callback: MatrixCallback<Unit>): Cancelable
|
||||
suspend fun setIntegrationEnabled(enable: Boolean)
|
||||
|
||||
/**
|
||||
* Offers to allow or disallow a widget.
|
||||
* @param stateEventId the eventId of the state event defining the widget.
|
||||
* @param allowed the param to change
|
||||
* @param callback the matrix callback to listen for result.
|
||||
* @return Cancelable
|
||||
*/
|
||||
fun setWidgetAllowed(stateEventId: String, allowed: Boolean, callback: MatrixCallback<Unit>): Cancelable
|
||||
suspend fun setWidgetAllowed(stateEventId: String, allowed: Boolean)
|
||||
|
||||
/**
|
||||
* Returns true if the widget is allowed, false otherwise.
|
||||
|
@ -105,7 +100,7 @@ interface IntegrationManagerService {
|
|||
* @param widgetType the widget type to check for
|
||||
* @param domain the domain to check for
|
||||
*/
|
||||
fun setNativeWidgetDomainAllowed(widgetType: String, domain: String, allowed: Boolean, callback: MatrixCallback<Unit>): Cancelable
|
||||
suspend fun setNativeWidgetDomainAllowed(widgetType: String, domain: String, allowed: Boolean)
|
||||
|
||||
/**
|
||||
* Returns true if the widget domain is allowed, false otherwise.
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.matrix.android.sdk.api.session.room
|
|||
|
||||
import androidx.lifecycle.LiveData
|
||||
import org.matrix.android.sdk.api.MatrixCallback
|
||||
import org.matrix.android.sdk.api.session.room.alias.AliasService
|
||||
import org.matrix.android.sdk.api.session.room.call.RoomCallService
|
||||
import org.matrix.android.sdk.api.session.room.crypto.RoomCryptoService
|
||||
import org.matrix.android.sdk.api.session.room.members.MembershipService
|
||||
|
@ -46,6 +47,7 @@ interface Room :
|
|||
DraftService,
|
||||
ReadService,
|
||||
TypingService,
|
||||
AliasService,
|
||||
TagsService,
|
||||
MembershipService,
|
||||
StateService,
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package org.matrix.android.sdk.api.session.room
|
||||
|
||||
import org.matrix.android.sdk.api.MatrixCallback
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
|
||||
import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsParams
|
||||
import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsResponse
|
||||
import org.matrix.android.sdk.api.session.room.model.thirdparty.ThirdPartyProtocol
|
||||
|
@ -39,4 +40,14 @@ interface RoomDirectoryService {
|
|||
* Includes both the available protocols and all fields required for queries against each protocol.
|
||||
*/
|
||||
fun getThirdPartyProtocol(callback: MatrixCallback<Map<String, ThirdPartyProtocol>>): Cancelable
|
||||
|
||||
/**
|
||||
* Get the visibility of a room in the directory
|
||||
*/
|
||||
suspend fun getRoomDirectoryVisibility(roomId: String): RoomDirectoryVisibility
|
||||
|
||||
/**
|
||||
* Set the visibility of a room in the directory
|
||||
*/
|
||||
suspend fun setRoomDirectoryVisibility(roomId: String, roomDirectoryVisibility: RoomDirectoryVisibility)
|
||||
}
|
||||
|
|
|
@ -122,6 +122,11 @@ interface RoomService {
|
|||
searchOnServer: Boolean,
|
||||
callback: MatrixCallback<Optional<String>>): Cancelable
|
||||
|
||||
/**
|
||||
* Delete a room alias
|
||||
*/
|
||||
suspend fun deleteRoomAlias(roomAlias: String)
|
||||
|
||||
/**
|
||||
* Return a live data of all local changes membership that happened since the session has been opened.
|
||||
* It allows you to track this in your client to known what is currently being processed by the SDK.
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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.room.alias
|
||||
|
||||
interface AliasService {
|
||||
/**
|
||||
* Get list of local alias of the room
|
||||
* @return the list of the aliases (full aliases, not only the local part)
|
||||
*/
|
||||
suspend fun getRoomAliases(): List<String>
|
||||
|
||||
/**
|
||||
* Add local alias to the room
|
||||
* @param aliasLocalPart the local part of the alias.
|
||||
* Ex: for the alias "#my_alias:example.org", the local part is "my_alias"
|
||||
*/
|
||||
suspend fun addAlias(aliasLocalPart: String)
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* 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.room.alias
|
||||
|
||||
sealed class RoomAliasError : Throwable() {
|
||||
object AliasEmpty : RoomAliasError()
|
||||
object AliasNotAvailable : RoomAliasError()
|
||||
object AliasInvalid : RoomAliasError()
|
||||
}
|
|
@ -18,13 +18,10 @@ package org.matrix.android.sdk.api.session.room.failure
|
|||
|
||||
import org.matrix.android.sdk.api.failure.Failure
|
||||
import org.matrix.android.sdk.api.failure.MatrixError
|
||||
import org.matrix.android.sdk.api.session.room.alias.RoomAliasError
|
||||
|
||||
sealed class CreateRoomFailure : Failure.FeatureFailure() {
|
||||
object CreatedWithTimeout : CreateRoomFailure()
|
||||
data class CreatedWithFederationFailure(val matrixError: MatrixError) : CreateRoomFailure()
|
||||
sealed class RoomAliasError : CreateRoomFailure() {
|
||||
object AliasEmpty : RoomAliasError()
|
||||
object AliasNotAvailable : RoomAliasError()
|
||||
object AliasInvalid : RoomAliasError()
|
||||
}
|
||||
data class AliasError(val aliasError: RoomAliasError) : CreateRoomFailure()
|
||||
}
|
||||
|
|
|
@ -21,6 +21,9 @@ import com.squareup.moshi.JsonClass
|
|||
|
||||
/**
|
||||
* Class representing the EventType.STATE_ROOM_ALIASES state event content
|
||||
* Note that this Event has been deprecated, see
|
||||
* - https://matrix.org/docs/spec/client_server/r0.6.1#historical-events
|
||||
* - https://github.com/matrix-org/matrix-doc/pull/2432
|
||||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class RoomAliasesContent(
|
||||
|
|
|
@ -24,5 +24,14 @@ import com.squareup.moshi.JsonClass
|
|||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class RoomCanonicalAliasContent(
|
||||
@Json(name = "alias") val canonicalAlias: String? = null
|
||||
/**
|
||||
* The canonical alias for the room. If not present, null, or empty the room should be considered to have no canonical alias.
|
||||
*/
|
||||
@Json(name = "alias") val canonicalAlias: String? = null,
|
||||
|
||||
/**
|
||||
* Alternative aliases the room advertises.
|
||||
* This list can have aliases despite the alias field being null, empty, or otherwise not present.
|
||||
*/
|
||||
@Json(name = "alt_aliases") val alternativeAliases: List<String>? = null
|
||||
)
|
||||
|
|
|
@ -21,7 +21,9 @@ import androidx.lifecycle.LiveData
|
|||
import org.matrix.android.sdk.api.MatrixCallback
|
||||
import org.matrix.android.sdk.api.query.QueryStringValue
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.room.model.GuestAccess
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
|
||||
import org.matrix.android.sdk.api.util.Cancelable
|
||||
import org.matrix.android.sdk.api.util.JsonDict
|
||||
import org.matrix.android.sdk.api.util.Optional
|
||||
|
@ -38,21 +40,23 @@ interface StateService {
|
|||
*/
|
||||
fun updateName(name: String, callback: MatrixCallback<Unit>): Cancelable
|
||||
|
||||
/**
|
||||
* Add new alias to the room.
|
||||
*/
|
||||
fun addRoomAlias(roomAlias: String, callback: MatrixCallback<Unit>): Cancelable
|
||||
|
||||
/**
|
||||
* Update the canonical alias of the room
|
||||
* @param alias the canonical alias, or null to reset the canonical alias of this room
|
||||
* @param altAliases the alternative aliases for this room. It should include the canonical alias if any.
|
||||
*/
|
||||
fun updateCanonicalAlias(alias: String, callback: MatrixCallback<Unit>): Cancelable
|
||||
fun updateCanonicalAlias(alias: String?, altAliases: List<String>, callback: MatrixCallback<Unit>): Cancelable
|
||||
|
||||
/**
|
||||
* Update the history readability of the room
|
||||
*/
|
||||
fun updateHistoryReadability(readability: RoomHistoryVisibility, callback: MatrixCallback<Unit>): Cancelable
|
||||
|
||||
/**
|
||||
* Update the join rule and/or the guest access
|
||||
*/
|
||||
fun updateJoinRule(joinRules: RoomJoinRules?, guestAccess: GuestAccess?, callback: MatrixCallback<Unit>): Cancelable
|
||||
|
||||
/**
|
||||
* Update the avatar of the room
|
||||
*/
|
||||
|
|
|
@ -16,9 +16,6 @@
|
|||
|
||||
package org.matrix.android.sdk.api.session.room.uploads
|
||||
|
||||
import org.matrix.android.sdk.api.MatrixCallback
|
||||
import org.matrix.android.sdk.api.util.Cancelable
|
||||
|
||||
/**
|
||||
* This interface defines methods to get event with uploads (= attachments) sent to a room. It's implemented at the room level.
|
||||
*/
|
||||
|
@ -29,7 +26,5 @@ interface UploadsService {
|
|||
* @param numberOfEvents the expected number of events to retrieve. The result can contain less events.
|
||||
* @param since token to get next page, or null to get the first page
|
||||
*/
|
||||
fun getUploads(numberOfEvents: Int,
|
||||
since: String?,
|
||||
callback: MatrixCallback<GetUploadsResult>): Cancelable
|
||||
suspend fun getUploads(numberOfEvents: Int, since: String?): GetUploadsResult
|
||||
}
|
||||
|
|
|
@ -16,22 +16,16 @@
|
|||
|
||||
package org.matrix.android.sdk.api.session.terms
|
||||
|
||||
import org.matrix.android.sdk.api.MatrixCallback
|
||||
import org.matrix.android.sdk.api.util.Cancelable
|
||||
|
||||
interface TermsService {
|
||||
enum class ServiceType {
|
||||
IntegrationManager,
|
||||
IdentityService
|
||||
}
|
||||
|
||||
fun getTerms(serviceType: ServiceType,
|
||||
baseUrl: String,
|
||||
callback: MatrixCallback<GetTermsResponse>): Cancelable
|
||||
suspend fun getTerms(serviceType: ServiceType, baseUrl: String): GetTermsResponse
|
||||
|
||||
fun agreeToTerms(serviceType: ServiceType,
|
||||
baseUrl: String,
|
||||
agreedUrls: List<String>,
|
||||
token: String?,
|
||||
callback: MatrixCallback<Unit>): Cancelable
|
||||
suspend fun agreeToTerms(serviceType: ServiceType,
|
||||
baseUrl: String,
|
||||
agreedUrls: List<String>,
|
||||
token: String?)
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.matrix.android.sdk.internal.extensions.toUnsignedInt
|
|||
import org.matrix.olm.OlmSAS
|
||||
import org.matrix.olm.OlmUtility
|
||||
import timber.log.Timber
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* Represents an ongoing short code interactive key verification between two devices.
|
||||
|
@ -344,7 +345,7 @@ internal abstract class SASDefaultVerificationTransaction(
|
|||
}
|
||||
|
||||
protected fun hashUsingAgreedHashMethod(toHash: String): String? {
|
||||
if ("sha256".toLowerCase() == accepted?.hash?.toLowerCase()) {
|
||||
if ("sha256" == accepted?.hash?.toLowerCase(Locale.ROOT)) {
|
||||
val olmUtil = OlmUtility()
|
||||
val hashBytes = olmUtil.sha256(toHash)
|
||||
olmUtil.releaseUtility()
|
||||
|
@ -354,12 +355,11 @@ internal abstract class SASDefaultVerificationTransaction(
|
|||
}
|
||||
|
||||
private fun macUsingAgreedMethod(message: String, info: String): String? {
|
||||
if (SAS_MAC_SHA256_LONGKDF.toLowerCase() == accepted?.messageAuthenticationCode?.toLowerCase()) {
|
||||
return getSAS().calculateMacLongKdf(message, info)
|
||||
} else if (SAS_MAC_SHA256.toLowerCase() == accepted?.messageAuthenticationCode?.toLowerCase()) {
|
||||
return getSAS().calculateMac(message, info)
|
||||
return when (accepted?.messageAuthenticationCode?.toLowerCase(Locale.ROOT)) {
|
||||
SAS_MAC_SHA256_LONGKDF -> getSAS().calculateMacLongKdf(message, info)
|
||||
SAS_MAC_SHA256 -> getSAS().calculateMac(message, info)
|
||||
else -> null
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun getDecimalCodeRepresentation(): String {
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* 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.internal.session.directory
|
||||
|
||||
import org.matrix.android.sdk.internal.network.NetworkConstants
|
||||
import org.matrix.android.sdk.internal.session.room.alias.AddRoomAliasBody
|
||||
import org.matrix.android.sdk.internal.session.room.alias.RoomAliasDescription
|
||||
import retrofit2.Call
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.DELETE
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.PUT
|
||||
import retrofit2.http.Path
|
||||
|
||||
internal interface DirectoryAPI {
|
||||
/**
|
||||
* Get the room ID associated to the room alias.
|
||||
*
|
||||
* @param roomAlias the room alias.
|
||||
*/
|
||||
@GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "directory/room/{roomAlias}")
|
||||
fun getRoomIdByAlias(@Path("roomAlias") roomAlias: String): Call<RoomAliasDescription>
|
||||
|
||||
/**
|
||||
* Get the room directory visibility.
|
||||
*
|
||||
* @param roomId the room id.
|
||||
*/
|
||||
@GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "directory/list/room/{roomId}")
|
||||
fun getRoomDirectoryVisibility(@Path("roomId") roomId: String): Call<RoomDirectoryVisibilityJson>
|
||||
|
||||
/**
|
||||
* Set the room directory visibility.
|
||||
*
|
||||
* @param roomId the room id.
|
||||
* @param body the body containing the new directory visibility
|
||||
*/
|
||||
@PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "directory/list/room/{roomId}")
|
||||
fun setRoomDirectoryVisibility(@Path("roomId") roomId: String,
|
||||
@Body body: RoomDirectoryVisibilityJson): Call<Unit>
|
||||
|
||||
/**
|
||||
* Add alias to the room.
|
||||
* @param roomAlias the room alias.
|
||||
*/
|
||||
@PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "directory/room/{roomAlias}")
|
||||
fun addRoomAlias(@Path("roomAlias") roomAlias: String,
|
||||
@Body body: AddRoomAliasBody): Call<Unit>
|
||||
|
||||
/**
|
||||
* Delete a room alias
|
||||
* @param roomAlias the room alias.
|
||||
*/
|
||||
@DELETE(NetworkConstants.URI_API_PREFIX_PATH_R0 + "directory/room/{roomAlias}")
|
||||
fun deleteRoomAlias(@Path("roomAlias") roomAlias: String): Call<Unit>
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* 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.internal.session.directory
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class RoomDirectoryVisibilityJson(
|
||||
/**
|
||||
* The visibility of the room in the directory. One of: ["private", "public"]
|
||||
*/
|
||||
@Json(name = "visibility") val visibility: RoomDirectoryVisibility
|
||||
)
|
|
@ -16,10 +16,8 @@
|
|||
|
||||
package org.matrix.android.sdk.internal.session.integrationmanager
|
||||
|
||||
import org.matrix.android.sdk.api.MatrixCallback
|
||||
import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerConfig
|
||||
import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerService
|
||||
import org.matrix.android.sdk.api.util.Cancelable
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class DefaultIntegrationManagerService @Inject constructor(private val integrationManager: IntegrationManager) : IntegrationManagerService {
|
||||
|
@ -44,20 +42,20 @@ internal class DefaultIntegrationManagerService @Inject constructor(private val
|
|||
return integrationManager.isIntegrationEnabled()
|
||||
}
|
||||
|
||||
override fun setIntegrationEnabled(enable: Boolean, callback: MatrixCallback<Unit>): Cancelable {
|
||||
return integrationManager.setIntegrationEnabled(enable, callback)
|
||||
override suspend fun setIntegrationEnabled(enable: Boolean) {
|
||||
integrationManager.setIntegrationEnabled(enable)
|
||||
}
|
||||
|
||||
override fun setWidgetAllowed(stateEventId: String, allowed: Boolean, callback: MatrixCallback<Unit>): Cancelable {
|
||||
return integrationManager.setWidgetAllowed(stateEventId, allowed, callback)
|
||||
override suspend fun setWidgetAllowed(stateEventId: String, allowed: Boolean) {
|
||||
integrationManager.setWidgetAllowed(stateEventId, allowed)
|
||||
}
|
||||
|
||||
override fun isWidgetAllowed(stateEventId: String): Boolean {
|
||||
return integrationManager.isWidgetAllowed(stateEventId)
|
||||
}
|
||||
|
||||
override fun setNativeWidgetDomainAllowed(widgetType: String, domain: String, allowed: Boolean, callback: MatrixCallback<Unit>): Cancelable {
|
||||
return integrationManager.setNativeWidgetDomainAllowed(widgetType, domain, allowed, callback)
|
||||
override suspend fun setNativeWidgetDomainAllowed(widgetType: String, domain: String, allowed: Boolean) {
|
||||
integrationManager.setNativeWidgetDomainAllowed(widgetType, domain, allowed)
|
||||
}
|
||||
|
||||
override fun isNativeWidgetDomainAllowed(widgetType: String, domain: String): Boolean {
|
||||
|
|
|
@ -20,15 +20,12 @@ import androidx.lifecycle.Lifecycle
|
|||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.LifecycleRegistry
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import org.matrix.android.sdk.api.MatrixCallback
|
||||
import org.matrix.android.sdk.api.MatrixConfiguration
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerConfig
|
||||
import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerService
|
||||
import org.matrix.android.sdk.api.session.widgets.model.WidgetContent
|
||||
import org.matrix.android.sdk.api.session.widgets.model.WidgetType
|
||||
import org.matrix.android.sdk.api.util.Cancelable
|
||||
import org.matrix.android.sdk.api.util.NoOpCancellable
|
||||
import org.matrix.android.sdk.internal.database.model.WellknownIntegrationManagerConfigEntity
|
||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||
import org.matrix.android.sdk.internal.extensions.observeNotNull
|
||||
|
@ -41,7 +38,6 @@ import org.matrix.android.sdk.internal.session.user.accountdata.UpdateUserAccoun
|
|||
import org.matrix.android.sdk.internal.session.widgets.helper.WidgetFactory
|
||||
import org.matrix.android.sdk.internal.session.widgets.helper.extractWidgetSequence
|
||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
||||
import org.matrix.android.sdk.internal.task.configureWith
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -137,22 +133,17 @@ internal class IntegrationManager @Inject constructor(matrixConfiguration: Matri
|
|||
return integrationProvisioningContent?.enabled ?: false
|
||||
}
|
||||
|
||||
fun setIntegrationEnabled(enable: Boolean, callback: MatrixCallback<Unit>): Cancelable {
|
||||
suspend fun setIntegrationEnabled(enable: Boolean) {
|
||||
val isIntegrationEnabled = isIntegrationEnabled()
|
||||
if (enable == isIntegrationEnabled) {
|
||||
callback.onSuccess(Unit)
|
||||
return NoOpCancellable
|
||||
return
|
||||
}
|
||||
val integrationProvisioningContent = IntegrationProvisioningContent(enabled = enable)
|
||||
val params = UpdateUserAccountDataTask.IntegrationProvisioning(integrationProvisioningContent = integrationProvisioningContent)
|
||||
return updateUserAccountDataTask
|
||||
.configureWith(params) {
|
||||
this.callback = callback
|
||||
}
|
||||
.executeBy(taskExecutor)
|
||||
return updateUserAccountDataTask.execute(params)
|
||||
}
|
||||
|
||||
fun setWidgetAllowed(stateEventId: String, allowed: Boolean, callback: MatrixCallback<Unit>): Cancelable {
|
||||
suspend fun setWidgetAllowed(stateEventId: String, allowed: Boolean) {
|
||||
val currentAllowedWidgets = accountDataDataSource.getAccountDataEvent(UserAccountDataTypes.TYPE_ALLOWED_WIDGETS)
|
||||
val currentContent = currentAllowedWidgets?.content?.toModel<AllowedWidgetsContent>()
|
||||
val newContent = if (currentContent == null) {
|
||||
|
@ -165,11 +156,7 @@ internal class IntegrationManager @Inject constructor(matrixConfiguration: Matri
|
|||
currentContent.copy(widgets = allowedWidgets)
|
||||
}
|
||||
val params = UpdateUserAccountDataTask.AllowedWidgets(allowedWidgetsContent = newContent)
|
||||
return updateUserAccountDataTask
|
||||
.configureWith(params) {
|
||||
this.callback = callback
|
||||
}
|
||||
.executeBy(taskExecutor)
|
||||
return updateUserAccountDataTask.execute(params)
|
||||
}
|
||||
|
||||
fun isWidgetAllowed(stateEventId: String): Boolean {
|
||||
|
@ -178,7 +165,7 @@ internal class IntegrationManager @Inject constructor(matrixConfiguration: Matri
|
|||
return currentContent?.widgets?.get(stateEventId) ?: false
|
||||
}
|
||||
|
||||
fun setNativeWidgetDomainAllowed(widgetType: String, domain: String, allowed: Boolean, callback: MatrixCallback<Unit>): Cancelable {
|
||||
suspend fun setNativeWidgetDomainAllowed(widgetType: String, domain: String, allowed: Boolean) {
|
||||
val currentAllowedWidgets = accountDataDataSource.getAccountDataEvent(UserAccountDataTypes.TYPE_ALLOWED_WIDGETS)
|
||||
val currentContent = currentAllowedWidgets?.content?.toModel<AllowedWidgetsContent>()
|
||||
val newContent = if (currentContent == null) {
|
||||
|
@ -195,11 +182,7 @@ internal class IntegrationManager @Inject constructor(matrixConfiguration: Matri
|
|||
currentContent.copy(native = nativeAllowedWidgets)
|
||||
}
|
||||
val params = UpdateUserAccountDataTask.AllowedWidgets(allowedWidgetsContent = newContent)
|
||||
return updateUserAccountDataTask
|
||||
.configureWith(params) {
|
||||
this.callback = callback
|
||||
}
|
||||
.executeBy(taskExecutor)
|
||||
return updateUserAccountDataTask.execute(params)
|
||||
}
|
||||
|
||||
fun isNativeWidgetDomainAllowed(widgetType: String, domain: String?): Boolean {
|
||||
|
|
|
@ -21,6 +21,7 @@ import org.matrix.android.sdk.api.MatrixCallback
|
|||
import org.matrix.android.sdk.api.session.crypto.CryptoService
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.room.Room
|
||||
import org.matrix.android.sdk.api.session.room.alias.AliasService
|
||||
import org.matrix.android.sdk.api.session.room.call.RoomCallService
|
||||
import org.matrix.android.sdk.api.session.room.members.MembershipService
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
|
@ -58,6 +59,7 @@ internal class DefaultRoom @Inject constructor(override val roomId: String,
|
|||
private val roomCallService: RoomCallService,
|
||||
private val readService: ReadService,
|
||||
private val typingService: TypingService,
|
||||
private val aliasService: AliasService,
|
||||
private val tagsService: TagsService,
|
||||
private val cryptoService: CryptoService,
|
||||
private val relationService: RelationService,
|
||||
|
@ -76,6 +78,7 @@ internal class DefaultRoom @Inject constructor(override val roomId: String,
|
|||
RoomCallService by roomCallService,
|
||||
ReadService by readService,
|
||||
TypingService by typingService,
|
||||
AliasService by aliasService,
|
||||
TagsService by tagsService,
|
||||
RelationService by relationService,
|
||||
MembershipService by roomMembersService,
|
||||
|
|
|
@ -18,19 +18,25 @@ package org.matrix.android.sdk.internal.session.room
|
|||
|
||||
import org.matrix.android.sdk.api.MatrixCallback
|
||||
import org.matrix.android.sdk.api.session.room.RoomDirectoryService
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
|
||||
import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsParams
|
||||
import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsResponse
|
||||
import org.matrix.android.sdk.api.session.room.model.thirdparty.ThirdPartyProtocol
|
||||
import org.matrix.android.sdk.api.util.Cancelable
|
||||
import org.matrix.android.sdk.internal.session.room.directory.GetPublicRoomTask
|
||||
import org.matrix.android.sdk.internal.session.room.directory.GetRoomDirectoryVisibilityTask
|
||||
import org.matrix.android.sdk.internal.session.room.directory.GetThirdPartyProtocolsTask
|
||||
import org.matrix.android.sdk.internal.session.room.directory.SetRoomDirectoryVisibilityTask
|
||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
||||
import org.matrix.android.sdk.internal.task.configureWith
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class DefaultRoomDirectoryService @Inject constructor(private val getPublicRoomTask: GetPublicRoomTask,
|
||||
private val getThirdPartyProtocolsTask: GetThirdPartyProtocolsTask,
|
||||
private val taskExecutor: TaskExecutor) : RoomDirectoryService {
|
||||
internal class DefaultRoomDirectoryService @Inject constructor(
|
||||
private val getPublicRoomTask: GetPublicRoomTask,
|
||||
private val getThirdPartyProtocolsTask: GetThirdPartyProtocolsTask,
|
||||
private val getRoomDirectoryVisibilityTask: GetRoomDirectoryVisibilityTask,
|
||||
private val setRoomDirectoryVisibilityTask: SetRoomDirectoryVisibilityTask,
|
||||
private val taskExecutor: TaskExecutor) : RoomDirectoryService {
|
||||
|
||||
override fun getPublicRooms(server: String?,
|
||||
publicRoomsParams: PublicRoomsParams,
|
||||
|
@ -49,4 +55,12 @@ internal class DefaultRoomDirectoryService @Inject constructor(private val getPu
|
|||
}
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
|
||||
override suspend fun getRoomDirectoryVisibility(roomId: String): RoomDirectoryVisibility {
|
||||
return getRoomDirectoryVisibilityTask.execute(GetRoomDirectoryVisibilityTask.Params(roomId))
|
||||
}
|
||||
|
||||
override suspend fun setRoomDirectoryVisibility(roomId: String, roomDirectoryVisibility: RoomDirectoryVisibility) {
|
||||
setRoomDirectoryVisibilityTask.execute(SetRoomDirectoryVisibilityTask.Params(roomId, roomDirectoryVisibility))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ import org.matrix.android.sdk.api.util.toOptional
|
|||
import org.matrix.android.sdk.internal.database.mapper.asDomain
|
||||
import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntityFields
|
||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||
import org.matrix.android.sdk.internal.session.room.alias.DeleteRoomAliasTask
|
||||
import org.matrix.android.sdk.internal.session.room.alias.GetRoomIdByAliasTask
|
||||
import org.matrix.android.sdk.internal.session.room.create.CreateRoomTask
|
||||
import org.matrix.android.sdk.internal.session.room.membership.RoomChangeMembershipStateDataSource
|
||||
|
@ -53,6 +54,7 @@ internal class DefaultRoomService @Inject constructor(
|
|||
private val markAllRoomsReadTask: MarkAllRoomsReadTask,
|
||||
private val updateBreadcrumbsTask: UpdateBreadcrumbsTask,
|
||||
private val roomIdByAliasTask: GetRoomIdByAliasTask,
|
||||
private val deleteRoomAliasTask: DeleteRoomAliasTask,
|
||||
private val roomGetter: RoomGetter,
|
||||
private val roomSummaryDataSource: RoomSummaryDataSource,
|
||||
private val roomChangeMembershipStateDataSource: RoomChangeMembershipStateDataSource,
|
||||
|
@ -125,6 +127,10 @@ internal class DefaultRoomService @Inject constructor(
|
|||
.executeBy(taskExecutor)
|
||||
}
|
||||
|
||||
override suspend fun deleteRoomAlias(roomAlias: String) {
|
||||
deleteRoomAliasTask.execute(DeleteRoomAliasTask.Params(roomAlias))
|
||||
}
|
||||
|
||||
override fun getChangeMembershipsLive(): LiveData<Map<String, ChangeMembershipState>> {
|
||||
return roomChangeMembershipStateDataSource.getLiveStates()
|
||||
}
|
||||
|
|
|
@ -23,8 +23,7 @@ import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsRe
|
|||
import org.matrix.android.sdk.api.session.room.model.thirdparty.ThirdPartyProtocol
|
||||
import org.matrix.android.sdk.api.util.JsonDict
|
||||
import org.matrix.android.sdk.internal.network.NetworkConstants
|
||||
import org.matrix.android.sdk.internal.session.room.alias.AddRoomAliasBody
|
||||
import org.matrix.android.sdk.internal.session.room.alias.RoomAliasDescription
|
||||
import org.matrix.android.sdk.internal.session.room.alias.GetAliasesResponse
|
||||
import org.matrix.android.sdk.internal.session.room.create.CreateRoomBody
|
||||
import org.matrix.android.sdk.internal.session.room.create.CreateRoomResponse
|
||||
import org.matrix.android.sdk.internal.session.room.create.JoinRoomResponse
|
||||
|
@ -321,20 +320,11 @@ internal interface RoomAPI {
|
|||
@Body body: ReportContentBody): Call<Unit>
|
||||
|
||||
/**
|
||||
* Get the room ID associated to the room alias.
|
||||
*
|
||||
* @param roomAlias the room alias.
|
||||
* Get a list of aliases maintained by the local server for the given room.
|
||||
* Ref: https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-rooms-roomid-aliases
|
||||
*/
|
||||
@GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "directory/room/{roomAlias}")
|
||||
fun getRoomIdByAlias(@Path("roomAlias") roomAlias: String): Call<RoomAliasDescription>
|
||||
|
||||
/**
|
||||
* Add alias to the room.
|
||||
* @param roomAlias the room alias.
|
||||
*/
|
||||
@PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "directory/room/{roomAlias}")
|
||||
fun addRoomAlias(@Path("roomAlias") roomAlias: String,
|
||||
@Body body: AddRoomAliasBody): Call<Unit>
|
||||
@GET(NetworkConstants.URI_API_PREFIX_PATH_UNSTABLE + "org.matrix.msc2432/rooms/{roomId}/aliases")
|
||||
fun getAliases(@Path("roomId") roomId: String): Call<GetAliasesResponse>
|
||||
|
||||
/**
|
||||
* Inform that the user is starting to type or has stopped typing
|
||||
|
|
|
@ -19,6 +19,7 @@ package org.matrix.android.sdk.internal.session.room
|
|||
import org.matrix.android.sdk.api.session.crypto.CryptoService
|
||||
import org.matrix.android.sdk.api.session.room.Room
|
||||
import org.matrix.android.sdk.internal.session.SessionScope
|
||||
import org.matrix.android.sdk.internal.session.room.alias.DefaultAliasService
|
||||
import org.matrix.android.sdk.internal.session.room.call.DefaultRoomCallService
|
||||
import org.matrix.android.sdk.internal.session.room.draft.DefaultDraftService
|
||||
import org.matrix.android.sdk.internal.session.room.membership.DefaultMembershipService
|
||||
|
@ -54,6 +55,7 @@ internal class DefaultRoomFactory @Inject constructor(private val cryptoService:
|
|||
private val roomCallServiceFactory: DefaultRoomCallService.Factory,
|
||||
private val readServiceFactory: DefaultReadService.Factory,
|
||||
private val typingServiceFactory: DefaultTypingService.Factory,
|
||||
private val aliasServiceFactory: DefaultAliasService.Factory,
|
||||
private val tagsServiceFactory: DefaultTagsService.Factory,
|
||||
private val relationServiceFactory: DefaultRelationService.Factory,
|
||||
private val membershipServiceFactory: DefaultMembershipService.Factory,
|
||||
|
@ -76,6 +78,7 @@ internal class DefaultRoomFactory @Inject constructor(private val cryptoService:
|
|||
roomCallService = roomCallServiceFactory.create(roomId),
|
||||
readService = readServiceFactory.create(roomId),
|
||||
typingService = typingServiceFactory.create(roomId),
|
||||
aliasService = aliasServiceFactory.create(roomId),
|
||||
tagsService = tagsServiceFactory.create(roomId),
|
||||
cryptoService = cryptoService,
|
||||
relationService = relationServiceFactory.create(roomId),
|
||||
|
|
|
@ -26,16 +26,25 @@ import org.matrix.android.sdk.api.session.room.RoomDirectoryService
|
|||
import org.matrix.android.sdk.api.session.room.RoomService
|
||||
import org.matrix.android.sdk.internal.session.DefaultFileService
|
||||
import org.matrix.android.sdk.internal.session.SessionScope
|
||||
import org.matrix.android.sdk.internal.session.directory.DirectoryAPI
|
||||
import org.matrix.android.sdk.internal.session.room.alias.AddRoomAliasTask
|
||||
import org.matrix.android.sdk.internal.session.room.alias.DefaultAddRoomAliasTask
|
||||
import org.matrix.android.sdk.internal.session.room.alias.DefaultDeleteRoomAliasTask
|
||||
import org.matrix.android.sdk.internal.session.room.alias.DefaultGetRoomIdByAliasTask
|
||||
import org.matrix.android.sdk.internal.session.room.alias.DefaultGetRoomLocalAliasesTask
|
||||
import org.matrix.android.sdk.internal.session.room.alias.DeleteRoomAliasTask
|
||||
import org.matrix.android.sdk.internal.session.room.alias.GetRoomIdByAliasTask
|
||||
import org.matrix.android.sdk.internal.session.room.alias.GetRoomLocalAliasesTask
|
||||
import org.matrix.android.sdk.internal.session.room.create.CreateRoomTask
|
||||
import org.matrix.android.sdk.internal.session.room.create.DefaultCreateRoomTask
|
||||
import org.matrix.android.sdk.internal.session.room.directory.DefaultGetPublicRoomTask
|
||||
import org.matrix.android.sdk.internal.session.room.directory.DefaultGetRoomDirectoryVisibilityTask
|
||||
import org.matrix.android.sdk.internal.session.room.directory.DefaultGetThirdPartyProtocolsTask
|
||||
import org.matrix.android.sdk.internal.session.room.directory.DefaultSetRoomDirectoryVisibilityTask
|
||||
import org.matrix.android.sdk.internal.session.room.directory.GetPublicRoomTask
|
||||
import org.matrix.android.sdk.internal.session.room.directory.GetRoomDirectoryVisibilityTask
|
||||
import org.matrix.android.sdk.internal.session.room.directory.GetThirdPartyProtocolsTask
|
||||
import org.matrix.android.sdk.internal.session.room.directory.SetRoomDirectoryVisibilityTask
|
||||
import org.matrix.android.sdk.internal.session.room.membership.DefaultLoadRoomMembersTask
|
||||
import org.matrix.android.sdk.internal.session.room.membership.LoadRoomMembersTask
|
||||
import org.matrix.android.sdk.internal.session.room.membership.admin.DefaultMembershipAdminTask
|
||||
|
@ -90,6 +99,13 @@ internal abstract class RoomModule {
|
|||
return retrofit.create(RoomAPI::class.java)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@JvmStatic
|
||||
@SessionScope
|
||||
fun providesDirectoryAPI(retrofit: Retrofit): DirectoryAPI {
|
||||
return retrofit.create(DirectoryAPI::class.java)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@JvmStatic
|
||||
fun providesParser(): Parser {
|
||||
|
@ -127,6 +143,12 @@ internal abstract class RoomModule {
|
|||
@Binds
|
||||
abstract fun bindGetPublicRoomTask(task: DefaultGetPublicRoomTask): GetPublicRoomTask
|
||||
|
||||
@Binds
|
||||
abstract fun bindGetRoomDirectoryVisibilityTask(task: DefaultGetRoomDirectoryVisibilityTask): GetRoomDirectoryVisibilityTask
|
||||
|
||||
@Binds
|
||||
abstract fun bindSetRoomDirectoryVisibilityTask(task: DefaultSetRoomDirectoryVisibilityTask): SetRoomDirectoryVisibilityTask
|
||||
|
||||
@Binds
|
||||
abstract fun bindGetThirdPartyProtocolsTask(task: DefaultGetThirdPartyProtocolsTask): GetThirdPartyProtocolsTask
|
||||
|
||||
|
@ -181,9 +203,15 @@ internal abstract class RoomModule {
|
|||
@Binds
|
||||
abstract fun bindGetRoomIdByAliasTask(task: DefaultGetRoomIdByAliasTask): GetRoomIdByAliasTask
|
||||
|
||||
@Binds
|
||||
abstract fun bindGetRoomLocalAliasesTask(task: DefaultGetRoomLocalAliasesTask): GetRoomLocalAliasesTask
|
||||
|
||||
@Binds
|
||||
abstract fun bindAddRoomAliasTask(task: DefaultAddRoomAliasTask): AddRoomAliasTask
|
||||
|
||||
@Binds
|
||||
abstract fun bindDeleteRoomAliasTask(task: DefaultDeleteRoomAliasTask): DeleteRoomAliasTask
|
||||
|
||||
@Binds
|
||||
abstract fun bindSendTypingTask(task: DefaultSendTypingTask): SendTypingTask
|
||||
|
||||
|
|
|
@ -16,28 +16,38 @@
|
|||
|
||||
package org.matrix.android.sdk.internal.session.room.alias
|
||||
|
||||
import org.matrix.android.sdk.internal.network.executeRequest
|
||||
import org.matrix.android.sdk.internal.session.room.RoomAPI
|
||||
import org.matrix.android.sdk.internal.task.Task
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.matrix.android.sdk.internal.di.UserId
|
||||
import org.matrix.android.sdk.internal.network.executeRequest
|
||||
import org.matrix.android.sdk.internal.session.directory.DirectoryAPI
|
||||
import org.matrix.android.sdk.internal.session.room.alias.RoomAliasAvailabilityChecker.Companion.toFullLocalAlias
|
||||
import org.matrix.android.sdk.internal.task.Task
|
||||
import javax.inject.Inject
|
||||
|
||||
internal interface AddRoomAliasTask : Task<AddRoomAliasTask.Params, Unit> {
|
||||
data class Params(
|
||||
val roomId: String,
|
||||
val roomAlias: String
|
||||
/**
|
||||
* the local part of the alias.
|
||||
* Ex: for the alias "#my_alias:example.org", the local part is "my_alias"
|
||||
*/
|
||||
val aliasLocalPart: String
|
||||
)
|
||||
}
|
||||
|
||||
internal class DefaultAddRoomAliasTask @Inject constructor(
|
||||
private val roomAPI: RoomAPI,
|
||||
@UserId private val userId: String,
|
||||
private val directoryAPI: DirectoryAPI,
|
||||
private val aliasAvailabilityChecker: RoomAliasAvailabilityChecker,
|
||||
private val eventBus: EventBus
|
||||
) : AddRoomAliasTask {
|
||||
|
||||
override suspend fun execute(params: AddRoomAliasTask.Params) {
|
||||
aliasAvailabilityChecker.check(params.aliasLocalPart)
|
||||
|
||||
executeRequest<Unit>(eventBus) {
|
||||
apiCall = roomAPI.addRoomAlias(
|
||||
roomAlias = params.roomAlias,
|
||||
apiCall = directoryAPI.addRoomAlias(
|
||||
roomAlias = params.aliasLocalPart.toFullLocalAlias(userId),
|
||||
body = AddRoomAliasBody(
|
||||
roomId = params.roomId
|
||||
)
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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.internal.session.room.alias
|
||||
|
||||
import com.squareup.inject.assisted.Assisted
|
||||
import com.squareup.inject.assisted.AssistedInject
|
||||
import org.matrix.android.sdk.api.session.room.alias.AliasService
|
||||
|
||||
internal class DefaultAliasService @AssistedInject constructor(
|
||||
@Assisted private val roomId: String,
|
||||
private val getRoomLocalAliasesTask: GetRoomLocalAliasesTask,
|
||||
private val addRoomAliasTask: AddRoomAliasTask
|
||||
) : AliasService {
|
||||
|
||||
@AssistedInject.Factory
|
||||
interface Factory {
|
||||
fun create(roomId: String): AliasService
|
||||
}
|
||||
|
||||
override suspend fun getRoomAliases(): List<String> {
|
||||
return getRoomLocalAliasesTask.execute(GetRoomLocalAliasesTask.Params(roomId))
|
||||
}
|
||||
|
||||
override suspend fun addAlias(aliasLocalPart: String) {
|
||||
addRoomAliasTask.execute(AddRoomAliasTask.Params(roomId, aliasLocalPart))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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.internal.session.room.alias
|
||||
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.matrix.android.sdk.internal.network.executeRequest
|
||||
import org.matrix.android.sdk.internal.session.directory.DirectoryAPI
|
||||
import org.matrix.android.sdk.internal.task.Task
|
||||
import javax.inject.Inject
|
||||
|
||||
internal interface DeleteRoomAliasTask : Task<DeleteRoomAliasTask.Params, Unit> {
|
||||
data class Params(
|
||||
val roomAlias: String
|
||||
)
|
||||
}
|
||||
|
||||
internal class DefaultDeleteRoomAliasTask @Inject constructor(
|
||||
private val directoryAPI: DirectoryAPI,
|
||||
private val eventBus: EventBus
|
||||
) : DeleteRoomAliasTask {
|
||||
|
||||
override suspend fun execute(params: DeleteRoomAliasTask.Params) {
|
||||
executeRequest<Unit>(eventBus) {
|
||||
apiCall = directoryAPI.deleteRoomAlias(
|
||||
roomAlias = params.roomAlias
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* 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.internal.session.room.alias
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class GetAliasesResponse(
|
||||
/**
|
||||
* Required. The server's local aliases on the room. Can be empty.
|
||||
*/
|
||||
@Json(name = "aliases") val aliases: List<String> = emptyList()
|
||||
)
|
|
@ -25,7 +25,7 @@ import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
|
|||
import org.matrix.android.sdk.internal.database.query.findByAlias
|
||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||
import org.matrix.android.sdk.internal.network.executeRequest
|
||||
import org.matrix.android.sdk.internal.session.room.RoomAPI
|
||||
import org.matrix.android.sdk.internal.session.directory.DirectoryAPI
|
||||
import org.matrix.android.sdk.internal.task.Task
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -38,7 +38,7 @@ internal interface GetRoomIdByAliasTask : Task<GetRoomIdByAliasTask.Params, Opti
|
|||
|
||||
internal class DefaultGetRoomIdByAliasTask @Inject constructor(
|
||||
@SessionDatabase private val monarchy: Monarchy,
|
||||
private val roomAPI: RoomAPI,
|
||||
private val directoryAPI: DirectoryAPI,
|
||||
private val eventBus: EventBus
|
||||
) : GetRoomIdByAliasTask {
|
||||
|
||||
|
@ -53,7 +53,7 @@ internal class DefaultGetRoomIdByAliasTask @Inject constructor(
|
|||
} else {
|
||||
roomId = tryOrNull("## Failed to get roomId from alias") {
|
||||
executeRequest<RoomAliasDescription>(eventBus) {
|
||||
apiCall = roomAPI.getRoomIdByAlias(params.roomAlias)
|
||||
apiCall = directoryAPI.getRoomIdByAlias(params.roomAlias)
|
||||
}
|
||||
}?.roomId
|
||||
Optional.from(roomId)
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* 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.internal.session.room.alias
|
||||
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.matrix.android.sdk.internal.network.executeRequest
|
||||
import org.matrix.android.sdk.internal.session.room.RoomAPI
|
||||
import org.matrix.android.sdk.internal.task.Task
|
||||
import javax.inject.Inject
|
||||
|
||||
internal interface GetRoomLocalAliasesTask : Task<GetRoomLocalAliasesTask.Params, List<String>> {
|
||||
data class Params(
|
||||
val roomId: String
|
||||
)
|
||||
}
|
||||
|
||||
internal class DefaultGetRoomLocalAliasesTask @Inject constructor(
|
||||
private val roomAPI: RoomAPI,
|
||||
private val eventBus: EventBus
|
||||
) : GetRoomLocalAliasesTask {
|
||||
|
||||
override suspend fun execute(params: GetRoomLocalAliasesTask.Params): List<String> {
|
||||
// We do not check for "org.matrix.msc2432", so the API may be missing
|
||||
val response = executeRequest<GetAliasesResponse>(eventBus) {
|
||||
apiCall = roomAPI.getAliases(roomId = params.roomId)
|
||||
}
|
||||
|
||||
return response.aliases
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* 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.internal.session.room.alias
|
||||
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.matrix.android.sdk.api.failure.Failure
|
||||
import org.matrix.android.sdk.api.session.room.alias.RoomAliasError
|
||||
import org.matrix.android.sdk.internal.di.UserId
|
||||
import org.matrix.android.sdk.internal.network.executeRequest
|
||||
import org.matrix.android.sdk.internal.session.directory.DirectoryAPI
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class RoomAliasAvailabilityChecker @Inject constructor(
|
||||
@UserId private val userId: String,
|
||||
private val directoryAPI: DirectoryAPI,
|
||||
private val eventBus: EventBus
|
||||
) {
|
||||
/**
|
||||
* @param aliasLocalPart the local part of the alias.
|
||||
* Ex: for the alias "#my_alias:example.org", the local part is "my_alias"
|
||||
*/
|
||||
@Throws(RoomAliasError::class)
|
||||
suspend fun check(aliasLocalPart: String?) {
|
||||
if (aliasLocalPart.isNullOrEmpty()) {
|
||||
throw RoomAliasError.AliasEmpty
|
||||
}
|
||||
// Check alias availability
|
||||
val fullAlias = aliasLocalPart.toFullLocalAlias(userId)
|
||||
try {
|
||||
executeRequest<RoomAliasDescription>(eventBus) {
|
||||
apiCall = directoryAPI.getRoomIdByAlias(fullAlias)
|
||||
}
|
||||
} catch (throwable: Throwable) {
|
||||
if (throwable is Failure.ServerError && throwable.httpCode == 404) {
|
||||
// This is a 404, so the alias is available: nominal case
|
||||
null
|
||||
} else {
|
||||
// Other error, propagate it
|
||||
throw throwable
|
||||
}
|
||||
}
|
||||
?.let {
|
||||
// Alias already exists: error case
|
||||
throw RoomAliasError.AliasNotAvailable
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
internal fun String.toFullLocalAlias(userId: String) = "#" + this + ":" + userId.substringAfter(":")
|
||||
}
|
||||
}
|
|
@ -22,6 +22,7 @@ import kotlinx.coroutines.TimeoutCancellationException
|
|||
import org.greenrobot.eventbus.EventBus
|
||||
import org.matrix.android.sdk.api.failure.Failure
|
||||
import org.matrix.android.sdk.api.failure.MatrixError
|
||||
import org.matrix.android.sdk.api.session.room.alias.RoomAliasError
|
||||
import org.matrix.android.sdk.api.session.room.failure.CreateRoomFailure
|
||||
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
||||
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomPreset
|
||||
|
@ -31,10 +32,9 @@ import org.matrix.android.sdk.internal.database.model.RoomEntityFields
|
|||
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
|
||||
import org.matrix.android.sdk.internal.database.query.where
|
||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||
import org.matrix.android.sdk.internal.di.UserId
|
||||
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.alias.RoomAliasDescription
|
||||
import org.matrix.android.sdk.internal.session.room.alias.RoomAliasAvailabilityChecker
|
||||
import org.matrix.android.sdk.internal.session.room.read.SetReadMarkersTask
|
||||
import org.matrix.android.sdk.internal.session.user.accountdata.DirectChatsHelper
|
||||
import org.matrix.android.sdk.internal.session.user.accountdata.UpdateUserAccountDataTask
|
||||
|
@ -47,8 +47,8 @@ internal interface CreateRoomTask : Task<CreateRoomParams, String>
|
|||
|
||||
internal class DefaultCreateRoomTask @Inject constructor(
|
||||
private val roomAPI: RoomAPI,
|
||||
@UserId private val userId: String,
|
||||
@SessionDatabase private val monarchy: Monarchy,
|
||||
private val aliasAvailabilityChecker: RoomAliasAvailabilityChecker,
|
||||
private val directChatsHelper: DirectChatsHelper,
|
||||
private val updateUserAccountDataTask: UpdateUserAccountDataTask,
|
||||
private val readMarkersTask: SetReadMarkersTask,
|
||||
|
@ -65,28 +65,11 @@ internal class DefaultCreateRoomTask @Inject constructor(
|
|||
} else null
|
||||
|
||||
if (params.preset == CreateRoomPreset.PRESET_PUBLIC_CHAT) {
|
||||
if (params.roomAliasName.isNullOrEmpty()) {
|
||||
throw CreateRoomFailure.RoomAliasError.AliasEmpty
|
||||
}
|
||||
// Check alias availability
|
||||
val fullAlias = "#" + params.roomAliasName + ":" + userId.substringAfter(":")
|
||||
try {
|
||||
executeRequest<RoomAliasDescription>(eventBus) {
|
||||
apiCall = roomAPI.getRoomIdByAlias(fullAlias)
|
||||
}
|
||||
} catch (throwable: Throwable) {
|
||||
if (throwable is Failure.ServerError && throwable.httpCode == 404) {
|
||||
// This is a 404, so the alias is available: nominal case
|
||||
null
|
||||
} else {
|
||||
// Other error, propagate it
|
||||
throw throwable
|
||||
}
|
||||
aliasAvailabilityChecker.check(params.roomAliasName)
|
||||
} catch (aliasError: RoomAliasError) {
|
||||
throw CreateRoomFailure.AliasError(aliasError)
|
||||
}
|
||||
?.let {
|
||||
// Alias already exists: error case
|
||||
throw CreateRoomFailure.RoomAliasError.AliasNotAvailable
|
||||
}
|
||||
}
|
||||
|
||||
val createRoomBody = createRoomBodyBuilder.build(params)
|
||||
|
@ -104,7 +87,7 @@ internal class DefaultCreateRoomTask @Inject constructor(
|
|||
} else if (throwable.httpCode == 400
|
||||
&& throwable.error.code == MatrixError.M_UNKNOWN
|
||||
&& throwable.error.message == "Invalid characters in room alias") {
|
||||
throw CreateRoomFailure.RoomAliasError.AliasInvalid
|
||||
throw CreateRoomFailure.AliasError(RoomAliasError.AliasInvalid)
|
||||
}
|
||||
}
|
||||
throw throwable
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* 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.internal.session.room.directory
|
||||
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
|
||||
import org.matrix.android.sdk.internal.network.executeRequest
|
||||
import org.matrix.android.sdk.internal.session.directory.DirectoryAPI
|
||||
import org.matrix.android.sdk.internal.session.directory.RoomDirectoryVisibilityJson
|
||||
import org.matrix.android.sdk.internal.task.Task
|
||||
import javax.inject.Inject
|
||||
|
||||
internal interface GetRoomDirectoryVisibilityTask : Task<GetRoomDirectoryVisibilityTask.Params, RoomDirectoryVisibility> {
|
||||
data class Params(
|
||||
val roomId: String
|
||||
)
|
||||
}
|
||||
|
||||
internal class DefaultGetRoomDirectoryVisibilityTask @Inject constructor(
|
||||
private val directoryAPI: DirectoryAPI,
|
||||
private val eventBus: EventBus
|
||||
) : GetRoomDirectoryVisibilityTask {
|
||||
|
||||
override suspend fun execute(params: GetRoomDirectoryVisibilityTask.Params): RoomDirectoryVisibility {
|
||||
return executeRequest<RoomDirectoryVisibilityJson>(eventBus) {
|
||||
apiCall = directoryAPI.getRoomDirectoryVisibility(params.roomId)
|
||||
}
|
||||
.visibility
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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.internal.session.room.directory
|
||||
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
|
||||
import org.matrix.android.sdk.internal.network.executeRequest
|
||||
import org.matrix.android.sdk.internal.session.directory.DirectoryAPI
|
||||
import org.matrix.android.sdk.internal.session.directory.RoomDirectoryVisibilityJson
|
||||
import org.matrix.android.sdk.internal.task.Task
|
||||
import javax.inject.Inject
|
||||
|
||||
internal interface SetRoomDirectoryVisibilityTask : Task<SetRoomDirectoryVisibilityTask.Params, Unit> {
|
||||
data class Params(
|
||||
val roomId: String,
|
||||
val roomDirectoryVisibility: RoomDirectoryVisibility
|
||||
)
|
||||
}
|
||||
|
||||
internal class DefaultSetRoomDirectoryVisibilityTask @Inject constructor(
|
||||
private val directoryAPI: DirectoryAPI,
|
||||
private val eventBus: EventBus
|
||||
) : SetRoomDirectoryVisibilityTask {
|
||||
|
||||
override suspend fun execute(params: SetRoomDirectoryVisibilityTask.Params) {
|
||||
executeRequest<Unit>(eventBus) {
|
||||
apiCall = directoryAPI.setRoomDirectoryVisibility(
|
||||
params.roomId,
|
||||
RoomDirectoryVisibilityJson(visibility = params.roomDirectoryVisibility)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,7 +21,6 @@ import org.matrix.android.sdk.R
|
|||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomAliasesContent
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomCanonicalAliasContent
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomNameContent
|
||||
import org.matrix.android.sdk.internal.database.mapper.ContentMapper
|
||||
|
@ -71,12 +70,6 @@ internal class RoomDisplayNameResolver @Inject constructor(
|
|||
return name
|
||||
}
|
||||
|
||||
val aliases = CurrentStateEventEntity.getOrNull(realm, roomId, type = EventType.STATE_ROOM_ALIASES, stateKey = "")?.root
|
||||
name = ContentMapper.map(aliases?.content).toModel<RoomAliasesContent>()?.aliases?.firstOrNull()
|
||||
if (!name.isNullOrEmpty()) {
|
||||
return name
|
||||
}
|
||||
|
||||
val roomMembers = RoomMemberHelper(realm, roomId)
|
||||
val activeMembers = roomMembers.queryActiveRoomMembersEvent().findAll()
|
||||
|
||||
|
|
|
@ -24,7 +24,13 @@ import org.matrix.android.sdk.api.MatrixCallback
|
|||
import org.matrix.android.sdk.api.query.QueryStringValue
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||
import org.matrix.android.sdk.api.session.room.model.GuestAccess
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomCanonicalAliasContent
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomGuestAccessContent
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesContent
|
||||
import org.matrix.android.sdk.api.session.room.state.StateService
|
||||
import org.matrix.android.sdk.api.util.Cancelable
|
||||
import org.matrix.android.sdk.api.util.JsonDict
|
||||
|
@ -104,18 +110,19 @@ internal class DefaultStateService @AssistedInject constructor(@Assisted private
|
|||
)
|
||||
}
|
||||
|
||||
override fun addRoomAlias(roomAlias: String, callback: MatrixCallback<Unit>): Cancelable {
|
||||
return addRoomAliasTask
|
||||
.configureWith(AddRoomAliasTask.Params(roomId, roomAlias)) {
|
||||
this.callback = callback
|
||||
}
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
|
||||
override fun updateCanonicalAlias(alias: String, callback: MatrixCallback<Unit>): Cancelable {
|
||||
override fun updateCanonicalAlias(alias: String?, altAliases: List<String>, callback: MatrixCallback<Unit>): Cancelable {
|
||||
return sendStateEvent(
|
||||
eventType = EventType.STATE_ROOM_CANONICAL_ALIAS,
|
||||
body = mapOf("alias" to alias),
|
||||
body = RoomCanonicalAliasContent(
|
||||
canonicalAlias = alias,
|
||||
alternativeAliases = altAliases
|
||||
// Ensure there is no duplicate
|
||||
.distinct()
|
||||
// Ensure the canonical alias is not also included in the alt alias
|
||||
.minus(listOfNotNull(alias))
|
||||
// Sort for the cleanup
|
||||
.sorted()
|
||||
).toContent(),
|
||||
callback = callback,
|
||||
stateKey = null
|
||||
)
|
||||
|
@ -130,6 +137,31 @@ internal class DefaultStateService @AssistedInject constructor(@Assisted private
|
|||
)
|
||||
}
|
||||
|
||||
override fun updateJoinRule(joinRules: RoomJoinRules?, guestAccess: GuestAccess?, callback: MatrixCallback<Unit>): Cancelable {
|
||||
return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, callback) {
|
||||
if (joinRules != null) {
|
||||
awaitCallback<Unit> {
|
||||
sendStateEvent(
|
||||
eventType = EventType.STATE_ROOM_JOIN_RULES,
|
||||
body = RoomJoinRulesContent(joinRules).toContent(),
|
||||
callback = it,
|
||||
stateKey = null
|
||||
)
|
||||
}
|
||||
}
|
||||
if (guestAccess != null) {
|
||||
awaitCallback<Unit> {
|
||||
sendStateEvent(
|
||||
eventType = EventType.STATE_ROOM_GUEST_ACCESS,
|
||||
body = RoomGuestAccessContent(guestAccess).toContent(),
|
||||
callback = it,
|
||||
stateKey = null
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateAvatar(avatarUri: Uri, fileName: String, callback: MatrixCallback<Unit>): Cancelable {
|
||||
return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, callback) {
|
||||
val response = fileUploader.uploadFromUri(avatarUri, fileName, "image/jpeg")
|
||||
|
|
|
@ -18,17 +18,12 @@ package org.matrix.android.sdk.internal.session.room.uploads
|
|||
|
||||
import com.squareup.inject.assisted.Assisted
|
||||
import com.squareup.inject.assisted.AssistedInject
|
||||
import org.matrix.android.sdk.api.MatrixCallback
|
||||
import org.matrix.android.sdk.api.session.crypto.CryptoService
|
||||
import org.matrix.android.sdk.api.session.room.uploads.GetUploadsResult
|
||||
import org.matrix.android.sdk.api.session.room.uploads.UploadsService
|
||||
import org.matrix.android.sdk.api.util.Cancelable
|
||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
||||
import org.matrix.android.sdk.internal.task.configureWith
|
||||
|
||||
internal class DefaultUploadsService @AssistedInject constructor(
|
||||
@Assisted private val roomId: String,
|
||||
private val taskExecutor: TaskExecutor,
|
||||
private val getUploadsTask: GetUploadsTask,
|
||||
private val cryptoService: CryptoService
|
||||
) : UploadsService {
|
||||
|
@ -38,11 +33,7 @@ internal class DefaultUploadsService @AssistedInject constructor(
|
|||
fun create(roomId: String): UploadsService
|
||||
}
|
||||
|
||||
override fun getUploads(numberOfEvents: Int, since: String?, callback: MatrixCallback<GetUploadsResult>): Cancelable {
|
||||
return getUploadsTask
|
||||
.configureWith(GetUploadsTask.Params(roomId, cryptoService.isRoomEncrypted(roomId), numberOfEvents, since)) {
|
||||
this.callback = callback
|
||||
}
|
||||
.executeBy(taskExecutor)
|
||||
override suspend fun getUploads(numberOfEvents: Int, since: String?): GetUploadsResult {
|
||||
return getUploadsTask.execute(GetUploadsTask.Params(roomId, cryptoService.isRoomEncrypted(roomId), numberOfEvents, since))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,11 +17,10 @@
|
|||
package org.matrix.android.sdk.internal.session.terms
|
||||
|
||||
import dagger.Lazy
|
||||
import org.matrix.android.sdk.api.MatrixCallback
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.terms.GetTermsResponse
|
||||
import org.matrix.android.sdk.api.session.terms.TermsService
|
||||
import org.matrix.android.sdk.api.util.Cancelable
|
||||
import org.matrix.android.sdk.internal.di.UnauthenticatedWithCertificate
|
||||
import org.matrix.android.sdk.internal.network.NetworkConstants
|
||||
import org.matrix.android.sdk.internal.network.RetrofitFactory
|
||||
|
@ -33,8 +32,6 @@ import org.matrix.android.sdk.internal.session.sync.model.accountdata.AcceptedTe
|
|||
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes
|
||||
import org.matrix.android.sdk.internal.session.user.accountdata.AccountDataDataSource
|
||||
import org.matrix.android.sdk.internal.session.user.accountdata.UpdateUserAccountDataTask
|
||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
||||
import org.matrix.android.sdk.internal.task.launchToCallback
|
||||
import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
|
||||
import org.matrix.android.sdk.internal.util.ensureTrailingSlash
|
||||
import okhttp3.OkHttpClient
|
||||
|
@ -49,13 +46,11 @@ internal class DefaultTermsService @Inject constructor(
|
|||
private val getOpenIdTokenTask: GetOpenIdTokenTask,
|
||||
private val identityRegisterTask: IdentityRegisterTask,
|
||||
private val updateUserAccountDataTask: UpdateUserAccountDataTask,
|
||||
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||
private val taskExecutor: TaskExecutor
|
||||
private val coroutineDispatchers: MatrixCoroutineDispatchers
|
||||
) : TermsService {
|
||||
override fun getTerms(serviceType: TermsService.ServiceType,
|
||||
baseUrl: String,
|
||||
callback: MatrixCallback<GetTermsResponse>): Cancelable {
|
||||
return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, callback) {
|
||||
override suspend fun getTerms(serviceType: TermsService.ServiceType,
|
||||
baseUrl: String): GetTermsResponse {
|
||||
return withContext(coroutineDispatchers.main) {
|
||||
val url = buildUrl(baseUrl, serviceType)
|
||||
val termsResponse = executeRequest<TermsResponse>(null) {
|
||||
apiCall = termsAPI.getTerms("${url}terms")
|
||||
|
@ -64,12 +59,11 @@ internal class DefaultTermsService @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun agreeToTerms(serviceType: TermsService.ServiceType,
|
||||
baseUrl: String,
|
||||
agreedUrls: List<String>,
|
||||
token: String?,
|
||||
callback: MatrixCallback<Unit>): Cancelable {
|
||||
return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, callback) {
|
||||
override suspend fun agreeToTerms(serviceType: TermsService.ServiceType,
|
||||
baseUrl: String,
|
||||
agreedUrls: List<String>,
|
||||
token: String?) {
|
||||
withContext(coroutineDispatchers.main) {
|
||||
val url = buildUrl(baseUrl, serviceType)
|
||||
val tokenToUse = token?.takeIf { it.isNotEmpty() } ?: getToken(baseUrl)
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package org.matrix.android.sdk.internal.util
|
||||
|
||||
import java.security.MessageDigest
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* Compute a Hash of a String, using md5 algorithm
|
||||
|
@ -26,7 +27,7 @@ fun String.md5() = try {
|
|||
digest.update(toByteArray())
|
||||
digest.digest()
|
||||
.joinToString("") { String.format("%02X", it) }
|
||||
.toLowerCase()
|
||||
.toLowerCase(Locale.ROOT)
|
||||
} catch (exc: Exception) {
|
||||
// Should not happen, but just in case
|
||||
hashCode().toString()
|
||||
|
|
|
@ -246,7 +246,7 @@
|
|||
|
||||
<plurals name="notice_room_aliases_removed">
|
||||
<item quantity="one">%1$s removed %2$s as an address for this room.</item>
|
||||
<item quantity="other">%1$s removed %3$s as addresses for this room.</item>
|
||||
<item quantity="other">%1$s removed %2$s as addresses for this room.</item>
|
||||
</plurals>
|
||||
|
||||
<plurals name="notice_room_aliases_removed_by_you">
|
||||
|
@ -262,6 +262,33 @@
|
|||
<string name="notice_room_canonical_alias_unset">"%1$s removed the main address for this room."</string>
|
||||
<string name="notice_room_canonical_alias_unset_by_you">"You removed the main address for this room."</string>
|
||||
|
||||
<plurals name="notice_room_canonical_alias_alternative_added">
|
||||
<item quantity="one">%1$s added the alternative address %2$s for this room.</item>
|
||||
<item quantity="other">%1$s added the alternative addresses %2$s for this room.</item>
|
||||
</plurals>
|
||||
|
||||
<plurals name="notice_room_canonical_alias_alternative_added_by_you">
|
||||
<item quantity="one">You added the alternative address %1$s for this room.</item>
|
||||
<item quantity="other">You added the alternative addresses %1$s for this room.</item>
|
||||
</plurals>
|
||||
|
||||
<plurals name="notice_room_canonical_alias_alternative_removed">
|
||||
<item quantity="one">%1$s removed the alternative address %2$s for this room.</item>
|
||||
<item quantity="other">%1$s removed the alternative addresses %2$s for this room.</item>
|
||||
</plurals>
|
||||
|
||||
<plurals name="notice_room_canonical_alias_alternative_removed_by_you">
|
||||
<item quantity="one">You removed the alternative address %1$s for this room.</item>
|
||||
<item quantity="other">You removed the alternative addresses %1$s for this room.</item>
|
||||
</plurals>
|
||||
|
||||
<string name="notice_room_canonical_alias_alternative_changed">%1$s changed the alternative addresses for this room.</string>
|
||||
<string name="notice_room_canonical_alias_alternative_changed_by_you">You changed the alternative addresses for this room.</string>
|
||||
<string name="notice_room_canonical_alias_main_and_alternative_changed">%1$s changed the main and alternative addresses for this room.</string>
|
||||
<string name="notice_room_canonical_alias_main_and_alternative_changed_by_you">You changed the main and alternative addresses for this room.</string>
|
||||
<string name="notice_room_canonical_alias_no_change">%1$s changed the addresses for this room.</string>
|
||||
<string name="notice_room_canonical_alias_no_change_by_you">You changed the addresses for this room.</string>
|
||||
|
||||
<string name="notice_room_guest_access_can_join">"%1$s has allowed guests to join the room."</string>
|
||||
<string name="notice_room_guest_access_can_join_by_you">"You have allowed guests to join the room."</string>
|
||||
<string name="notice_direct_room_guest_access_can_join">"%1$s has allowed guests to join here."</string>
|
||||
|
|
|
@ -43,8 +43,8 @@ android {
|
|||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||
implementation "androidx.fragment:fragment:1.3.0-beta01"
|
||||
implementation 'androidx.exifinterface:exifinterface:1.3.0'
|
||||
implementation "androidx.fragment:fragment-ktx:1.3.0-beta01"
|
||||
implementation 'androidx.exifinterface:exifinterface:1.3.1'
|
||||
|
||||
// Log
|
||||
implementation 'com.jakewharton.timber:timber:4.7.1'
|
||||
|
|
|
@ -315,9 +315,8 @@ dependencies {
|
|||
|
||||
implementation "androidx.recyclerview:recyclerview:1.2.0-alpha06"
|
||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||
implementation "androidx.fragment:fragment:$fragment_version"
|
||||
implementation "androidx.fragment:fragment-ktx:$fragment_version"
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.2'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
|
||||
implementation "androidx.sharetarget:sharetarget:1.0.0"
|
||||
implementation 'androidx.core:core-ktx:1.3.2'
|
||||
|
||||
|
@ -362,11 +361,11 @@ dependencies {
|
|||
implementation "io.arrow-kt:arrow-core:$arrow_version"
|
||||
|
||||
// Pref
|
||||
implementation 'androidx.preference:preference:1.1.1'
|
||||
implementation 'androidx.preference:preference-ktx:1.1.1'
|
||||
|
||||
// UI
|
||||
implementation 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1'
|
||||
implementation 'com.google.android.material:material:1.3.0-alpha02'
|
||||
implementation 'com.google.android.material:material:1.3.0-alpha04'
|
||||
implementation 'me.gujun.android:span:1.7'
|
||||
implementation "io.noties.markwon:core:$markwon_version"
|
||||
implementation "io.noties.markwon:html:$markwon_version"
|
||||
|
@ -374,7 +373,7 @@ dependencies {
|
|||
implementation 'me.saket:better-link-movement-method:2.2.0'
|
||||
implementation 'com.google.android:flexbox:1.1.1'
|
||||
implementation "androidx.autofill:autofill:$autofill_version"
|
||||
implementation 'com.github.vector-im:PFLockScreen-Android:1.0.0-beta10'
|
||||
implementation 'com.github.vector-im:PFLockScreen-Android:1.0.0-beta12'
|
||||
|
||||
// Custom Tab
|
||||
implementation 'androidx.browser:browser:1.2.0'
|
||||
|
@ -418,7 +417,7 @@ dependencies {
|
|||
kapt 'com.squareup.inject:assisted-inject-processor-dagger2:0.5.0'
|
||||
|
||||
// gplay flavor only
|
||||
gplayImplementation('com.google.firebase:firebase-messaging:20.3.0') {
|
||||
gplayImplementation('com.google.firebase:firebase-messaging:21.0.0') {
|
||||
exclude group: 'com.google.firebase', module: 'firebase-core'
|
||||
exclude group: 'com.google.firebase', module: 'firebase-analytics'
|
||||
exclude group: 'com.google.firebase', module: 'firebase-measurement-connector'
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
<issue id="ObsoleteSdkInt" severity="error" />
|
||||
<issue id="Recycle" severity="error" />
|
||||
<issue id="KotlinPropertyAccess" severity="error" />
|
||||
<issue id="DefaultLocale" severity="error" />
|
||||
|
||||
<issue id="InvalidPackage">
|
||||
<!-- Ignore error from HtmlCompressor lib -->
|
||||
|
@ -52,6 +53,9 @@
|
|||
<!-- Manifest -->
|
||||
<issue id="PermissionImpliesUnsupportedChromeOsHardware" severity="error" />
|
||||
|
||||
<!-- Dependencies -->
|
||||
<issue id="KtxExtensionAvailable" severity="error" />
|
||||
|
||||
<!-- Timber -->
|
||||
<!-- This rule is failing on CI because it's marked as unknwown rule id :/-->
|
||||
<!-- <issue id="BinaryOperationInTimber" severity="error" />-->
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
android:id="@+id/test_linkify_coordinator"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/riot_secondary_text_color_status"
|
||||
android:background="#7F70808D"
|
||||
tools:context=".features.debug.TestLinkifyActivity">
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
|
|
|
@ -41,14 +41,4 @@
|
|||
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:theme="@style/AppTheme.Status">
|
||||
|
||||
<include layout="@layout/demo_theme_sample" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</LinearLayout>
|
|
@ -18,7 +18,7 @@ package im.vector.app.gplay.features.settings.troubleshoot
|
|||
import android.content.Intent
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.google.firebase.iid.FirebaseInstanceId
|
||||
import com.google.firebase.messaging.FirebaseMessaging
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import im.vector.app.core.utils.startAddGoogleAccountIntent
|
||||
|
@ -36,29 +36,33 @@ class TestFirebaseToken @Inject constructor(private val context: AppCompatActivi
|
|||
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||
status = TestStatus.RUNNING
|
||||
try {
|
||||
FirebaseInstanceId.getInstance().instanceId
|
||||
FirebaseMessaging.getInstance().token
|
||||
.addOnCompleteListener(context) { task ->
|
||||
if (!task.isSuccessful) {
|
||||
val errorMsg = if (task.exception == null) "Unknown" else task.exception!!.localizedMessage
|
||||
// Can't find where this constant is (not documented -or deprecated in docs- and all obfuscated)
|
||||
if ("SERVICE_NOT_AVAILABLE".equals(errorMsg)) {
|
||||
description = stringProvider.getString(R.string.settings_troubleshoot_test_fcm_failed_service_not_available, errorMsg)
|
||||
} else if ("TOO_MANY_REGISTRATIONS".equals(errorMsg)) {
|
||||
description = stringProvider.getString(R.string.settings_troubleshoot_test_fcm_failed_too_many_registration, errorMsg)
|
||||
} else if ("ACCOUNT_MISSING".equals(errorMsg)) {
|
||||
description = stringProvider.getString(R.string.settings_troubleshoot_test_fcm_failed_account_missing, errorMsg)
|
||||
quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_fcm_failed_account_missing_quick_fix) {
|
||||
override fun doFix() {
|
||||
startAddGoogleAccountIntent(context, activityResultLauncher)
|
||||
}
|
||||
description = when (val errorMsg = task.exception?.localizedMessage ?: "Unknown") {
|
||||
"SERVICE_NOT_AVAILABLE" -> {
|
||||
stringProvider.getString(R.string.settings_troubleshoot_test_fcm_failed_service_not_available, errorMsg)
|
||||
}
|
||||
"TOO_MANY_REGISTRATIONS" -> {
|
||||
stringProvider.getString(R.string.settings_troubleshoot_test_fcm_failed_too_many_registration, errorMsg)
|
||||
}
|
||||
"ACCOUNT_MISSING" -> {
|
||||
quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_fcm_failed_account_missing_quick_fix) {
|
||||
override fun doFix() {
|
||||
startAddGoogleAccountIntent(context, activityResultLauncher)
|
||||
}
|
||||
}
|
||||
stringProvider.getString(R.string.settings_troubleshoot_test_fcm_failed_account_missing, errorMsg)
|
||||
}
|
||||
else -> {
|
||||
stringProvider.getString(R.string.settings_troubleshoot_test_fcm_failed, errorMsg)
|
||||
}
|
||||
} else {
|
||||
description = stringProvider.getString(R.string.settings_troubleshoot_test_fcm_failed, errorMsg)
|
||||
}
|
||||
status = TestStatus.FAILED
|
||||
} else {
|
||||
task.result?.token?.let { token ->
|
||||
val tok = token.substring(0, Math.min(8, token.length)) + "********************"
|
||||
task.result?.let { token ->
|
||||
val tok = token.take(8) + "********************"
|
||||
description = stringProvider.getString(R.string.settings_troubleshoot_test_fcm_success, tok)
|
||||
Timber.e("Retrieved FCM token success [$tok].")
|
||||
// Ensure it is well store in our local storage
|
||||
|
|
|
@ -21,7 +21,7 @@ import android.widget.Toast
|
|||
import androidx.core.content.edit
|
||||
import com.google.android.gms.common.ConnectionResult
|
||||
import com.google.android.gms.common.GoogleApiAvailability
|
||||
import com.google.firebase.iid.FirebaseInstanceId
|
||||
import com.google.firebase.messaging.FirebaseMessaging
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.di.ActiveSessionHolder
|
||||
import im.vector.app.core.di.DefaultSharedPreferences
|
||||
|
@ -71,14 +71,16 @@ object FcmHelper {
|
|||
// 'app should always check the device for a compatible Google Play services APK before accessing Google Play services features'
|
||||
if (checkPlayServices(activity)) {
|
||||
try {
|
||||
FirebaseInstanceId.getInstance().instanceId
|
||||
.addOnSuccessListener(activity) { instanceIdResult ->
|
||||
storeFcmToken(activity, instanceIdResult.token)
|
||||
FirebaseMessaging.getInstance().token
|
||||
.addOnSuccessListener { token ->
|
||||
storeFcmToken(activity, token)
|
||||
if (registerPusher) {
|
||||
pushersManager.registerPusherWithFcmKey(instanceIdResult.token)
|
||||
pushersManager.registerPusherWithFcmKey(token)
|
||||
}
|
||||
}
|
||||
.addOnFailureListener(activity) { e -> Timber.e(e, "## ensureFcmTokenIsRetrieved() : failed") }
|
||||
.addOnFailureListener { e ->
|
||||
Timber.e(e, "## ensureFcmTokenIsRetrieved() : failed")
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
Timber.e(e, "## ensureFcmTokenIsRetrieved() : failed")
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ import im.vector.app.features.crypto.recover.BootstrapMigrateBackupFragment
|
|||
import im.vector.app.features.crypto.recover.BootstrapSaveRecoveryKeyFragment
|
||||
import im.vector.app.features.crypto.recover.BootstrapSetupRecoveryKeyFragment
|
||||
import im.vector.app.features.crypto.recover.BootstrapWaitingFragment
|
||||
import im.vector.app.features.crypto.verification.QuadSLoadingFragment
|
||||
import im.vector.app.features.crypto.verification.cancel.VerificationCancelFragment
|
||||
import im.vector.app.features.crypto.verification.cancel.VerificationNotMeFragment
|
||||
import im.vector.app.features.crypto.verification.choose.VerificationChooseMethodFragment
|
||||
|
@ -83,6 +84,7 @@ import im.vector.app.features.roomprofile.RoomProfileFragment
|
|||
import im.vector.app.features.roomprofile.banned.RoomBannedMemberListFragment
|
||||
import im.vector.app.features.roomprofile.members.RoomMemberListFragment
|
||||
import im.vector.app.features.roomprofile.settings.RoomSettingsFragment
|
||||
import im.vector.app.features.roomprofile.alias.RoomAliasFragment
|
||||
import im.vector.app.features.roomprofile.uploads.RoomUploadsFragment
|
||||
import im.vector.app.features.roomprofile.uploads.files.RoomUploadsFilesFragment
|
||||
import im.vector.app.features.roomprofile.uploads.media.RoomUploadsMediaFragment
|
||||
|
@ -363,6 +365,11 @@ interface FragmentModule {
|
|||
@FragmentKey(RoomSettingsFragment::class)
|
||||
fun bindRoomSettingsFragment(fragment: RoomSettingsFragment): Fragment
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FragmentKey(RoomAliasFragment::class)
|
||||
fun bindRoomAliasFragment(fragment: RoomAliasFragment): Fragment
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FragmentKey(RoomMemberProfileFragment::class)
|
||||
|
@ -418,6 +425,11 @@ interface FragmentModule {
|
|||
@FragmentKey(VerificationCancelFragment::class)
|
||||
fun bindVerificationCancelFragment(fragment: VerificationCancelFragment): Fragment
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FragmentKey(QuadSLoadingFragment::class)
|
||||
fun bindQuadSLoadingFragment(fragment: QuadSLoadingFragment): Fragment
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FragmentKey(VerificationNotMeFragment::class)
|
||||
|
|
|
@ -67,6 +67,9 @@ import im.vector.app.features.roomdirectory.createroom.CreateRoomActivity
|
|||
import im.vector.app.features.roommemberprofile.RoomMemberProfileActivity
|
||||
import im.vector.app.features.roommemberprofile.devices.DeviceListBottomSheet
|
||||
import im.vector.app.features.roomprofile.RoomProfileActivity
|
||||
import im.vector.app.features.roomprofile.alias.detail.RoomAliasBottomSheet
|
||||
import im.vector.app.features.roomprofile.settings.historyvisibility.RoomHistoryVisibilityBottomSheet
|
||||
import im.vector.app.features.roomprofile.settings.joinrule.RoomJoinRuleBottomSheet
|
||||
import im.vector.app.features.settings.VectorSettingsActivity
|
||||
import im.vector.app.features.settings.devices.DeviceVerificationInfoBottomSheet
|
||||
import im.vector.app.features.share.IncomingShareActivity
|
||||
|
@ -153,6 +156,9 @@ interface ScreenComponent {
|
|||
fun inject(bottomSheet: ViewEditHistoryBottomSheet)
|
||||
fun inject(bottomSheet: DisplayReadReceiptsBottomSheet)
|
||||
fun inject(bottomSheet: RoomListQuickActionsBottomSheet)
|
||||
fun inject(bottomSheet: RoomAliasBottomSheet)
|
||||
fun inject(bottomSheet: RoomHistoryVisibilityBottomSheet)
|
||||
fun inject(bottomSheet: RoomJoinRuleBottomSheet)
|
||||
fun inject(bottomSheet: VerificationBottomSheet)
|
||||
fun inject(bottomSheet: DeviceVerificationInfoBottomSheet)
|
||||
fun inject(bottomSheet: DeviceListBottomSheet)
|
||||
|
|
|
@ -35,6 +35,9 @@ import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedA
|
|||
import im.vector.app.features.reactions.EmojiChooserViewModel
|
||||
import im.vector.app.features.roomdirectory.RoomDirectorySharedActionViewModel
|
||||
import im.vector.app.features.roomprofile.RoomProfileSharedActionViewModel
|
||||
import im.vector.app.features.roomprofile.alias.detail.RoomAliasBottomSheetSharedActionViewModel
|
||||
import im.vector.app.features.roomprofile.settings.historyvisibility.RoomHistoryVisibilitySharedActionViewModel
|
||||
import im.vector.app.features.roomprofile.settings.joinrule.RoomJoinRuleSharedActionViewModel
|
||||
import im.vector.app.features.userdirectory.UserListSharedActionViewModel
|
||||
|
||||
@Module
|
||||
|
@ -105,6 +108,21 @@ interface ViewModelModule {
|
|||
@ViewModelKey(RoomListQuickActionsSharedActionViewModel::class)
|
||||
fun bindRoomListQuickActionsSharedActionViewModel(viewModel: RoomListQuickActionsSharedActionViewModel): ViewModel
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ViewModelKey(RoomAliasBottomSheetSharedActionViewModel::class)
|
||||
fun bindRoomAliasBottomSheetSharedActionViewModel(viewModel: RoomAliasBottomSheetSharedActionViewModel): ViewModel
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ViewModelKey(RoomHistoryVisibilitySharedActionViewModel::class)
|
||||
fun bindRoomHistoryVisibilitySharedActionViewModel(viewModel: RoomHistoryVisibilitySharedActionViewModel): ViewModel
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ViewModelKey(RoomJoinRuleSharedActionViewModel::class)
|
||||
fun bindRoomJoinRuleSharedActionViewModel(viewModel: RoomJoinRuleSharedActionViewModel): ViewModel
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ViewModelKey(RoomDirectorySharedActionViewModel::class)
|
||||
|
|
|
@ -21,6 +21,7 @@ import android.view.View
|
|||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.graphics.drawable.DrawableCompat
|
||||
import androidx.core.view.isInvisible
|
||||
|
@ -43,6 +44,13 @@ abstract class BottomSheetActionItem : VectorEpoxyModel<BottomSheetActionItem.Ho
|
|||
@DrawableRes
|
||||
var iconRes: Int = 0
|
||||
|
||||
@EpoxyAttribute
|
||||
var showIcon = true
|
||||
|
||||
@EpoxyAttribute
|
||||
var text: String? = null
|
||||
|
||||
@StringRes
|
||||
@EpoxyAttribute
|
||||
var textRes: Int = 0
|
||||
|
||||
|
@ -75,9 +83,14 @@ abstract class BottomSheetActionItem : VectorEpoxyModel<BottomSheetActionItem.Ho
|
|||
} else {
|
||||
ThemeUtils.getColor(holder.view.context, R.attr.riotx_text_secondary)
|
||||
}
|
||||
holder.icon.isVisible = showIcon
|
||||
holder.icon.setImageResource(iconRes)
|
||||
ImageViewCompat.setImageTintList(holder.icon, ColorStateList.valueOf(tintColor))
|
||||
holder.text.setText(textRes)
|
||||
if (text != null) {
|
||||
holder.text.text = text
|
||||
} else {
|
||||
holder.text.setText(textRes)
|
||||
}
|
||||
holder.text.setTextColor(tintColor)
|
||||
holder.selected.isInvisible = !selected
|
||||
if (showExpand) {
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
package im.vector.app.core.epoxy.profiles
|
||||
|
||||
import android.content.res.ColorStateList
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
|
@ -26,8 +25,10 @@ import androidx.core.widget.ImageViewCompat
|
|||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import im.vector.app.R
|
||||
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.setTextOrHide
|
||||
import im.vector.app.features.home.AvatarRenderer
|
||||
import im.vector.app.features.themes.ThemeUtils
|
||||
|
@ -67,11 +68,11 @@ abstract class ProfileActionItem : VectorEpoxyModel<ProfileActionItem.Holder>()
|
|||
var destructive: Boolean = false
|
||||
|
||||
@EpoxyAttribute
|
||||
var listener: View.OnClickListener? = null
|
||||
var listener: ClickListener? = null
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
super.bind(holder)
|
||||
holder.view.setOnClickListener(listener)
|
||||
holder.view.onClick(listener)
|
||||
if (listener == null) {
|
||||
holder.view.isClickable = false
|
||||
}
|
||||
|
|
|
@ -59,9 +59,7 @@ fun EpoxyController.buildProfileAction(
|
|||
accessoryRes(accessory)
|
||||
accessoryMatrixItem(accessoryMatrixItem)
|
||||
avatarRenderer(avatarRenderer)
|
||||
listener { _ ->
|
||||
action?.invoke()
|
||||
}
|
||||
listener(action)
|
||||
}
|
||||
|
||||
if (divider) {
|
||||
|
|
|
@ -57,3 +57,15 @@ fun EditText.setupAsSearch(@DrawableRes searchIconRes: Int = R.drawable.ic_searc
|
|||
return@OnTouchListener false
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the edit text value, only if necessary and move the cursor to the end of the text
|
||||
*/
|
||||
fun EditText.setTextSafe(value: String?) {
|
||||
if (value != null && text.toString() != value) {
|
||||
setText(value)
|
||||
// To fix jumping cursor to the start https://github.com/airbnb/epoxy/issues/426
|
||||
// Note: there is still a known bug if deleting char in the middle of the text, by long pressing on the backspace button.
|
||||
setSelection(value.length)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import android.net.Uri
|
|||
import android.webkit.MimeTypeMap
|
||||
import im.vector.app.core.utils.getFileExtension
|
||||
import timber.log.Timber
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* Returns the mimetype from a uri.
|
||||
|
@ -44,7 +45,7 @@ fun getMimeTypeFromUri(context: Context, uri: Uri): String? {
|
|||
|
||||
if (null != mimeType) {
|
||||
// the mimetype is sometimes in uppercase.
|
||||
mimeType = mimeType.toLowerCase()
|
||||
mimeType = mimeType.toLowerCase(Locale.ROOT)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Failed to open resource input stream")
|
||||
|
|
|
@ -43,7 +43,7 @@ abstract class VectorViewModel<S : MvRxState, VA : VectorViewModelAction, VE : V
|
|||
* so you can use this in a switchMap or a flatMap
|
||||
*/
|
||||
// False positive
|
||||
@Suppress("USELESS_CAST")
|
||||
@Suppress("USELESS_CAST", "NULLABLE_TYPE_PARAMETER_AGAINST_NOT_NULL_TYPE_PARAMETER")
|
||||
fun <T> Single<T>.toAsync(stateReducer: S.(Async<T>) -> S): Single<Async<T>> {
|
||||
setState { stateReducer(Loading()) }
|
||||
return map { Success(it) as Async<T> }
|
||||
|
@ -56,7 +56,7 @@ abstract class VectorViewModel<S : MvRxState, VA : VectorViewModelAction, VE : V
|
|||
* so you can use this in a switchMap or a flatMap
|
||||
*/
|
||||
// False positive
|
||||
@Suppress("USELESS_CAST")
|
||||
@Suppress("USELESS_CAST", "NULLABLE_TYPE_PARAMETER_AGAINST_NOT_NULL_TYPE_PARAMETER")
|
||||
fun <T> Observable<T>.toAsync(stateReducer: S.(Async<T>) -> S): Observable<Async<T>> {
|
||||
setState { stateReducer(Loading()) }
|
||||
return map { Success(it) as Async<T> }
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* 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 im.vector.app.core.ui.bottomsheet
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.annotation.CallSuper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import butterknife.BindView
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.extensions.cleanup
|
||||
import im.vector.app.core.extensions.configureWith
|
||||
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Generic Bottom sheet with actions
|
||||
*/
|
||||
abstract class BottomSheetGeneric<STATE : BottomSheetGenericState, ACTION : BottomSheetGenericAction> :
|
||||
VectorBaseBottomSheetDialogFragment(),
|
||||
BottomSheetGenericController.Listener<ACTION> {
|
||||
|
||||
@Inject lateinit var sharedViewPool: RecyclerView.RecycledViewPool
|
||||
|
||||
@BindView(R.id.bottomSheetRecyclerView)
|
||||
lateinit var recyclerView: RecyclerView
|
||||
|
||||
final override val showExpanded = true
|
||||
|
||||
final override fun getLayoutResId() = R.layout.bottom_sheet_generic_list
|
||||
|
||||
abstract fun getController(): BottomSheetGenericController<STATE, ACTION>
|
||||
|
||||
@CallSuper
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
recyclerView.configureWith(getController(), viewPool = sharedViewPool, hasFixedSize = false, disableItemAnimation = true)
|
||||
getController().listener = this
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
override fun onDestroyView() {
|
||||
recyclerView.cleanup()
|
||||
getController().listener = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* 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 im.vector.app.core.ui.bottomsheet
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import im.vector.app.core.epoxy.bottomsheet.BottomSheetActionItem_
|
||||
import im.vector.app.core.platform.VectorSharedAction
|
||||
|
||||
/**
|
||||
* Parent class for a bottom sheet action
|
||||
*/
|
||||
open class BottomSheetGenericAction(
|
||||
open val title: String,
|
||||
@DrawableRes open val iconResId: Int,
|
||||
open val isSelected: Boolean,
|
||||
open val destructive: Boolean
|
||||
) : VectorSharedAction {
|
||||
|
||||
fun toBottomSheetItem(): BottomSheetActionItem_ {
|
||||
return BottomSheetActionItem_().apply {
|
||||
id("action_$title")
|
||||
iconRes(iconResId)
|
||||
text(title)
|
||||
selected(isSelected)
|
||||
destructive(destructive)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* 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 im.vector.app.core.ui.bottomsheet
|
||||
|
||||
import android.view.View
|
||||
import com.airbnb.epoxy.TypedEpoxyController
|
||||
import im.vector.app.core.epoxy.dividerItem
|
||||
|
||||
/**
|
||||
* Epoxy controller for generic bottom sheet actions
|
||||
*/
|
||||
abstract class BottomSheetGenericController<State : BottomSheetGenericState, Action : BottomSheetGenericAction>
|
||||
: TypedEpoxyController<State>() {
|
||||
|
||||
var listener: Listener<Action>? = null
|
||||
|
||||
abstract fun getTitle(): String?
|
||||
|
||||
open fun getSubTitle(): String? = null
|
||||
|
||||
abstract fun getActions(state: State): List<Action>
|
||||
|
||||
override fun buildModels(state: State?) {
|
||||
state ?: return
|
||||
// Title
|
||||
getTitle()?.let { title ->
|
||||
bottomSheetTitleItem {
|
||||
id("title")
|
||||
title(title)
|
||||
subTitle(getSubTitle())
|
||||
}
|
||||
|
||||
dividerItem {
|
||||
id("title_separator")
|
||||
}
|
||||
}
|
||||
// Actions
|
||||
val actions = getActions(state)
|
||||
val showIcons = actions.any { it.iconResId > 0 }
|
||||
actions.forEach { action ->
|
||||
action.toBottomSheetItem()
|
||||
.showIcon(showIcons)
|
||||
.listener(View.OnClickListener { listener?.didSelectAction(action) })
|
||||
.addTo(this)
|
||||
}
|
||||
}
|
||||
|
||||
interface Listener<Action> {
|
||||
fun didSelectAction(action: Action)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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 im.vector.app.core.ui.bottomsheet
|
||||
|
||||
import im.vector.app.core.platform.VectorSharedAction
|
||||
import im.vector.app.core.platform.VectorSharedActionViewModel
|
||||
|
||||
/**
|
||||
* Activity shared view model to handle bottom sheet quick actions
|
||||
*/
|
||||
abstract class BottomSheetGenericSharedActionViewModel<Action : VectorSharedAction> : VectorSharedActionViewModel<Action>()
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* 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 im.vector.app.core.ui.bottomsheet
|
||||
|
||||
import com.airbnb.mvrx.MvRxState
|
||||
|
||||
abstract class BottomSheetGenericState : MvRxState
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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 im.vector.app.core.ui.bottomsheet
|
||||
|
||||
import com.airbnb.mvrx.MvRxState
|
||||
import im.vector.app.core.platform.EmptyAction
|
||||
import im.vector.app.core.platform.EmptyViewEvents
|
||||
import im.vector.app.core.platform.VectorViewModel
|
||||
|
||||
abstract class BottomSheetGenericViewModel<State : MvRxState>(initialState: State) :
|
||||
VectorViewModel<State, EmptyAction, EmptyViewEvents>(initialState) {
|
||||
|
||||
override fun handle(action: EmptyAction) {
|
||||
// No op
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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 im.vector.app.core.ui.bottomsheet
|
||||
|
||||
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.core.extensions.setTextOrHide
|
||||
|
||||
/**
|
||||
* A title for bottom sheet, with an optional subtitle. It does not include the bottom separator.
|
||||
*/
|
||||
@EpoxyModelClass(layout = R.layout.item_bottom_sheet_title)
|
||||
abstract class BottomSheetTitleItem : VectorEpoxyModel<BottomSheetTitleItem.Holder>() {
|
||||
|
||||
@EpoxyAttribute
|
||||
lateinit var title: String
|
||||
|
||||
@EpoxyAttribute
|
||||
var subTitle: String? = null
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
super.bind(holder)
|
||||
holder.title.text = title
|
||||
holder.subtitle.setTextOrHide(subTitle)
|
||||
}
|
||||
|
||||
class Holder : VectorEpoxyHolder() {
|
||||
val title by bind<TextView>(R.id.itemBottomSheetTitleTitle)
|
||||
val subtitle by bind<TextView>(R.id.itemBottomSheetTitleSubtitle)
|
||||
}
|
||||
}
|
|
@ -44,7 +44,7 @@ open class BehaviorDataSource<T>(private val defaultValue: T? = null) : MutableD
|
|||
}
|
||||
|
||||
override fun post(value: T) {
|
||||
behaviorRelay.accept(value)
|
||||
behaviorRelay.accept(value!!)
|
||||
}
|
||||
|
||||
private fun createRelay(): BehaviorRelay<T> {
|
||||
|
@ -68,6 +68,6 @@ open class PublishDataSource<T> : MutableDataSource<T> {
|
|||
}
|
||||
|
||||
override fun post(value: T) {
|
||||
publishRelay.accept(value)
|
||||
publishRelay.accept(value!!)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ package im.vector.app.core.utils
|
|||
import android.content.Context
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.util.Locale
|
||||
|
||||
// Implementation should return true in case of success
|
||||
typealias ActionOnFile = (file: File) -> Boolean
|
||||
|
@ -113,7 +114,7 @@ fun getFileExtension(fileUri: String): String? {
|
|||
val ext = filename.substring(dotPos + 1)
|
||||
|
||||
if (ext.isNotBlank()) {
|
||||
return ext.toLowerCase()
|
||||
return ext.toLowerCase(Locale.ROOT)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,7 +59,6 @@ data class AttachmentsPreviewArgs(
|
|||
) : Parcelable
|
||||
|
||||
class AttachmentsPreviewFragment @Inject constructor(
|
||||
val viewModelFactory: AttachmentsPreviewViewModel.Factory,
|
||||
private val attachmentMiniaturePreviewController: AttachmentMiniaturePreviewController,
|
||||
private val attachmentBigPreviewController: AttachmentBigPreviewController,
|
||||
private val colorProvider: ColorProvider
|
||||
|
|
|
@ -17,31 +17,12 @@
|
|||
|
||||
package im.vector.app.features.attachments.preview
|
||||
|
||||
import com.airbnb.mvrx.FragmentViewModelContext
|
||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||
import com.airbnb.mvrx.ViewModelContext
|
||||
import com.squareup.inject.assisted.Assisted
|
||||
import com.squareup.inject.assisted.AssistedInject
|
||||
import im.vector.app.core.extensions.exhaustive
|
||||
import im.vector.app.core.platform.VectorViewModel
|
||||
|
||||
class AttachmentsPreviewViewModel @AssistedInject constructor(@Assisted initialState: AttachmentsPreviewViewState)
|
||||
class AttachmentsPreviewViewModel(initialState: AttachmentsPreviewViewState)
|
||||
: VectorViewModel<AttachmentsPreviewViewState, AttachmentsPreviewAction, AttachmentsPreviewViewEvents>(initialState) {
|
||||
|
||||
@AssistedInject.Factory
|
||||
interface Factory {
|
||||
fun create(initialState: AttachmentsPreviewViewState): AttachmentsPreviewViewModel
|
||||
}
|
||||
|
||||
companion object : MvRxViewModelFactory<AttachmentsPreviewViewModel, AttachmentsPreviewViewState> {
|
||||
|
||||
@JvmStatic
|
||||
override fun create(viewModelContext: ViewModelContext, state: AttachmentsPreviewViewState): AttachmentsPreviewViewModel? {
|
||||
val fragment: AttachmentsPreviewFragment = (viewModelContext as FragmentViewModelContext).fragment()
|
||||
return fragment.viewModelFactory.create(state)
|
||||
}
|
||||
}
|
||||
|
||||
override fun handle(action: AttachmentsPreviewAction) {
|
||||
when (action) {
|
||||
is AttachmentsPreviewAction.SetCurrentAttachment -> handleSetCurrentAttachment(action)
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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 im.vector.app.features.crypto.verification
|
||||
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.platform.VectorBaseFragment
|
||||
import javax.inject.Inject
|
||||
|
||||
class QuadSLoadingFragment @Inject constructor() : VectorBaseFragment() {
|
||||
override fun getLayoutResId() = R.layout.fragment_progress
|
||||
}
|
|
@ -31,5 +31,6 @@ sealed class VerificationAction : VectorViewModelAction {
|
|||
object SkipVerification : VerificationAction()
|
||||
object VerifyFromPassphrase : VerificationAction()
|
||||
data class GotResultFromSsss(val cypherData: String, val alias: String) : VerificationAction()
|
||||
object CancelledFromSsss : VerificationAction()
|
||||
object SecuredStorageHasBeenReset : VerificationAction()
|
||||
}
|
||||
|
|
|
@ -155,6 +155,8 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
|||
// all have been reset, so we are verified?
|
||||
viewModel.handle(VerificationAction.SecuredStorageHasBeenReset)
|
||||
}
|
||||
} else {
|
||||
viewModel.handle(VerificationAction.CancelledFromSsss)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -209,6 +211,10 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
|||
return@withState
|
||||
}
|
||||
|
||||
if (state.selfVerificationMode && state.verifyingFrom4S) {
|
||||
showFragment(QuadSLoadingFragment::class, Bundle())
|
||||
return@withState
|
||||
}
|
||||
if (state.selfVerificationMode && state.verifiedFromPrivateKeys) {
|
||||
showFragment(VerificationConclusionFragment::class, Bundle().apply {
|
||||
putParcelable(MvRx.KEY_ARG, VerificationConclusionFragment.Args(true, null, state.isMe))
|
||||
|
|
|
@ -32,6 +32,7 @@ import im.vector.app.core.extensions.exhaustive
|
|||
import im.vector.app.core.platform.VectorViewModel
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import org.matrix.android.sdk.api.MatrixCallback
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
|
@ -70,6 +71,7 @@ data class VerificationBottomSheetViewState(
|
|||
// true when we display the loading and we wait for the other (incoming request)
|
||||
val selfVerificationMode: Boolean = false,
|
||||
val verifiedFromPrivateKeys: Boolean = false,
|
||||
val verifyingFrom4S: Boolean = false,
|
||||
val isMe: Boolean = false,
|
||||
val currentDeviceCanCrossSign: Boolean = false,
|
||||
val userWantsToCancel: Boolean = false,
|
||||
|
@ -170,7 +172,9 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(
|
|||
}
|
||||
} else {
|
||||
// if the verification is already done you can't cancel anymore
|
||||
if (state.pendingRequest.invoke()?.cancelConclusion != null || state.sasTransactionState is VerificationTxState.TerminalTxState) {
|
||||
if (state.pendingRequest.invoke()?.cancelConclusion != null
|
||||
|| state.sasTransactionState is VerificationTxState.TerminalTxState
|
||||
|| state.verifyingFrom4S) {
|
||||
// you cannot cancel anymore
|
||||
} else {
|
||||
setState {
|
||||
|
@ -346,6 +350,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(
|
|||
_viewEvents.post(VerificationBottomSheetViewEvents.Dismiss)
|
||||
}
|
||||
is VerificationAction.VerifyFromPassphrase -> {
|
||||
setState { copy(verifyingFrom4S = true) }
|
||||
_viewEvents.post(VerificationBottomSheetViewEvents.AccessSecretStore)
|
||||
}
|
||||
is VerificationAction.GotResultFromSsss -> {
|
||||
|
@ -354,56 +359,73 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(
|
|||
VerificationAction.SecuredStorageHasBeenReset -> {
|
||||
if (session.cryptoService().crossSigningService().allPrivateKeysKnown()) {
|
||||
setState {
|
||||
copy(quadSHasBeenReset = true)
|
||||
copy(quadSHasBeenReset = true, verifyingFrom4S = false)
|
||||
}
|
||||
}
|
||||
Unit
|
||||
}
|
||||
VerificationAction.CancelledFromSsss -> {
|
||||
setState {
|
||||
copy(verifyingFrom4S = false)
|
||||
}
|
||||
}
|
||||
}.exhaustive
|
||||
}
|
||||
|
||||
private fun handleSecretBackFromSSSS(action: VerificationAction.GotResultFromSsss) {
|
||||
try {
|
||||
action.cypherData.fromBase64().inputStream().use { ins ->
|
||||
val res = session.loadSecureSecret<Map<String, String>>(ins, action.alias)
|
||||
val trustResult = session.cryptoService().crossSigningService().checkTrustFromPrivateKeys(
|
||||
res?.get(MASTER_KEY_SSSS_NAME),
|
||||
res?.get(USER_SIGNING_KEY_SSSS_NAME),
|
||||
res?.get(SELF_SIGNING_KEY_SSSS_NAME)
|
||||
)
|
||||
if (trustResult.isVerified()) {
|
||||
// Sign this device and upload the signature
|
||||
session.sessionParams.deviceId?.let { deviceId ->
|
||||
session.cryptoService()
|
||||
.crossSigningService().trustDevice(deviceId, object : MatrixCallback<Unit> {
|
||||
override fun onFailure(failure: Throwable) {
|
||||
Timber.w(failure, "Failed to sign my device after recovery")
|
||||
}
|
||||
})
|
||||
}
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
action.cypherData.fromBase64().inputStream().use { ins ->
|
||||
val res = session.loadSecureSecret<Map<String, String>>(ins, action.alias)
|
||||
val trustResult = session.cryptoService().crossSigningService().checkTrustFromPrivateKeys(
|
||||
res?.get(MASTER_KEY_SSSS_NAME),
|
||||
res?.get(USER_SIGNING_KEY_SSSS_NAME),
|
||||
res?.get(SELF_SIGNING_KEY_SSSS_NAME)
|
||||
)
|
||||
if (trustResult.isVerified()) {
|
||||
// Sign this device and upload the signature
|
||||
session.sessionParams.deviceId?.let { deviceId ->
|
||||
session.cryptoService()
|
||||
.crossSigningService().trustDevice(deviceId, object : MatrixCallback<Unit> {
|
||||
override fun onFailure(failure: Throwable) {
|
||||
Timber.w(failure, "Failed to sign my device after recovery")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
setState {
|
||||
copy(verifiedFromPrivateKeys = true)
|
||||
}
|
||||
setState {
|
||||
copy(
|
||||
verifyingFrom4S = false,
|
||||
verifiedFromPrivateKeys = true
|
||||
)
|
||||
}
|
||||
|
||||
// try to get keybackup key
|
||||
} else {
|
||||
// POP UP something
|
||||
_viewEvents.post(VerificationBottomSheetViewEvents.ModalError(stringProvider.getString(R.string.error_failed_to_import_keys)))
|
||||
// try the keybackup
|
||||
tentativeRestoreBackup(res)
|
||||
} else {
|
||||
setState {
|
||||
copy(
|
||||
verifyingFrom4S = false
|
||||
)
|
||||
}
|
||||
// POP UP something
|
||||
_viewEvents.post(VerificationBottomSheetViewEvents.ModalError(stringProvider.getString(R.string.error_failed_to_import_keys)))
|
||||
}
|
||||
}
|
||||
|
||||
// try the keybackup
|
||||
tentativeRestoreBackup(res)
|
||||
Unit
|
||||
} catch (failure: Throwable) {
|
||||
setState {
|
||||
copy(
|
||||
verifyingFrom4S = false
|
||||
)
|
||||
}
|
||||
_viewEvents.post(
|
||||
VerificationBottomSheetViewEvents.ModalError(failure.localizedMessage ?: stringProvider.getString(R.string.unexpected_error)))
|
||||
}
|
||||
} catch (failure: Throwable) {
|
||||
_viewEvents.post(
|
||||
VerificationBottomSheetViewEvents.ModalError(failure.localizedMessage ?: stringProvider.getString(R.string.unexpected_error)))
|
||||
}
|
||||
}
|
||||
|
||||
private fun tentativeRestoreBackup(res: Map<String, String>?) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
val secret = res?.get(KEYBACKUP_SECRET_SSSS_NAME) ?: return@launch Unit.also {
|
||||
Timber.v("## Keybackup secret not restored from SSSS")
|
||||
|
|
|
@ -27,6 +27,9 @@ import im.vector.app.core.epoxy.onClick
|
|||
@EpoxyModelClass(layout = R.layout.item_settings_continue_cancel)
|
||||
abstract class SettingsContinueCancelItem : EpoxyModelWithHolder<SettingsContinueCancelItem.Holder>() {
|
||||
|
||||
@EpoxyAttribute
|
||||
var continueText: String? = null
|
||||
|
||||
@EpoxyAttribute
|
||||
var continueOnClick: ClickListener? = null
|
||||
|
||||
|
@ -37,6 +40,8 @@ abstract class SettingsContinueCancelItem : EpoxyModelWithHolder<SettingsContinu
|
|||
super.bind(holder)
|
||||
|
||||
holder.cancelButton.onClick(cancelOnClick)
|
||||
|
||||
continueText?.let { holder.continueButton.text = it }
|
||||
holder.continueButton.onClick(continueOnClick)
|
||||
}
|
||||
|
||||
|
|
|
@ -31,7 +31,6 @@ import kotlinx.coroutines.launch
|
|||
import org.matrix.android.sdk.api.failure.Failure
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.identity.IdentityServiceError
|
||||
import org.matrix.android.sdk.api.session.terms.GetTermsResponse
|
||||
import org.matrix.android.sdk.api.session.terms.TermsService
|
||||
import org.matrix.android.sdk.internal.util.awaitCallback
|
||||
import java.net.UnknownHostException
|
||||
|
@ -117,9 +116,7 @@ class SetIdentityServerViewModel @AssistedInject constructor(
|
|||
|
||||
private suspend fun checkTerms(baseUrl: String) {
|
||||
try {
|
||||
val data = awaitCallback<GetTermsResponse> {
|
||||
mxSession.getTerms(TermsService.ServiceType.IdentityService, baseUrl, it)
|
||||
}
|
||||
val data = mxSession.getTerms(TermsService.ServiceType.IdentityService, baseUrl)
|
||||
|
||||
// has all been accepted?
|
||||
val resp = data.serverResponse
|
||||
|
|
|
@ -26,6 +26,7 @@ import com.google.android.material.textfield.TextInputLayout
|
|||
import im.vector.app.R
|
||||
import im.vector.app.core.epoxy.VectorEpoxyHolder
|
||||
import im.vector.app.core.epoxy.VectorEpoxyModel
|
||||
import im.vector.app.core.extensions.setTextSafe
|
||||
import im.vector.app.core.platform.SimpleTextWatcher
|
||||
|
||||
@EpoxyModelClass(layout = R.layout.item_form_text_input)
|
||||
|
@ -65,9 +66,7 @@ abstract class FormEditTextItem : VectorEpoxyModel<FormEditTextItem.Holder>() {
|
|||
holder.textInputLayout.error = errorMessage
|
||||
|
||||
// Update only if text is different and value is not null
|
||||
if (value != null && holder.textInputEditText.text.toString() != value) {
|
||||
holder.textInputEditText.setText(value)
|
||||
}
|
||||
holder.textInputEditText.setTextSafe(value)
|
||||
holder.textInputEditText.isEnabled = enabled
|
||||
inputType?.let { holder.textInputEditText.inputType = it }
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ import com.google.android.material.textfield.TextInputLayout
|
|||
import im.vector.app.R
|
||||
import im.vector.app.core.epoxy.VectorEpoxyHolder
|
||||
import im.vector.app.core.epoxy.VectorEpoxyModel
|
||||
import im.vector.app.core.extensions.setTextSafe
|
||||
import im.vector.app.core.platform.SimpleTextWatcher
|
||||
|
||||
@EpoxyModelClass(layout = R.layout.item_form_text_input_with_button)
|
||||
|
@ -61,9 +62,7 @@ abstract class FormEditTextWithButtonItem : VectorEpoxyModel<FormEditTextWithBut
|
|||
holder.textInputLayout.hint = hint
|
||||
|
||||
// Update only if text is different
|
||||
if (holder.textInputEditText.text.toString() != value) {
|
||||
holder.textInputEditText.setText(value)
|
||||
}
|
||||
holder.textInputEditText.setTextSafe(value)
|
||||
holder.textInputEditText.isEnabled = enabled
|
||||
|
||||
holder.textInputEditText.addTextChangedListener(onTextChangeListener)
|
||||
|
|
|
@ -61,8 +61,8 @@ abstract class FormSwitchItem : VectorEpoxyModel<FormSwitchItem.Holder>() {
|
|||
|
||||
holder.switchView.isEnabled = enabled
|
||||
|
||||
holder.switchView.setOnCheckedChangeListener(null)
|
||||
holder.switchView.isChecked = switchChecked
|
||||
|
||||
holder.switchView.setOnCheckedChangeListener { _, isChecked ->
|
||||
listener?.invoke(isChecked)
|
||||
}
|
||||
|
|
|
@ -53,7 +53,6 @@ class TimelineItemFactory @Inject constructor(private val messageItemFactory: Me
|
|||
EventType.STATE_ROOM_AVATAR,
|
||||
EventType.STATE_ROOM_MEMBER,
|
||||
EventType.STATE_ROOM_THIRD_PARTY_INVITE,
|
||||
EventType.STATE_ROOM_ALIASES,
|
||||
EventType.STATE_ROOM_CANONICAL_ALIAS,
|
||||
EventType.STATE_ROOM_JOIN_RULES,
|
||||
EventType.STATE_ROOM_HISTORY_VISIBILITY,
|
||||
|
@ -79,6 +78,7 @@ class TimelineItemFactory @Inject constructor(private val messageItemFactory: Me
|
|||
encryptedItemFactory.create(event, nextEvent, highlight, callback)
|
||||
}
|
||||
}
|
||||
EventType.STATE_ROOM_ALIASES,
|
||||
EventType.KEY_VERIFICATION_ACCEPT,
|
||||
EventType.KEY_VERIFICATION_START,
|
||||
EventType.KEY_VERIFICATION_KEY,
|
||||
|
|
|
@ -260,13 +260,13 @@ class NoticeEventFormatter @Inject constructor(
|
|||
private fun formatRoomHistoryVisibilityEvent(event: Event, senderName: String?, rs: RoomSummary?): CharSequence? {
|
||||
val historyVisibility = event.getClearContent().toModel<RoomHistoryVisibilityContent>()?.historyVisibility ?: return null
|
||||
|
||||
val formattedVisibility = roomHistoryVisibilityFormatter.format(historyVisibility)
|
||||
val historyVisibilitySuffix = roomHistoryVisibilityFormatter.getNoticeSuffix(historyVisibility)
|
||||
return if (event.isSentByCurrentUser()) {
|
||||
sp.getString(if (rs.isDm()) R.string.notice_made_future_direct_room_visibility_by_you else R.string.notice_made_future_room_visibility_by_you,
|
||||
formattedVisibility)
|
||||
historyVisibilitySuffix)
|
||||
} else {
|
||||
sp.getString(if (rs.isDm()) R.string.notice_made_future_direct_room_visibility else R.string.notice_made_future_room_visibility,
|
||||
senderName, formattedVisibility)
|
||||
senderName, historyVisibilitySuffix)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -465,21 +465,72 @@ class NoticeEventFormatter @Inject constructor(
|
|||
|
||||
private fun formatRoomCanonicalAliasEvent(event: Event, senderName: String?): String? {
|
||||
val eventContent: RoomCanonicalAliasContent? = event.getClearContent().toModel()
|
||||
val canonicalAlias = eventContent?.canonicalAlias
|
||||
return canonicalAlias
|
||||
?.takeIf { it.isNotBlank() }
|
||||
?.let {
|
||||
val prevContent: RoomCanonicalAliasContent? = event.resolvedPrevContent().toModel()
|
||||
val canonicalAlias = eventContent?.canonicalAlias?.takeIf { it.isNotEmpty() }
|
||||
val prevCanonicalAlias = prevContent?.canonicalAlias?.takeIf { it.isNotEmpty() }
|
||||
val altAliases = eventContent?.alternativeAliases.orEmpty()
|
||||
val prevAltAliases = prevContent?.alternativeAliases.orEmpty()
|
||||
val added = altAliases - prevAltAliases
|
||||
val removed = prevAltAliases - altAliases
|
||||
|
||||
return when {
|
||||
added.isEmpty() && removed.isEmpty() && canonicalAlias == prevCanonicalAlias -> {
|
||||
// No difference between the two events say something as we can't simply hide the event from here
|
||||
if (event.isSentByCurrentUser()) {
|
||||
sp.getString(R.string.notice_room_canonical_alias_no_change_by_you)
|
||||
} else {
|
||||
sp.getString(R.string.notice_room_canonical_alias_no_change, senderName)
|
||||
}
|
||||
}
|
||||
added.isEmpty() && removed.isEmpty() -> {
|
||||
// Canonical has changed
|
||||
if (canonicalAlias != null) {
|
||||
if (event.isSentByCurrentUser()) {
|
||||
sp.getString(R.string.notice_room_canonical_alias_set_by_you, it)
|
||||
sp.getString(R.string.notice_room_canonical_alias_set_by_you, canonicalAlias)
|
||||
} else {
|
||||
sp.getString(R.string.notice_room_canonical_alias_set, senderName, it)
|
||||
sp.getString(R.string.notice_room_canonical_alias_set, senderName, canonicalAlias)
|
||||
}
|
||||
} else {
|
||||
if (event.isSentByCurrentUser()) {
|
||||
sp.getString(R.string.notice_room_canonical_alias_unset_by_you)
|
||||
} else {
|
||||
sp.getString(R.string.notice_room_canonical_alias_unset, senderName)
|
||||
}
|
||||
}
|
||||
?: if (event.isSentByCurrentUser()) {
|
||||
sp.getString(R.string.notice_room_canonical_alias_unset_by_you)
|
||||
}
|
||||
added.isEmpty() && canonicalAlias == prevCanonicalAlias -> {
|
||||
// Some alternative has been removed
|
||||
if (event.isSentByCurrentUser()) {
|
||||
sp.getQuantityString(R.plurals.notice_room_canonical_alias_alternative_removed_by_you, removed.size, removed.joinToString())
|
||||
} else {
|
||||
sp.getString(R.string.notice_room_canonical_alias_unset, senderName)
|
||||
sp.getQuantityString(R.plurals.notice_room_canonical_alias_alternative_removed, removed.size, senderName, removed.joinToString())
|
||||
}
|
||||
}
|
||||
removed.isEmpty() && canonicalAlias == prevCanonicalAlias -> {
|
||||
// Some alternative has been added
|
||||
if (event.isSentByCurrentUser()) {
|
||||
sp.getQuantityString(R.plurals.notice_room_canonical_alias_alternative_added_by_you, added.size, added.joinToString())
|
||||
} else {
|
||||
sp.getQuantityString(R.plurals.notice_room_canonical_alias_alternative_added, added.size, senderName, added.joinToString())
|
||||
}
|
||||
}
|
||||
canonicalAlias == prevCanonicalAlias -> {
|
||||
// Alternative added and removed
|
||||
if (event.isSentByCurrentUser()) {
|
||||
sp.getString(R.string.notice_room_canonical_alias_alternative_changed_by_you)
|
||||
} else {
|
||||
sp.getString(R.string.notice_room_canonical_alias_alternative_changed, senderName)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
// Main and removed, or main and added, or main and added and removed
|
||||
if (event.isSentByCurrentUser()) {
|
||||
sp.getString(R.string.notice_room_canonical_alias_main_and_alternative_changed_by_you)
|
||||
} else {
|
||||
sp.getString(R.string.notice_room_canonical_alias_main_and_alternative_changed, senderName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatRoomGuestAccessEvent(event: Event, senderName: String?, rs: RoomSummary?): String? {
|
||||
|
|
|
@ -24,13 +24,21 @@ import javax.inject.Inject
|
|||
class RoomHistoryVisibilityFormatter @Inject constructor(
|
||||
private val stringProvider: StringProvider
|
||||
) {
|
||||
fun getNoticeSuffix(roomHistoryVisibility: RoomHistoryVisibility): String {
|
||||
return stringProvider.getString(when (roomHistoryVisibility) {
|
||||
RoomHistoryVisibility.WORLD_READABLE -> R.string.notice_room_visibility_world_readable
|
||||
RoomHistoryVisibility.SHARED -> R.string.notice_room_visibility_shared
|
||||
RoomHistoryVisibility.INVITED -> R.string.notice_room_visibility_invited
|
||||
RoomHistoryVisibility.JOINED -> R.string.notice_room_visibility_joined
|
||||
})
|
||||
}
|
||||
|
||||
fun format(roomHistoryVisibility: RoomHistoryVisibility): String {
|
||||
return when (roomHistoryVisibility) {
|
||||
RoomHistoryVisibility.SHARED -> stringProvider.getString(R.string.notice_room_visibility_shared)
|
||||
RoomHistoryVisibility.INVITED -> stringProvider.getString(R.string.notice_room_visibility_invited)
|
||||
RoomHistoryVisibility.JOINED -> stringProvider.getString(R.string.notice_room_visibility_joined)
|
||||
RoomHistoryVisibility.WORLD_READABLE -> stringProvider.getString(R.string.notice_room_visibility_world_readable)
|
||||
}
|
||||
fun getSetting(roomHistoryVisibility: RoomHistoryVisibility): String {
|
||||
return stringProvider.getString(when (roomHistoryVisibility) {
|
||||
RoomHistoryVisibility.WORLD_READABLE -> R.string.room_settings_read_history_entry_anyone
|
||||
RoomHistoryVisibility.SHARED -> R.string.room_settings_read_history_entry_members_only_option_time_shared
|
||||
RoomHistoryVisibility.INVITED -> R.string.room_settings_read_history_entry_members_only_invited
|
||||
RoomHistoryVisibility.JOINED -> R.string.room_settings_read_history_entry_members_only_joined
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,6 +77,7 @@ class RoomListQuickActionsBottomSheet : VectorBaseBottomSheetDialogFragment(), R
|
|||
|
||||
override fun onDestroyView() {
|
||||
recyclerView.cleanup()
|
||||
roomListActionsEpoxyController.listener = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
|
|
|
@ -56,6 +56,7 @@ class PinFragment @Inject constructor(
|
|||
when (fragmentArgs.pinMode) {
|
||||
PinMode.CREATE -> showCreateFragment()
|
||||
PinMode.AUTH -> showAuthFragment()
|
||||
PinMode.MODIFY -> showCreateFragment() // No need to create another function for now because texts are generic
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -73,6 +74,10 @@ class PinFragment @Inject constructor(
|
|||
Toast.makeText(requireContext(), getString(R.string.create_pin_confirm_failure), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
override fun onPinCodeEnteredFirst(pinCode: String?): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onCodeCreated(encodedCode: String) {
|
||||
lifecycleScope.launch {
|
||||
pinCodeStore.storeEncodedPin(encodedCode)
|
||||
|
|
|
@ -18,5 +18,6 @@ package im.vector.app.features.pin
|
|||
|
||||
enum class PinMode {
|
||||
CREATE,
|
||||
AUTH
|
||||
AUTH,
|
||||
MODIFY
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ import com.airbnb.epoxy.TypedEpoxyController
|
|||
import com.airbnb.mvrx.Fail
|
||||
import com.airbnb.mvrx.Loading
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.error.ErrorFormatter
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import im.vector.app.features.discovery.settingsSectionTitleItem
|
||||
import im.vector.app.features.form.formAdvancedToggleItem
|
||||
|
@ -31,8 +30,9 @@ import im.vector.app.features.form.formSwitchItem
|
|||
import org.matrix.android.sdk.api.session.room.failure.CreateRoomFailure
|
||||
import javax.inject.Inject
|
||||
|
||||
class CreateRoomController @Inject constructor(private val stringProvider: StringProvider,
|
||||
private val errorFormatter: ErrorFormatter
|
||||
class CreateRoomController @Inject constructor(
|
||||
private val stringProvider: StringProvider,
|
||||
private val roomAliasErrorFormatter: RoomAliasErrorFormatter
|
||||
) : TypedEpoxyController<CreateRoomViewState>() {
|
||||
|
||||
var listener: Listener? = null
|
||||
|
@ -104,13 +104,8 @@ class CreateRoomController @Inject constructor(private val stringProvider: Strin
|
|||
value(viewState.roomType.aliasLocalPart)
|
||||
homeServer(":" + viewState.homeServerName)
|
||||
errorMessage(
|
||||
when ((viewState.asyncCreateRoomRequest as? Fail)?.error) {
|
||||
is CreateRoomFailure.RoomAliasError.AliasEmpty -> R.string.create_room_alias_empty
|
||||
is CreateRoomFailure.RoomAliasError.AliasNotAvailable -> R.string.create_room_alias_already_in_use
|
||||
is CreateRoomFailure.RoomAliasError.AliasInvalid -> R.string.create_room_alias_invalid
|
||||
else -> null
|
||||
}
|
||||
?.let { stringProvider.getString(it) }
|
||||
roomAliasErrorFormatter.format(
|
||||
(((viewState.asyncCreateRoomRequest as? Fail)?.error) as? CreateRoomFailure.AliasError)?.aliasError)
|
||||
)
|
||||
onTextChange { value ->
|
||||
listener?.setAliasLocalPart(value)
|
||||
|
|
|
@ -84,7 +84,7 @@ class CreateRoomFragment @Inject constructor(
|
|||
|
||||
override fun showFailure(throwable: Throwable) {
|
||||
// Note: RoomAliasError are displayed directly in the form
|
||||
if (throwable !is CreateRoomFailure.RoomAliasError) {
|
||||
if (throwable !is CreateRoomFailure.AliasError) {
|
||||
super.showFailure(throwable)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import com.google.android.material.textfield.TextInputLayout
|
|||
import im.vector.app.R
|
||||
import im.vector.app.core.epoxy.VectorEpoxyHolder
|
||||
import im.vector.app.core.epoxy.VectorEpoxyModel
|
||||
import im.vector.app.core.extensions.setTextSafe
|
||||
import im.vector.app.core.platform.SimpleTextWatcher
|
||||
|
||||
@EpoxyModelClass(layout = R.layout.item_room_alias_text_input)
|
||||
|
@ -62,9 +63,7 @@ abstract class RoomAliasEditItem : VectorEpoxyModel<RoomAliasEditItem.Holder>()
|
|||
holder.textInputLayout.error = errorMessage
|
||||
|
||||
// Update only if text is different and value is not null
|
||||
if (value != null && holder.textInputEditText.text.toString() != value) {
|
||||
holder.textInputEditText.setText(value)
|
||||
}
|
||||
holder.textInputEditText.setTextSafe(value)
|
||||
holder.textInputEditText.isEnabled = enabled
|
||||
holder.textInputEditText.addTextChangedListener(onTextChangeListener)
|
||||
holder.homeServerText.text = homeServer
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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 im.vector.app.features.roomdirectory.createroom
|
||||
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import org.matrix.android.sdk.api.session.room.alias.RoomAliasError
|
||||
import javax.inject.Inject
|
||||
|
||||
class RoomAliasErrorFormatter @Inject constructor(
|
||||
private val stringProvider: StringProvider
|
||||
) {
|
||||
fun format(roomAliasError: RoomAliasError?): String? {
|
||||
return when (roomAliasError) {
|
||||
is RoomAliasError.AliasEmpty -> R.string.create_room_alias_empty
|
||||
is RoomAliasError.AliasNotAvailable -> R.string.create_room_alias_already_in_use
|
||||
is RoomAliasError.AliasInvalid -> R.string.create_room_alias_invalid
|
||||
else -> null
|
||||
}
|
||||
?.let { stringProvider.getString(it) }
|
||||
}
|
||||
}
|
|
@ -36,6 +36,7 @@ import im.vector.app.features.room.RequireActiveMembershipViewState
|
|||
import im.vector.app.features.roomprofile.banned.RoomBannedMemberListFragment
|
||||
import im.vector.app.features.roomprofile.members.RoomMemberListFragment
|
||||
import im.vector.app.features.roomprofile.settings.RoomSettingsFragment
|
||||
import im.vector.app.features.roomprofile.alias.RoomAliasFragment
|
||||
import im.vector.app.features.roomprofile.uploads.RoomUploadsFragment
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -98,10 +99,11 @@ class RoomProfileActivity :
|
|||
.observe()
|
||||
.subscribe { sharedAction ->
|
||||
when (sharedAction) {
|
||||
is RoomProfileSharedAction.OpenRoomMembers -> openRoomMembers()
|
||||
is RoomProfileSharedAction.OpenRoomSettings -> openRoomSettings()
|
||||
is RoomProfileSharedAction.OpenRoomUploads -> openRoomUploads()
|
||||
is RoomProfileSharedAction.OpenBannedRoomMembers -> openBannedRoomMembers()
|
||||
is RoomProfileSharedAction.OpenRoomMembers -> openRoomMembers()
|
||||
is RoomProfileSharedAction.OpenRoomSettings -> openRoomSettings()
|
||||
is RoomProfileSharedAction.OpenRoomAliasesSettings -> openRoomAlias()
|
||||
is RoomProfileSharedAction.OpenRoomUploads -> openRoomUploads()
|
||||
is RoomProfileSharedAction.OpenBannedRoomMembers -> openBannedRoomMembers()
|
||||
}
|
||||
}
|
||||
.disposeOnDestroy()
|
||||
|
@ -135,6 +137,10 @@ class RoomProfileActivity :
|
|||
addFragmentToBackstack(R.id.simpleFragmentContainer, RoomSettingsFragment::class.java, roomProfileArgs)
|
||||
}
|
||||
|
||||
private fun openRoomAlias() {
|
||||
addFragmentToBackstack(R.id.simpleFragmentContainer, RoomAliasFragment::class.java, roomProfileArgs)
|
||||
}
|
||||
|
||||
private fun openRoomMembers() {
|
||||
addFragmentToBackstack(R.id.simpleFragmentContainer, RoomMemberListFragment::class.java, roomProfileArgs)
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import im.vector.app.core.platform.VectorSharedAction
|
|||
*/
|
||||
sealed class RoomProfileSharedAction : VectorSharedAction {
|
||||
object OpenRoomSettings : RoomProfileSharedAction()
|
||||
object OpenRoomAliasesSettings : RoomProfileSharedAction()
|
||||
object OpenRoomUploads : RoomProfileSharedAction()
|
||||
object OpenRoomMembers : RoomProfileSharedAction()
|
||||
object OpenBannedRoomMembers : RoomProfileSharedAction()
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright 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 im.vector.app.features.roomprofile.alias
|
||||
|
||||
import im.vector.app.core.platform.VectorViewModelAction
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
|
||||
|
||||
sealed class RoomAliasAction : VectorViewModelAction {
|
||||
// Canonical
|
||||
object ToggleManualPublishForm : RoomAliasAction()
|
||||
data class SetNewAlias(val alias: String) : RoomAliasAction()
|
||||
object ManualPublishAlias : RoomAliasAction()
|
||||
data class PublishAlias(val alias: String) : RoomAliasAction()
|
||||
data class UnpublishAlias(val alias: String) : RoomAliasAction()
|
||||
data class SetCanonicalAlias(val canonicalAlias: String?) : RoomAliasAction()
|
||||
|
||||
// Room directory
|
||||
data class SetRoomDirectoryVisibility(val roomDirectoryVisibility: RoomDirectoryVisibility) : RoomAliasAction()
|
||||
|
||||
// Local
|
||||
data class RemoveLocalAlias(val alias: String) : RoomAliasAction()
|
||||
object ToggleAddLocalAliasForm : RoomAliasAction()
|
||||
data class SetNewLocalAliasLocalPart(val aliasLocalPart: String) : RoomAliasAction()
|
||||
object AddLocalAlias : RoomAliasAction()
|
||||
}
|
|
@ -0,0 +1,262 @@
|
|||
/*
|
||||
* Copyright 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 im.vector.app.features.roomprofile.alias
|
||||
|
||||
import android.text.InputType
|
||||
import com.airbnb.epoxy.TypedEpoxyController
|
||||
import com.airbnb.mvrx.Fail
|
||||
import com.airbnb.mvrx.Loading
|
||||
import com.airbnb.mvrx.Success
|
||||
import com.airbnb.mvrx.Uninitialized
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.epoxy.errorWithRetryItem
|
||||
import im.vector.app.core.epoxy.loadingItem
|
||||
import im.vector.app.core.epoxy.profiles.buildProfileSection
|
||||
import im.vector.app.core.epoxy.profiles.profileActionItem
|
||||
import im.vector.app.core.error.ErrorFormatter
|
||||
import im.vector.app.core.resources.ColorProvider
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import im.vector.app.features.discovery.settingsButtonItem
|
||||
import im.vector.app.features.discovery.settingsContinueCancelItem
|
||||
import im.vector.app.features.discovery.settingsInfoItem
|
||||
import im.vector.app.features.form.formEditTextItem
|
||||
import im.vector.app.features.form.formSwitchItem
|
||||
import im.vector.app.features.roomdirectory.createroom.RoomAliasErrorFormatter
|
||||
import im.vector.app.features.roomdirectory.createroom.roomAliasEditItem
|
||||
import org.matrix.android.sdk.api.session.room.alias.RoomAliasError
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
|
||||
import javax.inject.Inject
|
||||
|
||||
class RoomAliasController @Inject constructor(
|
||||
private val stringProvider: StringProvider,
|
||||
private val errorFormatter: ErrorFormatter,
|
||||
private val colorProvider: ColorProvider,
|
||||
private val roomAliasErrorFormatter: RoomAliasErrorFormatter
|
||||
) : TypedEpoxyController<RoomAliasViewState>() {
|
||||
|
||||
interface Callback {
|
||||
fun toggleManualPublishForm()
|
||||
fun setNewAlias(alias: String)
|
||||
fun addAlias()
|
||||
fun setRoomDirectoryVisibility(roomDirectoryVisibility: RoomDirectoryVisibility)
|
||||
fun toggleLocalAliasForm()
|
||||
fun setNewLocalAliasLocalPart(aliasLocalPart: String)
|
||||
fun addLocalAlias()
|
||||
fun openAliasDetail(alias: String)
|
||||
}
|
||||
|
||||
var callback: Callback? = null
|
||||
|
||||
init {
|
||||
setData(null)
|
||||
}
|
||||
|
||||
override fun buildModels(data: RoomAliasViewState?) {
|
||||
data ?: return
|
||||
|
||||
// Published alias
|
||||
buildPublishInfo(data)
|
||||
// Room directory visibility
|
||||
buildRoomDirectoryVisibility(data)
|
||||
// Local alias
|
||||
buildLocalInfo(data)
|
||||
}
|
||||
|
||||
private fun buildRoomDirectoryVisibility(data: RoomAliasViewState) {
|
||||
when (data.roomDirectoryVisibility) {
|
||||
Uninitialized -> Unit
|
||||
is Loading -> Unit
|
||||
is Success -> {
|
||||
formSwitchItem {
|
||||
id("roomVisibility")
|
||||
title(stringProvider.getString(R.string.room_alias_publish_to_directory, data.homeServerName))
|
||||
showDivider(false)
|
||||
switchChecked(data.roomDirectoryVisibility() == RoomDirectoryVisibility.PUBLIC)
|
||||
listener {
|
||||
if (it) {
|
||||
callback?.setRoomDirectoryVisibility(RoomDirectoryVisibility.PUBLIC)
|
||||
} else {
|
||||
callback?.setRoomDirectoryVisibility(RoomDirectoryVisibility.PRIVATE)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is Fail -> {
|
||||
errorWithRetryItem {
|
||||
text(stringProvider.getString(R.string.room_alias_publish_to_directory_error,
|
||||
errorFormatter.toHumanReadable(data.roomDirectoryVisibility.error)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildPublishInfo(data: RoomAliasViewState) {
|
||||
buildProfileSection(
|
||||
stringProvider.getString(R.string.room_alias_published_alias_title)
|
||||
)
|
||||
settingsInfoItem {
|
||||
id("publishedInfo")
|
||||
helperTextResId(R.string.room_alias_published_alias_subtitle)
|
||||
}
|
||||
|
||||
data.canonicalAlias
|
||||
?.takeIf { it.isNotEmpty() }
|
||||
?.let { canonicalAlias ->
|
||||
|
||||
profileActionItem {
|
||||
id("canonical")
|
||||
title(data.canonicalAlias)
|
||||
subtitle(stringProvider.getString(R.string.room_alias_published_alias_main))
|
||||
listener { callback?.openAliasDetail(canonicalAlias) }
|
||||
}
|
||||
}
|
||||
|
||||
if (data.alternativeAliases.isEmpty()) {
|
||||
settingsInfoItem {
|
||||
id("otherPublishedEmpty")
|
||||
if (data.actionPermissions.canChangeCanonicalAlias) {
|
||||
helperTextResId(R.string.room_alias_address_empty_can_add)
|
||||
} else {
|
||||
helperTextResId(R.string.room_alias_address_empty)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
settingsInfoItem {
|
||||
id("otherPublished")
|
||||
helperTextResId(R.string.room_alias_published_other)
|
||||
}
|
||||
data.alternativeAliases.forEachIndexed { idx, altAlias ->
|
||||
profileActionItem {
|
||||
id("alt_$idx")
|
||||
title(altAlias)
|
||||
listener { callback?.openAliasDetail(altAlias) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (data.actionPermissions.canChangeCanonicalAlias) {
|
||||
buildPublishManuallyForm(data)
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildPublishManuallyForm(data: RoomAliasViewState) {
|
||||
when (data.publishManuallyState) {
|
||||
RoomAliasViewState.AddAliasState.Hidden -> Unit
|
||||
RoomAliasViewState.AddAliasState.Closed -> {
|
||||
settingsButtonItem {
|
||||
id("publishManually")
|
||||
colorProvider(colorProvider)
|
||||
buttonTitleId(R.string.room_alias_published_alias_add_manually)
|
||||
buttonClickListener { callback?.toggleManualPublishForm() }
|
||||
}
|
||||
}
|
||||
is RoomAliasViewState.AddAliasState.Editing -> {
|
||||
formEditTextItem {
|
||||
id("publishManuallyEdit")
|
||||
value(data.publishManuallyState.value)
|
||||
showBottomSeparator(false)
|
||||
hint(stringProvider.getString(R.string.room_alias_address_hint))
|
||||
inputType(InputType.TYPE_CLASS_TEXT)
|
||||
onTextChange { text ->
|
||||
callback?.setNewAlias(text)
|
||||
}
|
||||
}
|
||||
settingsContinueCancelItem {
|
||||
id("publishManuallySubmit")
|
||||
continueText(stringProvider.getString(R.string.room_alias_published_alias_add_manually_submit))
|
||||
continueOnClick { callback?.addAlias() }
|
||||
cancelOnClick { callback?.toggleManualPublishForm() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildLocalInfo(data: RoomAliasViewState) {
|
||||
buildProfileSection(
|
||||
stringProvider.getString(R.string.room_alias_local_address_title)
|
||||
)
|
||||
settingsInfoItem {
|
||||
id("localInfo")
|
||||
helperText(stringProvider.getString(R.string.room_alias_local_address_subtitle, data.homeServerName))
|
||||
}
|
||||
|
||||
when (val localAliases = data.localAliases) {
|
||||
is Uninitialized -> {
|
||||
loadingItem {
|
||||
id("loadingAliases")
|
||||
}
|
||||
}
|
||||
is Success -> {
|
||||
if (localAliases().isEmpty()) {
|
||||
settingsInfoItem {
|
||||
id("locEmpty")
|
||||
helperTextResId(R.string.room_alias_local_address_empty)
|
||||
}
|
||||
} else {
|
||||
localAliases().forEachIndexed { idx, localAlias ->
|
||||
profileActionItem {
|
||||
id("loc_$idx")
|
||||
title(localAlias)
|
||||
listener { callback?.openAliasDetail(localAlias) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is Fail -> {
|
||||
errorWithRetryItem {
|
||||
id("alt_error")
|
||||
text(errorFormatter.toHumanReadable(localAliases.error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add local
|
||||
buildAddLocalAlias(data)
|
||||
}
|
||||
|
||||
private fun buildAddLocalAlias(data: RoomAliasViewState) {
|
||||
when (data.newLocalAliasState) {
|
||||
RoomAliasViewState.AddAliasState.Hidden -> Unit
|
||||
RoomAliasViewState.AddAliasState.Closed -> {
|
||||
settingsButtonItem {
|
||||
id("newLocalAliasButton")
|
||||
colorProvider(colorProvider)
|
||||
buttonTitleId(R.string.room_alias_local_address_add)
|
||||
buttonClickListener { callback?.toggleLocalAliasForm() }
|
||||
}
|
||||
}
|
||||
is RoomAliasViewState.AddAliasState.Editing -> {
|
||||
roomAliasEditItem {
|
||||
id("newLocalAlias")
|
||||
value(data.newLocalAliasState.value)
|
||||
homeServer(":" + data.homeServerName)
|
||||
showBottomSeparator(false)
|
||||
errorMessage(roomAliasErrorFormatter.format((data.newLocalAliasState.asyncRequest as? Fail)?.error as? RoomAliasError))
|
||||
onTextChange { value ->
|
||||
callback?.setNewLocalAliasLocalPart(value)
|
||||
}
|
||||
}
|
||||
settingsContinueCancelItem {
|
||||
id("newLocalAliasSubmit")
|
||||
continueText(stringProvider.getString(R.string.action_add))
|
||||
continueOnClick { callback?.addLocalAlias() }
|
||||
cancelOnClick { callback?.toggleLocalAliasForm() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
* Copyright 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 im.vector.app.features.roomprofile.alias
|
||||
|
||||
import android.content.DialogInterface
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.view.isVisible
|
||||
import com.airbnb.mvrx.args
|
||||
import com.airbnb.mvrx.fragmentViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.dialogs.withColoredButton
|
||||
import im.vector.app.core.extensions.cleanup
|
||||
import im.vector.app.core.extensions.configureWith
|
||||
import im.vector.app.core.extensions.exhaustive
|
||||
import im.vector.app.core.platform.VectorBaseFragment
|
||||
import im.vector.app.core.utils.shareText
|
||||
import im.vector.app.core.utils.toast
|
||||
import im.vector.app.features.home.AvatarRenderer
|
||||
import im.vector.app.features.roomprofile.RoomProfileArgs
|
||||
import im.vector.app.features.roomprofile.alias.detail.RoomAliasBottomSheet
|
||||
import im.vector.app.features.roomprofile.alias.detail.RoomAliasBottomSheetSharedAction
|
||||
import im.vector.app.features.roomprofile.alias.detail.RoomAliasBottomSheetSharedActionViewModel
|
||||
import kotlinx.android.synthetic.main.fragment_room_setting_generic.*
|
||||
import kotlinx.android.synthetic.main.merge_overlay_waiting_view.*
|
||||
import org.matrix.android.sdk.api.session.room.alias.RoomAliasError
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
|
||||
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||
import javax.inject.Inject
|
||||
|
||||
class RoomAliasFragment @Inject constructor(
|
||||
val viewModelFactory: RoomAliasViewModel.Factory,
|
||||
private val controller: RoomAliasController,
|
||||
private val avatarRenderer: AvatarRenderer
|
||||
) :
|
||||
VectorBaseFragment(),
|
||||
RoomAliasController.Callback {
|
||||
|
||||
private val viewModel: RoomAliasViewModel by fragmentViewModel()
|
||||
private lateinit var sharedActionViewModel: RoomAliasBottomSheetSharedActionViewModel
|
||||
|
||||
private val roomProfileArgs: RoomProfileArgs by args()
|
||||
|
||||
override fun getLayoutResId() = R.layout.fragment_room_setting_generic
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
sharedActionViewModel = activityViewModelProvider.get(RoomAliasBottomSheetSharedActionViewModel::class.java)
|
||||
|
||||
controller.callback = this
|
||||
setupToolbar(roomSettingsToolbar)
|
||||
roomSettingsRecyclerView.configureWith(controller, hasFixedSize = true)
|
||||
waiting_view_status_text.setText(R.string.please_wait)
|
||||
waiting_view_status_text.isVisible = true
|
||||
|
||||
viewModel.observeViewEvents {
|
||||
when (it) {
|
||||
is RoomAliasViewEvents.Failure -> showFailure(it.throwable)
|
||||
RoomAliasViewEvents.Success -> showSuccess()
|
||||
}.exhaustive
|
||||
}
|
||||
|
||||
sharedActionViewModel
|
||||
.observe()
|
||||
.subscribe { handleAliasAction(it) }
|
||||
.disposeOnDestroyView()
|
||||
}
|
||||
|
||||
private fun handleAliasAction(action: RoomAliasBottomSheetSharedAction?) {
|
||||
when (action) {
|
||||
is RoomAliasBottomSheetSharedAction.ShareAlias -> shareAlias(action.matrixTo)
|
||||
is RoomAliasBottomSheetSharedAction.PublishAlias -> viewModel.handle(RoomAliasAction.PublishAlias(action.alias))
|
||||
is RoomAliasBottomSheetSharedAction.UnPublishAlias -> unpublishAlias(action.alias)
|
||||
is RoomAliasBottomSheetSharedAction.DeleteAlias -> removeLocalAlias(action.alias)
|
||||
is RoomAliasBottomSheetSharedAction.SetMainAlias -> viewModel.handle(RoomAliasAction.SetCanonicalAlias(action.alias))
|
||||
RoomAliasBottomSheetSharedAction.UnsetMainAlias -> viewModel.handle(RoomAliasAction.SetCanonicalAlias(canonicalAlias = null))
|
||||
null -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
private fun shareAlias(matrixTo: String) {
|
||||
shareText(requireContext(), matrixTo)
|
||||
}
|
||||
|
||||
override fun showFailure(throwable: Throwable) {
|
||||
if (throwable !is RoomAliasError) {
|
||||
super.showFailure(throwable)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showSuccess() {
|
||||
activity?.toast(R.string.room_settings_save_success)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
controller.callback = null
|
||||
roomSettingsRecyclerView.cleanup()
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
override fun invalidate() = withState(viewModel) { state ->
|
||||
waiting_view.isVisible = state.isLoading
|
||||
controller.setData(state)
|
||||
renderRoomSummary(state)
|
||||
}
|
||||
|
||||
private fun renderRoomSummary(state: RoomAliasViewState) {
|
||||
state.roomSummary()?.let {
|
||||
roomSettingsToolbarTitleView.text = it.displayName
|
||||
avatarRenderer.render(it.toMatrixItem(), roomSettingsToolbarAvatarImageView)
|
||||
}
|
||||
}
|
||||
|
||||
private fun unpublishAlias(alias: String) {
|
||||
AlertDialog.Builder(requireContext())
|
||||
.setTitle(R.string.dialog_title_confirmation)
|
||||
.setMessage(getString(R.string.room_alias_unpublish_confirmation, alias))
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.setPositiveButton(R.string.action_unpublish) { _, _ ->
|
||||
viewModel.handle(RoomAliasAction.UnpublishAlias(alias))
|
||||
}
|
||||
.show()
|
||||
.withColoredButton(DialogInterface.BUTTON_POSITIVE)
|
||||
}
|
||||
|
||||
override fun toggleManualPublishForm() {
|
||||
viewModel.handle(RoomAliasAction.ToggleManualPublishForm)
|
||||
}
|
||||
|
||||
override fun setNewAlias(alias: String) {
|
||||
viewModel.handle(RoomAliasAction.SetNewAlias(alias))
|
||||
}
|
||||
|
||||
override fun addAlias() {
|
||||
viewModel.handle(RoomAliasAction.ManualPublishAlias)
|
||||
}
|
||||
|
||||
override fun setRoomDirectoryVisibility(roomDirectoryVisibility: RoomDirectoryVisibility) {
|
||||
viewModel.handle(RoomAliasAction.SetRoomDirectoryVisibility(roomDirectoryVisibility))
|
||||
}
|
||||
|
||||
override fun toggleLocalAliasForm() {
|
||||
viewModel.handle(RoomAliasAction.ToggleAddLocalAliasForm)
|
||||
}
|
||||
|
||||
override fun setNewLocalAliasLocalPart(aliasLocalPart: String) {
|
||||
viewModel.handle(RoomAliasAction.SetNewLocalAliasLocalPart(aliasLocalPart))
|
||||
}
|
||||
|
||||
override fun addLocalAlias() {
|
||||
viewModel.handle(RoomAliasAction.AddLocalAlias)
|
||||
}
|
||||
|
||||
override fun openAliasDetail(alias: String) = withState(viewModel) { state ->
|
||||
RoomAliasBottomSheet
|
||||
.newInstance(
|
||||
alias = alias,
|
||||
isPublished = alias in state.allPublishedAliases,
|
||||
isMainAlias = alias == state.canonicalAlias,
|
||||
isLocal = alias in state.localAliases().orEmpty(),
|
||||
canEditCanonicalAlias = state.actionPermissions.canChangeCanonicalAlias
|
||||
)
|
||||
.show(childFragmentManager, "ROOM_ALIAS_ACTIONS")
|
||||
}
|
||||
|
||||
private fun removeLocalAlias(alias: String) {
|
||||
AlertDialog.Builder(requireContext())
|
||||
.setTitle(R.string.dialog_title_confirmation)
|
||||
.setMessage(getString(R.string.room_alias_delete_confirmation, alias))
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.setPositiveButton(R.string.delete) { _, _ ->
|
||||
viewModel.handle(RoomAliasAction.RemoveLocalAlias(alias))
|
||||
}
|
||||
.show()
|
||||
.withColoredButton(DialogInterface.BUTTON_POSITIVE)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright 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 im.vector.app.features.roomprofile.alias
|
||||
|
||||
import im.vector.app.core.platform.VectorViewEvents
|
||||
|
||||
/**
|
||||
* Transient events for room settings screen
|
||||
*/
|
||||
sealed class RoomAliasViewEvents : VectorViewEvents {
|
||||
data class Failure(val throwable: Throwable) : RoomAliasViewEvents()
|
||||
object Success : RoomAliasViewEvents()
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue