diff --git a/CHANGES.md b/CHANGES.md index 5b3c34d9a0..ec853d96ef 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -30,6 +30,10 @@ SDK API changes ⚠️: Build 🧱: - Use Update Gradle Wrapper Action - Updates Gradle Wrapper from 5.6.4 to 6.6.1. (#2193) + - Upgrade kotlin version from `1.3.72` to `1.4.10` and kotlin coroutines version from `1.3.8` to `1.3.9` + - Upgrade build tools from `3.5.3` to `4.0.1` + - Upgrade com.google.gms:google-services from `4.3.2` to `4.3.4` + - Upgrade Moshi to `1.11.0`, Dagger to `2.29.1`, Epoxy to `4.1.0` Other changes: - Added registration/verification automated UI tests diff --git a/attachment-viewer/build.gradle b/attachment-viewer/build.gradle index 3a5c3298d4..91ddd519df 100644 --- a/attachment-viewer/build.gradle +++ b/attachment-viewer/build.gradle @@ -58,21 +58,16 @@ android { } dependencies { - implementation 'com.github.chrisbanes:PhotoView:2.0.0' + implementation 'com.github.chrisbanes:PhotoView:2.1.4' implementation 'io.reactivex.rxjava2:rxkotlin:2.3.0' implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' - implementation fileTree(dir: "libs", include: ["*.jar"]) implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - implementation 'androidx.core:core-ktx:1.3.0' - implementation 'androidx.appcompat:appcompat:1.1.0' - implementation 'com.google.android.material:material:1.1.0' - implementation 'androidx.constraintlayout:constraintlayout:1.1.3' - implementation 'androidx.navigation:navigation-fragment-ktx:2.1.0' - implementation 'androidx.navigation:navigation-ui-ktx:2.1.0' - testImplementation 'junit:junit:4.12' - androidTestImplementation 'androidx.test.ext:junit:1.1.1' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' + 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' } \ No newline at end of file diff --git a/build.gradle b/build.gradle index f06d1859b5..e68d545c91 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,9 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '1.3.72' + // Ref: https://kotlinlang.org/releases.html + ext.kotlin_version = '1.4.10' + ext.kotlin_coroutines_version = "1.3.9" repositories { google() jcenter() @@ -10,10 +12,8 @@ buildscript { } } dependencies { - // Warning: 3.6.3 leads to infinite gradle builds. Stick to 3.5.3 for the moment - classpath 'com.android.tools.build:gradle:3.5.3' - classpath 'com.google.gms:google-services:4.3.2' - classpath "com.airbnb.okreplay:gradle-plugin:1.5.0" + classpath 'com.android.tools.build:gradle:4.0.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' classpath 'com.google.android.gms:oss-licenses-plugin:0.10.2' @@ -64,7 +64,8 @@ allprojects { tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { // Warnings are potential errors, so stop ignoring them - kotlinOptions.allWarningsAsErrors = true + // You can override by passing `-PallWarningsAsErrors=false` in the command line + kotlinOptions.allWarningsAsErrors = project.properties['allWarningsAsErrors']?.toBoolean() ?: true } } diff --git a/matrix-sdk-android-rx/build.gradle b/matrix-sdk-android-rx/build.gradle index 70a05114c2..3d62758065 100644 --- a/matrix-sdk-android-rx/build.gradle +++ b/matrix-sdk-android-rx/build.gradle @@ -35,7 +35,8 @@ android { dependencies { implementation project(":matrix-sdk-android") - implementation 'androidx.appcompat:appcompat:1.1.0' + 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 @@ -43,8 +44,4 @@ dependencies { // Logging implementation 'com.jakewharton.timber:timber:4.7.1' - - testImplementation 'junit:junit:4.12' - androidTestImplementation 'androidx.test:runner:1.2.0' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' } diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index 2c20137647..0bf4819d25 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -3,7 +3,6 @@ apply plugin: 'kotlin-android' apply plugin: 'kotlin-kapt' apply plugin: 'kotlin-android-extensions' apply plugin: 'realm-android' -apply plugin: 'okreplay' buildscript { repositories { @@ -109,21 +108,21 @@ static def gitRevisionDate() { dependencies { def arrow_version = "0.8.2" - def moshi_version = '1.8.0' + def moshi_version = '1.11.0' def lifecycle_version = '2.2.0' def arch_version = '2.1.0' - def coroutines_version = "1.3.8" def markwon_version = '3.1.0' - def daggerVersion = '2.25.4' + def daggerVersion = '2.29.1' def work_version = '2.4.0' def retrofit_version = '2.6.2' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version" - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version" implementation "androidx.appcompat:appcompat:1.2.0" - implementation "androidx.core:core-ktx:1.3.1" + implementation "androidx.fragment:fragment:1.3.0-beta01" + implementation "androidx.core:core-ktx:1.3.2" implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version" @@ -143,7 +142,7 @@ dependencies { implementation "ru.noties.markwon:core:$markwon_version" // Image - implementation 'androidx.exifinterface:exifinterface:1.3.0-alpha01' + implementation 'androidx.exifinterface:exifinterface:1.3.0' // Database implementation 'com.github.Zhuinden:realm-monarchy:0.5.1' @@ -181,33 +180,29 @@ dependencies { // Use the same WebRTC library than the one used by Jitsi library implementation('com.facebook.react:react-native-webrtc:1.84.0-jitsi-5112273@aar') - debugImplementation 'com.airbnb.okreplay:okreplay:1.5.0' - releaseImplementation 'com.airbnb.okreplay:noop:1.5.0' - androidTestImplementation 'com.airbnb.okreplay:espresso:1.5.0' - - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13' testImplementation 'org.robolectric:robolectric:4.3' //testImplementation 'org.robolectric:shadows-support-v4:3.0' // Note: version sticks to 1.9.2 due to https://github.com/mockk/mockk/issues/281 testImplementation 'io.mockk:mockk:1.9.2.kotlin12' - testImplementation 'org.amshove.kluent:kluent-android:1.44' - testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version" + testImplementation 'org.amshove.kluent:kluent-android:1.61' + testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version" // Plant Timber tree for test testImplementation 'net.lachlanmckee:timber-junit-rule:1.0.1' kaptAndroidTest "com.google.dagger:dagger-compiler:$daggerVersion" - androidTestImplementation 'androidx.test:core:1.2.0' - androidTestImplementation 'androidx.test:runner:1.2.0' - androidTestImplementation 'androidx.test:rules:1.2.0' - androidTestImplementation 'androidx.test.ext:junit:1.1.1' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' - androidTestImplementation 'org.amshove.kluent:kluent-android:1.44' + androidTestImplementation 'androidx.test:core:1.3.0' + androidTestImplementation 'androidx.test:runner:1.3.0' + androidTestImplementation 'androidx.test:rules:1.3.0' + androidTestImplementation 'androidx.test.ext:junit:1.1.2' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' + androidTestImplementation 'org.amshove.kluent:kluent-android:1.61' // Note: version sticks to 1.9.2 due to https://github.com/mockk/mockk/issues/281 androidTestImplementation 'io.mockk:mockk-android:1.9.2.kotlin12' androidTestImplementation "androidx.arch.core:core-testing:$arch_version" - androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version" + androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version" // Plant Timber tree for test androidTestImplementation 'net.lachlanmckee:timber-junit-rule:1.0.1' - androidTestUtil 'androidx.test:orchestrator:1.2.0' + androidTestUtil 'androidx.test:orchestrator:1.3.0' } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/content/ContentAttachmentData.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/content/ContentAttachmentData.kt index 045a9bc1a0..b89dfee77f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/content/ContentAttachmentData.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/content/ContentAttachmentData.kt @@ -20,9 +20,11 @@ package org.matrix.android.sdk.api.session.content import android.net.Uri import android.os.Parcelable import androidx.exifinterface.media.ExifInterface +import com.squareup.moshi.JsonClass import kotlinx.android.parcel.Parcelize @Parcelize +@JsonClass(generateAdapter = true) data class ContentAttachmentData( val size: Long = 0, val duration: Long? = 0, @@ -32,10 +34,11 @@ data class ContentAttachmentData( val exifOrientation: Int = ExifInterface.ORIENTATION_UNDEFINED, val name: String? = null, val queryUri: Uri, - private val mimeType: String?, + val mimeType: String?, val type: Type ) : Parcelable { + @JsonClass(generateAdapter = false) enum class Type { FILE, IMAGE, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/Signed.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/Signed.kt index 9c14275b3e..9f0e0c17ff 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/Signed.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/Signed.kt @@ -18,7 +18,9 @@ package org.matrix.android.sdk.api.session.room.model import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +@JsonClass(generateAdapter = true) data class Signed( @Json(name = "token") val token: String, @Json(name = "signatures") val signatures: Any, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/version/Versions.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/version/Versions.kt index 483c43f502..6f0c756127 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/version/Versions.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/version/Versions.kt @@ -105,6 +105,6 @@ private fun Versions.doesServerSeparatesAddAndBind(): Boolean { private fun Versions.getMaxVersion(): HomeServerVersion { return supportedVersions ?.mapNotNull { HomeServerVersion.parse(it) } - ?.max() + ?.maxOrNull() ?: HomeServerVersion.r0_0_0 } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/IncomingGossipingRequestManager.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/IncomingGossipingRequestManager.kt index da72186136..18356ffbb0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/IncomingGossipingRequestManager.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/IncomingGossipingRequestManager.kt @@ -17,6 +17,8 @@ package org.matrix.android.sdk.internal.crypto +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch import org.matrix.android.sdk.api.auth.data.Credentials import org.matrix.android.sdk.api.crypto.MXCryptoConfig import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME @@ -36,8 +38,6 @@ import org.matrix.android.sdk.internal.di.SessionId import org.matrix.android.sdk.internal.session.SessionScope import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers import org.matrix.android.sdk.internal.worker.WorkerParamsFactory -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject @@ -327,7 +327,9 @@ internal class IncomingGossipingRequestManager @Inject constructor( val params = SendGossipWorker.Params( sessionId = sessionId, secretValue = secretValue, - request = request + requestUserId = request.userId, + requestDeviceId = request.deviceId, + requestId = request.requestId ) cryptoStore.updateGossipingRequestState(request, GossipingRequestState.ACCEPTING) @@ -351,7 +353,9 @@ internal class IncomingGossipingRequestManager @Inject constructor( val params = SendGossipWorker.Params( sessionId = userId, secretValue = secretValue, - request = request + requestUserId = request.userId, + requestDeviceId = request.deviceId, + requestId = request.requestId ) cryptoStore.updateGossipingRequestState(request, GossipingRequestState.ACCEPTING) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/IncomingSecretShareRequest.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/IncomingSecretShareRequest.kt index 4b91ed5d76..c376a66f09 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/IncomingSecretShareRequest.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/IncomingSecretShareRequest.kt @@ -22,7 +22,7 @@ import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.internal.crypto.model.rest.SecretShareRequest /** - * IncomingRoomKeyRequest class defines the incoming room keys request. + * IncomingSecretShareRequest class defines the incoming secret keys request. */ data class IncomingSecretShareRequest( /** diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipWorker.kt index c8bfdcd542..1c28dc6fbb 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipWorker.kt @@ -47,7 +47,9 @@ internal class SendGossipWorker(context: Context, internal data class Params( override val sessionId: String, val secretValue: String, - val request: IncomingSecretShareRequest, + val requestUserId: String?, + val requestDeviceId: String?, + val requestId: String?, override val lastFailureMessage: String? = null ) : SessionWorkerParams @@ -67,16 +69,21 @@ internal class SendGossipWorker(context: Context, val eventType: String = EventType.SEND_SECRET val toDeviceContent = SecretSendEventContent( - requestId = params.request.requestId ?: "", + requestId = params.requestId ?: "", secretValue = params.secretValue ) - val requestingUserId = params.request.userId ?: "" - val requestingDeviceId = params.request.deviceId ?: "" + val requestingUserId = params.requestUserId ?: "" + val requestingDeviceId = params.requestDeviceId ?: "" val deviceInfo = cryptoStore.getUserDevice(requestingUserId, requestingDeviceId) ?: return buildErrorResult(params, "Unknown deviceInfo, cannot send message").also { - cryptoStore.updateGossipingRequestState(params.request, GossipingRequestState.FAILED_TO_ACCEPTED) - Timber.e("Unknown deviceInfo, cannot send message, sessionId: ${params.request.deviceId}") + cryptoStore.updateGossipingRequestState( + requestUserId = params.requestUserId, + requestDeviceId = params.requestDeviceId, + requestId = params.requestId, + state = GossipingRequestState.FAILED_TO_ACCEPTED + ) + Timber.e("Unknown deviceInfo, cannot send message, sessionId: ${params.requestDeviceId}") } val sendToDeviceMap = MXUsersDevicesMap() @@ -88,7 +95,12 @@ internal class SendGossipWorker(context: Context, // no session with this device, probably because there // were no one-time keys. return buildErrorResult(params, "no session with this device").also { - cryptoStore.updateGossipingRequestState(params.request, GossipingRequestState.FAILED_TO_ACCEPTED) + cryptoStore.updateGossipingRequestState( + requestUserId = params.requestUserId, + requestDeviceId = params.requestDeviceId, + requestId = params.requestId, + state = GossipingRequestState.FAILED_TO_ACCEPTED + ) Timber.e("no session with this device, probably because there were no one-time keys.") } } @@ -121,13 +133,23 @@ internal class SendGossipWorker(context: Context, transactionId = localId ) ) - cryptoStore.updateGossipingRequestState(params.request, GossipingRequestState.ACCEPTED) + cryptoStore.updateGossipingRequestState( + requestUserId = params.requestUserId, + requestDeviceId = params.requestDeviceId, + requestId = params.requestId, + state = GossipingRequestState.ACCEPTED + ) return Result.success() } catch (throwable: Throwable) { return if (throwable.shouldBeRetried()) { Result.retry() } else { - cryptoStore.updateGossipingRequestState(params.request, GossipingRequestState.FAILED_TO_ACCEPTED) + cryptoStore.updateGossipingRequestState( + requestUserId = params.requestUserId, + requestDeviceId = params.requestDeviceId, + requestId = params.requestId, + state = GossipingRequestState.FAILED_TO_ACCEPTED + ) buildErrorResult(params, throwable.localizedMessage ?: "error") } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/model/rest/KeysVersion.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/model/rest/KeysVersion.kt index 3ca8df3131..115e7814d6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/model/rest/KeysVersion.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/model/rest/KeysVersion.kt @@ -17,7 +17,12 @@ package org.matrix.android.sdk.internal.crypto.keysbackup.model.rest +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) data class KeysVersion( // the keys backup version - var version: String? = null + @Json(name = "version") + val version: String? = null ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/secrets/DefaultSharedSecretStorageService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/secrets/DefaultSharedSecretStorageService.kt index 7186bc3cd0..1cae1a4e2b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/secrets/DefaultSharedSecretStorageService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/secrets/DefaultSharedSecretStorageService.kt @@ -371,7 +371,7 @@ internal class DefaultSharedSecretStorageService @Inject constructor( callback.onFailure(SharedSecretStorageError.BadKeyFormat) } cryptoCoroutineScope.launch(coroutineDispatchers.main) { - kotlin.runCatching { + runCatching { // decrypt from recovery key withOlmDecryption { olmPkDecryption -> olmPkDecryption.setPrivateKey(keySpec.privateKey) @@ -390,7 +390,7 @@ internal class DefaultSharedSecretStorageService @Inject constructor( callback.onFailure(SharedSecretStorageError.BadKeyFormat) } cryptoCoroutineScope.launch(coroutineDispatchers.main) { - kotlin.runCatching { + runCatching { decryptAesHmacSha2(keySpec, name, secretContent) }.foldToCallback(callback) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt index f248e464c2..491a69a1de 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt @@ -1,4 +1,3 @@ - /* * Copyright 2016 OpenMarket Ltd * Copyright 2018 New Vector Ltd @@ -215,11 +214,12 @@ internal interface IMXCryptoStore { // TODO temp fun getLiveDeviceList(): LiveData> - fun getMyDevicesInfo() : List + fun getMyDevicesInfo(): List - fun getLiveMyDevicesInfo() : LiveData> + fun getLiveMyDevicesInfo(): LiveData> fun saveMyDevicesInfo(info: List) + /** * Store the crypto algorithm for a room. * @@ -367,7 +367,19 @@ internal interface IMXCryptoStore { fun saveGossipingEvent(event: Event) - fun updateGossipingRequestState(request: IncomingShareRequestCommon, state: GossipingRequestState) + fun updateGossipingRequestState(request: IncomingShareRequestCommon, state: GossipingRequestState) { + updateGossipingRequestState( + requestUserId = request.userId, + requestDeviceId = request.deviceId, + requestId = request.requestId, + state = state + ) + } + + fun updateGossipingRequestState(requestUserId: String?, + requestDeviceId: String?, + requestId: String?, + state: GossipingRequestState) /** * Search an IncomingRoomKeyRequest @@ -411,7 +423,7 @@ internal interface IMXCryptoStore { fun getLiveCrossSigningPrivateKeys(): LiveData> fun saveBackupRecoveryKey(recoveryKey: String?, version: String?) - fun getKeyBackupRecoveryKeyInfo() : SavedKeyBackupKeyInfo? + fun getKeyBackupRecoveryKeyInfo(): SavedKeyBackupKeyInfo? fun setUserKeysAsTrusted(userId: String, trusted: Boolean = true) fun setDeviceTrust(userId: String, deviceId: String, crossSignedVerified: Boolean, locallyVerified: Boolean?) @@ -421,12 +433,13 @@ internal interface IMXCryptoStore { fun updateUsersTrust(check: (String) -> Boolean) fun addWithHeldMegolmSession(withHeldContent: RoomKeyWithHeldContent) - fun getWithHeldMegolmSession(roomId: String, sessionId: String) : RoomKeyWithHeldContent? + fun getWithHeldMegolmSession(roomId: String, sessionId: String): RoomKeyWithHeldContent? fun markedSessionAsShared(roomId: String?, sessionId: String, userId: String, deviceId: String, chainIndex: Int) - fun wasSessionSharedWithUser(roomId: String?, sessionId: String, userId: String, deviceId: String) : SharedSessionResult + fun wasSessionSharedWithUser(roomId: String?, sessionId: String, userId: String, deviceId: String): SharedSessionResult data class SharedSessionResult(val found: Boolean, val chainIndex: Int?) - fun getSharedWithInfo(roomId: String?, sessionId: String) : MXUsersDevicesMap + + fun getSharedWithInfo(roomId: String?, sessionId: String): MXUsersDevicesMap // Dev tools fun getOutgoingRoomKeyRequests(): List diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt index df71ef9eba..4f63ddc238 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt @@ -1134,12 +1134,15 @@ internal class RealmCryptoStore @Inject constructor( // } // } - override fun updateGossipingRequestState(request: IncomingShareRequestCommon, state: GossipingRequestState) { + override fun updateGossipingRequestState(requestUserId: String?, + requestDeviceId: String?, + requestId: String?, + state: GossipingRequestState) { doRealmTransaction(realmConfiguration) { realm -> realm.where() - .equalTo(IncomingGossipingRequestEntityFields.OTHER_USER_ID, request.userId) - .equalTo(IncomingGossipingRequestEntityFields.OTHER_DEVICE_ID, request.deviceId) - .equalTo(IncomingGossipingRequestEntityFields.REQUEST_ID, request.requestId) + .equalTo(IncomingGossipingRequestEntityFields.OTHER_USER_ID, requestUserId) + .equalTo(IncomingGossipingRequestEntityFields.OTHER_DEVICE_ID, requestDeviceId) + .equalTo(IncomingGossipingRequestEntityFields.REQUEST_ID, requestId) .findAll().forEach { it.requestState = state } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MoshiProvider.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MoshiProvider.kt index 5e16d0b455..5921e28f59 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MoshiProvider.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MoshiProvider.kt @@ -32,8 +32,10 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageTextContent import org.matrix.android.sdk.api.session.room.model.message.MessageType import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationRequestContent import org.matrix.android.sdk.api.session.room.model.message.MessageVideoContent +import org.matrix.android.sdk.internal.network.parsing.CipherSuiteMoshiAdapter import org.matrix.android.sdk.internal.network.parsing.ForceToBooleanJsonAdapter import org.matrix.android.sdk.internal.network.parsing.RuntimeJsonAdapterFactory +import org.matrix.android.sdk.internal.network.parsing.TlsVersionMoshiAdapter import org.matrix.android.sdk.internal.network.parsing.UriMoshiAdapter object MoshiProvider { @@ -41,6 +43,8 @@ object MoshiProvider { private val moshi: Moshi = Moshi.Builder() .add(UriMoshiAdapter()) .add(ForceToBooleanJsonAdapter()) + .add(CipherSuiteMoshiAdapter()) + .add(TlsVersionMoshiAdapter()) .add(RuntimeJsonAdapterFactory.of(MessageContent::class.java, "msgtype", MessageDefaultContent::class.java) .registerSubtype(MessageTextContent::class.java, MessageType.MSGTYPE_TEXT) .registerSubtype(MessageNoticeContent::class.java, MessageType.MSGTYPE_NOTICE) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/NetworkModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/NetworkModule.kt index 5fff658a56..1c11ef79f9 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/NetworkModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/NetworkModule.kt @@ -29,7 +29,6 @@ import org.matrix.android.sdk.internal.network.interceptors.CurlLoggingIntercept import org.matrix.android.sdk.internal.network.interceptors.FormattedJsonHttpLogger import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor -import okreplay.OkReplayInterceptor import java.util.concurrent.TimeUnit @Module @@ -44,12 +43,6 @@ internal object NetworkModule { return interceptor } - @Provides - @JvmStatic - fun providesOkReplayInterceptor(): OkReplayInterceptor { - return OkReplayInterceptor() - } - @Provides @JvmStatic fun providesStethoInterceptor(): StethoInterceptor { @@ -71,8 +64,7 @@ internal object NetworkModule { timeoutInterceptor: TimeOutInterceptor, userAgentInterceptor: UserAgentInterceptor, httpLoggingInterceptor: HttpLoggingInterceptor, - curlLoggingInterceptor: CurlLoggingInterceptor, - okReplayInterceptor: OkReplayInterceptor): OkHttpClient { + curlLoggingInterceptor: CurlLoggingInterceptor): OkHttpClient { return OkHttpClient.Builder() .connectTimeout(30, TimeUnit.SECONDS) .readTimeout(60, TimeUnit.SECONDS) @@ -93,7 +85,6 @@ internal object NetworkModule { proxy(it) } } - .addInterceptor(okReplayInterceptor) .build() } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/parsing/CipherSuiteMoshiAdapter.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/parsing/CipherSuiteMoshiAdapter.kt new file mode 100644 index 0000000000..ad5821cb0f --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/parsing/CipherSuiteMoshiAdapter.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2019 New Vector Ltd + * 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.network.parsing + +import com.squareup.moshi.FromJson +import com.squareup.moshi.ToJson +import okhttp3.CipherSuite + +internal class CipherSuiteMoshiAdapter { + + @ToJson + fun toJson(cipherSuite: CipherSuite): String { + return cipherSuite.javaName + } + + @FromJson + fun fromJson(cipherSuiteString: String): CipherSuite { + return CipherSuite.forJavaName(cipherSuiteString) + } +} diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/OkReplayRuleChainNoActivity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/parsing/TlsVersionMoshiAdapter.kt similarity index 56% rename from matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/OkReplayRuleChainNoActivity.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/parsing/TlsVersionMoshiAdapter.kt index 372ef95be8..75301521d5 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/OkReplayRuleChainNoActivity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/parsing/TlsVersionMoshiAdapter.kt @@ -1,5 +1,6 @@ /* * Copyright 2019 New Vector Ltd + * 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. @@ -14,19 +15,21 @@ * limitations under the License. */ -package org.matrix.android.sdk +package org.matrix.android.sdk.internal.network.parsing -import okreplay.OkReplayConfig -import okreplay.PermissionRule -import okreplay.RecorderRule -import org.junit.rules.RuleChain -import org.junit.rules.TestRule +import com.squareup.moshi.FromJson +import com.squareup.moshi.ToJson +import okhttp3.TlsVersion -class OkReplayRuleChainNoActivity( - private val configuration: OkReplayConfig) { +internal class TlsVersionMoshiAdapter { - fun get(): TestRule { - return RuleChain.outerRule(PermissionRule(configuration)) - .around(RecorderRule(configuration)) + @ToJson + fun toJson(tlsVersion: TlsVersion): String { + return tlsVersion.javaName + } + + @FromJson + fun fromJson(tlsVersionString: String): TlsVersion { + return TlsVersion.forJavaName(tlsVersionString) } } diff --git a/multipicker/build.gradle b/multipicker/build.gradle index 8f2226e884..b6e500e493 100644 --- a/multipicker/build.gradle +++ b/multipicker/build.gradle @@ -41,15 +41,10 @@ android { } dependencies { - implementation fileTree(dir: 'libs', include: ['*.jar']) implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation 'androidx.appcompat:appcompat:1.1.0' - implementation 'androidx.core:core-ktx:1.3.0' - testImplementation 'junit:junit:4.12' - androidTestImplementation 'androidx.test.ext:junit:1.1.1' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' - - implementation 'androidx.exifinterface:exifinterface:1.3.0-alpha01' + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation "androidx.fragment:fragment:1.3.0-beta01" + implementation 'androidx.exifinterface:exifinterface:1.3.0' // Log implementation 'com.jakewharton.timber:timber:4.7.1' diff --git a/multipicker/src/main/java/im/vector/lib/multipicker/AudioPicker.kt b/multipicker/src/main/java/im/vector/lib/multipicker/AudioPicker.kt index c51084dd39..516022100d 100644 --- a/multipicker/src/main/java/im/vector/lib/multipicker/AudioPicker.kt +++ b/multipicker/src/main/java/im/vector/lib/multipicker/AudioPicker.kt @@ -16,7 +16,6 @@ package im.vector.lib.multipicker -import android.app.Activity import android.content.Context import android.content.Intent import android.media.MediaMetadataRetriever @@ -26,19 +25,13 @@ import im.vector.lib.multipicker.entity.MultiPickerAudioType /** * Audio file picker implementation */ -class AudioPicker(override val requestCode: Int) : Picker(requestCode) { +class AudioPicker : Picker() { /** * Call this function from onActivityResult(int, int, Intent). - * Returns selected audio files or empty list if request code is wrong - * or result code is not Activity.RESULT_OK - * or user did not select any files. + * Returns selected audio files or empty list if user did not select any files. */ - override fun getSelectedFiles(context: Context, requestCode: Int, resultCode: Int, data: Intent?): List { - if (requestCode != this.requestCode && resultCode != Activity.RESULT_OK) { - return emptyList() - } - + override fun getSelectedFiles(context: Context, data: Intent?): List { val audioList = mutableListOf() getSelectedUriList(data).forEach { selectedUri -> diff --git a/multipicker/src/main/java/im/vector/lib/multipicker/CameraPicker.kt b/multipicker/src/main/java/im/vector/lib/multipicker/CameraPicker.kt index be6fdb5ee8..3f24a28c28 100644 --- a/multipicker/src/main/java/im/vector/lib/multipicker/CameraPicker.kt +++ b/multipicker/src/main/java/im/vector/lib/multipicker/CameraPicker.kt @@ -16,13 +16,12 @@ package im.vector.lib.multipicker -import android.app.Activity import android.content.Context import android.content.Intent import android.net.Uri import android.provider.MediaStore +import androidx.activity.result.ActivityResultLauncher import androidx.core.content.FileProvider -import androidx.fragment.app.Fragment import im.vector.lib.multipicker.entity.MultiPickerImageType import im.vector.lib.multipicker.utils.ImageUtils import java.io.File @@ -33,33 +32,18 @@ import java.util.Locale /** * Implementation of taking a photo with Camera */ -class CameraPicker(val requestCode: Int) { +class CameraPicker { /** - * Start camera by using an Activity - * @param activity Activity to handle onActivityResult(). + * Start camera by using a ActivityResultLauncher * @return Uri of taken photo or null if the operation is cancelled. */ - fun startWithExpectingFile(activity: Activity): Uri? { - val photoUri = createPhotoUri(activity) + fun startWithExpectingFile(context: Context, activityResultLauncher: ActivityResultLauncher): Uri? { + val photoUri = createPhotoUri(context) val intent = createIntent().apply { putExtra(MediaStore.EXTRA_OUTPUT, photoUri) } - activity.startActivityForResult(intent, requestCode) - return photoUri - } - - /** - * Start camera by using a Fragment - * @param fragment Fragment to handle onActivityResult(). - * @return Uri of taken photo or null if the operation is cancelled. - */ - fun startWithExpectingFile(fragment: Fragment): Uri? { - val photoUri = createPhotoUri(fragment.requireContext()) - val intent = createIntent().apply { - putExtra(MediaStore.EXTRA_OUTPUT, photoUri) - } - fragment.startActivityForResult(intent, requestCode) + activityResultLauncher.launch(intent) return photoUri } @@ -69,40 +53,38 @@ class CameraPicker(val requestCode: Int) { * or result code is not Activity.RESULT_OK * or user cancelled the operation. */ - fun getTakenPhoto(context: Context, requestCode: Int, resultCode: Int, photoUri: Uri): MultiPickerImageType? { - if (requestCode == this.requestCode && resultCode == Activity.RESULT_OK) { - val projection = arrayOf( - MediaStore.Images.Media.DISPLAY_NAME, - MediaStore.Images.Media.SIZE - ) + fun getTakenPhoto(context: Context, photoUri: Uri): MultiPickerImageType? { + val projection = arrayOf( + MediaStore.Images.Media.DISPLAY_NAME, + MediaStore.Images.Media.SIZE + ) - context.contentResolver.query( - photoUri, - projection, - null, - null, - null - )?.use { cursor -> - val nameColumn = cursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME) - val sizeColumn = cursor.getColumnIndex(MediaStore.Images.Media.SIZE) + context.contentResolver.query( + photoUri, + projection, + null, + null, + null + )?.use { cursor -> + val nameColumn = cursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME) + val sizeColumn = cursor.getColumnIndex(MediaStore.Images.Media.SIZE) - if (cursor.moveToNext()) { - val name = cursor.getString(nameColumn) - val size = cursor.getLong(sizeColumn) + if (cursor.moveToNext()) { + val name = cursor.getString(nameColumn) + val size = cursor.getLong(sizeColumn) - val bitmap = ImageUtils.getBitmap(context, photoUri) - val orientation = ImageUtils.getOrientation(context, photoUri) + val bitmap = ImageUtils.getBitmap(context, photoUri) + val orientation = ImageUtils.getOrientation(context, photoUri) - return MultiPickerImageType( - name, - size, - context.contentResolver.getType(photoUri), - photoUri, - bitmap?.width ?: 0, - bitmap?.height ?: 0, - orientation - ) - } + return MultiPickerImageType( + name, + size, + context.contentResolver.getType(photoUri), + photoUri, + bitmap?.width ?: 0, + bitmap?.height ?: 0, + orientation + ) } } return null diff --git a/multipicker/src/main/java/im/vector/lib/multipicker/ContactPicker.kt b/multipicker/src/main/java/im/vector/lib/multipicker/ContactPicker.kt index e9ae096174..315fe6cbf2 100644 --- a/multipicker/src/main/java/im/vector/lib/multipicker/ContactPicker.kt +++ b/multipicker/src/main/java/im/vector/lib/multipicker/ContactPicker.kt @@ -16,7 +16,6 @@ package im.vector.lib.multipicker -import android.app.Activity import android.content.ContentResolver import android.content.Context import android.content.Intent @@ -26,19 +25,13 @@ import im.vector.lib.multipicker.entity.MultiPickerContactType /** * Contact Picker implementation */ -class ContactPicker(override val requestCode: Int) : Picker(requestCode) { +class ContactPicker : Picker() { /** * Call this function from onActivityResult(int, int, Intent). - * Returns selected contact or empty list if request code is wrong - * or result code is not Activity.RESULT_OK - * or user did not select any files. + * Returns selected contact or empty list if user did not select any contacts. */ - override fun getSelectedFiles(context: Context, requestCode: Int, resultCode: Int, data: Intent?): List { - if (requestCode != this.requestCode && resultCode != Activity.RESULT_OK) { - return emptyList() - } - + override fun getSelectedFiles(context: Context, data: Intent?): List { val contactList = mutableListOf() data?.data?.let { selectedUri -> diff --git a/multipicker/src/main/java/im/vector/lib/multipicker/FilePicker.kt b/multipicker/src/main/java/im/vector/lib/multipicker/FilePicker.kt index d5718b9951..39bd93d03e 100644 --- a/multipicker/src/main/java/im/vector/lib/multipicker/FilePicker.kt +++ b/multipicker/src/main/java/im/vector/lib/multipicker/FilePicker.kt @@ -16,7 +16,6 @@ package im.vector.lib.multipicker -import android.app.Activity import android.content.Context import android.content.Intent import android.provider.OpenableColumns @@ -25,19 +24,13 @@ import im.vector.lib.multipicker.entity.MultiPickerFileType /** * Implementation of selecting any type of files */ -class FilePicker(override val requestCode: Int) : Picker(requestCode) { +class FilePicker : Picker() { /** * Call this function from onActivityResult(int, int, Intent). - * Returns selected files or empty list if request code is wrong - * or result code is not Activity.RESULT_OK - * or user did not select any files. + * Returns selected files or empty list if user did not select any files. */ - override fun getSelectedFiles(context: Context, requestCode: Int, resultCode: Int, data: Intent?): List { - if (requestCode != this.requestCode && resultCode != Activity.RESULT_OK) { - return emptyList() - } - + override fun getSelectedFiles(context: Context, data: Intent?): List { val fileList = mutableListOf() getSelectedUriList(data).forEach { selectedUri -> diff --git a/multipicker/src/main/java/im/vector/lib/multipicker/ImagePicker.kt b/multipicker/src/main/java/im/vector/lib/multipicker/ImagePicker.kt index ec87e027f9..ce73058039 100644 --- a/multipicker/src/main/java/im/vector/lib/multipicker/ImagePicker.kt +++ b/multipicker/src/main/java/im/vector/lib/multipicker/ImagePicker.kt @@ -16,7 +16,6 @@ package im.vector.lib.multipicker -import android.app.Activity import android.content.Context import android.content.Intent import android.provider.MediaStore @@ -26,19 +25,13 @@ import im.vector.lib.multipicker.utils.ImageUtils /** * Image Picker implementation */ -class ImagePicker(override val requestCode: Int) : Picker(requestCode) { +class ImagePicker : Picker() { /** * Call this function from onActivityResult(int, int, Intent). - * Returns selected image files or empty list if request code is wrong - * or result code is not Activity.RESULT_OK - * or user did not select any files. + * Returns selected image files or empty list if user did not select any files. */ - override fun getSelectedFiles(context: Context, requestCode: Int, resultCode: Int, data: Intent?): List { - if (requestCode != this.requestCode && resultCode != Activity.RESULT_OK) { - return emptyList() - } - + override fun getSelectedFiles(context: Context, data: Intent?): List { val imageList = mutableListOf() getSelectedUriList(data).forEach { selectedUri -> diff --git a/multipicker/src/main/java/im/vector/lib/multipicker/MultiPicker.kt b/multipicker/src/main/java/im/vector/lib/multipicker/MultiPicker.kt index d28dcf9586..7e639a9bd3 100644 --- a/multipicker/src/main/java/im/vector/lib/multipicker/MultiPicker.kt +++ b/multipicker/src/main/java/im/vector/lib/multipicker/MultiPicker.kt @@ -26,23 +26,16 @@ class MultiPicker { val CONTACT by lazy { MultiPicker() } val CAMERA by lazy { MultiPicker() } - const val REQUEST_CODE_PICK_IMAGE = 5000 - const val REQUEST_CODE_PICK_VIDEO = 5001 - const val REQUEST_CODE_PICK_FILE = 5002 - const val REQUEST_CODE_PICK_AUDIO = 5003 - const val REQUEST_CODE_PICK_CONTACT = 5004 - const val REQUEST_CODE_TAKE_PHOTO = 5005 - @Suppress("UNCHECKED_CAST") fun get(type: MultiPicker): T { return when (type) { - IMAGE -> ImagePicker(REQUEST_CODE_PICK_IMAGE) as T - VIDEO -> VideoPicker(REQUEST_CODE_PICK_VIDEO) as T - FILE -> FilePicker(REQUEST_CODE_PICK_FILE) as T - AUDIO -> AudioPicker(REQUEST_CODE_PICK_AUDIO) as T - CONTACT -> ContactPicker(REQUEST_CODE_PICK_CONTACT) as T - CAMERA -> CameraPicker(REQUEST_CODE_TAKE_PHOTO) as T - else -> throw IllegalArgumentException("Unsupported type $type") + IMAGE -> ImagePicker() as T + VIDEO -> VideoPicker() as T + FILE -> FilePicker() as T + AUDIO -> AudioPicker() as T + CONTACT -> ContactPicker() as T + CAMERA -> CameraPicker() as T + else -> throw IllegalArgumentException("Unsupported type $type") } } } diff --git a/multipicker/src/main/java/im/vector/lib/multipicker/Picker.kt b/multipicker/src/main/java/im/vector/lib/multipicker/Picker.kt index 65ec77e02a..ba765a3b1d 100644 --- a/multipicker/src/main/java/im/vector/lib/multipicker/Picker.kt +++ b/multipicker/src/main/java/im/vector/lib/multipicker/Picker.kt @@ -16,28 +16,25 @@ package im.vector.lib.multipicker -import android.app.Activity import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.content.pm.ResolveInfo import android.net.Uri -import androidx.fragment.app.Fragment +import androidx.activity.result.ActivityResultLauncher /** * Abstract class to provide all types of Pickers */ -abstract class Picker(open val requestCode: Int) { +abstract class Picker { protected var single = false /** * Call this function from onActivityResult(int, int, Intent). - * @return selected files or empty list if request code is wrong - * or result code is not Activity.RESULT_OK - * or user did not select any files. + * @return selected files or empty list if user did not select any files. */ - abstract fun getSelectedFiles(context: Context, requestCode: Int, resultCode: Int, data: Intent?): List + abstract fun getSelectedFiles(context: Context, data: Intent?): List /** * Use this function to retrieve files which are shared from another application or internally @@ -61,7 +58,7 @@ abstract class Picker(open val requestCode: Int) { context.grantUriPermission(packageName, it, Intent.FLAG_GRANT_READ_URI_PERMISSION) } } - return getSelectedFiles(context, requestCode, Activity.RESULT_OK, data) + return getSelectedFiles(context, data) } /** @@ -75,19 +72,11 @@ abstract class Picker(open val requestCode: Int) { abstract fun createIntent(): Intent /** - * Start Storage Access Framework UI by using an Activity. - * @param activity Activity to handle onActivityResult(). + * Start Storage Access Framework UI by using a ActivityResultLauncher. + * @param activityResultLauncher to handle the result. */ - fun startWith(activity: Activity) { - activity.startActivityForResult(createIntent().apply { addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) }, requestCode) - } - - /** - * Start Storage Access Framework UI by using a Fragment. - * @param fragment Fragment to handle onActivityResult(). - */ - fun startWith(fragment: Fragment) { - fragment.startActivityForResult(createIntent().apply { addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) }, requestCode) + fun startWith(activityResultLauncher: ActivityResultLauncher) { + activityResultLauncher.launch(createIntent().apply { addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) }) } protected fun getSelectedUriList(data: Intent?): List { diff --git a/multipicker/src/main/java/im/vector/lib/multipicker/VideoPicker.kt b/multipicker/src/main/java/im/vector/lib/multipicker/VideoPicker.kt index 965c8e08e0..c7c06f795f 100644 --- a/multipicker/src/main/java/im/vector/lib/multipicker/VideoPicker.kt +++ b/multipicker/src/main/java/im/vector/lib/multipicker/VideoPicker.kt @@ -16,7 +16,6 @@ package im.vector.lib.multipicker -import android.app.Activity import android.content.Context import android.content.Intent import android.media.MediaMetadataRetriever @@ -26,19 +25,13 @@ import im.vector.lib.multipicker.entity.MultiPickerVideoType /** * Video Picker implementation */ -class VideoPicker(override val requestCode: Int) : Picker(requestCode) { +class VideoPicker : Picker() { /** * Call this function from onActivityResult(int, int, Intent). - * Returns selected video files or empty list if request code is wrong - * or result code is not Activity.RESULT_OK - * or user did not select any files. + * Returns selected video files or empty list if user did not select any files. */ - override fun getSelectedFiles(context: Context, requestCode: Int, resultCode: Int, data: Intent?): List { - if (requestCode != this.requestCode && resultCode != Activity.RESULT_OK) { - return emptyList() - } - + override fun getSelectedFiles(context: Context, data: Intent?): List { val videoList = mutableListOf() getSelectedUriList(data).forEach { selectedUri -> diff --git a/vector/build.gradle b/vector/build.gradle index 87c8696b02..3b43aa12e1 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -280,22 +280,21 @@ android { dependencies { - def epoxy_version = '3.11.0' - def fragment_version = '1.2.5' + def epoxy_version = '4.1.0' + def fragment_version = '1.3.0-beta01' def arrow_version = "0.8.2" - def coroutines_version = "1.3.8" def markwon_version = '4.1.2' def big_image_viewer_version = '1.6.2' def glide_version = '4.11.0' - def moshi_version = '1.8.0' - def daggerVersion = '2.25.4' + def moshi_version = '1.11.0' + def daggerVersion = '2.29.1' def autofill_version = "1.0.0" def work_version = '2.4.0' def arch_version = '2.1.0' def lifecycle_version = '2.2.0' // Tests - def kluent_version = '1.44' + def kluent_version = '1.61' def androidxTest_version = '1.3.0' def espresso_version = '3.3.0' @@ -307,16 +306,16 @@ dependencies { implementation 'com.android.support:multidex:1.0.3' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version" - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version" - implementation "androidx.recyclerview:recyclerview:1.2.0-alpha05" + 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" // Keep at 2.0.0-beta4 at the moment, as updating is breaking some UI implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta4' - implementation 'androidx.core:core-ktx:1.3.1' + implementation 'androidx.core:core-ktx:1.3.2' implementation "org.threeten:threetenbp:1.4.0:no-tzdb" implementation "com.gabrielittner.threetenbp:lazythreetenbp:0.7.0" @@ -347,7 +346,7 @@ dependencies { implementation "com.airbnb.android:epoxy-glide-preloading:$epoxy_version" kapt "com.airbnb.android:epoxy-processor:$epoxy_version" implementation "com.airbnb.android:epoxy-paging:$epoxy_version" - implementation 'com.airbnb.android:mvrx:1.3.0' + implementation 'com.airbnb.android:mvrx:1.5.1' // Work implementation "androidx.work:work-runtime-ktx:$work_version" @@ -363,7 +362,7 @@ dependencies { // UI implementation 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1' - implementation 'com.google.android.material:material:1.3.0-alpha01' + implementation 'com.google.android.material:material:1.3.0-alpha02' implementation 'me.gujun.android:span:1.7' implementation "io.noties.markwon:core:$markwon_version" implementation "io.noties.markwon:html:$markwon_version" @@ -398,7 +397,7 @@ dependencies { implementation "com.github.piasy:GlideImageViewFactory:$big_image_viewer_version" // implementation 'com.github.MikeOrtiz:TouchImageView:3.0.2' - implementation 'com.github.chrisbanes:PhotoView:2.0.0' + implementation 'com.github.chrisbanes:PhotoView:2.1.4' implementation "com.github.bumptech.glide:glide:$glide_version" kapt "com.github.bumptech.glide:compiler:$glide_version" @@ -415,7 +414,7 @@ dependencies { kapt 'com.squareup.inject:assisted-inject-processor-dagger2:0.5.0' // gplay flavor only - gplayImplementation('com.google.firebase:firebase-messaging:20.2.4') { + gplayImplementation('com.google.firebase:firebase-messaging:20.3.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' @@ -439,7 +438,7 @@ dependencies { implementation 'me.dm7.barcodescanner:zxing:1.9.13' // TESTS - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13' testImplementation "org.amshove.kluent:kluent-android:$kluent_version" // Plant Timber tree for test testImplementation 'net.lachlanmckee:timber-junit-rule:1.0.1' diff --git a/vector/lint.xml b/vector/lint.xml index bd49091a3f..4ac0f20e51 100644 --- a/vector/lint.xml +++ b/vector/lint.xml @@ -27,6 +27,7 @@ + diff --git a/vector/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt b/vector/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt index 5590e19c10..9cca462d1a 100644 --- a/vector/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt +++ b/vector/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt @@ -28,6 +28,7 @@ import butterknife.OnClick import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ScreenComponent +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO import im.vector.app.core.utils.PERMISSION_REQUEST_CODE_LAUNCH_CAMERA @@ -196,33 +197,29 @@ class DebugMenuActivity : VectorBaseActivity() { } private fun doScanQRCode() { - QrCodeScannerActivity.startForResult(this) + QrCodeScannerActivity.startForResult(this, qrStartForActivityResult) } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data) - if (resultCode == Activity.RESULT_OK) { - when (requestCode) { - QrCodeScannerActivity.QR_CODE_SCANNER_REQUEST_CODE -> { - toast("QrCode: " + QrCodeScannerActivity.getResultText(data) + " is QRCode: " + QrCodeScannerActivity.getResultIsQrCode(data)) + private val qrStartForActivityResult = registerStartForActivityResult { activityResult -> + if (activityResult.resultCode == Activity.RESULT_OK) { + toast("QrCode: " + QrCodeScannerActivity.getResultText(activityResult.data) + + " is QRCode: " + QrCodeScannerActivity.getResultIsQrCode(activityResult.data)) - // Also update the current QR Code (reverse operation) - // renderQrCode(QrCodeScannerActivity.getResultText(data) ?: "") - val result = QrCodeScannerActivity.getResultText(data)!! + // Also update the current QR Code (reverse operation) + // renderQrCode(QrCodeScannerActivity.getResultText(data) ?: "") + val result = QrCodeScannerActivity.getResultText(activityResult.data)!! - val qrCodeData = result.toQrCodeData() - Timber.e("qrCodeData: $qrCodeData") + val qrCodeData = result.toQrCodeData() + Timber.e("qrCodeData: $qrCodeData") - if (result.length != buffer.size) { - Timber.e("Error, length are not the same") - } else { - // Convert to ByteArray - val byteArrayResult = result.toByteArray(Charsets.ISO_8859_1) - for (i in byteArrayResult.indices) { - if (buffer[i] != byteArrayResult[i]) { - Timber.e("Error for byte $i, expecting ${buffer[i]} and get ${byteArrayResult[i]}") - } - } + if (result.length != buffer.size) { + Timber.e("Error, length are not the same") + } else { + // Convert to ByteArray + val byteArrayResult = result.toByteArray(Charsets.ISO_8859_1) + for (i in byteArrayResult.indices) { + if (buffer[i] != byteArrayResult[i]) { + Timber.e("Error for byte $i, expecting ${buffer[i]} and get ${byteArrayResult[i]}") } } } diff --git a/vector/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestAutoStartBoot.kt b/vector/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestAutoStartBoot.kt index b7834ecf45..e46a07f712 100644 --- a/vector/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestAutoStartBoot.kt +++ b/vector/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestAutoStartBoot.kt @@ -15,6 +15,8 @@ */ package im.vector.app.fdroid.features.settings.troubleshoot +import android.content.Intent +import androidx.activity.result.ActivityResultLauncher import im.vector.app.R import im.vector.app.core.resources.StringProvider import im.vector.app.features.settings.VectorPreferences @@ -28,7 +30,7 @@ class TestAutoStartBoot @Inject constructor(private val vectorPreferences: Vecto private val stringProvider: StringProvider) : TroubleshootTest(R.string.settings_troubleshoot_test_service_boot_title) { - override fun perform() { + override fun perform(activityResultLauncher: ActivityResultLauncher) { if (vectorPreferences.autoStartOnBoot()) { description = stringProvider.getString(R.string.settings_troubleshoot_test_service_boot_success) status = TestStatus.SUCCESS @@ -38,7 +40,7 @@ class TestAutoStartBoot @Inject constructor(private val vectorPreferences: Vecto quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_service_boot_quickfix) { override fun doFix() { vectorPreferences.setAutoStartOnBoot(true) - manager?.retry() + manager?.retry(activityResultLauncher) } } status = TestStatus.FAILED diff --git a/vector/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestBackgroundRestrictions.kt b/vector/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestBackgroundRestrictions.kt index 3e053d7fec..abdd696724 100644 --- a/vector/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestBackgroundRestrictions.kt +++ b/vector/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestBackgroundRestrictions.kt @@ -15,7 +15,9 @@ */ package im.vector.app.fdroid.features.settings.troubleshoot +import android.content.Intent import android.net.ConnectivityManager +import androidx.activity.result.ActivityResultLauncher import androidx.appcompat.app.AppCompatActivity import androidx.core.content.getSystemService import androidx.core.net.ConnectivityManagerCompat @@ -28,7 +30,7 @@ class TestBackgroundRestrictions @Inject constructor(private val context: AppCom private val stringProvider: StringProvider) : TroubleshootTest(R.string.settings_troubleshoot_test_bg_restricted_title) { - override fun perform() { + override fun perform(activityResultLauncher: ActivityResultLauncher) { context.getSystemService()!!.apply { // Checks if the device is on a metered network if (isActiveNetworkMetered) { diff --git a/vector/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestBatteryOptimization.kt b/vector/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestBatteryOptimization.kt index 510ade0a33..b1eeae6681 100644 --- a/vector/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestBatteryOptimization.kt +++ b/vector/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestBatteryOptimization.kt @@ -15,12 +15,13 @@ */ package im.vector.app.fdroid.features.settings.troubleshoot +import android.content.Intent +import androidx.activity.result.ActivityResultLauncher import androidx.appcompat.app.AppCompatActivity import im.vector.app.R import im.vector.app.core.resources.StringProvider import im.vector.app.core.utils.isIgnoringBatteryOptimizations import im.vector.app.core.utils.requestDisablingBatteryOptimization -import im.vector.app.features.settings.troubleshoot.NotificationTroubleshootTestManager import im.vector.app.features.settings.troubleshoot.TroubleshootTest import javax.inject.Inject @@ -29,7 +30,7 @@ class TestBatteryOptimization @Inject constructor( private val stringProvider: StringProvider ) : TroubleshootTest(R.string.settings_troubleshoot_test_battery_title) { - override fun perform() { + override fun perform(activityResultLauncher: ActivityResultLauncher) { if (isIgnoringBatteryOptimizations(context)) { description = stringProvider.getString(R.string.settings_troubleshoot_test_battery_success) status = TestStatus.SUCCESS @@ -38,7 +39,7 @@ class TestBatteryOptimization @Inject constructor( description = stringProvider.getString(R.string.settings_troubleshoot_test_battery_failed) quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_battery_quickfix) { override fun doFix() { - requestDisablingBatteryOptimization(context, null, NotificationTroubleshootTestManager.REQ_CODE_FIX) + requestDisablingBatteryOptimization(context, activityResultLauncher) } } status = TestStatus.FAILED diff --git a/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestFirebaseToken.kt b/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestFirebaseToken.kt index 318867af91..32888dafd7 100644 --- a/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestFirebaseToken.kt +++ b/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestFirebaseToken.kt @@ -15,12 +15,13 @@ */ 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 im.vector.app.R import im.vector.app.core.resources.StringProvider import im.vector.app.core.utils.startAddGoogleAccountIntent -import im.vector.app.features.settings.troubleshoot.NotificationTroubleshootTestManager import im.vector.app.features.settings.troubleshoot.TroubleshootTest import im.vector.app.push.fcm.FcmHelper import timber.log.Timber @@ -32,7 +33,7 @@ import javax.inject.Inject class TestFirebaseToken @Inject constructor(private val context: AppCompatActivity, private val stringProvider: StringProvider) : TroubleshootTest(R.string.settings_troubleshoot_test_fcm_title) { - override fun perform() { + override fun perform(activityResultLauncher: ActivityResultLauncher) { status = TestStatus.RUNNING try { FirebaseInstanceId.getInstance().instanceId @@ -48,7 +49,7 @@ class TestFirebaseToken @Inject constructor(private val context: AppCompatActivi 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, NotificationTroubleshootTestManager.REQ_CODE_FIX) + startAddGoogleAccountIntent(context, activityResultLauncher) } } } else { diff --git a/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestPlayServices.kt b/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestPlayServices.kt index 133fe1cb05..92e713de81 100644 --- a/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestPlayServices.kt +++ b/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestPlayServices.kt @@ -15,6 +15,8 @@ */ 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.android.gms.common.ConnectionResult import com.google.android.gms.common.GoogleApiAvailability @@ -31,7 +33,7 @@ class TestPlayServices @Inject constructor(private val context: AppCompatActivit private val stringProvider: StringProvider) : TroubleshootTest(R.string.settings_troubleshoot_test_play_services_title) { - override fun perform() { + override fun perform(activityResultLauncher: ActivityResultLauncher) { val apiAvailability = GoogleApiAvailability.getInstance() val resultCode = apiAvailability.isGooglePlayServicesAvailable(context) if (resultCode == ConnectionResult.SUCCESS) { diff --git a/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestTokenRegistration.kt b/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestTokenRegistration.kt index 6cb9c38fc6..f400c17d46 100644 --- a/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestTokenRegistration.kt +++ b/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestTokenRegistration.kt @@ -15,6 +15,8 @@ */ package im.vector.app.gplay.features.settings.troubleshoot +import android.content.Intent +import androidx.activity.result.ActivityResultLauncher import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.Observer import androidx.work.WorkInfo @@ -37,7 +39,7 @@ class TestTokenRegistration @Inject constructor(private val context: AppCompatAc private val activeSessionHolder: ActiveSessionHolder) : TroubleshootTest(R.string.settings_troubleshoot_test_token_registration_title) { - override fun perform() { + override fun perform(activityResultLauncher: ActivityResultLauncher) { // Check if we have a registered pusher for this token val fcmToken = FcmHelper.getFcmToken(context) ?: run { status = TestStatus.FAILED @@ -59,9 +61,9 @@ class TestTokenRegistration @Inject constructor(private val context: AppCompatAc WorkManager.getInstance(context).getWorkInfoByIdLiveData(workId).observe(context, Observer { workInfo -> if (workInfo != null) { if (workInfo.state == WorkInfo.State.SUCCEEDED) { - manager?.retry() + manager?.retry(activityResultLauncher) } else if (workInfo.state == WorkInfo.State.FAILED) { - manager?.retry() + manager?.retry(activityResultLauncher) } } }) diff --git a/vector/src/main/AndroidManifest.xml b/vector/src/main/AndroidManifest.xml index 3f7ff8c808..3f178a3d20 100644 --- a/vector/src/main/AndroidManifest.xml +++ b/vector/src/main/AndroidManifest.xml @@ -232,9 +232,11 @@ + + android:exported="false" + tools:ignore="Instantiatable" /> Unit): ActivityResultLauncher { + return registerForActivityResult(ActivityResultContracts.StartActivityForResult(), onResult) +} + fun VectorBaseActivity.addFragment( frameId: Int, fragment: Fragment, diff --git a/vector/src/main/java/im/vector/app/core/extensions/Fragment.kt b/vector/src/main/java/im/vector/app/core/extensions/Fragment.kt index fbcd6900c1..2740d5393a 100644 --- a/vector/src/main/java/im/vector/app/core/extensions/Fragment.kt +++ b/vector/src/main/java/im/vector/app/core/extensions/Fragment.kt @@ -17,7 +17,11 @@ package im.vector.app.core.extensions import android.app.Activity +import android.content.Intent import android.os.Parcelable +import androidx.activity.result.ActivityResult +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts import androidx.fragment.app.Fragment import im.vector.app.R import im.vector.app.core.platform.VectorBaseFragment @@ -26,6 +30,10 @@ import java.text.SimpleDateFormat import java.util.Date import java.util.Locale +fun Fragment.registerStartForActivityResult(onResult: (ActivityResult) -> Unit): ActivityResultLauncher { + return registerForActivityResult(ActivityResultContracts.StartActivityForResult(), onResult) +} + fun VectorBaseFragment.addFragment( frameId: Int, fragment: Fragment, @@ -160,26 +168,24 @@ fun Fragment.getAllChildFragments(): List { // Define a missing constant const val POP_BACK_STACK_EXCLUSIVE = 0 -fun Fragment.queryExportKeys(userId: String, requestCode: Int) { +fun Fragment.queryExportKeys(userId: String, activityResultLauncher: ActivityResultLauncher) { val timestamp = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(Date()) selectTxtFileToWrite( activity = requireActivity(), - fragment = this, + activityResultLauncher = activityResultLauncher, defaultFileName = "element-megolm-export-$userId-$timestamp.txt", - chooserHint = getString(R.string.keys_backup_setup_step1_manual_export), - requestCode = requestCode + chooserHint = getString(R.string.keys_backup_setup_step1_manual_export) ) } -fun Activity.queryExportKeys(userId: String, requestCode: Int) { +fun Activity.queryExportKeys(userId: String, activityResultLauncher: ActivityResultLauncher) { val timestamp = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(Date()) selectTxtFileToWrite( activity = this, - fragment = null, + activityResultLauncher = activityResultLauncher, defaultFileName = "element-megolm-export-$userId-$timestamp.txt", - chooserHint = getString(R.string.keys_backup_setup_step1_manual_export), - requestCode = requestCode + chooserHint = getString(R.string.keys_backup_setup_step1_manual_export) ) } diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt b/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt index 81f73556a5..cdba5b2ef2 100644 --- a/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt +++ b/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt @@ -18,7 +18,6 @@ package im.vector.app.core.platform import android.app.Activity import android.content.Context -import android.content.Intent import android.content.res.Configuration import android.os.Bundle import android.os.Parcelable @@ -60,6 +59,7 @@ import im.vector.app.core.dialogs.UnrecognizedCertificateDialog import im.vector.app.core.extensions.exhaustive import im.vector.app.core.extensions.observeEvent import im.vector.app.core.extensions.observeNotNull +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.extensions.restart import im.vector.app.core.extensions.vectorComponent import im.vector.app.core.utils.toast @@ -68,7 +68,6 @@ import im.vector.app.features.MainActivityArgs import im.vector.app.features.configuration.VectorConfiguration import im.vector.app.features.consent.ConsentNotGivenHelper import im.vector.app.features.navigation.Navigator -import im.vector.app.features.pin.PinActivity import im.vector.app.features.pin.PinLocker import im.vector.app.features.pin.PinMode import im.vector.app.features.pin.UnlockedActivity @@ -206,7 +205,7 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector { }) pinLocker.getLiveState().observeNotNull(this) { if (this@VectorBaseActivity !is UnlockedActivity && it == PinLocker.State.LOCKED) { - navigator.openPinCode(this, PinMode.AUTH) + navigator.openPinCode(this, pinStartForActivityResult, PinMode.AUTH) } } sessionListener = vectorComponent.sessionListener() @@ -313,22 +312,20 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector { uiDisposables.dispose() } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data) - if (requestCode == PinActivity.PIN_REQUEST_CODE) { - when (resultCode) { - Activity.RESULT_OK -> { - Timber.v("Pin ok, unlock app") - pinLocker.unlock() + private val pinStartForActivityResult = registerStartForActivityResult { activityResult -> + when (activityResult.resultCode) { + Activity.RESULT_OK -> { + Timber.v("Pin ok, unlock app") + pinLocker.unlock() - // Cancel any new started PinActivity, after a screen rotation for instance - finishActivity(PinActivity.PIN_REQUEST_CODE) - } - else -> { - if (pinLocker.getLiveState().value != PinLocker.State.UNLOCKED) { - // Remove the task, to be sure that PIN code will be requested when resumed - finishAndRemoveTask() - } + // Cancel any new started PinActivity, after a screen rotation for instance + // FIXME I cannot use this anymore :/ + // finishActivity(PinActivity.PIN_REQUEST_CODE) + } + else -> { + if (pinLocker.getLiveState().value != PinLocker.State.UNLOCKED) { + // Remove the task, to be sure that PIN code will be requested when resumed + finishAndRemoveTask() } } } diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorViewModel.kt b/vector/src/main/java/im/vector/app/core/platform/VectorViewModel.kt index 6429c9dfe5..002dfcf068 100644 --- a/vector/src/main/java/im/vector/app/core/platform/VectorViewModel.kt +++ b/vector/src/main/java/im/vector/app/core/platform/VectorViewModel.kt @@ -42,9 +42,11 @@ abstract class VectorViewModel Single.toAsync(stateReducer: S.(Async) -> S): Single> { setState { stateReducer(Loading()) } - return this.map { Success(it) as Async } + return map { Success(it) as Async } .onErrorReturn { Fail(it) } .doOnSuccess { setState { stateReducer(it) } } } @@ -53,9 +55,11 @@ abstract class VectorViewModel Observable.toAsync(stateReducer: S.(Async) -> S): Observable> { setState { stateReducer(Loading()) } - return this.map { Success(it) as Async } + return map { Success(it) as Async } .onErrorReturn { Fail(it) } .doOnNext { setState { stateReducer(it) } } } diff --git a/vector/src/main/java/im/vector/app/core/utils/ExternalApplicationsUtil.kt b/vector/src/main/java/im/vector/app/core/utils/ExternalApplicationsUtil.kt index d28f6749a6..1e19a1231a 100644 --- a/vector/src/main/java/im/vector/app/core/utils/ExternalApplicationsUtil.kt +++ b/vector/src/main/java/im/vector/app/core/utils/ExternalApplicationsUtil.kt @@ -31,12 +31,12 @@ import android.provider.Browser import android.provider.MediaStore import android.webkit.MimeTypeMap import android.widget.Toast +import androidx.activity.result.ActivityResultLauncher import androidx.browser.customtabs.CustomTabsIntent import androidx.browser.customtabs.CustomTabsSession import androidx.core.content.ContextCompat import androidx.core.content.FileProvider import androidx.core.content.getSystemService -import androidx.fragment.app.Fragment import im.vector.app.BuildConfig import im.vector.app.R import im.vector.app.features.notifications.NotificationUtils @@ -130,7 +130,7 @@ fun openSoundRecorder(activity: Activity, requestCode: Int) { * Open file selection activity */ fun openFileSelection(activity: Activity, - fragment: Fragment?, + activityResultLauncher: ActivityResultLauncher?, allowMultipleSelection: Boolean, requestCode: Int) { val fileIntent = Intent(Intent.ACTION_GET_CONTENT) @@ -140,8 +140,8 @@ fun openFileSelection(activity: Activity, fileIntent.type = "*/*" try { - fragment - ?.startActivityForResult(fileIntent, requestCode) + activityResultLauncher + ?.launch(fileIntent) ?: run { activity.startActivityForResult(fileIntent, requestCode) } @@ -440,10 +440,9 @@ fun openPlayStore(activity: Activity, appId: String = BuildConfig.APPLICATION_ID */ fun selectTxtFileToWrite( activity: Activity, - fragment: Fragment?, + activityResultLauncher: ActivityResultLauncher, defaultFileName: String, - chooserHint: String, - requestCode: Int + chooserHint: String ) { val intent = Intent(Intent.ACTION_CREATE_DOCUMENT) intent.addCategory(Intent.CATEGORY_OPENABLE) @@ -452,11 +451,7 @@ fun selectTxtFileToWrite( try { val chooserIntent = Intent.createChooser(intent, chooserHint) - if (fragment != null) { - fragment.startActivityForResult(chooserIntent, requestCode) - } else { - activity.startActivityForResult(chooserIntent, requestCode) - } + activityResultLauncher.launch(chooserIntent) } catch (activityNotFoundException: ActivityNotFoundException) { activity.toast(R.string.error_no_external_application_found) } diff --git a/vector/src/main/java/im/vector/app/core/utils/PermissionsTools.kt b/vector/src/main/java/im/vector/app/core/utils/PermissionsTools.kt index aff26ae494..44fc6afa4e 100644 --- a/vector/src/main/java/im/vector/app/core/utils/PermissionsTools.kt +++ b/vector/src/main/java/im/vector/app/core/utils/PermissionsTools.kt @@ -22,6 +22,8 @@ import android.content.Context import android.content.pm.PackageManager import android.os.Build import android.widget.Toast +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts import androidx.annotation.StringRes import androidx.appcompat.app.AlertDialog import androidx.core.app.ActivityCompat @@ -94,6 +96,12 @@ fun logPermissionStatuses(context: Context) { } } +fun Fragment.registerForPermissionsResult(allGranted: (Boolean) -> Unit): ActivityResultLauncher> { + return registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { result -> + allGranted.invoke(result.keys.all { result[it] == true }) + } +} + /** * See [.checkPermissions] * @@ -112,14 +120,14 @@ fun checkPermissions(permissionsToBeGrantedBitMap: Int, * See [.checkPermissions] * * @param permissionsToBeGrantedBitMap - * @param fragment + * @param activityResultLauncher from the calling fragment that is requesting the permissions * @return true if the permissions are granted (synchronous flow), false otherwise (asynchronous flow) */ fun checkPermissions(permissionsToBeGrantedBitMap: Int, - fragment: Fragment, - requestCode: Int, + activity: Activity, + activityResultLauncher: ActivityResultLauncher>, @StringRes rationaleMessage: Int = 0): Boolean { - return checkPermissions(permissionsToBeGrantedBitMap, fragment.activity, fragment, requestCode, rationaleMessage) + return checkPermissions(permissionsToBeGrantedBitMap, activity, activityResultLauncher, 0, rationaleMessage) } /** @@ -137,22 +145,19 @@ fun checkPermissions(permissionsToBeGrantedBitMap: Int, * * @param permissionsToBeGrantedBitMap the permissions bit map to be granted * @param activity the calling Activity that is requesting the permissions (or fragment parent) - * @param fragment the calling fragment that is requesting the permissions + * @param activityResultLauncher from the calling fragment that is requesting the permissions * @return true if the permissions are granted (synchronous flow), false otherwise (asynchronous flow) */ private fun checkPermissions(permissionsToBeGrantedBitMap: Int, - activity: Activity?, - fragment: Fragment?, + activity: Activity, + activityResultLauncher: ActivityResultLauncher>?, requestCode: Int, @StringRes rationaleMessage: Int ): Boolean { var isPermissionGranted = false // sanity check - if (null == activity) { - Timber.w("## checkPermissions(): invalid input data") - isPermissionGranted = false - } else if (PERMISSIONS_EMPTY == permissionsToBeGrantedBitMap) { + if (PERMISSIONS_EMPTY == permissionsToBeGrantedBitMap) { isPermissionGranted = true } else if (PERMISSIONS_FOR_AUDIO_IP_CALL != permissionsToBeGrantedBitMap && PERMISSIONS_FOR_VIDEO_IP_CALL != permissionsToBeGrantedBitMap @@ -222,7 +227,8 @@ private fun checkPermissions(permissionsToBeGrantedBitMap: Int, .setOnCancelListener { Toast.makeText(activity, R.string.missing_permissions_warning, Toast.LENGTH_SHORT).show() } .setPositiveButton(R.string.ok) { _, _ -> if (permissionsListToBeGranted.isNotEmpty()) { - fragment?.requestPermissions(permissionsListToBeGranted.toTypedArray(), requestCode) + activityResultLauncher + ?.launch(permissionsListToBeGranted.toTypedArray()) ?: run { ActivityCompat.requestPermissions(activity, permissionsListToBeGranted.toTypedArray(), requestCode) } @@ -262,7 +268,8 @@ private fun checkPermissions(permissionsToBeGrantedBitMap: Int, .show() */ } else { - fragment?.requestPermissions(permissionsArrayToBeGranted, requestCode) + activityResultLauncher + ?.launch(permissionsArrayToBeGranted) ?: run { ActivityCompat.requestPermissions(activity, permissionsArrayToBeGranted, requestCode) } @@ -307,43 +314,6 @@ private fun updatePermissionsToBeGranted(activity: Activity, return isRequestPermissionRequested } -/** - * Helper method to process [.PERMISSIONS_FOR_AUDIO_IP_CALL] - * on onRequestPermissionsResult() methods. - * - * @param context App context - * @param grantResults permissions granted results - * @return true if audio IP call is permitted, false otherwise - */ -fun onPermissionResultAudioIpCall(context: Context, grantResults: IntArray): Boolean { - val arePermissionsGranted = allGranted(grantResults) - - if (!arePermissionsGranted) { - Toast.makeText(context, R.string.permissions_action_not_performed_missing_permissions, Toast.LENGTH_SHORT).show() - } - - return arePermissionsGranted -} - -/** - * Helper method to process [.PERMISSIONS_FOR_VIDEO_IP_CALL] - * on onRequestPermissionsResult() methods. - * For video IP calls, record audio and camera permissions are both mandatory. - * - * @param context App context - * @param grantResults permissions granted results - * @return true if video IP call is permitted, false otherwise - */ -fun onPermissionResultVideoIpCall(context: Context, grantResults: IntArray): Boolean { - val arePermissionsGranted = allGranted(grantResults) - - if (!arePermissionsGranted) { - Toast.makeText(context, R.string.permissions_action_not_performed_missing_permissions, Toast.LENGTH_SHORT).show() - } - - return arePermissionsGranted -} - /** * Return true if all permissions are granted, false if not or if permission request has been cancelled */ diff --git a/vector/src/main/java/im/vector/app/core/utils/SystemUtils.kt b/vector/src/main/java/im/vector/app/core/utils/SystemUtils.kt index c1253e76d3..5e722ae209 100644 --- a/vector/src/main/java/im/vector/app/core/utils/SystemUtils.kt +++ b/vector/src/main/java/im/vector/app/core/utils/SystemUtils.kt @@ -28,6 +28,7 @@ import android.os.Build import android.os.PowerManager import android.provider.Settings import android.widget.Toast +import androidx.activity.result.ActivityResultLauncher import androidx.annotation.StringRes import androidx.appcompat.app.AppCompatActivity import androidx.core.content.getSystemService @@ -67,15 +68,11 @@ fun isAnimationDisabled(context: Context): Boolean { * will return false and the notification privacy will fallback to "LOW_DETAIL". */ @TargetApi(Build.VERSION_CODES.M) -fun requestDisablingBatteryOptimization(activity: Activity, fragment: Fragment?, requestCode: Int) { +fun requestDisablingBatteryOptimization(activity: Activity, activityResultLauncher: ActivityResultLauncher) { val intent = Intent() intent.action = Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS intent.data = Uri.parse("package:" + activity.packageName) - if (fragment != null) { - fragment.startActivityForResult(intent, requestCode) - } else { - activity.startActivityForResult(intent, requestCode) - } + activityResultLauncher.launch(intent) } // ============================================================================================================== @@ -100,7 +97,7 @@ fun copyToClipboard(context: Context, text: CharSequence, showToast: Boolean = t * Shows notification settings for the current app. * In android O will directly opens the notification settings, in lower version it will show the App settings */ -fun startNotificationSettingsIntent(activity: AppCompatActivity, requestCode: Int) { +fun startNotificationSettingsIntent(activity: AppCompatActivity, activityResultLauncher: ActivityResultLauncher) { val intent = Intent() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS @@ -110,7 +107,7 @@ fun startNotificationSettingsIntent(activity: AppCompatActivity, requestCode: In intent.putExtra("app_package", activity.packageName) intent.putExtra("app_uid", activity.applicationInfo?.uid) } - activity.startActivityForResult(intent, requestCode) + activityResultLauncher.launch(intent) } /** @@ -126,42 +123,47 @@ fun startNotificationChannelSettingsIntent(fragment: Fragment, channelID: String fragment.startActivity(intent) } -fun startAddGoogleAccountIntent(context: AppCompatActivity, requestCode: Int) { +fun startAddGoogleAccountIntent(context: Context, activityResultLauncher: ActivityResultLauncher) { try { val intent = Intent(Settings.ACTION_ADD_ACCOUNT) intent.putExtra(Settings.EXTRA_ACCOUNT_TYPES, arrayOf("com.google")) - context.startActivityForResult(intent, requestCode) + activityResultLauncher.launch(intent) } catch (activityNotFoundException: ActivityNotFoundException) { context.toast(R.string.error_no_external_application_found) } } -fun startSharePlainTextIntent(fragment: Fragment, chooserTitle: String?, text: String, subject: String? = null, requestCode: Int? = null) { +fun startSharePlainTextIntent(fragment: Fragment, + activityResultLauncher: ActivityResultLauncher?, + chooserTitle: String?, + text: String, + subject: String? = null) { val share = Intent(Intent.ACTION_SEND) share.type = "text/plain" share.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT) // Add data to the intent, the receiving app will decide what to do with it. share.putExtra(Intent.EXTRA_SUBJECT, subject) share.putExtra(Intent.EXTRA_TEXT, text) + val intent = Intent.createChooser(share, chooserTitle) try { - if (requestCode != null) { - fragment.startActivityForResult(Intent.createChooser(share, chooserTitle), requestCode) + if (activityResultLauncher != null) { + activityResultLauncher.launch(intent) } else { - fragment.startActivity(Intent.createChooser(share, chooserTitle)) + fragment.startActivity(intent) } } catch (activityNotFoundException: ActivityNotFoundException) { fragment.activity?.toast(R.string.error_no_external_application_found) } } -fun startImportTextFromFileIntent(fragment: Fragment, requestCode: Int) { +fun startImportTextFromFileIntent(context: Context, activityResultLauncher: ActivityResultLauncher) { val intent = Intent(Intent.ACTION_GET_CONTENT).apply { type = "text/plain" } - if (intent.resolveActivity(fragment.requireActivity().packageManager) != null) { - fragment.startActivityForResult(intent, requestCode) + if (intent.resolveActivity(context.packageManager) != null) { + activityResultLauncher.launch(intent) } else { - fragment.activity?.toast(R.string.error_no_external_application_found) + context.toast(R.string.error_no_external_application_found) } } diff --git a/vector/src/main/java/im/vector/app/features/attachments/AttachmentsHelper.kt b/vector/src/main/java/im/vector/app/features/attachments/AttachmentsHelper.kt index 1d7c67b046..d4efb22eb8 100644 --- a/vector/src/main/java/im/vector/app/features/attachments/AttachmentsHelper.kt +++ b/vector/src/main/java/im/vector/app/features/attachments/AttachmentsHelper.kt @@ -15,12 +15,11 @@ */ package im.vector.app.features.attachments -import android.app.Activity import android.content.Context import android.content.Intent import android.net.Uri import android.os.Bundle -import androidx.fragment.app.Fragment +import androidx.activity.result.ActivityResultLauncher import im.vector.app.core.platform.Restorable import im.vector.lib.multipicker.MultiPicker import org.matrix.android.sdk.BuildConfig @@ -48,6 +47,7 @@ class AttachmentsHelper(val context: Context, val callback: Callback) : Restorab // Capture path allows to handle camera image picking. It must be restored if the activity gets killed. private var captureUri: Uri? = null + // The pending type is set if we have to handle permission request. It must be restored if the activity gets killed. var pendingType: AttachmentTypeSelectorView.Type? = null @@ -72,99 +72,93 @@ class AttachmentsHelper(val context: Context, val callback: Callback) : Restorab /** * Starts the process for handling file picking */ - fun selectFile(fragment: Fragment) { - MultiPicker.get(MultiPicker.FILE).startWith(fragment) + fun selectFile(activityResultLauncher: ActivityResultLauncher) { + MultiPicker.get(MultiPicker.FILE).startWith(activityResultLauncher) } /** * Starts the process for handling image picking */ - fun selectGallery(fragment: Fragment) { - MultiPicker.get(MultiPicker.IMAGE).startWith(fragment) + fun selectGallery(activityResultLauncher: ActivityResultLauncher) { + MultiPicker.get(MultiPicker.IMAGE).startWith(activityResultLauncher) } /** * Starts the process for handling audio picking */ - fun selectAudio(fragment: Fragment) { - MultiPicker.get(MultiPicker.AUDIO).startWith(fragment) + fun selectAudio(activityResultLauncher: ActivityResultLauncher) { + MultiPicker.get(MultiPicker.AUDIO).startWith(activityResultLauncher) } /** * Starts the process for handling capture image picking */ - fun openCamera(fragment: Fragment) { - captureUri = MultiPicker.get(MultiPicker.CAMERA).startWithExpectingFile(fragment) + fun openCamera(context: Context, activityResultLauncher: ActivityResultLauncher) { + captureUri = MultiPicker.get(MultiPicker.CAMERA).startWithExpectingFile(context, activityResultLauncher) } /** * Starts the process for handling contact picking */ - fun selectContact(fragment: Fragment) { - MultiPicker.get(MultiPicker.CONTACT).startWith(fragment) + fun selectContact(activityResultLauncher: ActivityResultLauncher) { + MultiPicker.get(MultiPicker.CONTACT).startWith(activityResultLauncher) } /** - * This methods aims to handle on activity result data. - * - * @return true if it can handle the data, false otherwise + * This methods aims to handle the result data. */ - fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean { - if (resultCode == Activity.RESULT_OK) { - when (requestCode) { - MultiPicker.REQUEST_CODE_PICK_FILE -> { - callback.onContentAttachmentsReady( - MultiPicker.get(MultiPicker.FILE) - .getSelectedFiles(context, requestCode, resultCode, data) - .map { it.toContentAttachmentData() } - ) + fun onFileResult(data: Intent?) { + callback.onContentAttachmentsReady( + MultiPicker.get(MultiPicker.FILE) + .getSelectedFiles(context, data) + .map { it.toContentAttachmentData() } + ) + } + + fun onAudioResult(data: Intent?) { + callback.onContentAttachmentsReady( + MultiPicker.get(MultiPicker.AUDIO) + .getSelectedFiles(context, data) + .map { it.toContentAttachmentData() } + ) + } + + fun onContactResult(data: Intent?) { + MultiPicker.get(MultiPicker.CONTACT) + .getSelectedFiles(context, data) + .firstOrNull() + ?.toContactAttachment() + ?.let { + callback.onContactAttachmentReady(it) } - MultiPicker.REQUEST_CODE_PICK_AUDIO -> { - callback.onContentAttachmentsReady( - MultiPicker.get(MultiPicker.AUDIO) - .getSelectedFiles(context, requestCode, resultCode, data) - .map { it.toContentAttachmentData() } - ) - } - MultiPicker.REQUEST_CODE_PICK_CONTACT -> { - MultiPicker.get(MultiPicker.CONTACT) - .getSelectedFiles(context, requestCode, resultCode, data) - .firstOrNull() - ?.toContactAttachment() - ?.let { - callback.onContactAttachmentReady(it) - } - } - MultiPicker.REQUEST_CODE_PICK_IMAGE -> { - callback.onContentAttachmentsReady( - MultiPicker.get(MultiPicker.IMAGE) - .getSelectedFiles(context, requestCode, resultCode, data) - .map { it.toContentAttachmentData() } - ) - } - MultiPicker.REQUEST_CODE_TAKE_PHOTO -> { - captureUri?.let { captureUri -> - MultiPicker.get(MultiPicker.CAMERA) - .getTakenPhoto(context, requestCode, resultCode, captureUri) - ?.let { - callback.onContentAttachmentsReady( - listOf(it).map { it.toContentAttachmentData() } - ) - } + } + + fun onImageResult(data: Intent?) { + callback.onContentAttachmentsReady( + MultiPicker.get(MultiPicker.IMAGE) + .getSelectedFiles(context, data) + .map { it.toContentAttachmentData() } + ) + } + + fun onPhotoResult() { + captureUri?.let { captureUri -> + MultiPicker.get(MultiPicker.CAMERA) + .getTakenPhoto(context, captureUri) + ?.let { + callback.onContentAttachmentsReady( + listOf(it).map { it.toContentAttachmentData() } + ) } - } - MultiPicker.REQUEST_CODE_PICK_VIDEO -> { - callback.onContentAttachmentsReady( - MultiPicker.get(MultiPicker.VIDEO) - .getSelectedFiles(context, requestCode, resultCode, data) - .map { it.toContentAttachmentData() } - ) - } - else -> return false - } - return true } - return false + } + + fun onVideoResult(data: Intent?) { + callback.onContentAttachmentsReady( + MultiPicker.get(MultiPicker.VIDEO) + .getSelectedFiles(context, data) + .map { it.toContentAttachmentData() } + ) } /** diff --git a/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewActivity.kt b/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewActivity.kt index bfa44a7a70..8e830d00a4 100644 --- a/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewActivity.kt +++ b/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewActivity.kt @@ -30,8 +30,6 @@ import org.matrix.android.sdk.api.session.content.ContentAttachmentData class AttachmentsPreviewActivity : VectorBaseActivity(), ToolbarConfigurable { companion object { - const val REQUEST_CODE = 55 - private const val EXTRA_FRAGMENT_ARGS = "EXTRA_FRAGMENT_ARGS" private const val ATTACHMENTS_PREVIEW_RESULT = "ATTACHMENTS_PREVIEW_RESULT" private const val KEEP_ORIGINAL_IMAGES_SIZE = "KEEP_ORIGINAL_IMAGES_SIZE" diff --git a/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewFragment.kt b/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewFragment.kt index 387cfb2261..f6a6e3abcf 100644 --- a/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewFragment.kt +++ b/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewFragment.kt @@ -81,6 +81,10 @@ class AttachmentsPreviewFragment @Inject constructor( } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + // TODO handle this one (Ucrop lib) + @Suppress("DEPRECATION") + super.onActivityResult(requestCode, resultCode, data) + if (resultCode == RESULT_OK) { if (requestCode == UCrop.REQUEST_CROP && data != null) { Timber.v("Crop success") diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt index 4d6c4e3add..44c440c0d1 100644 --- a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt +++ b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt @@ -298,6 +298,7 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) if (requestCode == CAPTURE_PERMISSION_REQUEST_CODE && allGranted(grantResults)) { start() } else { diff --git a/vector/src/main/java/im/vector/app/features/call/conference/VectorJitsiActivity.kt b/vector/src/main/java/im/vector/app/features/call/conference/VectorJitsiActivity.kt index 5a4c25a8ad..1ab6fb6363 100644 --- a/vector/src/main/java/im/vector/app/features/call/conference/VectorJitsiActivity.kt +++ b/vector/src/main/java/im/vector/app/features/call/conference/VectorJitsiActivity.kt @@ -150,6 +150,7 @@ class VectorJitsiActivity : VectorBaseActivity(), JitsiMeetActivityInterface, Ji } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) JitsiMeetActivityDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults) } diff --git a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomActivity.kt b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomActivity.kt index 27ca636142..501d0fdbb7 100644 --- a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomActivity.kt +++ b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomActivity.kt @@ -111,6 +111,7 @@ class CreateDirectRoomActivity : SimpleFragmentActivity() { } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) if (allGranted(grantResults)) { if (requestCode == PERMISSION_REQUEST_CODE_READ_CONTACTS) { doOnPostResume { addFragmentToBackstack(R.id.container, ContactsBookFragment::class.java) } diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreActivity.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreActivity.kt index 40953cb5f6..80ae46262d 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreActivity.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreActivity.kt @@ -23,6 +23,7 @@ import androidx.lifecycle.Observer import im.vector.app.R import im.vector.app.core.extensions.addFragmentToBackstack import im.vector.app.core.extensions.observeEvent +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.extensions.replaceFragment import im.vector.app.core.platform.SimpleFragmentActivity import im.vector.app.core.ui.views.KeysBackupBanner @@ -32,8 +33,6 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_S class KeysBackupRestoreActivity : SimpleFragmentActivity() { companion object { - - private const val REQUEST_4S_SECRET = 100 const val SECRET_ALIAS = SharedSecureStorageActivity.DEFAULT_RESULT_KEYSTORE_ALIAS fun intent(context: Context): Intent { @@ -130,22 +129,19 @@ class KeysBackupRestoreActivity : SimpleFragmentActivity() { requestedSecrets = listOf(KEYBACKUP_SECRET_SSSS_NAME), resultKeyStoreAlias = SECRET_ALIAS ).let { - startActivityForResult(it, REQUEST_4S_SECRET) + secretStartForActivityResult.launch(it) } } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - if (requestCode == REQUEST_4S_SECRET) { - val extraResult = data?.getStringExtra(SharedSecureStorageActivity.EXTRA_DATA_RESULT) - if (resultCode == Activity.RESULT_OK && extraResult != null) { - viewModel.handleGotSecretFromSSSS( - extraResult, - SECRET_ALIAS - ) - } else { - finish() - } + private val secretStartForActivityResult = registerStartForActivityResult { activityResult -> + val extraResult = activityResult.data?.getStringExtra(SharedSecureStorageActivity.EXTRA_DATA_RESULT) + if (activityResult.resultCode == Activity.RESULT_OK && extraResult != null) { + viewModel.handleGotSecretFromSSSS( + extraResult, + SECRET_ALIAS + ) + } else { + finish() } - super.onActivityResult(requestCode, resultCode, data) } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyFragment.kt index cc10256fc4..580a3443e7 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyFragment.kt @@ -16,9 +16,9 @@ package im.vector.app.features.crypto.keysbackup.restore import android.app.Activity -import android.content.Intent import android.os.Bundle import android.text.Editable +import android.view.View import android.view.inputmethod.EditorInfo import android.widget.EditText import androidx.lifecycle.Observer @@ -27,19 +27,15 @@ import butterknife.OnClick import butterknife.OnTextChanged import com.google.android.material.textfield.TextInputLayout import im.vector.app.R +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.utils.startImportTextFromFileIntent -import timber.log.Timber +import org.matrix.android.sdk.api.extensions.tryOrNull import javax.inject.Inject class KeysBackupRestoreFromKeyFragment @Inject constructor() : VectorBaseFragment() { - companion object { - - private const val REQUEST_TEXT_FILE_GET = 1 - } - override fun getLayoutResId() = R.layout.fragment_keys_backup_restore_from_key private lateinit var viewModel: KeysBackupRestoreFromKeyViewModel @@ -47,11 +43,12 @@ class KeysBackupRestoreFromKeyFragment @Inject constructor() @BindView(R.id.keys_backup_key_enter_til) lateinit var mKeyInputLayout: TextInputLayout + @BindView(R.id.keys_restore_key_enter_edittext) lateinit var mKeyTextEdit: EditText - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) viewModel = fragmentViewModelProvider.get(KeysBackupRestoreFromKeyViewModel::class.java) sharedViewModel = activityViewModelProvider.get(KeysBackupRestoreSharedViewModel::class.java) mKeyTextEdit.setText(viewModel.recoveryCode.value) @@ -88,29 +85,23 @@ class KeysBackupRestoreFromKeyFragment @Inject constructor() @OnClick(R.id.keys_backup_import) fun onImport() { - startImportTextFromFileIntent(this, REQUEST_TEXT_FILE_GET) + startImportTextFromFileIntent(requireContext(), textFileStartForActivityResult) } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - if (requestCode == REQUEST_TEXT_FILE_GET && resultCode == Activity.RESULT_OK) { - val dataURI = data?.data - if (dataURI != null) { - try { - activity - ?.contentResolver - ?.openInputStream(dataURI) - ?.bufferedReader() - ?.use { it.readText() } - ?.let { - mKeyTextEdit.setText(it) - mKeyTextEdit.setSelection(it.length) - } - } catch (e: Exception) { - Timber.e(e, "Failed to read recovery kay from text") - } + private val textFileStartForActivityResult = registerStartForActivityResult { activityResult -> + if (activityResult.resultCode == Activity.RESULT_OK) { + val dataURI = activityResult.data?.data ?: return@registerStartForActivityResult + tryOrNull(message = "Failed to read recovery kay from text") { + activity + ?.contentResolver + ?.openInputStream(dataURI) + ?.bufferedReader() + ?.use { it.readText() } + ?.let { + mKeyTextEdit.setText(it) + mKeyTextEdit.setSelection(it.length) + } } - return } - super.onActivityResult(requestCode, resultCode, data) } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseFragment.kt index 7941d95add..79aa728da1 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseFragment.kt @@ -59,8 +59,8 @@ class KeysBackupRestoreFromPassphraseFragment @Inject constructor() : VectorBase viewModel.showPasswordMode.value = !(viewModel.showPasswordMode.value ?: false) } - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) viewModel = fragmentViewModelProvider.get(KeysBackupRestoreFromPassphraseViewModel::class.java) sharedViewModel = activityViewModelProvider.get(KeysBackupRestoreSharedViewModel::class.java) diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreSuccessFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreSuccessFragment.kt index fa571b86c1..30a6cfe696 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreSuccessFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreSuccessFragment.kt @@ -16,6 +16,7 @@ package im.vector.app.features.crypto.keysbackup.restore import android.os.Bundle +import android.view.View import android.widget.TextView import androidx.core.view.isVisible import butterknife.BindView @@ -36,8 +37,8 @@ class KeysBackupRestoreSuccessFragment @Inject constructor() : VectorBaseFragmen private lateinit var sharedViewModel: KeysBackupRestoreSharedViewModel - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) sharedViewModel = activityViewModelProvider.get(KeysBackupRestoreSharedViewModel::class.java) if (compareValues(sharedViewModel.importKeyResult?.totalNumberOfKeys, 0) > 0) { diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupActivity.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupActivity.kt index 1bba2c3a39..ab8e725959 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupActivity.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupActivity.kt @@ -26,6 +26,7 @@ import im.vector.app.R import im.vector.app.core.dialogs.ExportKeysDialog import im.vector.app.core.extensions.observeEvent import im.vector.app.core.extensions.queryExportKeys +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.extensions.replaceFragment import im.vector.app.core.platform.SimpleFragmentActivity import im.vector.app.core.utils.toast @@ -93,7 +94,7 @@ class KeysBackupSetupActivity : SimpleFragmentActivity() { .show() } KeysBackupSetupSharedViewModel.NAVIGATE_MANUAL_EXPORT -> { - queryExportKeys(session.myUserId, REQUEST_CODE_SAVE_MEGOLM_EXPORT) + queryExportKeys(session.myUserId, saveStartForActivityResult) } } } @@ -125,10 +126,10 @@ class KeysBackupSetupActivity : SimpleFragmentActivity() { }) } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - if (requestCode == REQUEST_CODE_SAVE_MEGOLM_EXPORT) { - val uri = data?.data - if (resultCode == Activity.RESULT_OK && uri != null) { + private val saveStartForActivityResult = registerStartForActivityResult { activityResult -> + if (activityResult.resultCode == Activity.RESULT_OK) { + val uri = activityResult.data?.data + if (uri != null) { ExportKeysDialog().show(this, object : ExportKeysDialog.ExportKeyDialogListener { override fun onPassphrase(passphrase: String) { showWaitingView() @@ -163,7 +164,6 @@ class KeysBackupSetupActivity : SimpleFragmentActivity() { hideWaitingView() } } - super.onActivityResult(requestCode, resultCode, data) } override fun onBackPressed() { @@ -198,7 +198,6 @@ class KeysBackupSetupActivity : SimpleFragmentActivity() { const val KEYS_VERSION = "KEYS_VERSION" const val MANUAL_EXPORT = "MANUAL_EXPORT" const val EXTRA_SHOW_MANUAL_EXPORT = "SHOW_MANUAL_EXPORT" - const val REQUEST_CODE_SAVE_MEGOLM_EXPORT = 101 fun intent(context: Context, showManualExport: Boolean): Intent { val intent = Intent(context, KeysBackupSetupActivity::class.java) diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep1Fragment.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep1Fragment.kt index 0a82edd150..f855b86b6c 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep1Fragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep1Fragment.kt @@ -40,8 +40,8 @@ class KeysBackupSetupStep1Fragment @Inject constructor() : VectorBaseFragment() @BindView(R.id.keys_backup_setup_step1_manualExport) lateinit var manualExportButton: Button - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) viewModel = activityViewModelProvider.get(KeysBackupSetupSharedViewModel::class.java) diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep2Fragment.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep2Fragment.kt index 0520098866..af279c9140 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep2Fragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep2Fragment.kt @@ -16,6 +16,7 @@ package im.vector.app.features.crypto.keysbackup.setup import android.os.Bundle +import android.view.View import android.view.ViewGroup import android.view.inputmethod.EditorInfo import android.widget.EditText @@ -77,8 +78,8 @@ class KeysBackupSetupStep2Fragment @Inject constructor() : VectorBaseFragment() private lateinit var viewModel: KeysBackupSetupSharedViewModel - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) viewModel = activityViewModelProvider.get(KeysBackupSetupSharedViewModel::class.java) diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep3Fragment.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep3Fragment.kt index 41b7cbddfe..78471c6848 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep3Fragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep3Fragment.kt @@ -16,7 +16,6 @@ package im.vector.app.features.crypto.keysbackup.setup import android.app.Activity -import android.content.Intent import android.net.Uri import android.os.Bundle import android.view.View @@ -31,6 +30,7 @@ import butterknife.BindView import butterknife.OnClick import com.google.android.material.bottomsheet.BottomSheetDialog import im.vector.app.R +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.utils.LiveEvent import im.vector.app.core.utils.copyToClipboard @@ -48,10 +48,6 @@ import javax.inject.Inject class KeysBackupSetupStep3Fragment @Inject constructor() : VectorBaseFragment() { - companion object { - private const val SAVE_RECOVERY_KEY_REQUEST_CODE = 2754 - } - override fun getLayoutResId() = R.layout.fragment_keys_backup_setup_step3 @BindView(R.id.keys_backup_setup_step3_button) @@ -65,8 +61,8 @@ class KeysBackupSetupStep3Fragment @Inject constructor() : VectorBaseFragment() private lateinit var viewModel: KeysBackupSetupSharedViewModel - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) viewModel = activityViewModelProvider.get(KeysBackupSetupSharedViewModel::class.java) viewModel.shouldPromptOnBack = false @@ -138,19 +134,20 @@ class KeysBackupSetupStep3Fragment @Inject constructor() : VectorBaseFragment() val timestamp = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(Date()) selectTxtFileToWrite( activity = requireActivity(), - fragment = this, + activityResultLauncher = saveRecoveryActivityResultLauncher, defaultFileName = "recovery-key-$userId-$timestamp.txt", - chooserHint = getString(R.string.save_recovery_key_chooser_hint), - requestCode = SAVE_RECOVERY_KEY_REQUEST_CODE + chooserHint = getString(R.string.save_recovery_key_chooser_hint) ) dialog.dismiss() } dialog.findViewById(R.id.keys_backup_setup_share)?.setOnClickListener { - startSharePlainTextIntent(this, - context?.getString(R.string.keys_backup_setup_step3_share_intent_chooser_title), - recoveryKey, - context?.getString(R.string.recovery_key)) + startSharePlainTextIntent( + fragment = this, + activityResultLauncher = null, + chooserTitle = context?.getString(R.string.keys_backup_setup_step3_share_intent_chooser_title), + text = recoveryKey, + subject = context?.getString(R.string.recovery_key)) viewModel.copyHasBeenMade = true dialog.dismiss() } @@ -202,15 +199,11 @@ class KeysBackupSetupStep3Fragment @Inject constructor() : VectorBaseFragment() } } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - when (requestCode) { - SAVE_RECOVERY_KEY_REQUEST_CODE -> { - val uri = data?.data - if (resultCode == Activity.RESULT_OK && uri != null) { - viewModel.recoveryKey.value?.let { - exportRecoveryKeyToFile(uri, it) - } - } + private val saveRecoveryActivityResultLauncher = registerStartForActivityResult { activityRessult -> + val uri = activityRessult.data?.data ?: return@registerStartForActivityResult + if (activityRessult.resultCode == Activity.RESULT_OK) { + viewModel.recoveryKey.value?.let { + exportRecoveryKeyToFile(uri, it) } } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysrequest/KeyRequestHandler.kt b/vector/src/main/java/im/vector/app/features/crypto/keysrequest/KeyRequestHandler.kt index 4ed0e037d4..4268317700 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysrequest/KeyRequestHandler.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysrequest/KeyRequestHandler.kt @@ -126,7 +126,7 @@ class KeyRequestHandler @Inject constructor( // can we get more info on this device? session?.cryptoService()?.getMyDevicesInfo()?.firstOrNull { it.deviceId == deviceId }?.let { postAlert(context, userId, deviceId, true, deviceInfo, it) - } ?: kotlin.run { + } ?: run { postAlert(context, userId, deviceId, true, deviceInfo) } } else { diff --git a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageActivity.kt b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageActivity.kt index 42d000ecc3..2abad9dd38 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageActivity.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageActivity.kt @@ -24,6 +24,8 @@ import android.os.Parcelable import android.view.View import androidx.appcompat.app.AlertDialog import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentManager +import androidx.fragment.app.FragmentOnAttachListener import com.airbnb.mvrx.MvRx import com.airbnb.mvrx.viewModel import im.vector.app.R @@ -38,7 +40,10 @@ import kotlinx.android.synthetic.main.activity.* import javax.inject.Inject import kotlin.reflect.KClass -class SharedSecureStorageActivity : SimpleFragmentActivity(), VectorBaseBottomSheetDialogFragment.ResultListener { +class SharedSecureStorageActivity : + SimpleFragmentActivity(), + VectorBaseBottomSheetDialogFragment.ResultListener, + FragmentOnAttachListener { @Parcelize data class Args( @@ -58,6 +63,8 @@ class SharedSecureStorageActivity : SimpleFragmentActivity(), VectorBaseBottomSh override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + supportFragmentManager.addFragmentOnAttachListener(this) + toolbar.visibility = View.GONE viewModel.observeViewEvents { observeViewEvents(it) } @@ -65,6 +72,11 @@ class SharedSecureStorageActivity : SimpleFragmentActivity(), VectorBaseBottomSh viewModel.subscribe(this) { renderState(it) } } + override fun onDestroy() { + super.onDestroy() + supportFragmentManager.removeFragmentOnAttachListener(this) + } + override fun onBackPressed() { viewModel.handle(SharedSecureStorageAction.Back) } @@ -119,8 +131,7 @@ class SharedSecureStorageActivity : SimpleFragmentActivity(), VectorBaseBottomSh } } - override fun onAttachFragment(fragment: Fragment) { - super.onAttachFragment(fragment) + override fun onAttachFragment(fragmentManager: FragmentManager, fragment: Fragment) { if (fragment is VectorBaseBottomSheetDialogFragment) { fragment.resultListener = this } diff --git a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecuredStorageKeyFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecuredStorageKeyFragment.kt index 366c979155..9fb3f637e1 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecuredStorageKeyFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecuredStorageKeyFragment.kt @@ -17,7 +17,6 @@ package im.vector.app.features.crypto.quads import android.app.Activity -import android.content.Intent import android.os.Bundle import android.view.View import android.view.inputmethod.EditorInfo @@ -25,6 +24,7 @@ import com.airbnb.mvrx.activityViewModel import com.jakewharton.rxbinding3.widget.editorActionEvents import com.jakewharton.rxbinding3.widget.textChanges import im.vector.app.R +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.utils.startImportTextFromFileIntent import io.reactivex.android.schedulers.AndroidSchedulers @@ -61,7 +61,7 @@ class SharedSecuredStorageKeyFragment @Inject constructor() : VectorBaseFragment } .disposeOnDestroyView() - ssss_key_use_file.debouncedClicks { startImportTextFromFileIntent(this, IMPORT_FILE_REQ) } + ssss_key_use_file.debouncedClicks { startImportTextFromFileIntent(requireContext(), importFileStartForActivityResult) } ssss_key_reset.clickableView.debouncedClicks { sharedViewModel.handle(SharedSecureStorageAction.ForgotResetAll) @@ -85,9 +85,9 @@ class SharedSecuredStorageKeyFragment @Inject constructor() : VectorBaseFragment sharedViewModel.handle(SharedSecureStorageAction.SubmitKey(text)) } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - if (requestCode == IMPORT_FILE_REQ && resultCode == Activity.RESULT_OK) { - data?.data?.let { dataURI -> + private val importFileStartForActivityResult = registerStartForActivityResult { activityResult -> + if (activityResult.resultCode == Activity.RESULT_OK) { + activityResult.data?.data?.let { dataURI -> tryOrNull { activity?.contentResolver?.openInputStream(dataURI) ?.bufferedReader() @@ -97,12 +97,6 @@ class SharedSecuredStorageKeyFragment @Inject constructor() : VectorBaseFragment } } } - return } - super.onActivityResult(requestCode, resultCode, data) - } - - companion object { - private const val IMPORT_FILE_REQ = 0 } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapCrossSigningTask.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapCrossSigningTask.kt index 4dc2b92751..b7c689f41f 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapCrossSigningTask.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapCrossSigningTask.kt @@ -139,7 +139,7 @@ class BootstrapCrossSigningTask @Inject constructor( null, it ) - } ?: kotlin.run { + } ?: run { ssssService.generateKey( UUID.randomUUID().toString(), params.keySpec, diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapMigrateBackupFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapMigrateBackupFragment.kt index a89e08988c..69c809c773 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapMigrateBackupFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapMigrateBackupFragment.kt @@ -17,7 +17,6 @@ package im.vector.app.features.crypto.recover import android.app.Activity -import android.content.Intent import android.os.Bundle import android.text.InputType.TYPE_CLASS_TEXT import android.text.InputType.TYPE_TEXT_FLAG_MULTI_LINE @@ -32,6 +31,7 @@ import com.jakewharton.rxbinding3.widget.editorActionEvents import com.jakewharton.rxbinding3.widget.textChanges import im.vector.app.R import im.vector.app.core.extensions.hideKeyboard +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.extensions.showPassword import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.resources.ColorProvider @@ -82,7 +82,7 @@ class BootstrapMigrateBackupFragment @Inject constructor( bootstrapMigrateContinueButton.debouncedClicks { submit() } bootstrapMigrateShowPassword.debouncedClicks { sharedViewModel.handle(BootstrapActions.TogglePasswordVisibility) } bootstrapMigrateForgotPassphrase.debouncedClicks { sharedViewModel.handle(BootstrapActions.HandleForgotBackupPassphrase) } - bootstrapMigrateUseFile.debouncedClicks { startImportTextFromFileIntent(this, IMPORT_FILE_REQ) } + bootstrapMigrateUseFile.debouncedClicks { startImportTextFromFileIntent(requireContext(), importFileStartForActivityResult) } } private fun submit() = withState(sharedViewModel) { state -> @@ -147,9 +147,9 @@ class BootstrapMigrateBackupFragment @Inject constructor( } } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - if (requestCode == IMPORT_FILE_REQ && resultCode == Activity.RESULT_OK) { - data?.data?.let { dataURI -> + private val importFileStartForActivityResult = registerStartForActivityResult { activityResult -> + if (activityResult.resultCode == Activity.RESULT_OK) { + activityResult.data?.data?.let { dataURI -> tryOrNull { activity?.contentResolver?.openInputStream(dataURI) ?.bufferedReader() @@ -159,12 +159,6 @@ class BootstrapMigrateBackupFragment @Inject constructor( } } } - return } - super.onActivityResult(requestCode, resultCode, data) - } - - companion object { - private const val IMPORT_FILE_REQ = 0 } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSaveRecoveryKeyFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSaveRecoveryKeyFragment.kt index 750dadbc9f..e426394d77 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSaveRecoveryKeyFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSaveRecoveryKeyFragment.kt @@ -16,7 +16,7 @@ package im.vector.app.features.crypto.recover -import android.app.Activity.RESULT_OK +import android.app.Activity import android.content.ActivityNotFoundException import android.content.Intent import android.os.Bundle @@ -25,6 +25,7 @@ import androidx.core.view.isVisible import com.airbnb.mvrx.parentFragmentViewModel import com.airbnb.mvrx.withState import im.vector.app.R +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.resources.ColorProvider import im.vector.app.core.utils.startSharePlainTextIntent @@ -65,43 +66,46 @@ class BootstrapSaveRecoveryKeyFragment @Inject constructor( try { sharedViewModel.handle(BootstrapActions.SaveReqQueryStarted) - startActivityForResult(Intent.createChooser(intent, getString(R.string.keys_backup_setup_step3_please_make_copy)), REQUEST_CODE_SAVE) + saveStartForActivityResult.launch(Intent.createChooser(intent, getString(R.string.keys_backup_setup_step3_please_make_copy))) } catch (activityNotFoundException: ActivityNotFoundException) { requireActivity().toast(R.string.error_no_external_application_found) sharedViewModel.handle(BootstrapActions.SaveReqFailed) } } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - if (requestCode == REQUEST_CODE_SAVE) { - val uri = data?.data - if (resultCode == RESULT_OK && uri != null) { - GlobalScope.launch(Dispatchers.IO) { - try { - sharedViewModel.handle(BootstrapActions.SaveKeyToUri(requireContext().contentResolver!!.openOutputStream(uri)!!)) - } catch (failure: Throwable) { - sharedViewModel.handle(BootstrapActions.SaveReqFailed) - } + private val saveStartForActivityResult = registerStartForActivityResult { activityResult -> + if (activityResult.resultCode == Activity.RESULT_OK) { + val uri = activityResult.data?.data ?: return@registerStartForActivityResult + GlobalScope.launch(Dispatchers.IO) { + try { + sharedViewModel.handle(BootstrapActions.SaveKeyToUri(requireContext().contentResolver!!.openOutputStream(uri)!!)) + } catch (failure: Throwable) { + sharedViewModel.handle(BootstrapActions.SaveReqFailed) } - } else { - // result code seems to be always cancelled here.. so act as if it was saved - sharedViewModel.handle(BootstrapActions.SaveReqFailed) } - return - } else if (requestCode == REQUEST_CODE_COPY) { + } else { + // result code seems to be always cancelled here.. so act as if it was saved + sharedViewModel.handle(BootstrapActions.SaveReqFailed) + } + } + + private val copyStartForActivityResult = registerStartForActivityResult { activityResult -> + if (activityResult.resultCode == Activity.RESULT_OK) { sharedViewModel.handle(BootstrapActions.RecoveryKeySaved) } - super.onActivityResult(requestCode, resultCode, data) } private fun shareRecoveryKey() = withState(sharedViewModel) { state -> val recoveryKey = state.recoveryKeyCreationInfo?.recoveryKey?.formatRecoveryKey() ?: return@withState - startSharePlainTextIntent(this, + startSharePlainTextIntent( + this, + copyStartForActivityResult, context?.getString(R.string.keys_backup_setup_step3_share_intent_chooser_title), recoveryKey, - context?.getString(R.string.recovery_key), REQUEST_CODE_COPY) + context?.getString(R.string.recovery_key) + ) } override fun invalidate() = withState(sharedViewModel) { state -> @@ -111,9 +115,4 @@ class BootstrapSaveRecoveryKeyFragment @Inject constructor( recoveryContinue.isVisible = step.isSaved bootstrapRecoveryKeyText.text = state.recoveryKeyCreationInfo?.recoveryKey?.formatRecoveryKey() } - - companion object { - const val REQUEST_CODE_SAVE = 123 - const val REQUEST_CODE_COPY = 124 - } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSharedViewModel.kt index a3955d561e..6d711ec0e9 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSharedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSharedViewModel.kt @@ -301,7 +301,7 @@ class BootstrapSharedViewModel @AssistedInject constructor( // ======================================= private fun saveRecoveryKeyToUri(os: OutputStream) = withState { state -> viewModelScope.launch(Dispatchers.IO) { - kotlin.runCatching { + runCatching { os.use { os.write((state.recoveryKeyCreationInfo?.recoveryKey?.formatRecoveryKey() ?: "").toByteArray()) } diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheet.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheet.kt index c7ce4d6f01..35ea96de6f 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheet.kt @@ -17,7 +17,6 @@ package im.vector.app.features.crypto.verification import android.app.Activity import android.app.Dialog -import android.content.Intent import android.os.Bundle import android.os.Parcelable import android.view.KeyEvent @@ -35,6 +34,7 @@ import im.vector.app.R import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.commitTransaction import im.vector.app.core.extensions.exhaustive +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.app.features.crypto.quads.SharedSecureStorageActivity @@ -108,12 +108,12 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() { when (it) { is VerificationBottomSheetViewEvents.Dismiss -> dismiss() is VerificationBottomSheetViewEvents.AccessSecretStore -> { - startActivityForResult(SharedSecureStorageActivity.newIntent( + secretStartForActivityResult.launch(SharedSecureStorageActivity.newIntent( requireContext(), null, // use default key listOf(MASTER_KEY_SSSS_NAME, USER_SIGNING_KEY_SSSS_NAME, SELF_SIGNING_KEY_SSSS_NAME, KEYBACKUP_SECRET_SSSS_NAME), SharedSecureStorageActivity.DEFAULT_RESULT_KEYSTORE_ALIAS - ), SECRET_REQUEST_CODE) + )) } is VerificationBottomSheetViewEvents.ModalError -> { AlertDialog.Builder(requireContext()) @@ -145,10 +145,10 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() { } } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - if (resultCode == Activity.RESULT_OK && requestCode == SECRET_REQUEST_CODE) { - val result = data?.getStringExtra(SharedSecureStorageActivity.EXTRA_DATA_RESULT) - val reset = data?.getBooleanExtra(SharedSecureStorageActivity.EXTRA_DATA_RESET, false) ?: false + private val secretStartForActivityResult = registerStartForActivityResult { activityResult -> + if (activityResult.resultCode == Activity.RESULT_OK) { + val result = activityResult.data?.getStringExtra(SharedSecureStorageActivity.EXTRA_DATA_RESULT) + val reset = activityResult.data?.getBooleanExtra(SharedSecureStorageActivity.EXTRA_DATA_RESET, false) ?: false if (result != null) { viewModel.handle(VerificationAction.GotResultFromSsss(result, SharedSecureStorageActivity.DEFAULT_RESULT_KEYSTORE_ALIAS)) } else if (reset) { @@ -156,11 +156,9 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() { viewModel.handle(VerificationAction.SecuredStorageHasBeenReset) } } - super.onActivityResult(requestCode, resultCode, data) } override fun invalidate() = withState(viewModel) { state -> - state.otherUserMxItem?.let { matrixItem -> if (state.isMe) { avatarRenderer.render(matrixItem, otherUserAvatarImageView) @@ -347,9 +345,6 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() { } companion object { - - const val SECRET_REQUEST_CODE = 101 - fun withArgs(roomId: String?, otherUserId: String, transactionId: String? = null): VerificationBottomSheet { return VerificationBottomSheet().apply { arguments = Bundle().apply { diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/choose/VerificationChooseMethodFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/choose/VerificationChooseMethodFragment.kt index fc715301f2..72cd063bbd 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/choose/VerificationChooseMethodFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/choose/VerificationChooseMethodFragment.kt @@ -16,7 +16,6 @@ package im.vector.app.features.crypto.verification.choose import android.app.Activity -import android.content.Intent import android.os.Bundle import android.view.View import com.airbnb.mvrx.fragmentViewModel @@ -25,11 +24,11 @@ import com.airbnb.mvrx.withState import im.vector.app.R import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.configureWith +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO -import im.vector.app.core.utils.PERMISSION_REQUEST_CODE_LAUNCH_CAMERA -import im.vector.app.core.utils.allGranted import im.vector.app.core.utils.checkPermissions +import im.vector.app.core.utils.registerForPermissionsResult import im.vector.app.features.crypto.verification.VerificationAction import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel import im.vector.app.features.qrcode.QrCodeScannerActivity @@ -75,16 +74,14 @@ class VerificationChooseMethodFragment @Inject constructor( state.pendingRequest.invoke()?.transactionId ?: "")) } - override fun openCamera() { - if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, this, PERMISSION_REQUEST_CODE_LAUNCH_CAMERA)) { + private val openCameraActivityResultLauncher = registerForPermissionsResult { allGranted -> + if (allGranted) { doOpenQRCodeScanner() } } - override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults) - - if (requestCode == PERMISSION_REQUEST_CODE_LAUNCH_CAMERA && allGranted(grantResults)) { + override fun openCamera() { + if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, requireActivity(), openCameraActivityResultLauncher)) { doOpenQRCodeScanner() } } @@ -94,24 +91,18 @@ class VerificationChooseMethodFragment @Inject constructor( } private fun doOpenQRCodeScanner() { - QrCodeScannerActivity.startForResult(this) + QrCodeScannerActivity.startForResult(requireActivity(), scanActivityResultLauncher) } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data) + private val scanActivityResultLauncher = registerStartForActivityResult { activityResult -> + if (activityResult.resultCode == Activity.RESULT_OK) { + val scannedQrCode = QrCodeScannerActivity.getResultText(activityResult.data) + val wasQrCode = QrCodeScannerActivity.getResultIsQrCode(activityResult.data) - if (resultCode == Activity.RESULT_OK) { - when (requestCode) { - QrCodeScannerActivity.QR_CODE_SCANNER_REQUEST_CODE -> { - val scannedQrCode = QrCodeScannerActivity.getResultText(data) - val wasQrCode = QrCodeScannerActivity.getResultIsQrCode(data) - - if (wasQrCode && !scannedQrCode.isNullOrBlank()) { - onRemoteQrCodeScanned(scannedQrCode) - } else { - Timber.w("It was not a QR code, or empty result") - } - } + if (wasQrCode && !scannedQrCode.isNullOrBlank()) { + onRemoteQrCodeScanned(scannedQrCode) + } else { + Timber.w("It was not a QR code, or empty result") } } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/epoxy/BottomSheetVerificationEmojisItem.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/epoxy/BottomSheetVerificationEmojisItem.kt index 77fa9cb775..daa9a913c9 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/epoxy/BottomSheetVerificationEmojisItem.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/epoxy/BottomSheetVerificationEmojisItem.kt @@ -72,7 +72,7 @@ abstract class BottomSheetVerificationEmojisItem : VectorEpoxyModel(R.id.item_emoji_tv).isVisible = false view.findViewById(R.id.item_emoji_image).isVisible = true view.findViewById(R.id.item_emoji_image).setImageDrawable(ContextCompat.getDrawable(view.context, it)) - } ?: kotlin.run { + } ?: run { view.findViewById(R.id.item_emoji_tv).isVisible = true view.findViewById(R.id.item_emoji_image).isVisible = false view.findViewById(R.id.item_emoji_tv).text = rep.emoji diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt index 21f65ec9ef..145dfc56c3 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt @@ -16,7 +16,6 @@ package im.vector.app.features.discovery import android.app.Activity -import android.content.Intent import android.os.Bundle import android.view.View import androidx.appcompat.app.AlertDialog @@ -27,12 +26,12 @@ 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.extensions.observeEvent +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.utils.ensureProtocol import im.vector.app.features.discovery.change.SetIdentityServerFragment import im.vector.app.features.settings.VectorSettingsActivity -import im.vector.app.features.terms.ReviewTermsActivity import org.matrix.android.sdk.api.session.identity.SharedState import org.matrix.android.sdk.api.session.identity.ThreePid import org.matrix.android.sdk.api.session.terms.TermsService @@ -92,22 +91,19 @@ class DiscoverySettingsFragment @Inject constructor( viewModel.handle(DiscoverySettingsAction.Refresh) } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - if (requestCode == ReviewTermsActivity.TERMS_REQUEST_CODE) { - if (Activity.RESULT_OK == resultCode) { - viewModel.handle(DiscoverySettingsAction.RetrieveBinding) - } else { - // add some error? - } + private val termsActivityResultLauncher = registerStartForActivityResult { + if (it.resultCode == Activity.RESULT_OK) { + viewModel.handle(DiscoverySettingsAction.RetrieveBinding) + } else { + // add some error? } - - super.onActivityResult(requestCode, resultCode, data) } override fun openIdentityServerTerms() = withState(viewModel) { state -> if (state.termsNotSigned) { navigator.openTerms( - this, + requireContext(), + termsActivityResultLauncher, TermsService.ServiceType.IdentityService, state.identityServer()?.ensureProtocol() ?: "", null) diff --git a/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerFragment.kt b/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerFragment.kt index 863270b762..05708089ea 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerFragment.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerFragment.kt @@ -16,7 +16,6 @@ package im.vector.app.features.discovery.change import android.app.Activity -import android.content.Intent import android.os.Bundle import android.view.View import android.view.inputmethod.EditorInfo @@ -28,13 +27,13 @@ import com.airbnb.mvrx.withState import com.jakewharton.rxbinding3.widget.textChanges import im.vector.app.R import im.vector.app.core.extensions.exhaustive +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.extensions.toReducedUrl import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.resources.ColorProvider import im.vector.app.core.utils.colorizeMatchingText import im.vector.app.features.discovery.DiscoverySharedViewModel -import im.vector.app.features.terms.ReviewTermsActivity import org.matrix.android.sdk.api.session.terms.TermsService import kotlinx.android.synthetic.main.fragment_set_identity_server.* import javax.inject.Inject @@ -121,7 +120,8 @@ class SetIdentityServerFragment @Inject constructor( is SetIdentityServerViewEvents.TermsAccepted -> processIdentityServerChange() is SetIdentityServerViewEvents.ShowTerms -> { navigator.openTerms( - this, + requireContext(), + termsActivityResultLauncher, TermsService.ServiceType.IdentityService, it.identityServerUrl, null) @@ -150,15 +150,12 @@ class SetIdentityServerFragment @Inject constructor( (activity as? VectorBaseActivity)?.supportActionBar?.setTitle(R.string.identity_server) } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - if (requestCode == ReviewTermsActivity.TERMS_REQUEST_CODE) { - if (Activity.RESULT_OK == resultCode) { - processIdentityServerChange() - } else { - // add some error? - } + private val termsActivityResultLauncher = registerStartForActivityResult { + if (it.resultCode == Activity.RESULT_OK) { + processIdentityServerChange() + } else { + // add some error? } - super.onActivityResult(requestCode, resultCode, data) } private fun processIdentityServerChange() { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt index cca7e9e542..66e49cf060 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt @@ -17,7 +17,7 @@ package im.vector.app.features.home.room.detail import android.annotation.SuppressLint -import android.app.Activity.RESULT_OK +import android.app.Activity import android.content.DialogInterface import android.content.Intent import android.graphics.Typeface @@ -73,6 +73,7 @@ import im.vector.app.core.epoxy.LayoutManagerStateRestorer import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.exhaustive import im.vector.app.core.extensions.hideKeyboard +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.extensions.setTextOrHide import im.vector.app.core.extensions.showKeyboard import im.vector.app.core.extensions.trackItemsVisibilityChange @@ -91,19 +92,15 @@ import im.vector.app.core.utils.KeyboardStateUtils import im.vector.app.core.utils.PERMISSIONS_FOR_AUDIO_IP_CALL import im.vector.app.core.utils.PERMISSIONS_FOR_VIDEO_IP_CALL import im.vector.app.core.utils.PERMISSIONS_FOR_WRITING_FILES -import im.vector.app.core.utils.PERMISSION_REQUEST_CODE_INCOMING_URI -import im.vector.app.core.utils.PERMISSION_REQUEST_CODE_PICK_ATTACHMENT import im.vector.app.core.utils.TextUtils -import im.vector.app.core.utils.allGranted import im.vector.app.core.utils.checkPermissions import im.vector.app.core.utils.colorizeMatchingText import im.vector.app.core.utils.copyToClipboard import im.vector.app.core.utils.createJSonViewerStyleProvider import im.vector.app.core.utils.createUIHandler import im.vector.app.core.utils.isValidUrl -import im.vector.app.core.utils.onPermissionResultAudioIpCall -import im.vector.app.core.utils.onPermissionResultVideoIpCall import im.vector.app.core.utils.openUrlInExternalBrowser +import im.vector.app.core.utils.registerForPermissionsResult import im.vector.app.core.utils.saveMedia import im.vector.app.core.utils.shareMedia import im.vector.app.core.utils.toast @@ -138,7 +135,6 @@ import im.vector.app.features.home.room.detail.timeline.item.MessageTextItem import im.vector.app.features.home.room.detail.timeline.item.ReadReceiptData import im.vector.app.features.home.room.detail.timeline.reactions.ViewReactionsBottomSheet import im.vector.app.features.home.room.detail.widget.RoomWidgetsBottomSheet -import im.vector.app.features.home.room.detail.widget.WidgetRequestCodes import im.vector.app.features.html.EventHtmlRenderer import im.vector.app.features.html.PillImageSpan import im.vector.app.features.invite.VectorInviteView @@ -206,8 +202,6 @@ data class RoomDetailArgs( val sharedData: SharedData? = null ) : Parcelable -private const val REACTION_SELECT_REQUEST_CODE = 0 - class RoomDetailFragment @Inject constructor( private val session: Session, private val avatarRenderer: AvatarRenderer, @@ -234,11 +228,6 @@ class RoomDetailFragment @Inject constructor( ActiveCallView.Callback { companion object { - - private const val AUDIO_CALL_PERMISSION_REQUEST_CODE = 1 - private const val VIDEO_CALL_PERMISSION_REQUEST_CODE = 2 - private const val SAVE_ATTACHEMENT_REQUEST_CODE = 3 - /** * Sanitize the display name. * @@ -371,6 +360,10 @@ class RoomDetailFragment @Inject constructor( is RoomDetailViewEvents.RequestNativeWidgetPermission -> requestNativeWidgetPermission(it) }.exhaustive } + + if (savedInstanceState == null) { + handleShareData() + } } private fun requestNativeWidgetPermission(it: RoomDetailViewEvents.RequestNativeWidgetPermission) { @@ -401,9 +394,14 @@ class RoomDetailFragment @Inject constructor( } } + private val integrationManagerActivityResultLauncher = registerStartForActivityResult { + // Noop + } + private fun openIntegrationManager(screen: String? = null) { navigator.openIntegrationManager( - fragment = this, + context = requireContext(), + activityResultLauncher = integrationManagerActivityResultLauncher, roomId = roomDetailArgs.roomId, integId = null, screen = screen @@ -440,7 +438,7 @@ class RoomDetailFragment @Inject constructor( } private fun openStickerPicker(event: RoomDetailViewEvents.OpenStickerPicker) { - navigator.openStickerPicker(this, roomDetailArgs.roomId, event.widget) + navigator.openStickerPicker(requireContext(), stickerActivityResultLauncher, roomDetailArgs.roomId, event.widget) } private fun startOpenFileIntent(action: RoomDetailViewEvents.OpenFile) { @@ -480,20 +478,17 @@ class RoomDetailFragment @Inject constructor( navigator.openRoom(vectorBaseActivity, action.roomId) } - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - if (savedInstanceState == null) { - when (val sharedData = roomDetailArgs.sharedData) { - is SharedData.Text -> { - roomDetailViewModel.handle(RoomDetailAction.EnterRegularMode(sharedData.text, fromSharing = true)) - } - is SharedData.Attachments -> { - // open share edition - onContentAttachmentsReady(sharedData.attachmentData) - } - null -> Timber.v("No share data to process") - }.exhaustive - } + private fun handleShareData() { + when (val sharedData = roomDetailArgs.sharedData) { + is SharedData.Text -> { + roomDetailViewModel.handle(RoomDetailAction.EnterRegularMode(sharedData.text, fromSharing = true)) + } + is SharedData.Attachments -> { + // open share edition + onContentAttachmentsReady(sharedData.attachmentData) + } + null -> Timber.v("No share data to process") + }.exhaustive } override fun onDestroyView() { @@ -791,19 +786,33 @@ class RoomDetailFragment @Inject constructor( } } + private val startCallActivityResultLauncher = registerForPermissionsResult { allGranted -> + if (allGranted) { + (roomDetailViewModel.pendingAction as? RoomDetailAction.StartCall)?.let { + roomDetailViewModel.pendingAction = null + roomDetailViewModel.handle(it) + } + } else { + context?.toast(R.string.permissions_action_not_performed_missing_permissions) + cleanUpAfterPermissionNotGranted() + } + } + private fun safeStartCall2(isVideoCall: Boolean) { val startCallAction = RoomDetailAction.StartCall(isVideoCall) roomDetailViewModel.pendingAction = startCallAction if (isVideoCall) { if (checkPermissions(PERMISSIONS_FOR_VIDEO_IP_CALL, - this, VIDEO_CALL_PERMISSION_REQUEST_CODE, + requireActivity(), + startCallActivityResultLauncher, R.string.permissions_rationale_msg_camera_and_audio)) { roomDetailViewModel.pendingAction = null roomDetailViewModel.handle(startCallAction) } } else { if (checkPermissions(PERMISSIONS_FOR_AUDIO_IP_CALL, - this, AUDIO_CALL_PERMISSION_REQUEST_CODE, + requireActivity(), + startCallActivityResultLauncher, R.string.permissions_rationale_msg_record_audio)) { roomDetailViewModel.pendingAction = null roomDetailViewModel.handle(startCallAction) @@ -879,27 +888,63 @@ class RoomDetailFragment @Inject constructor( roomDetailViewModel.handle(RoomDetailAction.SaveDraft(composerLayout.composerEditText.text.toString())) } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - val hasBeenHandled = attachmentsHelper.onActivityResult(requestCode, resultCode, data) - if (!hasBeenHandled && resultCode == RESULT_OK && data != null) { - when (requestCode) { - AttachmentsPreviewActivity.REQUEST_CODE -> { - val sendData = AttachmentsPreviewActivity.getOutput(data) - val keepOriginalSize = AttachmentsPreviewActivity.getKeepOriginalSize(data) - roomDetailViewModel.handle(RoomDetailAction.SendMedia(sendData, !keepOriginalSize)) - } - REACTION_SELECT_REQUEST_CODE -> { - val (eventId, reaction) = EmojiReactionPickerActivity.getOutput(data) ?: return - roomDetailViewModel.handle(RoomDetailAction.SendReaction(eventId, reaction)) - } - WidgetRequestCodes.STICKER_PICKER_REQUEST_CODE -> { - val content = WidgetActivity.getOutput(data).toModel() ?: return - roomDetailViewModel.handle(RoomDetailAction.SendSticker(content)) - } + private val attachmentFileActivityResultLauncher = registerStartForActivityResult { + if (it.resultCode == Activity.RESULT_OK) { + attachmentsHelper.onImageResult(it.data) + } + } + + private val attachmentAudioActivityResultLauncher = registerStartForActivityResult { + if (it.resultCode == Activity.RESULT_OK) { + attachmentsHelper.onAudioResult(it.data) + } + } + + private val attachmentContactActivityResultLauncher = registerStartForActivityResult { + if (it.resultCode == Activity.RESULT_OK) { + attachmentsHelper.onContactResult(it.data) + } + } + + private val attachmentImageActivityResultLauncher = registerStartForActivityResult { + if (it.resultCode == Activity.RESULT_OK) { + attachmentsHelper.onImageResult(it.data) + } + } + + private val attachmentPhotoActivityResultLauncher = registerStartForActivityResult { + if (it.resultCode == Activity.RESULT_OK) { + attachmentsHelper.onPhotoResult() + } + } + + private val contentAttachmentActivityResultLauncher = registerStartForActivityResult { activityResult -> + val data = activityResult.data ?: return@registerStartForActivityResult + if (activityResult.resultCode == Activity.RESULT_OK) { + val sendData = AttachmentsPreviewActivity.getOutput(data) + val keepOriginalSize = AttachmentsPreviewActivity.getKeepOriginalSize(data) + roomDetailViewModel.handle(RoomDetailAction.SendMedia(sendData, !keepOriginalSize)) + } + } + + private val emojiActivityResultLauncher = registerStartForActivityResult { activityResult -> + if (activityResult.resultCode == Activity.RESULT_OK) { + val eventId = EmojiReactionPickerActivity.getOutputEventId(activityResult.data) + val reaction = EmojiReactionPickerActivity.getOutputReaction(activityResult.data) + if (eventId != null && reaction != null) { + roomDetailViewModel.handle(RoomDetailAction.SendReaction(eventId, reaction)) } } - // TODO why don't we call super here? - // super.onActivityResult(requestCode, resultCode, data) + } + + private val stickerActivityResultLauncher = registerStartForActivityResult { activityResult -> + val data = activityResult.data ?: return@registerStartForActivityResult + if (activityResult.resultCode == Activity.RESULT_OK) { + WidgetActivity.getOutput(data).toModel() + ?.let { content -> + roomDetailViewModel.handle(RoomDetailAction.SendSticker(content)) + } + } } // PRIVATE METHODS ***************************************************************************** @@ -994,6 +1039,18 @@ class RoomDetailFragment @Inject constructor( } } + private val writingFileActivityResultLauncher = registerForPermissionsResult { allGranted -> + if (allGranted) { + val pendingUri = roomDetailViewModel.pendingUri + if (pendingUri != null) { + roomDetailViewModel.pendingUri = null + sendUri(pendingUri) + } + } else { + cleanUpAfterPermissionNotGranted() + } + } + private fun setupComposer() { autoCompleter.setup(composerLayout.composerEditText) @@ -1026,7 +1083,7 @@ class RoomDetailFragment @Inject constructor( override fun onRichContentSelected(contentUri: Uri): Boolean { // We need WRITE_EXTERNAL permission - return if (checkPermissions(PERMISSIONS_FOR_WRITING_FILES, this@RoomDetailFragment, PERMISSION_REQUEST_CODE_INCOMING_URI)) { + return if (checkPermissions(PERMISSIONS_FOR_WRITING_FILES, requireActivity(), writingFileActivityResultLauncher)) { sendUri(contentUri) } else { roomDetailViewModel.pendingUri = contentUri @@ -1416,52 +1473,11 @@ class RoomDetailFragment @Inject constructor( // // } // } - override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { - if (allGranted(grantResults)) { - when (requestCode) { - SAVE_ATTACHEMENT_REQUEST_CODE -> { - sharedActionViewModel.pendingAction?.let { - handleActions(it) - sharedActionViewModel.pendingAction = null - } - } - PERMISSION_REQUEST_CODE_INCOMING_URI -> { - val pendingUri = roomDetailViewModel.pendingUri - if (pendingUri != null) { - roomDetailViewModel.pendingUri = null - sendUri(pendingUri) - } - } - PERMISSION_REQUEST_CODE_PICK_ATTACHMENT -> { - val pendingType = attachmentsHelper.pendingType - if (pendingType != null) { - attachmentsHelper.pendingType = null - launchAttachmentProcess(pendingType) - } - } - AUDIO_CALL_PERMISSION_REQUEST_CODE -> { - if (onPermissionResultAudioIpCall(requireContext(), grantResults)) { - (roomDetailViewModel.pendingAction as? RoomDetailAction.StartCall)?.let { - roomDetailViewModel.pendingAction = null - roomDetailViewModel.handle(it) - } - } - } - VIDEO_CALL_PERMISSION_REQUEST_CODE -> { - if (onPermissionResultVideoIpCall(requireContext(), grantResults)) { - (roomDetailViewModel.pendingAction as? RoomDetailAction.StartCall)?.let { - roomDetailViewModel.pendingAction = null - roomDetailViewModel.handle(it) - } - } - } - } - } else { - // Reset all pending data - roomDetailViewModel.pendingAction = null - roomDetailViewModel.pendingUri = null - attachmentsHelper.pendingType = null - } + private fun cleanUpAfterPermissionNotGranted() { + // Reset all pending data + roomDetailViewModel.pendingAction = null + roomDetailViewModel.pendingUri = null + attachmentsHelper.pendingType = null } // override fun onAudioMessageClicked(messageAudioContent: MessageAudioContent) { @@ -1576,9 +1592,20 @@ class RoomDetailFragment @Inject constructor( ) } + private val saveActionActivityResultLauncher = registerForPermissionsResult { allGranted -> + if (allGranted) { + sharedActionViewModel.pendingAction?.let { + handleActions(it) + sharedActionViewModel.pendingAction = null + } + } else { + cleanUpAfterPermissionNotGranted() + } + } + private fun onSaveActionClicked(action: EventSharedAction.Save) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q - && !checkPermissions(PERMISSIONS_FOR_WRITING_FILES, this, SAVE_ATTACHEMENT_REQUEST_CODE)) { + && !checkPermissions(PERMISSIONS_FOR_WRITING_FILES, requireActivity(), saveActionActivityResultLauncher)) { sharedActionViewModel.pendingAction = action return } @@ -1611,7 +1638,7 @@ class RoomDetailFragment @Inject constructor( openRoomMemberProfile(action.userId) } is EventSharedAction.AddReaction -> { - startActivityForResult(EmojiReactionPickerActivity.intent(requireContext(), action.eventId), REACTION_SELECT_REQUEST_CODE) + emojiActivityResultLauncher.launch(EmojiReactionPickerActivity.intent(requireContext(), action.eventId)) } is EventSharedAction.ViewReactions -> { ViewReactionsBottomSheet.newInstance(roomDetailArgs.roomId, action.messageInformationData) @@ -1816,8 +1843,20 @@ class RoomDetailFragment @Inject constructor( // AttachmentTypeSelectorView.Callback + private val typeSelectedActivityResultLauncher = registerForPermissionsResult { allGranted -> + if (allGranted) { + val pendingType = attachmentsHelper.pendingType + if (pendingType != null) { + attachmentsHelper.pendingType = null + launchAttachmentProcess(pendingType) + } + } else { + cleanUpAfterPermissionNotGranted() + } + } + override fun onTypeSelected(type: AttachmentTypeSelectorView.Type) { - if (checkPermissions(type.permissionsBit, this, PERMISSION_REQUEST_CODE_PICK_ATTACHMENT)) { + if (checkPermissions(type.permissionsBit, requireActivity(), typeSelectedActivityResultLauncher)) { launchAttachmentProcess(type) } else { attachmentsHelper.pendingType = type @@ -1826,11 +1865,11 @@ class RoomDetailFragment @Inject constructor( private fun launchAttachmentProcess(type: AttachmentTypeSelectorView.Type) { when (type) { - AttachmentTypeSelectorView.Type.CAMERA -> attachmentsHelper.openCamera(this) - AttachmentTypeSelectorView.Type.FILE -> attachmentsHelper.selectFile(this) - AttachmentTypeSelectorView.Type.GALLERY -> attachmentsHelper.selectGallery(this) - AttachmentTypeSelectorView.Type.AUDIO -> attachmentsHelper.selectAudio(this) - AttachmentTypeSelectorView.Type.CONTACT -> attachmentsHelper.selectContact(this) + AttachmentTypeSelectorView.Type.CAMERA -> attachmentsHelper.openCamera(requireContext(), attachmentPhotoActivityResultLauncher) + AttachmentTypeSelectorView.Type.FILE -> attachmentsHelper.selectFile(attachmentFileActivityResultLauncher) + AttachmentTypeSelectorView.Type.GALLERY -> attachmentsHelper.selectGallery(attachmentImageActivityResultLauncher) + AttachmentTypeSelectorView.Type.AUDIO -> attachmentsHelper.selectAudio(attachmentAudioActivityResultLauncher) + AttachmentTypeSelectorView.Type.CONTACT -> attachmentsHelper.selectContact(attachmentContactActivityResultLauncher) AttachmentTypeSelectorView.Type.STICKER -> roomDetailViewModel.handle(RoomDetailAction.SelectStickerAttachment) }.exhaustive } @@ -1849,7 +1888,7 @@ class RoomDetailFragment @Inject constructor( } if (grouped.previewables.isNotEmpty()) { val intent = AttachmentsPreviewActivity.newIntent(requireContext(), AttachmentsPreviewArgs(grouped.previewables)) - startActivityForResult(intent, AttachmentsPreviewActivity.REQUEST_CODE) + contentAttachmentActivityResultLauncher.launch(intent) } } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt index 871a550bd6..ebc265d935 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt @@ -1074,7 +1074,7 @@ class RoomDetailViewModel @AssistedInject constructor( .buffer(1, TimeUnit.SECONDS) .filter { it.isNotEmpty() } .subscribeBy(onNext = { actions -> - val bufferedMostRecentDisplayedEvent = actions.maxBy { it.event.displayIndex }?.event ?: return@subscribeBy + val bufferedMostRecentDisplayedEvent = actions.maxByOrNull { it.event.displayIndex }?.event ?: return@subscribeBy val globalMostRecentDisplayedEvent = mostRecentDisplayedEvent if (trackUnreadMessages.get()) { if (globalMostRecentDisplayedEvent == null) { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/readreceipts/DisplayReadReceiptsBottomSheet.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/readreceipts/DisplayReadReceiptsBottomSheet.kt index 7593118c9f..5fefc9aba8 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/readreceipts/DisplayReadReceiptsBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/readreceipts/DisplayReadReceiptsBottomSheet.kt @@ -18,6 +18,7 @@ package im.vector.app.features.home.room.detail.readreceipts import android.os.Bundle import android.os.Parcelable +import android.view.View import androidx.recyclerview.widget.RecyclerView import butterknife.BindView import com.airbnb.mvrx.MvRx @@ -59,8 +60,8 @@ class DisplayReadReceiptsBottomSheet : VectorBaseBottomSheetDialogFragment(), Di override fun getLayoutResId() = R.layout.bottom_sheet_generic_list_with_title - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) sharedActionViewModel = activityViewModelProvider.get(MessageSharedActionViewModel::class.java) recyclerView.configureWith(epoxyController, hasFixedSize = false) bottomSheetTitle.text = getString(R.string.seen_by) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsBottomSheet.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsBottomSheet.kt index 9858d2a03c..f337f0ba5f 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsBottomSheet.kt @@ -16,6 +16,7 @@ package im.vector.app.features.home.room.detail.timeline.action import android.os.Bundle +import android.view.View import androidx.recyclerview.widget.RecyclerView import butterknife.BindView import com.airbnb.mvrx.fragmentViewModel @@ -51,8 +52,8 @@ class MessageActionsBottomSheet : VectorBaseBottomSheetDialogFragment(), Message override fun getLayoutResId() = R.layout.bottom_sheet_generic_list - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) sharedActionViewModel = activityViewModelProvider.get(MessageSharedActionViewModel::class.java) recyclerView.configureWith(messageActionsEpoxyController, hasFixedSize = false, disableItemAnimation = true) messageActionsEpoxyController.listener = this diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryBottomSheet.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryBottomSheet.kt index 93fdd253d8..080ccaea7c 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryBottomSheet.kt @@ -16,6 +16,7 @@ package im.vector.app.features.home.room.detail.timeline.edithistory import android.os.Bundle +import android.view.View import androidx.recyclerview.widget.RecyclerView import butterknife.BindView import com.airbnb.mvrx.MvRx @@ -55,8 +56,8 @@ class ViewEditHistoryBottomSheet : VectorBaseBottomSheetDialogFragment() { override fun getLayoutResId() = R.layout.bottom_sheet_generic_list_with_title - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) recyclerView.configureWith( epoxyController, showDivider = true, diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessagePollItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessagePollItem.kt index 9d500b99b0..a7db58deb1 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessagePollItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessagePollItem.kt @@ -84,7 +84,7 @@ abstract class MessagePollItem : AbsMessageItem() { } } else { holder.resultWrapper.isVisible = true - val maxCount = votes?.maxBy { it.value }?.value ?: 0 + val maxCount = votes?.maxByOrNull { it.value }?.value ?: 0 optionsContent?.options?.forEachIndexed { index, item -> if (index < resultLines.size) { val optionCount = votes?.get(index) ?: 0 diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsBottomSheet.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsBottomSheet.kt index a5555e4ebb..2e0d07aa67 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsBottomSheet.kt @@ -17,6 +17,7 @@ package im.vector.app.features.home.room.detail.timeline.reactions import android.os.Bundle +import android.view.View import androidx.recyclerview.widget.RecyclerView import butterknife.BindView import com.airbnb.mvrx.MvRx @@ -55,8 +56,8 @@ class ViewReactionsBottomSheet : VectorBaseBottomSheetDialogFragment(), ViewReac override fun getLayoutResId() = R.layout.bottom_sheet_generic_list_with_title - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) sharedActionViewModel = activityViewModelProvider.get(MessageSharedActionViewModel::class.java) recyclerView.configureWith(epoxyController, hasFixedSize = false, showDivider = true) bottomSheetTitle.text = context?.getString(R.string.reactions) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/widget/RoomWidgetsBottomSheet.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/widget/RoomWidgetsBottomSheet.kt index e87f5cdaa9..24f1299c19 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/widget/RoomWidgetsBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/widget/RoomWidgetsBottomSheet.kt @@ -17,6 +17,7 @@ package im.vector.app.features.home.room.detail.widget import android.os.Bundle +import android.view.View import androidx.recyclerview.widget.RecyclerView import butterknife.BindView import com.airbnb.mvrx.parentFragmentViewModel @@ -55,8 +56,8 @@ class RoomWidgetsBottomSheet : VectorBaseBottomSheetDialogFragment(), RoomWidget override fun getLayoutResId() = R.layout.bottom_sheet_generic_list_with_title - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) recyclerView.configureWith(epoxyController, hasFixedSize = false) bottomSheetTitle.text = getString(R.string.active_widgets_title) bottomSheetTitle.textSize = 20f diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/widget/WidgetRequestCodes.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/widget/WidgetRequestCodes.kt deleted file mode 100644 index 5f0e6866db..0000000000 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/widget/WidgetRequestCodes.kt +++ /dev/null @@ -1,22 +0,0 @@ -/* - * 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.home.room.detail.widget - -object WidgetRequestCodes { - const val STICKER_PICKER_REQUEST_CODE = 16000 - const val INTEGRATION_MANAGER_REQUEST_CODE = 16001 -} diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt index c33d964d22..ccd38125f9 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt @@ -18,6 +18,7 @@ package im.vector.app.features.home.room.list.actions import android.os.Bundle import android.os.Parcelable +import android.view.View import androidx.recyclerview.widget.RecyclerView import butterknife.BindView import com.airbnb.mvrx.fragmentViewModel @@ -67,8 +68,8 @@ class RoomListQuickActionsBottomSheet : VectorBaseBottomSheetDialogFragment(), R override fun getLayoutResId() = R.layout.bottom_sheet_generic_list - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) sharedActionViewModel = activityViewModelProvider.get(RoomListQuickActionsSharedActionViewModel::class.java) recyclerView.configureWith(roomListActionsEpoxyController, viewPool = sharedViewPool, hasFixedSize = false, disableItemAnimation = true) roomListActionsEpoxyController.listener = this diff --git a/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomActivity.kt b/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomActivity.kt index b4f0aabea5..8f3e11453e 100644 --- a/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomActivity.kt +++ b/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomActivity.kt @@ -112,6 +112,7 @@ class InviteUsersToRoomActivity : SimpleFragmentActivity() { } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) if (allGranted(grantResults)) { if (requestCode == PERMISSION_REQUEST_CODE_READ_CONTACTS) { doOnPostResume { addFragmentToBackstack(R.id.container, ContactsBookFragment::class.java) } diff --git a/vector/src/main/java/im/vector/app/features/media/BigImageViewerActivity.kt b/vector/src/main/java/im/vector/app/features/media/BigImageViewerActivity.kt index c619b4aa92..81d6f1f996 100644 --- a/vector/src/main/java/im/vector/app/features/media/BigImageViewerActivity.kt +++ b/vector/src/main/java/im/vector/app/features/media/BigImageViewerActivity.kt @@ -30,6 +30,7 @@ import com.yalantis.ucrop.UCrop import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ScreenComponent +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.StringProvider @@ -112,10 +113,10 @@ class BigImageViewerActivity : VectorBaseActivity() { private fun onAvatarTypeSelected(isCamera: Boolean) { if (isCamera) { if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, this, PERMISSION_REQUEST_CODE_LAUNCH_CAMERA)) { - avatarCameraUri = MultiPicker.get(MultiPicker.CAMERA).startWithExpectingFile(this) + avatarCameraUri = MultiPicker.get(MultiPicker.CAMERA).startWithExpectingFile(this, takePhotoActivityResultLauncher) } } else { - MultiPicker.get(MultiPicker.IMAGE).single().startWith(this) + MultiPicker.get(MultiPicker.IMAGE).single().startWith(pickImageActivityResultLauncher) } } @@ -127,33 +128,43 @@ class BigImageViewerActivity : VectorBaseActivity() { .start(this) } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - if (resultCode == Activity.RESULT_OK) { - when (requestCode) { - MultiPicker.REQUEST_CODE_TAKE_PHOTO -> { - avatarCameraUri?.let { uri -> - MultiPicker.get(MultiPicker.CAMERA) - .getTakenPhoto(this, requestCode, resultCode, uri) - ?.let { - onRoomAvatarSelected(it) - } - } - } - MultiPicker.REQUEST_CODE_PICK_IMAGE -> { - MultiPicker - .get(MultiPicker.IMAGE) - .getSelectedFiles(this, requestCode, resultCode, data) - .firstOrNull()?.let { - onRoomAvatarSelected(it) - } - } - UCrop.REQUEST_CROP -> data?.let { onAvatarCropped(UCrop.getOutput(it)) } + private val takePhotoActivityResultLauncher = registerStartForActivityResult { activityResult -> + if (activityResult.resultCode == Activity.RESULT_OK) { + avatarCameraUri?.let { uri -> + MultiPicker.get(MultiPicker.CAMERA) + .getTakenPhoto(this, uri) + ?.let { + onRoomAvatarSelected(it) + } } } + } + + private val pickImageActivityResultLauncher = registerStartForActivityResult { activityResult -> + if (activityResult.resultCode == Activity.RESULT_OK) { + MultiPicker + .get(MultiPicker.IMAGE) + .getSelectedFiles(this, activityResult.data) + .firstOrNull()?.let { + onRoomAvatarSelected(it) + } + } + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + // TODO handle this one (Ucrop lib) + @Suppress("DEPRECATION") super.onActivityResult(requestCode, resultCode, data) + + if (resultCode == Activity.RESULT_OK) { + when (requestCode) { + UCrop.REQUEST_CROP -> data?.let { onAvatarCropped(UCrop.getOutput(it)) } + } + } } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) if (allGranted(grantResults)) { when (requestCode) { PERMISSION_REQUEST_CODE_LAUNCH_CAMERA -> onAvatarTypeSelected(true) @@ -174,7 +185,6 @@ class BigImageViewerActivity : VectorBaseActivity() { private const val EXTRA_TITLE = "EXTRA_TITLE" private const val EXTRA_IMAGE_URL = "EXTRA_IMAGE_URL" private const val EXTRA_CAN_EDIT_IMAGE = "EXTRA_CAN_EDIT_IMAGE" - const val REQUEST_CODE = 1000 fun newIntent(context: Context, title: String?, imageUrl: String, canEditImage: Boolean = false): Intent { return Intent(context, BigImageViewerActivity::class.java).apply { diff --git a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt index 5ad600dfff..106d804cd3 100644 --- a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt +++ b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt @@ -21,11 +21,11 @@ import android.content.Context import android.content.Intent import android.view.View import android.view.Window +import androidx.activity.result.ActivityResultLauncher import androidx.core.app.ActivityOptionsCompat import androidx.core.app.TaskStackBuilder import androidx.core.util.Pair import androidx.core.view.ViewCompat -import androidx.fragment.app.Fragment import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.error.fatalError @@ -45,7 +45,6 @@ import im.vector.app.features.home.room.detail.RoomDetailActivity import im.vector.app.features.home.room.detail.RoomDetailArgs import im.vector.app.features.home.room.detail.search.SearchActivity import im.vector.app.features.home.room.detail.search.SearchArgs -import im.vector.app.features.home.room.detail.widget.WidgetRequestCodes import im.vector.app.features.home.room.filtered.FilteredRoomsActivity import im.vector.app.features.invite.InviteUsersToRoomActivity import im.vector.app.features.media.AttachmentData @@ -266,21 +265,32 @@ class DefaultNavigator @Inject constructor( } } - override fun openTerms(fragment: Fragment, serviceType: TermsService.ServiceType, baseUrl: String, token: String?, requestCode: Int) { - val intent = ReviewTermsActivity.intent(fragment.requireContext(), serviceType, baseUrl, token) - fragment.startActivityForResult(intent, requestCode) + override fun openTerms(context: Context, + activityResultLauncher: ActivityResultLauncher, + serviceType: TermsService.ServiceType, + baseUrl: String, + token: String?) { + val intent = ReviewTermsActivity.intent(context, serviceType, baseUrl, token) + activityResultLauncher.launch(intent) } - override fun openStickerPicker(fragment: Fragment, roomId: String, widget: Widget, requestCode: Int) { + override fun openStickerPicker(context: Context, + activityResultLauncher: ActivityResultLauncher, + roomId: String, + widget: Widget) { val widgetArgs = widgetArgsBuilder.buildStickerPickerArgs(roomId, widget) - val intent = WidgetActivity.newIntent(fragment.requireContext(), widgetArgs) - fragment.startActivityForResult(intent, WidgetRequestCodes.STICKER_PICKER_REQUEST_CODE) + val intent = WidgetActivity.newIntent(context, widgetArgs) + activityResultLauncher.launch(intent) } - override fun openIntegrationManager(fragment: Fragment, roomId: String, integId: String?, screen: String?) { + override fun openIntegrationManager(context: Context, + activityResultLauncher: ActivityResultLauncher, + roomId: String, + integId: String?, + screen: String?) { val widgetArgs = widgetArgsBuilder.buildIntegrationManagerArgs(roomId, integId, screen) - val intent = WidgetActivity.newIntent(fragment.requireContext(), widgetArgs) - fragment.startActivityForResult(intent, WidgetRequestCodes.INTEGRATION_MANAGER_REQUEST_CODE) + val intent = WidgetActivity.newIntent(context, widgetArgs) + activityResultLauncher.launch(intent) } override fun openRoomWidget(context: Context, roomId: String, widget: Widget, options: Map?) { @@ -293,14 +303,11 @@ class DefaultNavigator @Inject constructor( } } - override fun openPinCode(fragment: Fragment, pinMode: PinMode, requestCode: Int) { - val intent = PinActivity.newIntent(fragment.requireContext(), PinArgs(pinMode)) - fragment.startActivityForResult(intent, requestCode) - } - - override fun openPinCode(activity: Activity, pinMode: PinMode, requestCode: Int) { - val intent = PinActivity.newIntent(activity, PinArgs(pinMode)) - activity.startActivityForResult(intent, requestCode) + override fun openPinCode(context: Context, + activityResultLauncher: ActivityResultLauncher, + pinMode: PinMode) { + val intent = PinActivity.newIntent(context, PinArgs(pinMode)) + activityResultLauncher.launch(intent) } override fun openMediaViewer(activity: Activity, diff --git a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt index b2efb1e931..1d01a5e4f0 100644 --- a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt +++ b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt @@ -18,18 +18,16 @@ package im.vector.app.features.navigation import android.app.Activity import android.content.Context +import android.content.Intent import android.view.View +import androidx.activity.result.ActivityResultLauncher import androidx.core.util.Pair -import androidx.fragment.app.Fragment import im.vector.app.features.crypto.recover.SetupMode -import im.vector.app.features.home.room.detail.widget.WidgetRequestCodes import im.vector.app.features.media.AttachmentData -import im.vector.app.features.pin.PinActivity import im.vector.app.features.pin.PinMode import im.vector.app.features.roomdirectory.roompreview.RoomPreviewData import im.vector.app.features.settings.VectorSettingsActivity import im.vector.app.features.share.SharedData -import im.vector.app.features.terms.ReviewTermsActivity import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoom import org.matrix.android.sdk.api.session.room.model.thirdparty.RoomDirectoryData import org.matrix.android.sdk.api.session.terms.TermsService @@ -84,22 +82,26 @@ interface Navigator { fun openBigImageViewer(activity: Activity, sharedElement: View?, matrixItem: MatrixItem) - fun openPinCode(fragment: Fragment, pinMode: PinMode, requestCode: Int = PinActivity.PIN_REQUEST_CODE) + fun openPinCode(context: Context, + activityResultLauncher: ActivityResultLauncher, + pinMode: PinMode) - fun openPinCode(activity: Activity, pinMode: PinMode, requestCode: Int = PinActivity.PIN_REQUEST_CODE) - - fun openTerms(fragment: Fragment, + fun openTerms(context: Context, + activityResultLauncher: ActivityResultLauncher, serviceType: TermsService.ServiceType, baseUrl: String, - token: String?, - requestCode: Int = ReviewTermsActivity.TERMS_REQUEST_CODE) + token: String?) - fun openStickerPicker(fragment: Fragment, + fun openStickerPicker(context: Context, + activityResultLauncher: ActivityResultLauncher, roomId: String, - widget: Widget, - requestCode: Int = WidgetRequestCodes.STICKER_PICKER_REQUEST_CODE) + widget: Widget) - fun openIntegrationManager(fragment: Fragment, roomId: String, integId: String?, screen: String?) + fun openIntegrationManager(context: Context, + activityResultLauncher: ActivityResultLauncher, + roomId: String, + integId: String?, + screen: String?) fun openRoomWidget(context: Context, roomId: String, widget: Widget, options: Map? = null) diff --git a/vector/src/main/java/im/vector/app/features/pin/PinActivity.kt b/vector/src/main/java/im/vector/app/features/pin/PinActivity.kt index 0216ec512e..2e6a0b6c08 100644 --- a/vector/src/main/java/im/vector/app/features/pin/PinActivity.kt +++ b/vector/src/main/java/im/vector/app/features/pin/PinActivity.kt @@ -28,8 +28,6 @@ import im.vector.app.core.platform.VectorBaseActivity class PinActivity : VectorBaseActivity(), ToolbarConfigurable, UnlockedActivity { companion object { - const val PIN_REQUEST_CODE = 17890 - fun newIntent(context: Context, args: PinArgs): Intent { return Intent(context, PinActivity::class.java).apply { putExtra(MvRx.KEY_ARG, args) diff --git a/vector/src/main/java/im/vector/app/features/qrcode/QrCodeScannerActivity.kt b/vector/src/main/java/im/vector/app/features/qrcode/QrCodeScannerActivity.kt index 616da791d3..8a5126a160 100644 --- a/vector/src/main/java/im/vector/app/features/qrcode/QrCodeScannerActivity.kt +++ b/vector/src/main/java/im/vector/app/features/qrcode/QrCodeScannerActivity.kt @@ -19,7 +19,7 @@ package im.vector.app.features.qrcode import android.app.Activity import android.content.Intent import android.os.Bundle -import androidx.fragment.app.Fragment +import androidx.activity.result.ActivityResultLauncher import com.google.zxing.BarcodeFormat import com.google.zxing.Result import com.google.zxing.ResultMetadataType @@ -75,15 +75,8 @@ class QrCodeScannerActivity : VectorBaseActivity() { private const val EXTRA_OUT_TEXT = "EXTRA_OUT_TEXT" private const val EXTRA_OUT_IS_QR_CODE = "EXTRA_OUT_IS_QR_CODE" - const val QR_CODE_SCANNER_REQUEST_CODE = 429 - - // For test only - fun startForResult(activity: Activity, requestCode: Int = QR_CODE_SCANNER_REQUEST_CODE) { - activity.startActivityForResult(Intent(activity, QrCodeScannerActivity::class.java), requestCode) - } - - fun startForResult(fragment: Fragment, requestCode: Int = QR_CODE_SCANNER_REQUEST_CODE) { - fragment.startActivityForResult(Intent(fragment.requireActivity(), QrCodeScannerActivity::class.java), requestCode) + fun startForResult(activity: Activity, activityResultLauncher: ActivityResultLauncher) { + activityResultLauncher.launch(Intent(activity, QrCodeScannerActivity::class.java)) } fun getResultText(data: Intent?): String? { diff --git a/vector/src/main/java/im/vector/app/features/reactions/EmojiChooserFragment.kt b/vector/src/main/java/im/vector/app/features/reactions/EmojiChooserFragment.kt index 447525f72f..af40aa970d 100644 --- a/vector/src/main/java/im/vector/app/features/reactions/EmojiChooserFragment.kt +++ b/vector/src/main/java/im/vector/app/features/reactions/EmojiChooserFragment.kt @@ -17,7 +17,6 @@ package im.vector.app.features.reactions import android.os.Bundle import android.view.View -import androidx.lifecycle.observe import im.vector.app.R import im.vector.app.core.extensions.cleanup import im.vector.app.core.platform.VectorBaseFragment diff --git a/vector/src/main/java/im/vector/app/features/reactions/EmojiReactionPickerActivity.kt b/vector/src/main/java/im/vector/app/features/reactions/EmojiReactionPickerActivity.kt index 4fe74c2bcc..25cb90d3a4 100644 --- a/vector/src/main/java/im/vector/app/features/reactions/EmojiReactionPickerActivity.kt +++ b/vector/src/main/java/im/vector/app/features/reactions/EmojiReactionPickerActivity.kt @@ -212,10 +212,8 @@ class EmojiReactionPickerActivity : VectorBaseActivity(), return intent } - fun getOutput(data: Intent): Pair? { - val eventId = data.getStringExtra(EXTRA_EVENT_ID) ?: return null - val reaction = data.getStringExtra(EXTRA_REACTION_RESULT) ?: return null - return eventId to reaction - } + fun getOutputEventId(data: Intent?): String? = data?.getStringExtra(EXTRA_EVENT_ID) + + fun getOutputReaction(data: Intent?): String? = data?.getStringExtra(EXTRA_REACTION_RESULT) } } diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt index cda2d83e3d..169cba09eb 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt @@ -284,7 +284,12 @@ class RoomMemberProfileFragment @Inject constructor( } private fun handleShareRoomMemberProfile(permalink: String) { - startSharePlainTextIntent(fragment = this, chooserTitle = null, text = permalink) + startSharePlainTextIntent( + fragment = this, + activityResultLauncher = null, + chooserTitle = null, + text = permalink + ) } private fun onAvatarClicked(view: View, userMatrixItem: MatrixItem) { diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheet.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheet.kt index 9ba1c59983..323e25a022 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheet.kt @@ -20,6 +20,7 @@ import android.content.DialogInterface import android.os.Bundle import android.os.Parcelable import android.view.KeyEvent +import android.view.View import androidx.fragment.app.Fragment import com.airbnb.mvrx.MvRx import com.airbnb.mvrx.fragmentViewModel @@ -46,8 +47,8 @@ class DeviceListBottomSheet : VectorBaseBottomSheetDialogFragment() { injector.inject(this) } - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) viewModel.observeViewEvents { when (it) { is DeviceListBottomSheetViewEvents.Verify -> { diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListFragment.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListFragment.kt index 9f90ffbb84..e9fdd15af4 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListFragment.kt @@ -17,6 +17,7 @@ package im.vector.app.features.roommemberprofile.devices import android.os.Bundle +import android.view.View import androidx.recyclerview.widget.RecyclerView import butterknife.BindView import com.airbnb.mvrx.parentFragmentViewModel @@ -41,8 +42,8 @@ class DeviceListFragment @Inject constructor( @BindView(R.id.bottomSheetRecyclerView) lateinit var recyclerView: RecyclerView - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) recyclerView.setPadding(0, dimensionConverter.dpToPx(16), 0, dimensionConverter.dpToPx(16)) recyclerView.configureWith( epoxyController, diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceTrustInfoActionFragment.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceTrustInfoActionFragment.kt index decc22c979..e31f3172e9 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceTrustInfoActionFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceTrustInfoActionFragment.kt @@ -17,6 +17,7 @@ package im.vector.app.features.roommemberprofile.devices import android.os.Bundle +import android.view.View import androidx.recyclerview.widget.RecyclerView import butterknife.BindView import com.airbnb.mvrx.parentFragmentViewModel @@ -41,8 +42,8 @@ class DeviceTrustInfoActionFragment @Inject constructor( @BindView(R.id.bottomSheetRecyclerView) lateinit var recyclerView: RecyclerView - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) recyclerView.setPadding(0, dimensionConverter.dpToPx(16), 0, dimensionConverter.dpToPx(16)) recyclerView.configureWith( epoxyController, diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt index 477d557bea..399c1ecf32 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt @@ -42,14 +42,14 @@ import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.configureWith import im.vector.app.core.extensions.copyOnLongClick import im.vector.app.core.extensions.exhaustive +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.extensions.setTextOrHide import im.vector.app.core.intent.getFilenameFromUri import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO -import im.vector.app.core.utils.PERMISSION_REQUEST_CODE_LAUNCH_CAMERA -import im.vector.app.core.utils.allGranted import im.vector.app.core.utils.checkPermissions import im.vector.app.core.utils.copyToClipboard +import im.vector.app.core.utils.registerForPermissionsResult import im.vector.app.core.utils.startSharePlainTextIntent import im.vector.app.features.crypto.util.toImageRes import im.vector.app.features.home.AvatarRenderer @@ -260,14 +260,19 @@ class RoomProfileFragment @Inject constructor( } private fun onShareRoomProfile(permalink: String) { - startSharePlainTextIntent(fragment = this, chooserTitle = null, text = permalink) + startSharePlainTextIntent( + fragment = this, + activityResultLauncher = null, + chooserTitle = null, + text = permalink + ) } private fun onAvatarClicked(view: View, matrixItem: MatrixItem.RoomItem) = withState(roomProfileViewModel) { if (matrixItem.avatarUrl?.isNotEmpty() == true) { val intent = BigImageViewerActivity.newIntent(requireContext(), matrixItem.getBestName(), matrixItem.avatarUrl!!, it.canChangeAvatar) val options = ActivityOptionsCompat.makeSceneTransitionAnimation(requireActivity(), view, ViewCompat.getTransitionName(view) ?: "") - startActivityForResult(intent, BigImageViewerActivity.REQUEST_CODE, options.toBundle()) + bigImageStartForActivityResult.launch(intent, options) } else if (it.canChangeAvatar) { showAvatarSelector() } @@ -285,14 +290,20 @@ class RoomProfileFragment @Inject constructor( .show() } + private val takePhotoPermissionActivityResultLauncher = registerForPermissionsResult { allGranted -> + if (allGranted) { + onAvatarTypeSelected(true) + } + } + private var avatarCameraUri: Uri? = null private fun onAvatarTypeSelected(isCamera: Boolean) { if (isCamera) { - if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, this, PERMISSION_REQUEST_CODE_LAUNCH_CAMERA)) { - avatarCameraUri = MultiPicker.get(MultiPicker.CAMERA).startWithExpectingFile(this) + if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, requireActivity(), takePhotoPermissionActivityResultLauncher)) { + avatarCameraUri = MultiPicker.get(MultiPicker.CAMERA).startWithExpectingFile(requireActivity(), takePhotoActivityResultLauncher) } } else { - MultiPicker.get(MultiPicker.IMAGE).single().startWith(this) + MultiPicker.get(MultiPicker.IMAGE).single().startWith(pickImageActivityResultLauncher) } } @@ -304,37 +315,43 @@ class RoomProfileFragment @Inject constructor( .start(requireContext(), this) } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - if (resultCode == Activity.RESULT_OK) { - when (requestCode) { - MultiPicker.REQUEST_CODE_TAKE_PHOTO -> { - avatarCameraUri?.let { uri -> - MultiPicker.get(MultiPicker.CAMERA) - .getTakenPhoto(requireContext(), requestCode, resultCode, uri) - ?.let { - onRoomAvatarSelected(it) - } - } - } - MultiPicker.REQUEST_CODE_PICK_IMAGE -> { - MultiPicker - .get(MultiPicker.IMAGE) - .getSelectedFiles(requireContext(), requestCode, resultCode, data) - .firstOrNull()?.let { - onRoomAvatarSelected(it) - } - } - UCrop.REQUEST_CROP -> data?.let { onAvatarCropped(UCrop.getOutput(it)) } - BigImageViewerActivity.REQUEST_CODE -> data?.let { onAvatarCropped(it.data) } + private val takePhotoActivityResultLauncher = registerStartForActivityResult { activityResult -> + if (activityResult.resultCode == Activity.RESULT_OK) { + avatarCameraUri?.let { uri -> + MultiPicker.get(MultiPicker.CAMERA) + .getTakenPhoto(requireContext(), uri) + ?.let { + onRoomAvatarSelected(it) + } } } - super.onActivityResult(requestCode, resultCode, data) } - override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { - if (allGranted(grantResults)) { + private val pickImageActivityResultLauncher = registerStartForActivityResult { activityResult -> + if (activityResult.resultCode == Activity.RESULT_OK) { + MultiPicker + .get(MultiPicker.IMAGE) + .getSelectedFiles(requireContext(), activityResult.data) + .firstOrNull()?.let { + onRoomAvatarSelected(it) + } + } + } + + private val bigImageStartForActivityResult = registerStartForActivityResult { activityResult -> + if (activityResult.resultCode == Activity.RESULT_OK) { + activityResult.data?.let { onAvatarCropped(it.data) } + } + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + // TODO handle this one (Ucrop lib) + @Suppress("DEPRECATION") + super.onActivityResult(requestCode, resultCode, data) + + if (resultCode == Activity.RESULT_OK) { when (requestCode) { - PERMISSION_REQUEST_CODE_LAUNCH_CAMERA -> onAvatarTypeSelected(true) + UCrop.REQUEST_CROP -> data?.let { onAvatarCropped(UCrop.getOutput(it)) } } } } diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsActivity.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsActivity.kt index 50692ef255..8bea54fcbe 100755 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsActivity.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsActivity.kt @@ -107,7 +107,8 @@ class VectorSettingsActivity : VectorBaseActivity(), } if (oFragment != null) { - oFragment.setTargetFragment(caller, 0) + // Deprecated, I comment it, I think it is useless + // oFragment.setTargetFragment(caller, 0) // Replace the existing Fragment with the new Fragment supportFragmentManager.beginTransaction() .setCustomAnimations(R.anim.right_in, R.anim.fade_out, R.anim.fade_in, R.anim.right_out) diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt index 6c0aefe9da..dfae073e7e 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt @@ -41,6 +41,7 @@ import com.google.android.material.textfield.TextInputLayout import com.yalantis.ucrop.UCrop import im.vector.app.R import im.vector.app.core.extensions.hideKeyboard +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.extensions.showPassword import im.vector.app.core.intent.getFilenameFromUri import im.vector.app.core.platform.SimpleTextWatcher @@ -48,11 +49,10 @@ import im.vector.app.core.preference.UserAvatarPreference import im.vector.app.core.preference.VectorPreference import im.vector.app.core.preference.VectorSwitchPreference import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO -import im.vector.app.core.utils.PERMISSION_REQUEST_CODE_LAUNCH_CAMERA import im.vector.app.core.utils.TextUtils -import im.vector.app.core.utils.allGranted import im.vector.app.core.utils.checkPermissions import im.vector.app.core.utils.getSizeOfFiles +import im.vector.app.core.utils.registerForPermissionsResult import im.vector.app.core.utils.toast import im.vector.app.features.MainActivity import im.vector.app.features.MainActivityArgs @@ -279,89 +279,38 @@ class VectorSettingsGeneralFragment : VectorSettingsBaseFragment() { session.integrationManagerService().removeListener(integrationServiceListener) } - override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { - if (allGranted(grantResults)) { - if (requestCode == PERMISSION_REQUEST_CODE_LAUNCH_CAMERA) { - onAvatarTypeSelected(true) + private val attachmentPhotoActivityResultLauncher = registerStartForActivityResult { activityResult -> + if (activityResult.resultCode == Activity.RESULT_OK) { + avatarCameraUri?.let { uri -> + MultiPicker.get(MultiPicker.CAMERA) + .getTakenPhoto(requireContext(), uri) + ?.let { + onAvatarSelected(it) + } } } } + private val attachmentImageActivityResultLauncher = registerStartForActivityResult { activityResult -> + val data = activityResult.data ?: return@registerStartForActivityResult + if (activityResult.resultCode == Activity.RESULT_OK) { + MultiPicker + .get(MultiPicker.IMAGE) + .getSelectedFiles(requireContext(), data) + .firstOrNull()?.let { + onAvatarSelected(it) + } + } + } + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + // TODO handle this one (Ucrop lib) + @Suppress("DEPRECATION") super.onActivityResult(requestCode, resultCode, data) if (resultCode == Activity.RESULT_OK) { when (requestCode) { - REQUEST_NEW_PHONE_NUMBER -> refreshPhoneNumbersList() - REQUEST_PHONEBOOK_COUNTRY -> onPhonebookCountryUpdate(data) - MultiPicker.REQUEST_CODE_TAKE_PHOTO -> { - avatarCameraUri?.let { uri -> - MultiPicker.get(MultiPicker.CAMERA) - .getTakenPhoto(requireContext(), requestCode, resultCode, uri) - ?.let { - onAvatarSelected(it) - } - } - } - MultiPicker.REQUEST_CODE_PICK_IMAGE -> { - MultiPicker - .get(MultiPicker.IMAGE) - .getSelectedFiles(requireContext(), requestCode, resultCode, data) - .firstOrNull()?.let { - onAvatarSelected(it) - } - } - UCrop.REQUEST_CROP -> data?.let { onAvatarCropped(UCrop.getOutput(it)) } - /* TODO - VectorUtils.TAKE_IMAGE -> { - val thumbnailUri = VectorUtils.getThumbnailUriFromIntent(activity, data, session.mediaCache) - - if (null != thumbnailUri) { - displayLoadingView() - - val resource = ResourceUtils.openResource(activity, thumbnailUri, null) - - if (null != resource) { - session.mediaCache.uploadContent(resource.mContentStream, null, resource.mMimeType, null, object : MXMediaUploadListener() { - - override fun onUploadError(uploadId: String?, serverResponseCode: Int, serverErrorMessage: String?) { - activity?.runOnUiThread { onCommonDone(serverResponseCode.toString() + " : " + serverErrorMessage) } - } - - override fun onUploadComplete(uploadId: String?, contentUri: String?) { - activity?.runOnUiThread { - session.myUser.updateAvatarUrl(contentUri, object : MatrixCallback { - override fun onSuccess(info: Void?) { - onCommonDone(null) - refreshDisplay() - } - - override fun onNetworkError(e: Exception) { - onCommonDone(e.localizedMessage) - } - - override fun onMatrixError(e: MatrixError) { - if (MatrixError.M_CONSENT_NOT_GIVEN == e.errcode) { - activity?.runOnUiThread { - hideLoadingView() - (activity as VectorAppCompatActivity).consentNotGivenHelper.displayDialog(e) - } - } else { - onCommonDone(e.localizedMessage) - } - } - - override fun onUnexpectedError(e: Exception) { - onCommonDone(e.localizedMessage) - } - }) - } - } - }) - } - } - } - */ + UCrop.REQUEST_CROP -> data?.let { onAvatarCropped(UCrop.getOutput(it)) } } } } @@ -400,13 +349,19 @@ class VectorSettingsGeneralFragment : VectorSettingsBaseFragment() { }.show() } + private val takePhotoActivityResultLauncher = registerForPermissionsResult { allGranted -> + if (allGranted) { + onAvatarTypeSelected(true) + } + } + private fun onAvatarTypeSelected(isCamera: Boolean) { if (isCamera) { - if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, this, PERMISSION_REQUEST_CODE_LAUNCH_CAMERA)) { - avatarCameraUri = MultiPicker.get(MultiPicker.CAMERA).startWithExpectingFile(this) + if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, requireActivity(), takePhotoActivityResultLauncher)) { + avatarCameraUri = MultiPicker.get(MultiPicker.CAMERA).startWithExpectingFile(requireActivity(), attachmentPhotoActivityResultLauncher) } } else { - MultiPicker.get(MultiPicker.IMAGE).single().startWith(this) + MultiPicker.get(MultiPicker.IMAGE).single().startWith(attachmentImageActivityResultLauncher) } } @@ -464,29 +419,10 @@ class VectorSettingsGeneralFragment : VectorSettingsBaseFragment() { */ } - private fun onPhonebookCountryUpdate(data: Intent?) { - /* TODO - if (data != null && data.hasExtra(CountryPickerActivity.EXTRA_OUT_COUNTRY_NAME) - && data.hasExtra(CountryPickerActivity.EXTRA_OUT_COUNTRY_CODE)) { - val countryCode = data.getStringExtra(CountryPickerActivity.EXTRA_OUT_COUNTRY_CODE) - if (!TextUtils.equals(countryCode, PhoneNumberUtils.getCountryCode(activity))) { - PhoneNumberUtils.setCountryCode(activity, countryCode) - mContactPhonebookCountryPreference.summary = data.getStringExtra(CountryPickerActivity.EXTRA_OUT_COUNTRY_NAME) - } - } - */ - } - // ============================================================================================================== // Phone number management // ============================================================================================================== - /** - * Refresh phone number list - */ - private fun refreshPhoneNumbersList() { - } - /** * Update the password. */ @@ -646,9 +582,4 @@ class VectorSettingsGeneralFragment : VectorSettingsBaseFragment() { }) } } - - companion object { - private const val REQUEST_NEW_PHONE_NUMBER = 456 - private const val REQUEST_PHONEBOOK_COUNTRY = 789 - } } diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsNotificationPreferenceFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsNotificationPreferenceFragment.kt index d6d9cf6e31..4bee1ac0c8 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsNotificationPreferenceFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsNotificationPreferenceFragment.kt @@ -27,6 +27,7 @@ import androidx.preference.Preference import androidx.preference.SwitchPreference import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.preference.VectorEditTextPreference import im.vector.app.core.preference.VectorPreference import im.vector.app.core.preference.VectorPreferenceCategory @@ -114,6 +115,10 @@ class VectorSettingsNotificationPreferenceFragment @Inject constructor( handleSystemPreference() } + private val batteryStartForActivityResult = registerStartForActivityResult { + // Noop + } + // BackgroundSyncModeChooserDialog.InteractionListener override fun onOptionSelected(mode: BackgroundSyncMode) { // option has change, need to act @@ -122,9 +127,7 @@ class VectorSettingsNotificationPreferenceFragment @Inject constructor( // Even if using foreground service with foreground notif, it stops to work // in doze mode for certain devices :/ if (!isIgnoringBatteryOptimizations(requireContext())) { - requestDisablingBatteryOptimization(requireActivity(), - this@VectorSettingsNotificationPreferenceFragment, - REQUEST_BATTERY_OPTIMIZATION) + requestDisablingBatteryOptimization(requireActivity(), batteryStartForActivityResult) } } vectorPreferences.setFdroidSyncBackgroundMode(mode) @@ -210,27 +213,22 @@ class VectorSettingsNotificationPreferenceFragment @Inject constructor( intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, vectorPreferences.getNotificationRingTone()) } - startActivityForResult(intent, REQUEST_NOTIFICATION_RINGTONE) + ringtoneStartForActivityResult.launch(intent) false } } } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data) - if (resultCode == Activity.RESULT_OK) { - when (requestCode) { - REQUEST_NOTIFICATION_RINGTONE -> { - vectorPreferences.setNotificationRingTone(data?.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI) as Uri?) + private val ringtoneStartForActivityResult = registerStartForActivityResult { activityResult -> + if (activityResult.resultCode == Activity.RESULT_OK) { + vectorPreferences.setNotificationRingTone(activityResult.data?.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI) as Uri?) - // test if the selected ring tone can be played - val notificationRingToneName = vectorPreferences.getNotificationRingToneName() - if (null != notificationRingToneName) { - vectorPreferences.setNotificationRingTone(vectorPreferences.getNotificationRingTone()) - findPreference(VectorPreferences.SETTINGS_NOTIFICATION_RINGTONE_SELECTION_PREFERENCE_KEY)!! - .summary = notificationRingToneName - } - } + // test if the selected ring tone can be played + val notificationRingToneName = vectorPreferences.getNotificationRingToneName() + if (null != notificationRingToneName) { + vectorPreferences.setNotificationRingTone(vectorPreferences.getNotificationRingTone()) + findPreference(VectorPreferences.SETTINGS_NOTIFICATION_RINGTONE_SELECTION_PREFERENCE_KEY)!! + .summary = notificationRingToneName } } } @@ -340,9 +338,4 @@ class VectorSettingsNotificationPreferenceFragment @Inject constructor( }) } } - - companion object { - private const val REQUEST_NOTIFICATION_RINGTONE = 888 - private const val REQUEST_BATTERY_OPTIMIZATION = 500 - } } diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsNotificationsTroubleshootFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsNotificationsTroubleshootFragment.kt index f07d6471c9..b989ae44d0 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsNotificationsTroubleshootFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsNotificationsTroubleshootFragment.kt @@ -17,7 +17,6 @@ package im.vector.app.features.settings import android.app.Activity import android.content.Context -import android.content.Intent import android.os.Bundle import android.view.View import android.view.ViewGroup @@ -30,6 +29,7 @@ import androidx.transition.TransitionManager import butterknife.BindView import im.vector.app.R import im.vector.app.core.extensions.cleanup +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.features.rageshake.BugReporter @@ -76,7 +76,7 @@ class VectorSettingsNotificationsTroubleshootFragment @Inject constructor( } mRunButton.debouncedClicks { - testManager?.retry() + testManager?.retry(testStartForActivityResult) } startUI() } @@ -134,7 +134,7 @@ class VectorSettingsNotificationsTroubleshootFragment @Inject constructor( } } mRecyclerView.adapter = testManager?.adapter - testManager?.runDiagnostic() + testManager?.runDiagnostic(testStartForActivityResult) } override fun onDestroyView() { @@ -142,12 +142,14 @@ class VectorSettingsNotificationsTroubleshootFragment @Inject constructor( super.onDestroyView() } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - if (resultCode == Activity.RESULT_OK && requestCode == NotificationTroubleshootTestManager.REQ_CODE_FIX) { - testManager?.retry() - return + private val testStartForActivityResult = registerStartForActivityResult { activityResult -> + if (activityResult.resultCode == Activity.RESULT_OK) { + retry() } - super.onActivityResult(requestCode, resultCode, data) + } + + private fun retry() { + testManager?.retry(testStartForActivityResult) } override fun onDetach() { diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsPinFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsPinFragment.kt index 4a6965618e..37465258f6 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsPinFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsPinFragment.kt @@ -16,14 +16,13 @@ package im.vector.app.features.settings -import android.content.Intent import androidx.lifecycle.lifecycleScope import androidx.preference.Preference import androidx.preference.SwitchPreference import im.vector.app.R +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.features.navigation.Navigator import im.vector.app.features.notifications.NotificationDrawerManager -import im.vector.app.features.pin.PinActivity import im.vector.app.features.pin.PinCodeStore import im.vector.app.features.pin.PinMode import kotlinx.coroutines.launch @@ -67,17 +66,18 @@ class VectorSettingsPinFragment @Inject constructor( refreshPinCodeStatus() } } else { - navigator.openPinCode(this@VectorSettingsPinFragment, PinMode.CREATE) + navigator.openPinCode( + requireContext(), + pinActivityResultLauncher, + PinMode.CREATE + ) } true } } } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data) - if (requestCode == PinActivity.PIN_REQUEST_CODE) { - refreshPinCodeStatus() - } + private val pinActivityResultLauncher = registerStartForActivityResult { + refreshPinCodeStatus() } } diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsSecurityPrivacyFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsSecurityPrivacyFragment.kt index b0ec7426a7..457fc00273 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsSecurityPrivacyFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsSecurityPrivacyFragment.kt @@ -39,6 +39,7 @@ import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.dialogs.ExportKeysDialog import im.vector.app.core.extensions.queryExportKeys +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.extensions.showPassword import im.vector.app.core.intent.ExternalIntentData import im.vector.app.core.intent.analyseIntent @@ -55,7 +56,6 @@ import im.vector.app.features.crypto.keysbackup.settings.KeysBackupManageActivit import im.vector.app.features.crypto.recover.BootstrapBottomSheet import im.vector.app.features.crypto.recover.SetupMode import im.vector.app.features.navigation.Navigator -import im.vector.app.features.pin.PinActivity import im.vector.app.features.pin.PinCodeStore import im.vector.app.features.pin.PinMode import im.vector.app.features.raw.wellknown.getElementWellknown @@ -320,48 +320,46 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor( } } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data) - if (resultCode == Activity.RESULT_OK) { - when (requestCode) { - REQUEST_CODE_SAVE_MEGOLM_EXPORT -> { - val uri = data?.data - if (uri != null) { - activity?.let { activity -> - ExportKeysDialog().show(activity, object : ExportKeysDialog.ExportKeyDialogListener { - override fun onPassphrase(passphrase: String) { - displayLoadingView() + private val saveMegolmStartForActivityResult = registerStartForActivityResult { + val uri = it.data?.data ?: return@registerStartForActivityResult + if (it.resultCode == Activity.RESULT_OK) { + ExportKeysDialog().show(requireActivity(), object : ExportKeysDialog.ExportKeyDialogListener { + override fun onPassphrase(passphrase: String) { + displayLoadingView() - KeysExporter(session) - .export(requireContext(), - passphrase, - uri, - object : MatrixCallback { - override fun onSuccess(data: Boolean) { - if (data) { - requireActivity().toast(getString(R.string.encryption_exported_successfully)) - } else { - requireActivity().toast(getString(R.string.unexpected_error)) - } - hideLoadingView() - } + KeysExporter(session) + .export(requireContext(), + passphrase, + uri, + object : MatrixCallback { + override fun onSuccess(data: Boolean) { + if (data) { + requireActivity().toast(getString(R.string.encryption_exported_successfully)) + } else { + requireActivity().toast(getString(R.string.unexpected_error)) + } + hideLoadingView() + } - override fun onFailure(failure: Throwable) { - onCommonDone(failure.localizedMessage) - } - }) - } - }) - } - } + override fun onFailure(failure: Throwable) { + onCommonDone(failure.localizedMessage) + } + }) } - PinActivity.PIN_REQUEST_CODE -> { - doOpenPinCodePreferenceScreen() - } - REQUEST_E2E_FILE_REQUEST_CODE -> { - importKeys(data) - } - } + }) + } + } + + private val pinActivityResultLauncher = registerStartForActivityResult { + if (it.resultCode == Activity.RESULT_OK) { + doOpenPinCodePreferenceScreen() + } + } + + private val importKeysActivityResultLauncher = registerStartForActivityResult { + val data = it.data ?: return@registerStartForActivityResult + if (it.resultCode == Activity.RESULT_OK) { + importKeys(data) } } @@ -369,7 +367,10 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor( lifecycleScope.launchWhenResumed { val hasPinCode = pinCodeStore.hasEncodedPin() if (hasPinCode) { - navigator.openPinCode(this@VectorSettingsSecurityPrivacyFragment, PinMode.AUTH) + navigator.openPinCode( + requireContext(), + pinActivityResultLauncher, + PinMode.AUTH) } else { doOpenPinCodePreferenceScreen() } @@ -391,7 +392,7 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor( } exportPref.onPreferenceClickListener = Preference.OnPreferenceClickListener { - queryExportKeys(activeSessionHolder.getSafeActiveSession()?.myUserId ?: "", REQUEST_CODE_SAVE_MEGOLM_EXPORT) + queryExportKeys(activeSessionHolder.getSafeActiveSession()?.myUserId ?: "", saveMegolmStartForActivityResult) true } @@ -406,7 +407,12 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor( */ @SuppressLint("NewApi") private fun importKeys() { - activity?.let { openFileSelection(it, this, false, REQUEST_E2E_FILE_REQUEST_CODE) } + openFileSelection( + requireActivity(), + importKeysActivityResultLauncher, + false, + 0 + ) } /** @@ -414,12 +420,7 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor( * * @param intent the intent result */ - private fun importKeys(intent: Intent?) { - // sanity check - if (null == intent) { - return - } - + private fun importKeys(intent: Intent) { val sharedDataItems = analyseIntent(intent) val thisActivity = activity @@ -605,9 +606,4 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor( } }) } - - companion object { - private const val REQUEST_E2E_FILE_REQUEST_CODE = 123 - private const val REQUEST_CODE_SAVE_MEGOLM_EXPORT = 124 - } } diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsVoiceVideoFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsVoiceVideoFragment.kt index dba3c78220..05d1a1f60f 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsVoiceVideoFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsVoiceVideoFragment.kt @@ -23,6 +23,7 @@ import android.net.Uri import androidx.preference.Preference import androidx.preference.SwitchPreference import im.vector.app.R +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.preference.VectorPreference import im.vector.app.core.utils.getCallRingtoneName import im.vector.app.core.utils.getCallRingtoneUri @@ -57,19 +58,13 @@ class VectorSettingsVoiceVideoFragment : VectorSettingsBaseFragment() { } } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data) - - if (resultCode == Activity.RESULT_OK) { - when (requestCode) { - REQUEST_CALL_RINGTONE -> { - val callRingtoneUri: Uri? = data?.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI) - val thisActivity = activity - if (callRingtoneUri != null && thisActivity != null) { - setCallRingtoneUri(thisActivity, callRingtoneUri) - mCallRingtonePreference.summary = getCallRingtoneName(thisActivity) - } - } + private val ringtoneStartForActivityResult = registerStartForActivityResult { activityResult -> + if (activityResult.resultCode == Activity.RESULT_OK) { + val callRingtoneUri: Uri? = activityResult.data?.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI) + val thisActivity = activity + if (callRingtoneUri != null && thisActivity != null) { + setCallRingtoneUri(thisActivity, callRingtoneUri) + mCallRingtonePreference.summary = getCallRingtoneName(thisActivity) } } } @@ -82,10 +77,6 @@ class VectorSettingsVoiceVideoFragment : VectorSettingsBaseFragment() { putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_RINGTONE) activity?.let { putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, getCallRingtoneUri(it)) } } - startActivityForResult(intent, REQUEST_CALL_RINGTONE) - } - - companion object { - private const val REQUEST_CALL_RINGTONE = 999 + ringtoneStartForActivityResult.launch(intent) } } diff --git a/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsFragment.kt b/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsFragment.kt index 711819f764..ebeac5aca1 100644 --- a/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsFragment.kt @@ -41,8 +41,9 @@ class CrossSigningSettingsFragment @Inject constructor( private val viewModel: CrossSigningSettingsViewModel by fragmentViewModel() - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setupRecyclerView() viewModel.observeViewEvents { when (it) { is CrossSigningSettingsViewEvents.Failure -> { @@ -62,11 +63,6 @@ class CrossSigningSettingsFragment @Inject constructor( (activity as? VectorBaseActivity)?.supportActionBar?.setTitle(R.string.encryption_information_cross_signing_state) } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - setupRecyclerView() - } - override fun invalidate() = withState(viewModel) { state -> controller.setData(state) } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheet.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheet.kt index c8b12e26a5..dbaa99e1df 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheet.kt @@ -17,6 +17,7 @@ package im.vector.app.features.settings.devices import android.os.Bundle import android.os.Parcelable +import android.view.View import androidx.core.view.isVisible import androidx.recyclerview.widget.RecyclerView import butterknife.BindView @@ -58,8 +59,8 @@ class DeviceVerificationInfoBottomSheet : VectorBaseBottomSheetDialogFragment(), override fun getLayoutResId() = R.layout.bottom_sheet_generic_list_with_title - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) recyclerView.configureWith( controller, showDivider = false, diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/NotificationTroubleshootTestManager.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/NotificationTroubleshootTestManager.kt index 92e8eecb4d..e977dc5963 100644 --- a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/NotificationTroubleshootTestManager.kt +++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/NotificationTroubleshootTestManager.kt @@ -15,8 +15,10 @@ */ package im.vector.app.features.settings.troubleshoot +import android.content.Intent import android.os.Handler import android.os.Looper +import androidx.activity.result.ActivityResultLauncher import androidx.fragment.app.Fragment import kotlin.properties.Delegates @@ -41,7 +43,7 @@ class NotificationTroubleshootTestManager(val fragment: Fragment) { test.manager = this } - fun runDiagnostic() { + fun runDiagnostic(activityResultLauncher: ActivityResultLauncher) { if (isCancelled) return currentTestIndex = 0 val handler = Handler(Looper.getMainLooper()) @@ -60,7 +62,7 @@ class NotificationTroubleshootTestManager(val fragment: Fragment) { // Cosmetic: Start with a small delay for UI/UX reason (better animation effect) for non async tests handler.postDelayed({ if (fragment.isAdded) { - troubleshootTest.perform() + troubleshootTest.perform(activityResultLauncher) } }, 600) } else { @@ -72,18 +74,18 @@ class NotificationTroubleshootTestManager(val fragment: Fragment) { } } if (fragment.isAdded) { - testList.firstOrNull()?.perform() + testList.firstOrNull()?.perform(activityResultLauncher) } } - fun retry() { + fun retry(activityResultLauncher: ActivityResultLauncher) { for (test in testList) { test.cancel() test.description = null test.quickFix = null test.status = TroubleshootTest.TestStatus.NOT_STARTED } - runDiagnostic() + runDiagnostic(activityResultLauncher) } fun cancel() { @@ -92,8 +94,4 @@ class NotificationTroubleshootTestManager(val fragment: Fragment) { test.cancel() } } - - companion object { - const val REQ_CODE_FIX = 9099 - } } diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestAccountSettings.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestAccountSettings.kt index df1ff80b31..d0415c059c 100644 --- a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestAccountSettings.kt +++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestAccountSettings.kt @@ -15,6 +15,8 @@ */ package im.vector.app.features.settings.troubleshoot +import android.content.Intent +import androidx.activity.result.ActivityResultLauncher import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.pushrules.RuleIds import org.matrix.android.sdk.api.pushrules.RuleKind @@ -30,7 +32,7 @@ class TestAccountSettings @Inject constructor(private val stringProvider: String private val activeSessionHolder: ActiveSessionHolder) : TroubleshootTest(R.string.settings_troubleshoot_test_account_settings_title) { - override fun perform() { + override fun perform(activityResultLauncher: ActivityResultLauncher) { val session = activeSessionHolder.getSafeActiveSession() ?: return val defaultRule = session.getPushRules().getAllRules() .find { it.ruleId == RuleIds.RULE_ID_DISABLE_ALL } @@ -49,11 +51,11 @@ class TestAccountSettings @Inject constructor(private val stringProvider: String session.updatePushRuleEnableStatus(RuleKind.OVERRIDE, defaultRule, !defaultRule.enabled, object : MatrixCallback { override fun onSuccess(data: Unit) { - manager?.retry() + manager?.retry(activityResultLauncher) } override fun onFailure(failure: Throwable) { - manager?.retry() + manager?.retry(activityResultLauncher) } }) } diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestDeviceSettings.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestDeviceSettings.kt index 61c2fb120e..0d661e8b16 100644 --- a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestDeviceSettings.kt +++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestDeviceSettings.kt @@ -15,6 +15,8 @@ */ package im.vector.app.features.settings.troubleshoot +import android.content.Intent +import androidx.activity.result.ActivityResultLauncher import im.vector.app.R import im.vector.app.core.resources.StringProvider import im.vector.app.features.settings.VectorPreferences @@ -27,7 +29,7 @@ class TestDeviceSettings @Inject constructor(private val vectorPreferences: Vect private val stringProvider: StringProvider) : TroubleshootTest(R.string.settings_troubleshoot_test_device_settings_title) { - override fun perform() { + override fun perform(activityResultLauncher: ActivityResultLauncher) { if (vectorPreferences.areNotificationEnabledForDevice()) { description = stringProvider.getString(R.string.settings_troubleshoot_test_device_settings_success) quickFix = null @@ -36,7 +38,7 @@ class TestDeviceSettings @Inject constructor(private val vectorPreferences: Vect quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_device_settings_quickfix) { override fun doFix() { vectorPreferences.setNotificationEnabledForDevice(true) - manager?.retry() + manager?.retry(activityResultLauncher) } } description = stringProvider.getString(R.string.settings_troubleshoot_test_device_settings_failed) diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestPushRulesSettings.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestPushRulesSettings.kt index 22b2331449..31b4e213ca 100644 --- a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestPushRulesSettings.kt +++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestPushRulesSettings.kt @@ -15,6 +15,8 @@ */ package im.vector.app.features.settings.troubleshoot +import android.content.Intent +import androidx.activity.result.ActivityResultLauncher import org.matrix.android.sdk.api.pushrules.RuleIds import org.matrix.android.sdk.api.pushrules.getActions import im.vector.app.R @@ -38,7 +40,7 @@ class TestPushRulesSettings @Inject constructor(private val activeSessionHolder: R.string.settings_messages_in_one_to_one, R.string.settings_messages_in_group_chat) - override fun perform() { + override fun perform(activityResultLauncher: ActivityResultLauncher) { val session = activeSessionHolder.getSafeActiveSession() ?: return val pushRules = session.getPushRules().getAllRules() var oneOrMoreRuleIsOff = false diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestSystemSettings.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestSystemSettings.kt index 92eae017f9..8e64514e7f 100644 --- a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestSystemSettings.kt +++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestSystemSettings.kt @@ -15,6 +15,8 @@ */ package im.vector.app.features.settings.troubleshoot +import android.content.Intent +import androidx.activity.result.ActivityResultLauncher import androidx.appcompat.app.AppCompatActivity import androidx.core.app.NotificationManagerCompat import im.vector.app.R @@ -29,7 +31,7 @@ class TestSystemSettings @Inject constructor(private val context: AppCompatActiv private val stringProvider: StringProvider) : TroubleshootTest(R.string.settings_troubleshoot_test_system_settings_title) { - override fun perform() { + override fun perform(activityResultLauncher: ActivityResultLauncher) { if (NotificationManagerCompat.from(context).areNotificationsEnabled()) { description = stringProvider.getString(R.string.settings_troubleshoot_test_system_settings_success) quickFix = null @@ -39,7 +41,7 @@ class TestSystemSettings @Inject constructor(private val context: AppCompatActiv quickFix = object : TroubleshootQuickFix(R.string.open_settings) { override fun doFix() { if (manager?.diagStatus == TestStatus.RUNNING) return // wait before all is finished - startNotificationSettingsIntent(context, NotificationTroubleshootTestManager.REQ_CODE_FIX) + startNotificationSettingsIntent(context, activityResultLauncher) } } status = TestStatus.FAILED diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TroubleshootTest.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TroubleshootTest.kt index 7abec31ae4..f894fcc0ef 100644 --- a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TroubleshootTest.kt +++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TroubleshootTest.kt @@ -15,6 +15,8 @@ */ package im.vector.app.features.settings.troubleshoot +import android.content.Intent +import androidx.activity.result.ActivityResultLauncher import androidx.annotation.StringRes import kotlin.properties.Delegates @@ -37,7 +39,7 @@ abstract class TroubleshootTest(@StringRes val titleResId: Int) { var manager: NotificationTroubleshootTestManager? = null - abstract fun perform() + abstract fun perform(activityResultLauncher: ActivityResultLauncher) fun isFinished(): Boolean = (status == TestStatus.FAILED || status == TestStatus.SUCCESS) diff --git a/vector/src/main/java/im/vector/app/features/share/IncomingShareFragment.kt b/vector/src/main/java/im/vector/app/features/share/IncomingShareFragment.kt index a75cfa4304..5e776fb17a 100644 --- a/vector/src/main/java/im/vector/app/features/share/IncomingShareFragment.kt +++ b/vector/src/main/java/im/vector/app/features/share/IncomingShareFragment.kt @@ -33,6 +33,7 @@ import im.vector.app.core.di.ActiveSessionHolder 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.extensions.registerStartForActivityResult import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.features.attachments.AttachmentsHelper import im.vector.app.features.attachments.preview.AttachmentsPreviewActivity @@ -118,20 +119,16 @@ class IncomingShareFragment @Inject constructor( private fun handleEditMediaBeforeSending(event: IncomingShareViewEvents.EditMediaBeforeSending) { val intent = AttachmentsPreviewActivity.newIntent(requireContext(), AttachmentsPreviewArgs(event.contentAttachmentData)) - startActivityForResult(intent, AttachmentsPreviewActivity.REQUEST_CODE) + attachmentPreviewActivityResultLauncher.launch(intent) } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - val hasBeenHandled = attachmentsHelper.onActivityResult(requestCode, resultCode, data) - if (!hasBeenHandled && resultCode == Activity.RESULT_OK && data != null) { - when (requestCode) { - AttachmentsPreviewActivity.REQUEST_CODE -> { - val sendData = AttachmentsPreviewActivity.getOutput(data) - val keepOriginalSize = AttachmentsPreviewActivity.getKeepOriginalSize(data) - viewModel.handle(IncomingShareAction.UpdateSharedData(SharedData.Attachments(sendData))) - viewModel.handle(IncomingShareAction.ShareMedia(keepOriginalSize)) - } - } + private val attachmentPreviewActivityResultLauncher = registerStartForActivityResult { + val data = it.data ?: return@registerStartForActivityResult + if (it.resultCode == Activity.RESULT_OK) { + val sendData = AttachmentsPreviewActivity.getOutput(data) + val keepOriginalSize = AttachmentsPreviewActivity.getKeepOriginalSize(data) + viewModel.handle(IncomingShareAction.UpdateSharedData(SharedData.Attachments(sendData))) + viewModel.handle(IncomingShareAction.ShareMedia(keepOriginalSize)) } } diff --git a/vector/src/main/java/im/vector/app/features/terms/ReviewTermsActivity.kt b/vector/src/main/java/im/vector/app/features/terms/ReviewTermsActivity.kt index 4fea176249..9d1f10f39e 100644 --- a/vector/src/main/java/im/vector/app/features/terms/ReviewTermsActivity.kt +++ b/vector/src/main/java/im/vector/app/features/terms/ReviewTermsActivity.kt @@ -73,7 +73,6 @@ class ReviewTermsActivity : SimpleFragmentActivity() { } companion object { - const val TERMS_REQUEST_CODE = 15000 private const val EXTRA_INFO = "EXTRA_INFO" fun intent(context: Context, serviceType: TermsService.ServiceType, baseUrl: String, token: String?): Intent { diff --git a/vector/src/main/java/im/vector/app/features/widgets/WidgetFragment.kt b/vector/src/main/java/im/vector/app/features/widgets/WidgetFragment.kt index 37ae89ab36..2d3e5e4e69 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/WidgetFragment.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/WidgetFragment.kt @@ -37,11 +37,10 @@ import com.airbnb.mvrx.args import com.airbnb.mvrx.withState import org.matrix.android.sdk.api.session.terms.TermsService import im.vector.app.R +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.platform.OnBackPressed import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.utils.openUrlInExternalBrowser -import im.vector.app.features.home.room.detail.widget.WidgetRequestCodes -import im.vector.app.features.terms.ReviewTermsActivity import im.vector.app.features.webview.WebViewEventListener import im.vector.app.features.widgets.webview.clearAfterWidget import im.vector.app.features.widgets.webview.setupForWidget @@ -86,19 +85,18 @@ class WidgetFragment @Inject constructor() : VectorBaseFragment(), WebViewEventL viewModel.handle(WidgetAction.LoadFormattedUrl) } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - when (requestCode) { - ReviewTermsActivity.TERMS_REQUEST_CODE -> { - Timber.v("On terms results") - if (resultCode == Activity.RESULT_OK) { - viewModel.handle(WidgetAction.OnTermsReviewed) - } else { - vectorBaseActivity.finish() - } - } - WidgetRequestCodes.INTEGRATION_MANAGER_REQUEST_CODE -> { - viewModel.handle(WidgetAction.LoadFormattedUrl) - } + private val termsActivityResultLauncher = registerStartForActivityResult { + Timber.v("On terms results") + if (it.resultCode == Activity.RESULT_OK) { + viewModel.handle(WidgetAction.OnTermsReviewed) + } else { + vectorBaseActivity.finish() + } + } + + private val integrationManagerActivityResultLauncher = registerStartForActivityResult { + if (it.resultCode == Activity.RESULT_OK) { + viewModel.handle(WidgetAction.LoadFormattedUrl) } } @@ -146,7 +144,12 @@ class WidgetFragment @Inject constructor() : VectorBaseFragment(), WebViewEventL override fun onOptionsItemSelected(item: MenuItem): Boolean = withState(viewModel) { state -> when (item.itemId) { R.id.action_edit -> { - navigator.openIntegrationManager(this, state.roomId, state.widgetId, state.widgetKind.screenId) + navigator.openIntegrationManager( + requireContext(), + integrationManagerActivityResultLauncher, + state.roomId, + state.widgetId, + state.widgetKind.screenId) return@withState true } R.id.action_delete -> { @@ -261,7 +264,8 @@ class WidgetFragment @Inject constructor() : VectorBaseFragment(), WebViewEventL private fun displayTerms(displayTerms: WidgetViewEvents.DisplayTerms) { navigator.openTerms( - fragment = this, + context = requireContext(), + activityResultLauncher = termsActivityResultLauncher, serviceType = TermsService.ServiceType.IntegrationManager, baseUrl = displayTerms.url, token = displayTerms.token @@ -287,7 +291,8 @@ class WidgetFragment @Inject constructor() : VectorBaseFragment(), WebViewEventL private fun displayIntegrationManager(event: WidgetViewEvents.DisplayIntegrationManager) { navigator.openIntegrationManager( - fragment = this, + context = requireContext(), + activityResultLauncher = integrationManagerActivityResultLauncher, roomId = fragmentArgs.roomId, integId = event.integId, screen = event.integType diff --git a/vector/src/main/java/im/vector/app/features/workers/signout/SignOutBottomSheetDialogFragment.kt b/vector/src/main/java/im/vector/app/features/workers/signout/SignOutBottomSheetDialogFragment.kt index 30f7ed93a3..2fe984dfc7 100644 --- a/vector/src/main/java/im/vector/app/features/workers/signout/SignOutBottomSheetDialogFragment.kt +++ b/vector/src/main/java/im/vector/app/features/workers/signout/SignOutBottomSheetDialogFragment.kt @@ -18,7 +18,6 @@ package im.vector.app.features.workers.signout import android.app.Activity import android.app.Dialog -import android.content.Intent import android.os.Bundle import android.view.View import android.view.ViewGroup @@ -39,6 +38,7 @@ import im.vector.app.R import im.vector.app.core.di.ScreenComponent import im.vector.app.core.dialogs.ExportKeysDialog import im.vector.app.core.extensions.queryExportKeys +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.app.features.crypto.keysbackup.setup.KeysBackupSetupActivity import im.vector.app.features.crypto.recover.BootstrapBottomSheet @@ -77,9 +77,6 @@ class SignOutBottomSheetDialogFragment : VectorBaseBottomSheetDialogFragment(), companion object { fun newInstance() = SignOutBottomSheetDialogFragment() - - private const val EXPORT_REQ = 0 - private const val QUERY_EXPORT_KEYS = 1 } init { @@ -104,8 +101,8 @@ class SignOutBottomSheetDialogFragment : VectorBaseBottomSheetDialogFragment(), viewModel.refreshRemoteStateIfNeeded() } - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) setupRecoveryButton.action = { BootstrapBottomSheet.show(parentFragmentManager, SetupMode.NORMAL) @@ -130,12 +127,12 @@ class SignOutBottomSheetDialogFragment : VectorBaseBottomSheetDialogFragment(), exportManuallyButton.action = { withState(viewModel) { state -> - queryExportKeys(state.userId, QUERY_EXPORT_KEYS) + queryExportKeys(state.userId, manualExportKeysActivityResultLauncher) } } setupMegolmBackupButton.action = { - startActivityForResult(KeysBackupSetupActivity.intent(requireContext(), true), EXPORT_REQ) + setupBackupActivityResultLauncher.launch(KeysBackupSetupActivity.intent(requireContext(), true)) } viewModel.observeViewEvents { @@ -289,26 +286,26 @@ class SignOutBottomSheetDialogFragment : VectorBaseBottomSheetDialogFragment(), return dialog } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data) - - if (resultCode == Activity.RESULT_OK) { - if (requestCode == QUERY_EXPORT_KEYS) { - val uri = data?.data - if (resultCode == Activity.RESULT_OK && uri != null) { - activity?.let { activity -> - ExportKeysDialog().show(activity, object : ExportKeysDialog.ExportKeyDialogListener { - override fun onPassphrase(passphrase: String) { - viewModel.handle(SignoutCheckViewModel.Actions.ExportKeys(passphrase, uri)) - } - }) - } - } - } else if (requestCode == EXPORT_REQ) { - if (data?.getBooleanExtra(KeysBackupSetupActivity.MANUAL_EXPORT, false) == true) { - viewModel.handle(SignoutCheckViewModel.Actions.KeySuccessfullyManuallyExported) + private val manualExportKeysActivityResultLauncher = registerStartForActivityResult { + if (it.resultCode == Activity.RESULT_OK) { + val uri = it.data?.data + if (uri != null) { + activity?.let { activity -> + ExportKeysDialog().show(activity, object : ExportKeysDialog.ExportKeyDialogListener { + override fun onPassphrase(passphrase: String) { + viewModel.handle(SignoutCheckViewModel.Actions.ExportKeys(passphrase, uri)) + } + }) } } } } + + private val setupBackupActivityResultLauncher = registerStartForActivityResult { + if (it.resultCode == Activity.RESULT_OK) { + if (it.data?.getBooleanExtra(KeysBackupSetupActivity.MANUAL_EXPORT, false) == true) { + viewModel.handle(SignoutCheckViewModel.Actions.KeySuccessfullyManuallyExported) + } + } + } } diff --git a/vector/src/main/res/layout/bottom_sheet_bootstrap.xml b/vector/src/main/res/layout/bottom_sheet_bootstrap.xml index 0011bf1987..d3dfe09842 100644 --- a/vector/src/main/res/layout/bottom_sheet_bootstrap.xml +++ b/vector/src/main/res/layout/bottom_sheet_bootstrap.xml @@ -24,9 +24,10 @@ android:importantForAccessibility="no" android:scaleType="fitCenter" android:src="@drawable/ic_security_key_24dp" - android:tint="?riotx_text_primary" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintTop_toTopOf="parent" + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> + app:tint="?colorAccent" + tools:ignore="MissingPrefix" /> + app:tint="?colorAccent" + tools:ignore="MissingPrefix" /> + tools:ignore="MissingConstraints,MissingPrefix" + tools:src="@drawable/ic_edit" + app:tint="?riotx_text_primary" /> + tools:visibility="visible" + tools:ignore="MissingPrefix" /> + app:layout_constraintStart_toEndOf="@id/composerEditText" + tools:ignore="MissingPrefix" /> + app:layout_constraintStart_toEndOf="@id/attachmentButton" + tools:ignore="MissingPrefix" /> + tools:src="@drawable/ic_edit" + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> + app:layout_constraintTop_toTopOf="@id/composer_related_message_preview" + tools:ignore="MissingPrefix" /> + app:layout_constraintTop_toBottomOf="parent" + tools:ignore="MissingPrefix" /> + app:layout_constraintVertical_bias="1" + tools:ignore="MissingPrefix" /> + app:layout_constraintTop_toTopOf="parent" + app:tint="@color/riotx_accent" + tools:ignore="MissingPrefix" /> + app:tint="?attr/colorAccent" + tools:ignore="MissingPrefix" /> diff --git a/vector/src/main/res/layout/dialog_disclaimer_content.xml b/vector/src/main/res/layout/dialog_disclaimer_content.xml index 541b75e7c7..89fa1395b9 100644 --- a/vector/src/main/res/layout/dialog_disclaimer_content.xml +++ b/vector/src/main/res/layout/dialog_disclaimer_content.xml @@ -26,7 +26,8 @@ android:layout_height="wrap_content" android:layout_margin="32dp" android:src="@drawable/ic_arrow_right" - android:tint="?riotx_text_secondary" /> + app:tint="?riotx_text_secondary" + tools:ignore="MissingPrefix" /> + app:layout_constraintTop_toTopOf="@id/exportDialogTil" + app:tint="?attr/colorAccent" + tools:ignore="MissingPrefix" /> + app:layout_constraintTop_toTopOf="@id/importDialogTil" + app:tint="?attr/colorAccent" + tools:ignore="MissingPrefix" /> + android:text="@string/no_sticker_application_dialog_content" + app:drawableBottomCompat="@drawable/stickerpack_rabbit" /> diff --git a/vector/src/main/res/layout/dialog_prompt_password.xml b/vector/src/main/res/layout/dialog_prompt_password.xml index 081ce9cdae..8d96d8c68e 100644 --- a/vector/src/main/res/layout/dialog_prompt_password.xml +++ b/vector/src/main/res/layout/dialog_prompt_password.xml @@ -56,8 +56,9 @@ android:background="?attr/selectableItemBackground" android:scaleType="center" android:src="@drawable/ic_eye" - android:tint="?attr/colorAccent" - tools:contentDescription="@string/a11y_show_password" /> + tools:contentDescription="@string/a11y_show_password" + app:tint="?attr/colorAccent" + tools:ignore="MissingPrefix" /> diff --git a/vector/src/main/res/layout/dialog_recovery_key_saved_info.xml b/vector/src/main/res/layout/dialog_recovery_key_saved_info.xml index 36c9a68077..b131d45495 100644 --- a/vector/src/main/res/layout/dialog_recovery_key_saved_info.xml +++ b/vector/src/main/res/layout/dialog_recovery_key_saved_info.xml @@ -1,5 +1,6 @@ + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> + app:layout_constraintTop_toTopOf="@+id/bootstrapAccountPasswordTil" + app:tint="?colorAccent" + tools:ignore="MissingPrefix" /> + android:textSize="12sp" + app:drawableStartCompat="@drawable/ic_alert_triangle" + app:drawableTint="@color/riotx_destructive_accent" /> @@ -71,10 +71,11 @@ android:background="?attr/selectableItemBackground" android:scaleType="center" android:src="@drawable/ic_eye" - android:tint="?colorAccent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@+id/ssss_passphrase_enter_til" - app:layout_constraintTop_toTopOf="@+id/ssss_passphrase_enter_til" /> + app:layout_constraintTop_toTopOf="@+id/ssss_passphrase_enter_til" + app:tint="?colorAccent" + tools:ignore="MissingPrefix" /> + app:layout_constraintTop_toTopOf="@+id/bootstrapRecoveryKeyEnterTil" + app:tint="?colorAccent" + tools:ignore="MissingPrefix" /> + android:textSize="14sp" + app:drawableStartCompat="@drawable/ic_warning_badge" /> diff --git a/vector/src/main/res/layout/fragment_deactivate_account.xml b/vector/src/main/res/layout/fragment_deactivate_account.xml index a03f4dba00..db85c607e1 100644 --- a/vector/src/main/res/layout/fragment_deactivate_account.xml +++ b/vector/src/main/res/layout/fragment_deactivate_account.xml @@ -86,8 +86,9 @@ android:background="?attr/selectableItemBackground" android:scaleType="center" android:src="@drawable/ic_eye" - android:tint="?attr/colorAccent" - tools:contentDescription="@string/a11y_show_password" /> + tools:contentDescription="@string/a11y_show_password" + app:tint="?attr/colorAccent" + tools:ignore="MissingPrefix" /> diff --git a/vector/src/main/res/layout/fragment_home_drawer.xml b/vector/src/main/res/layout/fragment_home_drawer.xml index 12562bc98a..d34ea0b815 100644 --- a/vector/src/main/res/layout/fragment_home_drawer.xml +++ b/vector/src/main/res/layout/fragment_home_drawer.xml @@ -24,9 +24,10 @@ android:layout_height="@dimen/layout_touch_size" android:scaleType="center" android:src="@drawable/ic_settings_x" - android:tint="@color/riotx_accent" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintTop_toTopOf="parent" + app:tint="@color/riotx_accent" + tools:ignore="MissingPrefix" /> + app:layout_constraintTop_toTopOf="parent" + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> + app:layout_constraintTop_toTopOf="@id/keys_backup_key_enter_til" + app:tint="?attr/colorAccent" + tools:ignore="MissingPrefix" /> + app:layout_constraintTop_toTopOf="parent" + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> + app:layout_constraintTop_toTopOf="@+id/keys_backup_passphrase_enter_til" + app:tint="?colorAccent" + tools:ignore="MissingPrefix" /> + app:layout_constraintTop_toTopOf="parent" + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> + app:layout_constraintVertical_chainStyle="spread" + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> + app:layout_constraintTop_toTopOf="@id/keys_backup_passphrase_enter_til" + app:tint="?attr/colorAccent" + tools:ignore="MissingPrefix" /> + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> + tools:src="@drawable/ic_logo_matrix_org" + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> + tools:contentDescription="@string/a11y_show_password" + app:tint="?attr/colorAccent" + tools:ignore="MissingPrefix" /> diff --git a/vector/src/main/res/layout/fragment_login_reset_password.xml b/vector/src/main/res/layout/fragment_login_reset_password.xml index 341435bdc4..5da5298b3d 100644 --- a/vector/src/main/res/layout/fragment_login_reset_password.xml +++ b/vector/src/main/res/layout/fragment_login_reset_password.xml @@ -84,8 +84,9 @@ android:background="?attr/selectableItemBackground" android:scaleType="center" android:src="@drawable/ic_eye" - android:tint="?attr/colorAccent" - tools:contentDescription="@string/a11y_show_password" /> + tools:contentDescription="@string/a11y_show_password" + app:tint="?attr/colorAccent" + tools:ignore="MissingPrefix" /> diff --git a/vector/src/main/res/layout/fragment_login_server_selection.xml b/vector/src/main/res/layout/fragment_login_server_selection.xml index 00fcfa9183..b2046b73ee 100644 --- a/vector/src/main/res/layout/fragment_login_server_selection.xml +++ b/vector/src/main/res/layout/fragment_login_server_selection.xml @@ -62,11 +62,12 @@ android:layout_height="wrap_content" android:importantForAccessibility="no" android:src="@drawable/ic_logo_matrix_org" - android:tint="?riotx_text_primary" app:layout_constraintBottom_toTopOf="@+id/loginServerChoiceMatrixOrgText" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" - app:layout_constraintVertical_chainStyle="packed" /> + app:layout_constraintVertical_chainStyle="packed" + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> + app:layout_constraintVertical_chainStyle="packed" + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> + tools:visibility="visible" + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix,UnknownId" /> + app:tint="?colorAccent" + tools:ignore="MissingPrefix" /> @@ -93,9 +95,10 @@ android:layout_marginStart="2dp" android:importantForAccessibility="no" android:src="@drawable/ic_login_splash_message_circle" - android:tint="?riotx_text_secondary" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="@+id/loginSplashText1" /> + app:layout_constraintTop_toTopOf="@+id/loginSplashText1" + app:tint="?riotx_text_secondary" + tools:ignore="MissingPrefix" /> + app:layout_constraintTop_toTopOf="@+id/loginSplashText2" + app:tint="?riotx_text_secondary" + tools:ignore="MissingPrefix" /> + app:layout_constraintTop_toTopOf="@+id/loginSplashText3" + app:tint="?riotx_text_secondary" + tools:ignore="MissingPrefix" /> + app:layout_constraintTop_toTopOf="@+id/ssss_restore_with_key" + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> + app:layout_constraintTop_toTopOf="@+id/ssss_restore_with_passphrase" + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> + app:layout_constraintTop_toTopOf="@+id/ssss_passphrase_enter_til" + app:tint="?colorAccent" + tools:ignore="MissingPrefix" /> diff --git a/vector/src/main/res/layout/fragment_ssss_reset_all.xml b/vector/src/main/res/layout/fragment_ssss_reset_all.xml index a3b2984bce..d64750eef3 100644 --- a/vector/src/main/res/layout/fragment_ssss_reset_all.xml +++ b/vector/src/main/res/layout/fragment_ssss_reset_all.xml @@ -16,16 +16,16 @@ android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginTop="16dp" - android:drawableStart="@drawable/ic_alert_triangle" android:drawablePadding="8dp" - android:drawableTint="?riot_primary_text_color" android:text="@string/secure_backup_reset_all" android:textColor="?riotx_text_primary" android:textSize="20sp" android:textStyle="bold" android:tint="?riot_primary_text_color" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintTop_toTopOf="parent" + app:drawableTint="?riot_primary_text_color" + app:drawableStartCompat="@drawable/ic_alert_triangle" /> + tools:visibility="visible" + app:drawableStartCompat="@drawable/ic_smartphone" /> + tools:src="@drawable/ic_room_actions_notifications_all" + app:tint="?riotx_text_secondary" + tools:ignore="MissingPrefix" /> + tools:visibility="visible" + app:tint="@color/riotx_accent" + tools:ignore="MissingPrefix" /> diff --git a/vector/src/main/res/layout/item_bottom_sheet_message_status.xml b/vector/src/main/res/layout/item_bottom_sheet_message_status.xml index 742fa69a7b..fcb373fa0d 100644 --- a/vector/src/main/res/layout/item_bottom_sheet_message_status.xml +++ b/vector/src/main/res/layout/item_bottom_sheet_message_status.xml @@ -1,5 +1,6 @@ + tools:text="@string/unable_to_send_message" + app:drawableStartCompat="@drawable/ic_warning_badge" /> diff --git a/vector/src/main/res/layout/item_bottom_sheet_room_preview.xml b/vector/src/main/res/layout/item_bottom_sheet_room_preview.xml index bed1d5eb2f..74781468b2 100644 --- a/vector/src/main/res/layout/item_bottom_sheet_room_preview.xml +++ b/vector/src/main/res/layout/item_bottom_sheet_room_preview.xml @@ -64,9 +64,10 @@ android:contentDescription="@string/room_list_quick_actions_settings" android:scaleType="centerInside" android:src="@drawable/ic_room_actions_settings" - android:tint="?riotx_text_secondary" app:layout_constraintBottom_toBottomOf="@+id/bottomSheetRoomPreviewAvatar" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toTopOf="@id/bottomSheetRoomPreviewAvatar" /> + app:layout_constraintTop_toTopOf="@id/bottomSheetRoomPreviewAvatar" + app:tint="?riotx_text_secondary" + tools:ignore="MissingPrefix" /> diff --git a/vector/src/main/res/layout/item_create_direct_room_user.xml b/vector/src/main/res/layout/item_create_direct_room_user.xml index fa7e742584..108c02cd02 100644 --- a/vector/src/main/res/layout/item_create_direct_room_user.xml +++ b/vector/src/main/res/layout/item_create_direct_room_user.xml @@ -32,8 +32,9 @@ android:layout_height="40dp" android:scaleType="centerInside" android:src="@drawable/ic_material_done" - android:tint="@android:color/white" - android:visibility="visible" /> + android:visibility="visible" + app:tint="@android:color/white" + tools:ignore="MissingPrefix" /> + app:layout_constraintTop_toTopOf="parent" + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> + app:layout_constraintTop_toTopOf="parent" + app:tint="?attr/riotx_text_secondary" + tools:ignore="MissingPrefix" /> + android:visibility="visible" + app:tint="@android:color/white" + tools:ignore="MissingPrefix" /> + tools:contentDescription="@string/a11y_show_password" + app:tint="?attr/colorAccent" + tools:ignore="MissingPrefix" /> diff --git a/vector/src/main/res/layout/item_policy.xml b/vector/src/main/res/layout/item_policy.xml index b5a14b27db..fbf5e39987 100644 --- a/vector/src/main/res/layout/item_policy.xml +++ b/vector/src/main/res/layout/item_policy.xml @@ -54,9 +54,10 @@ android:paddingEnd="0dp" android:rotationY="@integer/rtl_mirror_flip" android:src="@drawable/ic_material_chevron_right_black" - android:tint="?riotx_android_secondary" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintTop_toTopOf="parent" + app:tint="?riotx_android_secondary" + tools:ignore="MissingPrefix" /> \ No newline at end of file diff --git a/vector/src/main/res/layout/item_profile_action.xml b/vector/src/main/res/layout/item_profile_action.xml index 6d3ba36d06..c928a5f59b 100644 --- a/vector/src/main/res/layout/item_profile_action.xml +++ b/vector/src/main/res/layout/item_profile_action.xml @@ -84,11 +84,12 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_arrow_right" - android:tint="?riotx_text_secondary" android:visibility="gone" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" - tools:visibility="visible" /> + tools:visibility="visible" + app:tint="?riotx_text_secondary" + tools:ignore="MissingPrefix" /> diff --git a/vector/src/main/res/layout/item_profile_matrix_item.xml b/vector/src/main/res/layout/item_profile_matrix_item.xml index 1c84a65691..f35141576c 100644 --- a/vector/src/main/res/layout/item_profile_matrix_item.xml +++ b/vector/src/main/res/layout/item_profile_matrix_item.xml @@ -80,10 +80,11 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_arrow_right" - android:tint="?riotx_text_secondary" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintTop_toTopOf="parent" + app:tint="?riotx_text_secondary" + tools:ignore="MissingPrefix" /> diff --git a/vector/src/main/res/layout/item_profile_matrix_item_progress.xml b/vector/src/main/res/layout/item_profile_matrix_item_progress.xml index 66cff4840e..69e4b40bb4 100644 --- a/vector/src/main/res/layout/item_profile_matrix_item_progress.xml +++ b/vector/src/main/res/layout/item_profile_matrix_item_progress.xml @@ -92,11 +92,12 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_arrow_right" - android:tint="?riotx_text_secondary" android:visibility="visible" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" - tools:visibility="visible" /> + tools:visibility="visible" + app:tint="?riotx_text_secondary" + tools:ignore="MissingPrefix" /> diff --git a/vector/src/main/res/layout/item_public_room.xml b/vector/src/main/res/layout/item_public_room.xml index ae8287fdc4..bb9d379f31 100644 --- a/vector/src/main/res/layout/item_public_room.xml +++ b/vector/src/main/res/layout/item_public_room.xml @@ -65,9 +65,7 @@ android:layout_height="wrap_content" android:layout_marginTop="2dp" android:layout_marginEnd="8dp" - android:drawableStart="@drawable/ic_user" android:drawablePadding="8dp" - android:drawableTint="?riotx_text_secondary" android:gravity="center_vertical" android:minWidth="56dp" android:textColor="?riotx_text_secondary" @@ -75,7 +73,9 @@ app:layout_constraintBottom_toTopOf="@+id/itemPublicRoomBottomSeparator" app:layout_constraintStart_toStartOf="@+id/itemPublicRoomName" app:layout_constraintTop_toBottomOf="@+id/itemPublicRoomTopic" - tools:text="148" /> + tools:text="148" + app:drawableTint="?riotx_text_secondary" + app:drawableStartCompat="@drawable/ic_user" /> + android:visibility="visible" + app:tint="@android:color/white" + tools:ignore="MissingPrefix" /> @@ -105,12 +106,13 @@ android:layout_marginEnd="4dp" android:src="@drawable/ic_edit" android:visibility="gone" - android:tint="?riotx_text_primary" app:layout_constraintBottom_toBottomOf="@+id/roomNameView" app:layout_constraintEnd_toStartOf="@+id/roomUnreadCounterBadgeView" app:layout_constraintStart_toEndOf="@+id/roomNameView" app:layout_constraintTop_toTopOf="@+id/roomNameView" - tools:visibility="visible" /> + tools:visibility="visible" + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> + tools:text="@string/room_participants_header_direct_chats" + app:drawableTint="?riotx_text_secondary" /> + tools:text="@sample/matrix.json/data/displayName" + app:drawableEndCompat="@drawable/ic_arrow_right" /> + tools:src="@drawable/ic_phone" + app:tint="?riotx_text_secondary" + tools:ignore="MissingPrefix" /> + app:layout_constraintTop_toTopOf="parent" + app:tint="@color/riotx_destructive_accent" + tools:ignore="MissingPrefix" /> \ No newline at end of file diff --git a/vector/src/main/res/layout/item_signout_action.xml b/vector/src/main/res/layout/item_signout_action.xml index 16d10a3ff0..10292beb55 100644 --- a/vector/src/main/res/layout/item_signout_action.xml +++ b/vector/src/main/res/layout/item_signout_action.xml @@ -1,5 +1,7 @@ + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> @@ -12,11 +13,11 @@ android:layout_marginTop="16dp" android:layout_marginBottom="16dp" android:background="?attr/riotx_keys_backup_banner_accent_color" - android:drawableStart="@drawable/error" android:drawablePadding="16dp" android:gravity="center|start" android:minHeight="80dp" android:padding="16dp" - tools:text="This room is continuation…" /> + tools:text="This room is continuation…" + app:drawableStartCompat="@drawable/error" /> \ No newline at end of file diff --git a/vector/src/main/res/layout/item_timeline_event_merged_utd_stub.xml b/vector/src/main/res/layout/item_timeline_event_merged_utd_stub.xml index 3f0c269e90..538a8930c4 100644 --- a/vector/src/main/res/layout/item_timeline_event_merged_utd_stub.xml +++ b/vector/src/main/res/layout/item_timeline_event_merged_utd_stub.xml @@ -25,13 +25,13 @@ android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginStart="8dp" - android:drawableStart="@drawable/ic_clock" android:drawablePadding="2dp" android:gravity="start" android:text="@string/notice_crypto_unable_to_decrypt_merged" android:textColor="?riotx_text_secondary" android:textSize="15sp" - app:drawableTint="?riotx_text_secondary" /> + app:drawableTint="?riotx_text_secondary" + app:drawableStartCompat="@drawable/ic_clock" /> + app:tint="?riotx_text_secondary" + tools:ignore="MissingPrefix" /> + app:tint="@color/riotx_accent" + tools:ignore="MissingPrefix" /> \ No newline at end of file + app:drawableTint="?riotx_text_primary_body_contrast" + app:drawableStartCompat="@drawable/ic_trash_16" /> \ No newline at end of file diff --git a/vector/src/main/res/layout/item_timeline_event_verification_stub.xml b/vector/src/main/res/layout/item_timeline_event_verification_stub.xml index 41a5c37042..cdfbd0add2 100644 --- a/vector/src/main/res/layout/item_timeline_event_verification_stub.xml +++ b/vector/src/main/res/layout/item_timeline_event_verification_stub.xml @@ -1,5 +1,6 @@ + tools:text="@string/verification_request" + app:drawableStartCompat="@drawable/ic_shield_black" /> + app:layout_constraintTop_toTopOf="parent" + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> \ No newline at end of file diff --git a/vector/src/main/res/layout/item_uploads_file.xml b/vector/src/main/res/layout/item_uploads_file.xml index 58edcc78a4..5c40327d95 100644 --- a/vector/src/main/res/layout/item_uploads_file.xml +++ b/vector/src/main/res/layout/item_uploads_file.xml @@ -13,10 +13,11 @@ android:layout_height="wrap_content" android:layout_marginStart="@dimen/layout_horizontal_margin" android:src="@drawable/ic_file" - android:tint="?riotx_text_primary" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintTop_toTopOf="parent" + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> + app:layout_constraintTop_toTopOf="parent" + app:tint="?colorAccent" + tools:ignore="MissingPrefix" /> + app:layout_constraintTop_toTopOf="parent" + app:tint="?colorAccent" + tools:ignore="MissingPrefix" /> \ No newline at end of file diff --git a/vector/src/main/res/layout/item_verification_action.xml b/vector/src/main/res/layout/item_verification_action.xml index 263a8ff107..ae49893792 100644 --- a/vector/src/main/res/layout/item_verification_action.xml +++ b/vector/src/main/res/layout/item_verification_action.xml @@ -20,13 +20,14 @@ android:layout_width="48dp" android:layout_height="48dp" android:scaleType="center" - android:tint="?riotx_text_primary" android:visibility="gone" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" tools:src="@drawable/ic_share" - tools:visibility="visible" /> + tools:visibility="visible" + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> + tools:src="@drawable/ic_arrow_right" + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> diff --git a/vector/src/main/res/layout/item_verification_wait.xml b/vector/src/main/res/layout/item_verification_wait.xml index 6cfc497b16..675af87cbf 100644 --- a/vector/src/main/res/layout/item_verification_wait.xml +++ b/vector/src/main/res/layout/item_verification_wait.xml @@ -1,6 +1,7 @@ + app:layout_constraintTop_toBottomOf="@+id/use_latest_riot" + app:tint="?vctr_notice_secondary" + tools:ignore="MissingPrefix" /> + app:layout_constraintTop_toBottomOf="@+id/use_latest_riot" + app:tint="?vctr_notice_secondary" + tools:ignore="MissingPrefix" /> + tools:ignore="MissingConstraints,MissingPrefix" + app:tint="?riotx_text_primary" /> + app:srcCompat="@drawable/ic_back_24dp" + app:tint="@color/white" + tools:ignore="MissingPrefix" /> + app:srcCompat="@drawable/ic_share" + app:tint="@color/white" + tools:ignore="MissingPrefix" /> + app:srcCompat="@drawable/ic_play_arrow" + app:tint="@color/white" + tools:ignore="MissingPrefix" /> + app:drawableTint="@color/white" + app:drawableStartCompat="@drawable/ic_call" /> + tools:text="@string/ongoing_conference_call" + app:drawableStartCompat="@drawable/ic_call" /> + tools:ignore="MissingConstraints,MissingPrefix" + app:tint="@color/white" /> + tools:ignore="MissingConstraints,MissingPrefix" + app:tint="@color/white" /> + tools:ignore="MissingConstraints,MissingPrefix" + app:tint="?attr/riotx_text_primary" /> + tools:ignore="MissingConstraints,MissingPrefix" + tools:src="@drawable/ic_microphone_on" + app:tint="?attr/riotx_text_primary" /> + tools:ignore="MissingConstraints,MissingPrefix" + app:tint="@color/white" /> + tools:ignore="MissingConstraints,MissingPrefix" + app:tint="?attr/riotx_text_primary" /> + tools:ignore="MissingConstraints,MissingPrefix" + app:tint="?attr/riotx_text_primary" /> + tools:src="@drawable/ic_paperclip" + app:tint="?vctr_notice_secondary" + tools:ignore="MissingPrefix" /> \ No newline at end of file diff --git a/vector/src/main/res/layout/view_jump_to_read_marker.xml b/vector/src/main/res/layout/view_jump_to_read_marker.xml index 019480e6fe..06db3f7af7 100644 --- a/vector/src/main/res/layout/view_jump_to_read_marker.xml +++ b/vector/src/main/res/layout/view_jump_to_read_marker.xml @@ -1,5 +1,6 @@ + android:textColor="@color/white" + app:drawableStartCompat="@drawable/arrow_up_circle" /> + app:layout_constraintTop_toTopOf="parent" + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> + tools:src="@drawable/ic_noun_party_popper" + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" />