mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-23 18:05:59 +03:00
Merge pull request #2217 from vector-im/feature/bma/kotlin_version
Some upgrade
This commit is contained in:
commit
1fd24e746c
183 changed files with 1437 additions and 1420 deletions
|
@ -30,6 +30,10 @@ SDK API changes ⚠️:
|
||||||
Build 🧱:
|
Build 🧱:
|
||||||
- Use Update Gradle Wrapper Action
|
- Use Update Gradle Wrapper Action
|
||||||
- Updates Gradle Wrapper from 5.6.4 to 6.6.1. (#2193)
|
- 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:
|
Other changes:
|
||||||
- Added registration/verification automated UI tests
|
- Added registration/verification automated UI tests
|
||||||
|
|
|
@ -58,21 +58,16 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
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:rxkotlin:2.3.0'
|
||||||
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
|
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
|
||||||
|
|
||||||
implementation fileTree(dir: "libs", include: ["*.jar"])
|
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||||
implementation 'androidx.core:core-ktx:1.3.0'
|
implementation 'androidx.core:core-ktx:1.3.2'
|
||||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||||
implementation 'com.google.android.material:material:1.1.0'
|
implementation "androidx.fragment:fragment:1.3.0-beta01"
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
implementation "androidx.recyclerview:recyclerview:1.1.0"
|
||||||
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 'com.google.android.material:material:1.2.1'
|
||||||
}
|
}
|
13
build.gradle
13
build.gradle
|
@ -1,7 +1,9 @@
|
||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
|
|
||||||
buildscript {
|
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 {
|
repositories {
|
||||||
google()
|
google()
|
||||||
jcenter()
|
jcenter()
|
||||||
|
@ -10,10 +12,8 @@ buildscript {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
// Warning: 3.6.3 leads to infinite gradle builds. Stick to 3.5.3 for the moment
|
classpath 'com.android.tools.build:gradle:4.0.1'
|
||||||
classpath 'com.android.tools.build:gradle:3.5.3'
|
classpath 'com.google.gms:google-services:4.3.4'
|
||||||
classpath 'com.google.gms:google-services:4.3.2'
|
|
||||||
classpath "com.airbnb.okreplay:gradle-plugin:1.5.0"
|
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.7.1'
|
classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.7.1'
|
||||||
classpath 'com.google.android.gms:oss-licenses-plugin:0.10.2'
|
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 {
|
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
|
||||||
// Warnings are potential errors, so stop ignoring them
|
// 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
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,8 @@ android {
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(":matrix-sdk-android")
|
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:rxkotlin:2.3.0'
|
||||||
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
|
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
|
||||||
// Paging
|
// Paging
|
||||||
|
@ -43,8 +44,4 @@ dependencies {
|
||||||
|
|
||||||
// Logging
|
// Logging
|
||||||
implementation 'com.jakewharton.timber:timber:4.7.1'
|
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'
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ apply plugin: 'kotlin-android'
|
||||||
apply plugin: 'kotlin-kapt'
|
apply plugin: 'kotlin-kapt'
|
||||||
apply plugin: 'kotlin-android-extensions'
|
apply plugin: 'kotlin-android-extensions'
|
||||||
apply plugin: 'realm-android'
|
apply plugin: 'realm-android'
|
||||||
apply plugin: 'okreplay'
|
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
repositories {
|
repositories {
|
||||||
|
@ -109,21 +108,21 @@ static def gitRevisionDate() {
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
def arrow_version = "0.8.2"
|
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 lifecycle_version = '2.2.0'
|
||||||
def arch_version = '2.1.0'
|
def arch_version = '2.1.0'
|
||||||
def coroutines_version = "1.3.8"
|
|
||||||
def markwon_version = '3.1.0'
|
def markwon_version = '3.1.0'
|
||||||
def daggerVersion = '2.25.4'
|
def daggerVersion = '2.29.1'
|
||||||
def work_version = '2.4.0'
|
def work_version = '2.4.0'
|
||||||
def retrofit_version = '2.6.2'
|
def retrofit_version = '2.6.2'
|
||||||
|
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version"
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version"
|
||||||
|
|
||||||
implementation "androidx.appcompat:appcompat:1.2.0"
|
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-extensions:$lifecycle_version"
|
||||||
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
|
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
|
||||||
|
@ -143,7 +142,7 @@ dependencies {
|
||||||
implementation "ru.noties.markwon:core:$markwon_version"
|
implementation "ru.noties.markwon:core:$markwon_version"
|
||||||
|
|
||||||
// Image
|
// Image
|
||||||
implementation 'androidx.exifinterface:exifinterface:1.3.0-alpha01'
|
implementation 'androidx.exifinterface:exifinterface:1.3.0'
|
||||||
|
|
||||||
// Database
|
// Database
|
||||||
implementation 'com.github.Zhuinden:realm-monarchy:0.5.1'
|
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
|
// 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')
|
implementation('com.facebook.react:react-native-webrtc:1.84.0-jitsi-5112273@aar')
|
||||||
|
|
||||||
debugImplementation 'com.airbnb.okreplay:okreplay:1.5.0'
|
testImplementation 'junit:junit:4.13'
|
||||||
releaseImplementation 'com.airbnb.okreplay:noop:1.5.0'
|
|
||||||
androidTestImplementation 'com.airbnb.okreplay:espresso:1.5.0'
|
|
||||||
|
|
||||||
testImplementation 'junit:junit:4.12'
|
|
||||||
testImplementation 'org.robolectric:robolectric:4.3'
|
testImplementation 'org.robolectric:robolectric:4.3'
|
||||||
//testImplementation 'org.robolectric:shadows-support-v4:3.0'
|
//testImplementation 'org.robolectric:shadows-support-v4:3.0'
|
||||||
// Note: version sticks to 1.9.2 due to https://github.com/mockk/mockk/issues/281
|
// 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 'io.mockk:mockk:1.9.2.kotlin12'
|
||||||
testImplementation 'org.amshove.kluent:kluent-android:1.44'
|
testImplementation 'org.amshove.kluent:kluent-android:1.61'
|
||||||
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
|
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version"
|
||||||
// Plant Timber tree for test
|
// Plant Timber tree for test
|
||||||
testImplementation 'net.lachlanmckee:timber-junit-rule:1.0.1'
|
testImplementation 'net.lachlanmckee:timber-junit-rule:1.0.1'
|
||||||
|
|
||||||
kaptAndroidTest "com.google.dagger:dagger-compiler:$daggerVersion"
|
kaptAndroidTest "com.google.dagger:dagger-compiler:$daggerVersion"
|
||||||
androidTestImplementation 'androidx.test:core:1.2.0'
|
androidTestImplementation 'androidx.test:core:1.3.0'
|
||||||
androidTestImplementation 'androidx.test:runner:1.2.0'
|
androidTestImplementation 'androidx.test:runner:1.3.0'
|
||||||
androidTestImplementation 'androidx.test:rules:1.2.0'
|
androidTestImplementation 'androidx.test:rules:1.3.0'
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
|
||||||
androidTestImplementation 'org.amshove.kluent:kluent-android:1.44'
|
androidTestImplementation 'org.amshove.kluent:kluent-android:1.61'
|
||||||
// Note: version sticks to 1.9.2 due to https://github.com/mockk/mockk/issues/281
|
// 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 'io.mockk:mockk-android:1.9.2.kotlin12'
|
||||||
androidTestImplementation "androidx.arch.core:core-testing:$arch_version"
|
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
|
// Plant Timber tree for test
|
||||||
androidTestImplementation 'net.lachlanmckee:timber-junit-rule:1.0.1'
|
androidTestImplementation 'net.lachlanmckee:timber-junit-rule:1.0.1'
|
||||||
|
|
||||||
androidTestUtil 'androidx.test:orchestrator:1.2.0'
|
androidTestUtil 'androidx.test:orchestrator:1.3.0'
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,9 +20,11 @@ package org.matrix.android.sdk.api.session.content
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import androidx.exifinterface.media.ExifInterface
|
import androidx.exifinterface.media.ExifInterface
|
||||||
|
import com.squareup.moshi.JsonClass
|
||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.android.parcel.Parcelize
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
data class ContentAttachmentData(
|
data class ContentAttachmentData(
|
||||||
val size: Long = 0,
|
val size: Long = 0,
|
||||||
val duration: Long? = 0,
|
val duration: Long? = 0,
|
||||||
|
@ -32,10 +34,11 @@ data class ContentAttachmentData(
|
||||||
val exifOrientation: Int = ExifInterface.ORIENTATION_UNDEFINED,
|
val exifOrientation: Int = ExifInterface.ORIENTATION_UNDEFINED,
|
||||||
val name: String? = null,
|
val name: String? = null,
|
||||||
val queryUri: Uri,
|
val queryUri: Uri,
|
||||||
private val mimeType: String?,
|
val mimeType: String?,
|
||||||
val type: Type
|
val type: Type
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = false)
|
||||||
enum class Type {
|
enum class Type {
|
||||||
FILE,
|
FILE,
|
||||||
IMAGE,
|
IMAGE,
|
||||||
|
|
|
@ -18,7 +18,9 @@
|
||||||
package org.matrix.android.sdk.api.session.room.model
|
package org.matrix.android.sdk.api.session.room.model
|
||||||
|
|
||||||
import com.squareup.moshi.Json
|
import com.squareup.moshi.Json
|
||||||
|
import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
data class Signed(
|
data class Signed(
|
||||||
@Json(name = "token") val token: String,
|
@Json(name = "token") val token: String,
|
||||||
@Json(name = "signatures") val signatures: Any,
|
@Json(name = "signatures") val signatures: Any,
|
||||||
|
|
|
@ -105,6 +105,6 @@ private fun Versions.doesServerSeparatesAddAndBind(): Boolean {
|
||||||
private fun Versions.getMaxVersion(): HomeServerVersion {
|
private fun Versions.getMaxVersion(): HomeServerVersion {
|
||||||
return supportedVersions
|
return supportedVersions
|
||||||
?.mapNotNull { HomeServerVersion.parse(it) }
|
?.mapNotNull { HomeServerVersion.parse(it) }
|
||||||
?.max()
|
?.maxOrNull()
|
||||||
?: HomeServerVersion.r0_0_0
|
?: HomeServerVersion.r0_0_0
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto
|
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.auth.data.Credentials
|
||||||
import org.matrix.android.sdk.api.crypto.MXCryptoConfig
|
import org.matrix.android.sdk.api.crypto.MXCryptoConfig
|
||||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME
|
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.session.SessionScope
|
||||||
import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
|
import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
|
||||||
import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
|
import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -327,7 +327,9 @@ internal class IncomingGossipingRequestManager @Inject constructor(
|
||||||
val params = SendGossipWorker.Params(
|
val params = SendGossipWorker.Params(
|
||||||
sessionId = sessionId,
|
sessionId = sessionId,
|
||||||
secretValue = secretValue,
|
secretValue = secretValue,
|
||||||
request = request
|
requestUserId = request.userId,
|
||||||
|
requestDeviceId = request.deviceId,
|
||||||
|
requestId = request.requestId
|
||||||
)
|
)
|
||||||
|
|
||||||
cryptoStore.updateGossipingRequestState(request, GossipingRequestState.ACCEPTING)
|
cryptoStore.updateGossipingRequestState(request, GossipingRequestState.ACCEPTING)
|
||||||
|
@ -351,7 +353,9 @@ internal class IncomingGossipingRequestManager @Inject constructor(
|
||||||
val params = SendGossipWorker.Params(
|
val params = SendGossipWorker.Params(
|
||||||
sessionId = userId,
|
sessionId = userId,
|
||||||
secretValue = secretValue,
|
secretValue = secretValue,
|
||||||
request = request
|
requestUserId = request.userId,
|
||||||
|
requestDeviceId = request.deviceId,
|
||||||
|
requestId = request.requestId
|
||||||
)
|
)
|
||||||
|
|
||||||
cryptoStore.updateGossipingRequestState(request, GossipingRequestState.ACCEPTING)
|
cryptoStore.updateGossipingRequestState(request, GossipingRequestState.ACCEPTING)
|
||||||
|
|
|
@ -22,7 +22,7 @@ import org.matrix.android.sdk.api.session.events.model.toModel
|
||||||
import org.matrix.android.sdk.internal.crypto.model.rest.SecretShareRequest
|
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(
|
data class IncomingSecretShareRequest(
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -47,7 +47,9 @@ internal class SendGossipWorker(context: Context,
|
||||||
internal data class Params(
|
internal data class Params(
|
||||||
override val sessionId: String,
|
override val sessionId: String,
|
||||||
val secretValue: String,
|
val secretValue: String,
|
||||||
val request: IncomingSecretShareRequest,
|
val requestUserId: String?,
|
||||||
|
val requestDeviceId: String?,
|
||||||
|
val requestId: String?,
|
||||||
override val lastFailureMessage: String? = null
|
override val lastFailureMessage: String? = null
|
||||||
) : SessionWorkerParams
|
) : SessionWorkerParams
|
||||||
|
|
||||||
|
@ -67,16 +69,21 @@ internal class SendGossipWorker(context: Context,
|
||||||
val eventType: String = EventType.SEND_SECRET
|
val eventType: String = EventType.SEND_SECRET
|
||||||
|
|
||||||
val toDeviceContent = SecretSendEventContent(
|
val toDeviceContent = SecretSendEventContent(
|
||||||
requestId = params.request.requestId ?: "",
|
requestId = params.requestId ?: "",
|
||||||
secretValue = params.secretValue
|
secretValue = params.secretValue
|
||||||
)
|
)
|
||||||
|
|
||||||
val requestingUserId = params.request.userId ?: ""
|
val requestingUserId = params.requestUserId ?: ""
|
||||||
val requestingDeviceId = params.request.deviceId ?: ""
|
val requestingDeviceId = params.requestDeviceId ?: ""
|
||||||
val deviceInfo = cryptoStore.getUserDevice(requestingUserId, requestingDeviceId)
|
val deviceInfo = cryptoStore.getUserDevice(requestingUserId, requestingDeviceId)
|
||||||
?: return buildErrorResult(params, "Unknown deviceInfo, cannot send message").also {
|
?: return buildErrorResult(params, "Unknown deviceInfo, cannot send message").also {
|
||||||
cryptoStore.updateGossipingRequestState(params.request, GossipingRequestState.FAILED_TO_ACCEPTED)
|
cryptoStore.updateGossipingRequestState(
|
||||||
Timber.e("Unknown deviceInfo, cannot send message, sessionId: ${params.request.deviceId}")
|
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<Any>()
|
val sendToDeviceMap = MXUsersDevicesMap<Any>()
|
||||||
|
@ -88,7 +95,12 @@ internal class SendGossipWorker(context: Context,
|
||||||
// no session with this device, probably because there
|
// no session with this device, probably because there
|
||||||
// were no one-time keys.
|
// were no one-time keys.
|
||||||
return buildErrorResult(params, "no session with this device").also {
|
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.")
|
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
|
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()
|
return Result.success()
|
||||||
} catch (throwable: Throwable) {
|
} catch (throwable: Throwable) {
|
||||||
return if (throwable.shouldBeRetried()) {
|
return if (throwable.shouldBeRetried()) {
|
||||||
Result.retry()
|
Result.retry()
|
||||||
} else {
|
} 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")
|
buildErrorResult(params, throwable.localizedMessage ?: "error")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,12 @@
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto.keysbackup.model.rest
|
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(
|
data class KeysVersion(
|
||||||
// the keys backup version
|
// the keys backup version
|
||||||
var version: String? = null
|
@Json(name = "version")
|
||||||
|
val version: String? = null
|
||||||
)
|
)
|
||||||
|
|
|
@ -371,7 +371,7 @@ internal class DefaultSharedSecretStorageService @Inject constructor(
|
||||||
callback.onFailure(SharedSecretStorageError.BadKeyFormat)
|
callback.onFailure(SharedSecretStorageError.BadKeyFormat)
|
||||||
}
|
}
|
||||||
cryptoCoroutineScope.launch(coroutineDispatchers.main) {
|
cryptoCoroutineScope.launch(coroutineDispatchers.main) {
|
||||||
kotlin.runCatching {
|
runCatching {
|
||||||
// decrypt from recovery key
|
// decrypt from recovery key
|
||||||
withOlmDecryption { olmPkDecryption ->
|
withOlmDecryption { olmPkDecryption ->
|
||||||
olmPkDecryption.setPrivateKey(keySpec.privateKey)
|
olmPkDecryption.setPrivateKey(keySpec.privateKey)
|
||||||
|
@ -390,7 +390,7 @@ internal class DefaultSharedSecretStorageService @Inject constructor(
|
||||||
callback.onFailure(SharedSecretStorageError.BadKeyFormat)
|
callback.onFailure(SharedSecretStorageError.BadKeyFormat)
|
||||||
}
|
}
|
||||||
cryptoCoroutineScope.launch(coroutineDispatchers.main) {
|
cryptoCoroutineScope.launch(coroutineDispatchers.main) {
|
||||||
kotlin.runCatching {
|
runCatching {
|
||||||
decryptAesHmacSha2(keySpec, name, secretContent)
|
decryptAesHmacSha2(keySpec, name, secretContent)
|
||||||
}.foldToCallback(callback)
|
}.foldToCallback(callback)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2016 OpenMarket Ltd
|
* Copyright 2016 OpenMarket Ltd
|
||||||
* Copyright 2018 New Vector Ltd
|
* Copyright 2018 New Vector Ltd
|
||||||
|
@ -215,11 +214,12 @@ internal interface IMXCryptoStore {
|
||||||
// TODO temp
|
// TODO temp
|
||||||
fun getLiveDeviceList(): LiveData<List<CryptoDeviceInfo>>
|
fun getLiveDeviceList(): LiveData<List<CryptoDeviceInfo>>
|
||||||
|
|
||||||
fun getMyDevicesInfo() : List<DeviceInfo>
|
fun getMyDevicesInfo(): List<DeviceInfo>
|
||||||
|
|
||||||
fun getLiveMyDevicesInfo() : LiveData<List<DeviceInfo>>
|
fun getLiveMyDevicesInfo(): LiveData<List<DeviceInfo>>
|
||||||
|
|
||||||
fun saveMyDevicesInfo(info: List<DeviceInfo>)
|
fun saveMyDevicesInfo(info: List<DeviceInfo>)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store the crypto algorithm for a room.
|
* Store the crypto algorithm for a room.
|
||||||
*
|
*
|
||||||
|
@ -367,7 +367,19 @@ internal interface IMXCryptoStore {
|
||||||
|
|
||||||
fun saveGossipingEvent(event: Event)
|
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
|
* Search an IncomingRoomKeyRequest
|
||||||
|
@ -411,7 +423,7 @@ internal interface IMXCryptoStore {
|
||||||
fun getLiveCrossSigningPrivateKeys(): LiveData<Optional<PrivateKeysInfo>>
|
fun getLiveCrossSigningPrivateKeys(): LiveData<Optional<PrivateKeysInfo>>
|
||||||
|
|
||||||
fun saveBackupRecoveryKey(recoveryKey: String?, version: String?)
|
fun saveBackupRecoveryKey(recoveryKey: String?, version: String?)
|
||||||
fun getKeyBackupRecoveryKeyInfo() : SavedKeyBackupKeyInfo?
|
fun getKeyBackupRecoveryKeyInfo(): SavedKeyBackupKeyInfo?
|
||||||
|
|
||||||
fun setUserKeysAsTrusted(userId: String, trusted: Boolean = true)
|
fun setUserKeysAsTrusted(userId: String, trusted: Boolean = true)
|
||||||
fun setDeviceTrust(userId: String, deviceId: String, crossSignedVerified: Boolean, locallyVerified: Boolean?)
|
fun setDeviceTrust(userId: String, deviceId: String, crossSignedVerified: Boolean, locallyVerified: Boolean?)
|
||||||
|
@ -421,12 +433,13 @@ internal interface IMXCryptoStore {
|
||||||
fun updateUsersTrust(check: (String) -> Boolean)
|
fun updateUsersTrust(check: (String) -> Boolean)
|
||||||
|
|
||||||
fun addWithHeldMegolmSession(withHeldContent: RoomKeyWithHeldContent)
|
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 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?)
|
data class SharedSessionResult(val found: Boolean, val chainIndex: Int?)
|
||||||
fun getSharedWithInfo(roomId: String?, sessionId: String) : MXUsersDevicesMap<Int>
|
|
||||||
|
fun getSharedWithInfo(roomId: String?, sessionId: String): MXUsersDevicesMap<Int>
|
||||||
// Dev tools
|
// Dev tools
|
||||||
|
|
||||||
fun getOutgoingRoomKeyRequests(): List<OutgoingRoomKeyRequest>
|
fun getOutgoingRoomKeyRequests(): List<OutgoingRoomKeyRequest>
|
||||||
|
|
|
@ -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 ->
|
doRealmTransaction(realmConfiguration) { realm ->
|
||||||
realm.where<IncomingGossipingRequestEntity>()
|
realm.where<IncomingGossipingRequestEntity>()
|
||||||
.equalTo(IncomingGossipingRequestEntityFields.OTHER_USER_ID, request.userId)
|
.equalTo(IncomingGossipingRequestEntityFields.OTHER_USER_ID, requestUserId)
|
||||||
.equalTo(IncomingGossipingRequestEntityFields.OTHER_DEVICE_ID, request.deviceId)
|
.equalTo(IncomingGossipingRequestEntityFields.OTHER_DEVICE_ID, requestDeviceId)
|
||||||
.equalTo(IncomingGossipingRequestEntityFields.REQUEST_ID, request.requestId)
|
.equalTo(IncomingGossipingRequestEntityFields.REQUEST_ID, requestId)
|
||||||
.findAll().forEach {
|
.findAll().forEach {
|
||||||
it.requestState = state
|
it.requestState = state
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.MessageType
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationRequestContent
|
import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationRequestContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessageVideoContent
|
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.ForceToBooleanJsonAdapter
|
||||||
import org.matrix.android.sdk.internal.network.parsing.RuntimeJsonAdapterFactory
|
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
|
import org.matrix.android.sdk.internal.network.parsing.UriMoshiAdapter
|
||||||
|
|
||||||
object MoshiProvider {
|
object MoshiProvider {
|
||||||
|
@ -41,6 +43,8 @@ object MoshiProvider {
|
||||||
private val moshi: Moshi = Moshi.Builder()
|
private val moshi: Moshi = Moshi.Builder()
|
||||||
.add(UriMoshiAdapter())
|
.add(UriMoshiAdapter())
|
||||||
.add(ForceToBooleanJsonAdapter())
|
.add(ForceToBooleanJsonAdapter())
|
||||||
|
.add(CipherSuiteMoshiAdapter())
|
||||||
|
.add(TlsVersionMoshiAdapter())
|
||||||
.add(RuntimeJsonAdapterFactory.of(MessageContent::class.java, "msgtype", MessageDefaultContent::class.java)
|
.add(RuntimeJsonAdapterFactory.of(MessageContent::class.java, "msgtype", MessageDefaultContent::class.java)
|
||||||
.registerSubtype(MessageTextContent::class.java, MessageType.MSGTYPE_TEXT)
|
.registerSubtype(MessageTextContent::class.java, MessageType.MSGTYPE_TEXT)
|
||||||
.registerSubtype(MessageNoticeContent::class.java, MessageType.MSGTYPE_NOTICE)
|
.registerSubtype(MessageNoticeContent::class.java, MessageType.MSGTYPE_NOTICE)
|
||||||
|
|
|
@ -29,7 +29,6 @@ import org.matrix.android.sdk.internal.network.interceptors.CurlLoggingIntercept
|
||||||
import org.matrix.android.sdk.internal.network.interceptors.FormattedJsonHttpLogger
|
import org.matrix.android.sdk.internal.network.interceptors.FormattedJsonHttpLogger
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.logging.HttpLoggingInterceptor
|
import okhttp3.logging.HttpLoggingInterceptor
|
||||||
import okreplay.OkReplayInterceptor
|
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
|
@ -44,12 +43,6 @@ internal object NetworkModule {
|
||||||
return interceptor
|
return interceptor
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
|
||||||
@JvmStatic
|
|
||||||
fun providesOkReplayInterceptor(): OkReplayInterceptor {
|
|
||||||
return OkReplayInterceptor()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun providesStethoInterceptor(): StethoInterceptor {
|
fun providesStethoInterceptor(): StethoInterceptor {
|
||||||
|
@ -71,8 +64,7 @@ internal object NetworkModule {
|
||||||
timeoutInterceptor: TimeOutInterceptor,
|
timeoutInterceptor: TimeOutInterceptor,
|
||||||
userAgentInterceptor: UserAgentInterceptor,
|
userAgentInterceptor: UserAgentInterceptor,
|
||||||
httpLoggingInterceptor: HttpLoggingInterceptor,
|
httpLoggingInterceptor: HttpLoggingInterceptor,
|
||||||
curlLoggingInterceptor: CurlLoggingInterceptor,
|
curlLoggingInterceptor: CurlLoggingInterceptor): OkHttpClient {
|
||||||
okReplayInterceptor: OkReplayInterceptor): OkHttpClient {
|
|
||||||
return OkHttpClient.Builder()
|
return OkHttpClient.Builder()
|
||||||
.connectTimeout(30, TimeUnit.SECONDS)
|
.connectTimeout(30, TimeUnit.SECONDS)
|
||||||
.readTimeout(60, TimeUnit.SECONDS)
|
.readTimeout(60, TimeUnit.SECONDS)
|
||||||
|
@ -93,7 +85,6 @@ internal object NetworkModule {
|
||||||
proxy(it)
|
proxy(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.addInterceptor(okReplayInterceptor)
|
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2019 New Vector Ltd
|
* Copyright 2019 New Vector Ltd
|
||||||
|
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -14,19 +15,21 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.matrix.android.sdk
|
package org.matrix.android.sdk.internal.network.parsing
|
||||||
|
|
||||||
import okreplay.OkReplayConfig
|
import com.squareup.moshi.FromJson
|
||||||
import okreplay.PermissionRule
|
import com.squareup.moshi.ToJson
|
||||||
import okreplay.RecorderRule
|
import okhttp3.TlsVersion
|
||||||
import org.junit.rules.RuleChain
|
|
||||||
import org.junit.rules.TestRule
|
|
||||||
|
|
||||||
class OkReplayRuleChainNoActivity(
|
internal class TlsVersionMoshiAdapter {
|
||||||
private val configuration: OkReplayConfig) {
|
|
||||||
|
|
||||||
fun get(): TestRule {
|
@ToJson
|
||||||
return RuleChain.outerRule(PermissionRule(configuration))
|
fun toJson(tlsVersion: TlsVersion): String {
|
||||||
.around(RecorderRule(configuration))
|
return tlsVersion.javaName
|
||||||
|
}
|
||||||
|
|
||||||
|
@FromJson
|
||||||
|
fun fromJson(tlsVersionString: String): TlsVersion {
|
||||||
|
return TlsVersion.forJavaName(tlsVersionString)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -41,15 +41,10 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||||
implementation 'androidx.core:core-ktx:1.3.0'
|
implementation "androidx.fragment:fragment:1.3.0-beta01"
|
||||||
testImplementation 'junit:junit:4.12'
|
implementation 'androidx.exifinterface:exifinterface:1.3.0'
|
||||||
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'
|
|
||||||
|
|
||||||
// Log
|
// Log
|
||||||
implementation 'com.jakewharton.timber:timber:4.7.1'
|
implementation 'com.jakewharton.timber:timber:4.7.1'
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
package im.vector.lib.multipicker
|
package im.vector.lib.multipicker
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.media.MediaMetadataRetriever
|
import android.media.MediaMetadataRetriever
|
||||||
|
@ -26,19 +25,13 @@ import im.vector.lib.multipicker.entity.MultiPickerAudioType
|
||||||
/**
|
/**
|
||||||
* Audio file picker implementation
|
* Audio file picker implementation
|
||||||
*/
|
*/
|
||||||
class AudioPicker(override val requestCode: Int) : Picker<MultiPickerAudioType>(requestCode) {
|
class AudioPicker : Picker<MultiPickerAudioType>() {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call this function from onActivityResult(int, int, Intent).
|
* Call this function from onActivityResult(int, int, Intent).
|
||||||
* Returns selected audio files or empty list if request code is wrong
|
* Returns selected audio files or empty list if user did not select any files.
|
||||||
* or result code is not Activity.RESULT_OK
|
|
||||||
* or user did not select any files.
|
|
||||||
*/
|
*/
|
||||||
override fun getSelectedFiles(context: Context, requestCode: Int, resultCode: Int, data: Intent?): List<MultiPickerAudioType> {
|
override fun getSelectedFiles(context: Context, data: Intent?): List<MultiPickerAudioType> {
|
||||||
if (requestCode != this.requestCode && resultCode != Activity.RESULT_OK) {
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
val audioList = mutableListOf<MultiPickerAudioType>()
|
val audioList = mutableListOf<MultiPickerAudioType>()
|
||||||
|
|
||||||
getSelectedUriList(data).forEach { selectedUri ->
|
getSelectedUriList(data).forEach { selectedUri ->
|
||||||
|
|
|
@ -16,13 +16,12 @@
|
||||||
|
|
||||||
package im.vector.lib.multipicker
|
package im.vector.lib.multipicker
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
import androidx.core.content.FileProvider
|
import androidx.core.content.FileProvider
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import im.vector.lib.multipicker.entity.MultiPickerImageType
|
import im.vector.lib.multipicker.entity.MultiPickerImageType
|
||||||
import im.vector.lib.multipicker.utils.ImageUtils
|
import im.vector.lib.multipicker.utils.ImageUtils
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
@ -33,33 +32,18 @@ import java.util.Locale
|
||||||
/**
|
/**
|
||||||
* Implementation of taking a photo with Camera
|
* Implementation of taking a photo with Camera
|
||||||
*/
|
*/
|
||||||
class CameraPicker(val requestCode: Int) {
|
class CameraPicker {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start camera by using an Activity
|
* Start camera by using a ActivityResultLauncher
|
||||||
* @param activity Activity to handle onActivityResult().
|
|
||||||
* @return Uri of taken photo or null if the operation is cancelled.
|
* @return Uri of taken photo or null if the operation is cancelled.
|
||||||
*/
|
*/
|
||||||
fun startWithExpectingFile(activity: Activity): Uri? {
|
fun startWithExpectingFile(context: Context, activityResultLauncher: ActivityResultLauncher<Intent>): Uri? {
|
||||||
val photoUri = createPhotoUri(activity)
|
val photoUri = createPhotoUri(context)
|
||||||
val intent = createIntent().apply {
|
val intent = createIntent().apply {
|
||||||
putExtra(MediaStore.EXTRA_OUTPUT, photoUri)
|
putExtra(MediaStore.EXTRA_OUTPUT, photoUri)
|
||||||
}
|
}
|
||||||
activity.startActivityForResult(intent, requestCode)
|
activityResultLauncher.launch(intent)
|
||||||
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)
|
|
||||||
return photoUri
|
return photoUri
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,8 +53,7 @@ class CameraPicker(val requestCode: Int) {
|
||||||
* or result code is not Activity.RESULT_OK
|
* or result code is not Activity.RESULT_OK
|
||||||
* or user cancelled the operation.
|
* or user cancelled the operation.
|
||||||
*/
|
*/
|
||||||
fun getTakenPhoto(context: Context, requestCode: Int, resultCode: Int, photoUri: Uri): MultiPickerImageType? {
|
fun getTakenPhoto(context: Context, photoUri: Uri): MultiPickerImageType? {
|
||||||
if (requestCode == this.requestCode && resultCode == Activity.RESULT_OK) {
|
|
||||||
val projection = arrayOf(
|
val projection = arrayOf(
|
||||||
MediaStore.Images.Media.DISPLAY_NAME,
|
MediaStore.Images.Media.DISPLAY_NAME,
|
||||||
MediaStore.Images.Media.SIZE
|
MediaStore.Images.Media.SIZE
|
||||||
|
@ -104,7 +87,6 @@ class CameraPicker(val requestCode: Int) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
package im.vector.lib.multipicker
|
package im.vector.lib.multipicker
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.ContentResolver
|
import android.content.ContentResolver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
@ -26,19 +25,13 @@ import im.vector.lib.multipicker.entity.MultiPickerContactType
|
||||||
/**
|
/**
|
||||||
* Contact Picker implementation
|
* Contact Picker implementation
|
||||||
*/
|
*/
|
||||||
class ContactPicker(override val requestCode: Int) : Picker<MultiPickerContactType>(requestCode) {
|
class ContactPicker : Picker<MultiPickerContactType>() {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call this function from onActivityResult(int, int, Intent).
|
* Call this function from onActivityResult(int, int, Intent).
|
||||||
* Returns selected contact or empty list if request code is wrong
|
* Returns selected contact or empty list if user did not select any contacts.
|
||||||
* or result code is not Activity.RESULT_OK
|
|
||||||
* or user did not select any files.
|
|
||||||
*/
|
*/
|
||||||
override fun getSelectedFiles(context: Context, requestCode: Int, resultCode: Int, data: Intent?): List<MultiPickerContactType> {
|
override fun getSelectedFiles(context: Context, data: Intent?): List<MultiPickerContactType> {
|
||||||
if (requestCode != this.requestCode && resultCode != Activity.RESULT_OK) {
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
val contactList = mutableListOf<MultiPickerContactType>()
|
val contactList = mutableListOf<MultiPickerContactType>()
|
||||||
|
|
||||||
data?.data?.let { selectedUri ->
|
data?.data?.let { selectedUri ->
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
package im.vector.lib.multipicker
|
package im.vector.lib.multipicker
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.provider.OpenableColumns
|
import android.provider.OpenableColumns
|
||||||
|
@ -25,19 +24,13 @@ import im.vector.lib.multipicker.entity.MultiPickerFileType
|
||||||
/**
|
/**
|
||||||
* Implementation of selecting any type of files
|
* Implementation of selecting any type of files
|
||||||
*/
|
*/
|
||||||
class FilePicker(override val requestCode: Int) : Picker<MultiPickerFileType>(requestCode) {
|
class FilePicker : Picker<MultiPickerFileType>() {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call this function from onActivityResult(int, int, Intent).
|
* Call this function from onActivityResult(int, int, Intent).
|
||||||
* Returns selected files or empty list if request code is wrong
|
* Returns selected files or empty list if user did not select any files.
|
||||||
* or result code is not Activity.RESULT_OK
|
|
||||||
* or user did not select any files.
|
|
||||||
*/
|
*/
|
||||||
override fun getSelectedFiles(context: Context, requestCode: Int, resultCode: Int, data: Intent?): List<MultiPickerFileType> {
|
override fun getSelectedFiles(context: Context, data: Intent?): List<MultiPickerFileType> {
|
||||||
if (requestCode != this.requestCode && resultCode != Activity.RESULT_OK) {
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
val fileList = mutableListOf<MultiPickerFileType>()
|
val fileList = mutableListOf<MultiPickerFileType>()
|
||||||
|
|
||||||
getSelectedUriList(data).forEach { selectedUri ->
|
getSelectedUriList(data).forEach { selectedUri ->
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
package im.vector.lib.multipicker
|
package im.vector.lib.multipicker
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
|
@ -26,19 +25,13 @@ import im.vector.lib.multipicker.utils.ImageUtils
|
||||||
/**
|
/**
|
||||||
* Image Picker implementation
|
* Image Picker implementation
|
||||||
*/
|
*/
|
||||||
class ImagePicker(override val requestCode: Int) : Picker<MultiPickerImageType>(requestCode) {
|
class ImagePicker : Picker<MultiPickerImageType>() {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call this function from onActivityResult(int, int, Intent).
|
* Call this function from onActivityResult(int, int, Intent).
|
||||||
* Returns selected image files or empty list if request code is wrong
|
* Returns selected image files or empty list if user did not select any files.
|
||||||
* or result code is not Activity.RESULT_OK
|
|
||||||
* or user did not select any files.
|
|
||||||
*/
|
*/
|
||||||
override fun getSelectedFiles(context: Context, requestCode: Int, resultCode: Int, data: Intent?): List<MultiPickerImageType> {
|
override fun getSelectedFiles(context: Context, data: Intent?): List<MultiPickerImageType> {
|
||||||
if (requestCode != this.requestCode && resultCode != Activity.RESULT_OK) {
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
val imageList = mutableListOf<MultiPickerImageType>()
|
val imageList = mutableListOf<MultiPickerImageType>()
|
||||||
|
|
||||||
getSelectedUriList(data).forEach { selectedUri ->
|
getSelectedUriList(data).forEach { selectedUri ->
|
||||||
|
|
|
@ -26,22 +26,15 @@ class MultiPicker<T> {
|
||||||
val CONTACT by lazy { MultiPicker<ContactPicker>() }
|
val CONTACT by lazy { MultiPicker<ContactPicker>() }
|
||||||
val CAMERA by lazy { MultiPicker<CameraPicker>() }
|
val CAMERA by lazy { MultiPicker<CameraPicker>() }
|
||||||
|
|
||||||
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")
|
@Suppress("UNCHECKED_CAST")
|
||||||
fun <T> get(type: MultiPicker<T>): T {
|
fun <T> get(type: MultiPicker<T>): T {
|
||||||
return when (type) {
|
return when (type) {
|
||||||
IMAGE -> ImagePicker(REQUEST_CODE_PICK_IMAGE) as T
|
IMAGE -> ImagePicker() as T
|
||||||
VIDEO -> VideoPicker(REQUEST_CODE_PICK_VIDEO) as T
|
VIDEO -> VideoPicker() as T
|
||||||
FILE -> FilePicker(REQUEST_CODE_PICK_FILE) as T
|
FILE -> FilePicker() as T
|
||||||
AUDIO -> AudioPicker(REQUEST_CODE_PICK_AUDIO) as T
|
AUDIO -> AudioPicker() as T
|
||||||
CONTACT -> ContactPicker(REQUEST_CODE_PICK_CONTACT) as T
|
CONTACT -> ContactPicker() as T
|
||||||
CAMERA -> CameraPicker(REQUEST_CODE_TAKE_PHOTO) as T
|
CAMERA -> CameraPicker() as T
|
||||||
else -> throw IllegalArgumentException("Unsupported type $type")
|
else -> throw IllegalArgumentException("Unsupported type $type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,28 +16,25 @@
|
||||||
|
|
||||||
package im.vector.lib.multipicker
|
package im.vector.lib.multipicker
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.content.pm.ResolveInfo
|
import android.content.pm.ResolveInfo
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract class to provide all types of Pickers
|
* Abstract class to provide all types of Pickers
|
||||||
*/
|
*/
|
||||||
abstract class Picker<T>(open val requestCode: Int) {
|
abstract class Picker<T> {
|
||||||
|
|
||||||
protected var single = false
|
protected var single = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call this function from onActivityResult(int, int, Intent).
|
* Call this function from onActivityResult(int, int, Intent).
|
||||||
* @return selected files or empty list if request code is wrong
|
* @return selected files or empty list if user did not select any files.
|
||||||
* or result code is not Activity.RESULT_OK
|
|
||||||
* or user did not select any files.
|
|
||||||
*/
|
*/
|
||||||
abstract fun getSelectedFiles(context: Context, requestCode: Int, resultCode: Int, data: Intent?): List<T>
|
abstract fun getSelectedFiles(context: Context, data: Intent?): List<T>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use this function to retrieve files which are shared from another application or internally
|
* Use this function to retrieve files which are shared from another application or internally
|
||||||
|
@ -61,7 +58,7 @@ abstract class Picker<T>(open val requestCode: Int) {
|
||||||
context.grantUriPermission(packageName, it, Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
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<T>(open val requestCode: Int) {
|
||||||
abstract fun createIntent(): Intent
|
abstract fun createIntent(): Intent
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start Storage Access Framework UI by using an Activity.
|
* Start Storage Access Framework UI by using a ActivityResultLauncher.
|
||||||
* @param activity Activity to handle onActivityResult().
|
* @param activityResultLauncher to handle the result.
|
||||||
*/
|
*/
|
||||||
fun startWith(activity: Activity) {
|
fun startWith(activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||||
activity.startActivityForResult(createIntent().apply { addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) }, requestCode)
|
activityResultLauncher.launch(createIntent().apply { addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) })
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun getSelectedUriList(data: Intent?): List<Uri> {
|
protected fun getSelectedUriList(data: Intent?): List<Uri> {
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
package im.vector.lib.multipicker
|
package im.vector.lib.multipicker
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.media.MediaMetadataRetriever
|
import android.media.MediaMetadataRetriever
|
||||||
|
@ -26,19 +25,13 @@ import im.vector.lib.multipicker.entity.MultiPickerVideoType
|
||||||
/**
|
/**
|
||||||
* Video Picker implementation
|
* Video Picker implementation
|
||||||
*/
|
*/
|
||||||
class VideoPicker(override val requestCode: Int) : Picker<MultiPickerVideoType>(requestCode) {
|
class VideoPicker : Picker<MultiPickerVideoType>() {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call this function from onActivityResult(int, int, Intent).
|
* Call this function from onActivityResult(int, int, Intent).
|
||||||
* Returns selected video files or empty list if request code is wrong
|
* Returns selected video files or empty list if user did not select any files.
|
||||||
* or result code is not Activity.RESULT_OK
|
|
||||||
* or user did not select any files.
|
|
||||||
*/
|
*/
|
||||||
override fun getSelectedFiles(context: Context, requestCode: Int, resultCode: Int, data: Intent?): List<MultiPickerVideoType> {
|
override fun getSelectedFiles(context: Context, data: Intent?): List<MultiPickerVideoType> {
|
||||||
if (requestCode != this.requestCode && resultCode != Activity.RESULT_OK) {
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
val videoList = mutableListOf<MultiPickerVideoType>()
|
val videoList = mutableListOf<MultiPickerVideoType>()
|
||||||
|
|
||||||
getSelectedUriList(data).forEach { selectedUri ->
|
getSelectedUriList(data).forEach { selectedUri ->
|
||||||
|
|
|
@ -280,22 +280,21 @@ android {
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
def epoxy_version = '3.11.0'
|
def epoxy_version = '4.1.0'
|
||||||
def fragment_version = '1.2.5'
|
def fragment_version = '1.3.0-beta01'
|
||||||
def arrow_version = "0.8.2"
|
def arrow_version = "0.8.2"
|
||||||
def coroutines_version = "1.3.8"
|
|
||||||
def markwon_version = '4.1.2'
|
def markwon_version = '4.1.2'
|
||||||
def big_image_viewer_version = '1.6.2'
|
def big_image_viewer_version = '1.6.2'
|
||||||
def glide_version = '4.11.0'
|
def glide_version = '4.11.0'
|
||||||
def moshi_version = '1.8.0'
|
def moshi_version = '1.11.0'
|
||||||
def daggerVersion = '2.25.4'
|
def daggerVersion = '2.29.1'
|
||||||
def autofill_version = "1.0.0"
|
def autofill_version = "1.0.0"
|
||||||
def work_version = '2.4.0'
|
def work_version = '2.4.0'
|
||||||
def arch_version = '2.1.0'
|
def arch_version = '2.1.0'
|
||||||
def lifecycle_version = '2.2.0'
|
def lifecycle_version = '2.2.0'
|
||||||
|
|
||||||
// Tests
|
// Tests
|
||||||
def kluent_version = '1.44'
|
def kluent_version = '1.61'
|
||||||
def androidxTest_version = '1.3.0'
|
def androidxTest_version = '1.3.0'
|
||||||
def espresso_version = '3.3.0'
|
def espresso_version = '3.3.0'
|
||||||
|
|
||||||
|
@ -307,16 +306,16 @@ dependencies {
|
||||||
implementation 'com.android.support:multidex:1.0.3'
|
implementation 'com.android.support:multidex:1.0.3'
|
||||||
|
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version"
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$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.appcompat:appcompat:1.2.0'
|
||||||
implementation "androidx.fragment:fragment:$fragment_version"
|
implementation "androidx.fragment:fragment:$fragment_version"
|
||||||
implementation "androidx.fragment:fragment-ktx:$fragment_version"
|
implementation "androidx.fragment:fragment-ktx:$fragment_version"
|
||||||
// Keep at 2.0.0-beta4 at the moment, as updating is breaking some UI
|
// 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.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 "org.threeten:threetenbp:1.4.0:no-tzdb"
|
||||||
implementation "com.gabrielittner.threetenbp:lazythreetenbp:0.7.0"
|
implementation "com.gabrielittner.threetenbp:lazythreetenbp:0.7.0"
|
||||||
|
@ -347,7 +346,7 @@ dependencies {
|
||||||
implementation "com.airbnb.android:epoxy-glide-preloading:$epoxy_version"
|
implementation "com.airbnb.android:epoxy-glide-preloading:$epoxy_version"
|
||||||
kapt "com.airbnb.android:epoxy-processor:$epoxy_version"
|
kapt "com.airbnb.android:epoxy-processor:$epoxy_version"
|
||||||
implementation "com.airbnb.android:epoxy-paging:$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
|
// Work
|
||||||
implementation "androidx.work:work-runtime-ktx:$work_version"
|
implementation "androidx.work:work-runtime-ktx:$work_version"
|
||||||
|
@ -363,7 +362,7 @@ dependencies {
|
||||||
|
|
||||||
// UI
|
// UI
|
||||||
implementation 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1'
|
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 'me.gujun.android:span:1.7'
|
||||||
implementation "io.noties.markwon:core:$markwon_version"
|
implementation "io.noties.markwon:core:$markwon_version"
|
||||||
implementation "io.noties.markwon:html:$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.piasy:GlideImageViewFactory:$big_image_viewer_version"
|
||||||
|
|
||||||
// implementation 'com.github.MikeOrtiz:TouchImageView:3.0.2'
|
// 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"
|
implementation "com.github.bumptech.glide:glide:$glide_version"
|
||||||
kapt "com.github.bumptech.glide:compiler:$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'
|
kapt 'com.squareup.inject:assisted-inject-processor-dagger2:0.5.0'
|
||||||
|
|
||||||
// gplay flavor only
|
// 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-core'
|
||||||
exclude group: 'com.google.firebase', module: 'firebase-analytics'
|
exclude group: 'com.google.firebase', module: 'firebase-analytics'
|
||||||
exclude group: 'com.google.firebase', module: 'firebase-measurement-connector'
|
exclude group: 'com.google.firebase', module: 'firebase-measurement-connector'
|
||||||
|
@ -439,7 +438,7 @@ dependencies {
|
||||||
implementation 'me.dm7.barcodescanner:zxing:1.9.13'
|
implementation 'me.dm7.barcodescanner:zxing:1.9.13'
|
||||||
|
|
||||||
// TESTS
|
// TESTS
|
||||||
testImplementation 'junit:junit:4.12'
|
testImplementation 'junit:junit:4.13'
|
||||||
testImplementation "org.amshove.kluent:kluent-android:$kluent_version"
|
testImplementation "org.amshove.kluent:kluent-android:$kluent_version"
|
||||||
// Plant Timber tree for test
|
// Plant Timber tree for test
|
||||||
testImplementation 'net.lachlanmckee:timber-junit-rule:1.0.1'
|
testImplementation 'net.lachlanmckee:timber-junit-rule:1.0.1'
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
<issue id="DisableBaselineAlignment" severity="error" />
|
<issue id="DisableBaselineAlignment" severity="error" />
|
||||||
<issue id="ScrollViewSize" severity="error" />
|
<issue id="ScrollViewSize" severity="error" />
|
||||||
<issue id="NegativeMargin" severity="error" />
|
<issue id="NegativeMargin" severity="error" />
|
||||||
|
<issue id="UseCompatTextViewDrawableXml" severity="error" />
|
||||||
|
|
||||||
<!-- RTL -->
|
<!-- RTL -->
|
||||||
<issue id="RtlEnabled" severity="error" />
|
<issue id="RtlEnabled" severity="error" />
|
||||||
|
|
|
@ -28,6 +28,7 @@ import butterknife.OnClick
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.di.ActiveSessionHolder
|
import im.vector.app.core.di.ActiveSessionHolder
|
||||||
import im.vector.app.core.di.ScreenComponent
|
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.platform.VectorBaseActivity
|
||||||
import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO
|
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.PERMISSION_REQUEST_CODE_LAUNCH_CAMERA
|
||||||
|
@ -196,19 +197,17 @@ class DebugMenuActivity : VectorBaseActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun doScanQRCode() {
|
private fun doScanQRCode() {
|
||||||
QrCodeScannerActivity.startForResult(this)
|
QrCodeScannerActivity.startForResult(this, qrStartForActivityResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
private val qrStartForActivityResult = registerStartForActivityResult { activityResult ->
|
||||||
super.onActivityResult(requestCode, resultCode, data)
|
if (activityResult.resultCode == Activity.RESULT_OK) {
|
||||||
if (resultCode == Activity.RESULT_OK) {
|
toast("QrCode: " + QrCodeScannerActivity.getResultText(activityResult.data)
|
||||||
when (requestCode) {
|
+ " is QRCode: " + QrCodeScannerActivity.getResultIsQrCode(activityResult.data))
|
||||||
QrCodeScannerActivity.QR_CODE_SCANNER_REQUEST_CODE -> {
|
|
||||||
toast("QrCode: " + QrCodeScannerActivity.getResultText(data) + " is QRCode: " + QrCodeScannerActivity.getResultIsQrCode(data))
|
|
||||||
|
|
||||||
// Also update the current QR Code (reverse operation)
|
// Also update the current QR Code (reverse operation)
|
||||||
// renderQrCode(QrCodeScannerActivity.getResultText(data) ?: "")
|
// renderQrCode(QrCodeScannerActivity.getResultText(data) ?: "")
|
||||||
val result = QrCodeScannerActivity.getResultText(data)!!
|
val result = QrCodeScannerActivity.getResultText(activityResult.data)!!
|
||||||
|
|
||||||
val qrCodeData = result.toQrCodeData()
|
val qrCodeData = result.toQrCodeData()
|
||||||
Timber.e("qrCodeData: $qrCodeData")
|
Timber.e("qrCodeData: $qrCodeData")
|
||||||
|
@ -226,6 +225,4 @@ class DebugMenuActivity : VectorBaseActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package im.vector.app.fdroid.features.settings.troubleshoot
|
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.R
|
||||||
import im.vector.app.core.resources.StringProvider
|
import im.vector.app.core.resources.StringProvider
|
||||||
import im.vector.app.features.settings.VectorPreferences
|
import im.vector.app.features.settings.VectorPreferences
|
||||||
|
@ -28,7 +30,7 @@ class TestAutoStartBoot @Inject constructor(private val vectorPreferences: Vecto
|
||||||
private val stringProvider: StringProvider)
|
private val stringProvider: StringProvider)
|
||||||
: TroubleshootTest(R.string.settings_troubleshoot_test_service_boot_title) {
|
: TroubleshootTest(R.string.settings_troubleshoot_test_service_boot_title) {
|
||||||
|
|
||||||
override fun perform() {
|
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||||
if (vectorPreferences.autoStartOnBoot()) {
|
if (vectorPreferences.autoStartOnBoot()) {
|
||||||
description = stringProvider.getString(R.string.settings_troubleshoot_test_service_boot_success)
|
description = stringProvider.getString(R.string.settings_troubleshoot_test_service_boot_success)
|
||||||
status = TestStatus.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) {
|
quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_service_boot_quickfix) {
|
||||||
override fun doFix() {
|
override fun doFix() {
|
||||||
vectorPreferences.setAutoStartOnBoot(true)
|
vectorPreferences.setAutoStartOnBoot(true)
|
||||||
manager?.retry()
|
manager?.retry(activityResultLauncher)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
status = TestStatus.FAILED
|
status = TestStatus.FAILED
|
||||||
|
|
|
@ -15,7 +15,9 @@
|
||||||
*/
|
*/
|
||||||
package im.vector.app.fdroid.features.settings.troubleshoot
|
package im.vector.app.fdroid.features.settings.troubleshoot
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
import android.net.ConnectivityManager
|
import android.net.ConnectivityManager
|
||||||
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.content.getSystemService
|
import androidx.core.content.getSystemService
|
||||||
import androidx.core.net.ConnectivityManagerCompat
|
import androidx.core.net.ConnectivityManagerCompat
|
||||||
|
@ -28,7 +30,7 @@ class TestBackgroundRestrictions @Inject constructor(private val context: AppCom
|
||||||
private val stringProvider: StringProvider)
|
private val stringProvider: StringProvider)
|
||||||
: TroubleshootTest(R.string.settings_troubleshoot_test_bg_restricted_title) {
|
: TroubleshootTest(R.string.settings_troubleshoot_test_bg_restricted_title) {
|
||||||
|
|
||||||
override fun perform() {
|
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||||
context.getSystemService<ConnectivityManager>()!!.apply {
|
context.getSystemService<ConnectivityManager>()!!.apply {
|
||||||
// Checks if the device is on a metered network
|
// Checks if the device is on a metered network
|
||||||
if (isActiveNetworkMetered) {
|
if (isActiveNetworkMetered) {
|
||||||
|
|
|
@ -15,12 +15,13 @@
|
||||||
*/
|
*/
|
||||||
package im.vector.app.fdroid.features.settings.troubleshoot
|
package im.vector.app.fdroid.features.settings.troubleshoot
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.resources.StringProvider
|
import im.vector.app.core.resources.StringProvider
|
||||||
import im.vector.app.core.utils.isIgnoringBatteryOptimizations
|
import im.vector.app.core.utils.isIgnoringBatteryOptimizations
|
||||||
import im.vector.app.core.utils.requestDisablingBatteryOptimization
|
import im.vector.app.core.utils.requestDisablingBatteryOptimization
|
||||||
import im.vector.app.features.settings.troubleshoot.NotificationTroubleshootTestManager
|
|
||||||
import im.vector.app.features.settings.troubleshoot.TroubleshootTest
|
import im.vector.app.features.settings.troubleshoot.TroubleshootTest
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -29,7 +30,7 @@ class TestBatteryOptimization @Inject constructor(
|
||||||
private val stringProvider: StringProvider
|
private val stringProvider: StringProvider
|
||||||
) : TroubleshootTest(R.string.settings_troubleshoot_test_battery_title) {
|
) : TroubleshootTest(R.string.settings_troubleshoot_test_battery_title) {
|
||||||
|
|
||||||
override fun perform() {
|
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||||
if (isIgnoringBatteryOptimizations(context)) {
|
if (isIgnoringBatteryOptimizations(context)) {
|
||||||
description = stringProvider.getString(R.string.settings_troubleshoot_test_battery_success)
|
description = stringProvider.getString(R.string.settings_troubleshoot_test_battery_success)
|
||||||
status = TestStatus.SUCCESS
|
status = TestStatus.SUCCESS
|
||||||
|
@ -38,7 +39,7 @@ class TestBatteryOptimization @Inject constructor(
|
||||||
description = stringProvider.getString(R.string.settings_troubleshoot_test_battery_failed)
|
description = stringProvider.getString(R.string.settings_troubleshoot_test_battery_failed)
|
||||||
quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_battery_quickfix) {
|
quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_battery_quickfix) {
|
||||||
override fun doFix() {
|
override fun doFix() {
|
||||||
requestDisablingBatteryOptimization(context, null, NotificationTroubleshootTestManager.REQ_CODE_FIX)
|
requestDisablingBatteryOptimization(context, activityResultLauncher)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
status = TestStatus.FAILED
|
status = TestStatus.FAILED
|
||||||
|
|
|
@ -15,12 +15,13 @@
|
||||||
*/
|
*/
|
||||||
package im.vector.app.gplay.features.settings.troubleshoot
|
package im.vector.app.gplay.features.settings.troubleshoot
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import com.google.firebase.iid.FirebaseInstanceId
|
import com.google.firebase.iid.FirebaseInstanceId
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.resources.StringProvider
|
import im.vector.app.core.resources.StringProvider
|
||||||
import im.vector.app.core.utils.startAddGoogleAccountIntent
|
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.features.settings.troubleshoot.TroubleshootTest
|
||||||
import im.vector.app.push.fcm.FcmHelper
|
import im.vector.app.push.fcm.FcmHelper
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
@ -32,7 +33,7 @@ import javax.inject.Inject
|
||||||
class TestFirebaseToken @Inject constructor(private val context: AppCompatActivity,
|
class TestFirebaseToken @Inject constructor(private val context: AppCompatActivity,
|
||||||
private val stringProvider: StringProvider) : TroubleshootTest(R.string.settings_troubleshoot_test_fcm_title) {
|
private val stringProvider: StringProvider) : TroubleshootTest(R.string.settings_troubleshoot_test_fcm_title) {
|
||||||
|
|
||||||
override fun perform() {
|
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||||
status = TestStatus.RUNNING
|
status = TestStatus.RUNNING
|
||||||
try {
|
try {
|
||||||
FirebaseInstanceId.getInstance().instanceId
|
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)
|
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) {
|
quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_fcm_failed_account_missing_quick_fix) {
|
||||||
override fun doFix() {
|
override fun doFix() {
|
||||||
startAddGoogleAccountIntent(context, NotificationTroubleshootTestManager.REQ_CODE_FIX)
|
startAddGoogleAccountIntent(context, activityResultLauncher)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package im.vector.app.gplay.features.settings.troubleshoot
|
package im.vector.app.gplay.features.settings.troubleshoot
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import com.google.android.gms.common.ConnectionResult
|
import com.google.android.gms.common.ConnectionResult
|
||||||
import com.google.android.gms.common.GoogleApiAvailability
|
import com.google.android.gms.common.GoogleApiAvailability
|
||||||
|
@ -31,7 +33,7 @@ class TestPlayServices @Inject constructor(private val context: AppCompatActivit
|
||||||
private val stringProvider: StringProvider)
|
private val stringProvider: StringProvider)
|
||||||
: TroubleshootTest(R.string.settings_troubleshoot_test_play_services_title) {
|
: TroubleshootTest(R.string.settings_troubleshoot_test_play_services_title) {
|
||||||
|
|
||||||
override fun perform() {
|
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||||
val apiAvailability = GoogleApiAvailability.getInstance()
|
val apiAvailability = GoogleApiAvailability.getInstance()
|
||||||
val resultCode = apiAvailability.isGooglePlayServicesAvailable(context)
|
val resultCode = apiAvailability.isGooglePlayServicesAvailable(context)
|
||||||
if (resultCode == ConnectionResult.SUCCESS) {
|
if (resultCode == ConnectionResult.SUCCESS) {
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package im.vector.app.gplay.features.settings.troubleshoot
|
package im.vector.app.gplay.features.settings.troubleshoot
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
import androidx.work.WorkInfo
|
import androidx.work.WorkInfo
|
||||||
|
@ -37,7 +39,7 @@ class TestTokenRegistration @Inject constructor(private val context: AppCompatAc
|
||||||
private val activeSessionHolder: ActiveSessionHolder)
|
private val activeSessionHolder: ActiveSessionHolder)
|
||||||
: TroubleshootTest(R.string.settings_troubleshoot_test_token_registration_title) {
|
: TroubleshootTest(R.string.settings_troubleshoot_test_token_registration_title) {
|
||||||
|
|
||||||
override fun perform() {
|
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||||
// Check if we have a registered pusher for this token
|
// Check if we have a registered pusher for this token
|
||||||
val fcmToken = FcmHelper.getFcmToken(context) ?: run {
|
val fcmToken = FcmHelper.getFcmToken(context) ?: run {
|
||||||
status = TestStatus.FAILED
|
status = TestStatus.FAILED
|
||||||
|
@ -59,9 +61,9 @@ class TestTokenRegistration @Inject constructor(private val context: AppCompatAc
|
||||||
WorkManager.getInstance(context).getWorkInfoByIdLiveData(workId).observe(context, Observer { workInfo ->
|
WorkManager.getInstance(context).getWorkInfoByIdLiveData(workId).observe(context, Observer { workInfo ->
|
||||||
if (workInfo != null) {
|
if (workInfo != null) {
|
||||||
if (workInfo.state == WorkInfo.State.SUCCEEDED) {
|
if (workInfo.state == WorkInfo.State.SUCCEEDED) {
|
||||||
manager?.retry()
|
manager?.retry(activityResultLauncher)
|
||||||
} else if (workInfo.state == WorkInfo.State.FAILED) {
|
} else if (workInfo.state == WorkInfo.State.FAILED) {
|
||||||
manager?.retry()
|
manager?.retry(activityResultLauncher)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -232,9 +232,11 @@
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
|
<!-- Add tools:ignore="Instantiatable" for the error reported only by Buildkite and for lintGplayRelease check :/ -->
|
||||||
<service
|
<service
|
||||||
android:name=".core.services.VectorSyncService"
|
android:name=".core.services.VectorSyncService"
|
||||||
android:exported="false" />
|
android:exported="false"
|
||||||
|
tools:ignore="Instantiatable" />
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".features.call.telecom.VectorConnectionService"
|
android:name=".features.call.telecom.VectorConnectionService"
|
||||||
|
|
|
@ -17,11 +17,20 @@
|
||||||
package im.vector.app.core.extensions
|
package im.vector.app.core.extensions
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
|
import android.content.Intent
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.activity.result.ActivityResult
|
||||||
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.FragmentTransaction
|
import androidx.fragment.app.FragmentTransaction
|
||||||
import im.vector.app.core.platform.VectorBaseActivity
|
import im.vector.app.core.platform.VectorBaseActivity
|
||||||
|
|
||||||
|
fun ComponentActivity.registerStartForActivityResult(onResult: (ActivityResult) -> Unit): ActivityResultLauncher<Intent> {
|
||||||
|
return registerForActivityResult(ActivityResultContracts.StartActivityForResult(), onResult)
|
||||||
|
}
|
||||||
|
|
||||||
fun VectorBaseActivity.addFragment(
|
fun VectorBaseActivity.addFragment(
|
||||||
frameId: Int,
|
frameId: Int,
|
||||||
fragment: Fragment,
|
fragment: Fragment,
|
||||||
|
|
|
@ -17,7 +17,11 @@
|
||||||
package im.vector.app.core.extensions
|
package im.vector.app.core.extensions
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
|
import android.content.Intent
|
||||||
import android.os.Parcelable
|
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 androidx.fragment.app.Fragment
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.platform.VectorBaseFragment
|
import im.vector.app.core.platform.VectorBaseFragment
|
||||||
|
@ -26,6 +30,10 @@ import java.text.SimpleDateFormat
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
|
fun Fragment.registerStartForActivityResult(onResult: (ActivityResult) -> Unit): ActivityResultLauncher<Intent> {
|
||||||
|
return registerForActivityResult(ActivityResultContracts.StartActivityForResult(), onResult)
|
||||||
|
}
|
||||||
|
|
||||||
fun VectorBaseFragment.addFragment(
|
fun VectorBaseFragment.addFragment(
|
||||||
frameId: Int,
|
frameId: Int,
|
||||||
fragment: Fragment,
|
fragment: Fragment,
|
||||||
|
@ -160,26 +168,24 @@ fun Fragment.getAllChildFragments(): List<Fragment> {
|
||||||
// Define a missing constant
|
// Define a missing constant
|
||||||
const val POP_BACK_STACK_EXCLUSIVE = 0
|
const val POP_BACK_STACK_EXCLUSIVE = 0
|
||||||
|
|
||||||
fun Fragment.queryExportKeys(userId: String, requestCode: Int) {
|
fun Fragment.queryExportKeys(userId: String, activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||||
val timestamp = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(Date())
|
val timestamp = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(Date())
|
||||||
|
|
||||||
selectTxtFileToWrite(
|
selectTxtFileToWrite(
|
||||||
activity = requireActivity(),
|
activity = requireActivity(),
|
||||||
fragment = this,
|
activityResultLauncher = activityResultLauncher,
|
||||||
defaultFileName = "element-megolm-export-$userId-$timestamp.txt",
|
defaultFileName = "element-megolm-export-$userId-$timestamp.txt",
|
||||||
chooserHint = getString(R.string.keys_backup_setup_step1_manual_export),
|
chooserHint = getString(R.string.keys_backup_setup_step1_manual_export)
|
||||||
requestCode = requestCode
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Activity.queryExportKeys(userId: String, requestCode: Int) {
|
fun Activity.queryExportKeys(userId: String, activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||||
val timestamp = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(Date())
|
val timestamp = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(Date())
|
||||||
|
|
||||||
selectTxtFileToWrite(
|
selectTxtFileToWrite(
|
||||||
activity = this,
|
activity = this,
|
||||||
fragment = null,
|
activityResultLauncher = activityResultLauncher,
|
||||||
defaultFileName = "element-megolm-export-$userId-$timestamp.txt",
|
defaultFileName = "element-megolm-export-$userId-$timestamp.txt",
|
||||||
chooserHint = getString(R.string.keys_backup_setup_step1_manual_export),
|
chooserHint = getString(R.string.keys_backup_setup_step1_manual_export)
|
||||||
requestCode = requestCode
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@ package im.vector.app.core.platform
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Parcelable
|
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.exhaustive
|
||||||
import im.vector.app.core.extensions.observeEvent
|
import im.vector.app.core.extensions.observeEvent
|
||||||
import im.vector.app.core.extensions.observeNotNull
|
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.restart
|
||||||
import im.vector.app.core.extensions.vectorComponent
|
import im.vector.app.core.extensions.vectorComponent
|
||||||
import im.vector.app.core.utils.toast
|
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.configuration.VectorConfiguration
|
||||||
import im.vector.app.features.consent.ConsentNotGivenHelper
|
import im.vector.app.features.consent.ConsentNotGivenHelper
|
||||||
import im.vector.app.features.navigation.Navigator
|
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.PinLocker
|
||||||
import im.vector.app.features.pin.PinMode
|
import im.vector.app.features.pin.PinMode
|
||||||
import im.vector.app.features.pin.UnlockedActivity
|
import im.vector.app.features.pin.UnlockedActivity
|
||||||
|
@ -206,7 +205,7 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector {
|
||||||
})
|
})
|
||||||
pinLocker.getLiveState().observeNotNull(this) {
|
pinLocker.getLiveState().observeNotNull(this) {
|
||||||
if (this@VectorBaseActivity !is UnlockedActivity && it == PinLocker.State.LOCKED) {
|
if (this@VectorBaseActivity !is UnlockedActivity && it == PinLocker.State.LOCKED) {
|
||||||
navigator.openPinCode(this, PinMode.AUTH)
|
navigator.openPinCode(this, pinStartForActivityResult, PinMode.AUTH)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sessionListener = vectorComponent.sessionListener()
|
sessionListener = vectorComponent.sessionListener()
|
||||||
|
@ -313,16 +312,15 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector {
|
||||||
uiDisposables.dispose()
|
uiDisposables.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
private val pinStartForActivityResult = registerStartForActivityResult { activityResult ->
|
||||||
super.onActivityResult(requestCode, resultCode, data)
|
when (activityResult.resultCode) {
|
||||||
if (requestCode == PinActivity.PIN_REQUEST_CODE) {
|
|
||||||
when (resultCode) {
|
|
||||||
Activity.RESULT_OK -> {
|
Activity.RESULT_OK -> {
|
||||||
Timber.v("Pin ok, unlock app")
|
Timber.v("Pin ok, unlock app")
|
||||||
pinLocker.unlock()
|
pinLocker.unlock()
|
||||||
|
|
||||||
// Cancel any new started PinActivity, after a screen rotation for instance
|
// Cancel any new started PinActivity, after a screen rotation for instance
|
||||||
finishActivity(PinActivity.PIN_REQUEST_CODE)
|
// FIXME I cannot use this anymore :/
|
||||||
|
// finishActivity(PinActivity.PIN_REQUEST_CODE)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
if (pinLocker.getLiveState().value != PinLocker.State.UNLOCKED) {
|
if (pinLocker.getLiveState().value != PinLocker.State.UNLOCKED) {
|
||||||
|
@ -332,7 +330,6 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
|
|
|
@ -42,9 +42,11 @@ abstract class VectorViewModel<S : MvRxState, VA : VectorViewModelAction, VE : V
|
||||||
* This method does the same thing as the execute function, but it doesn't subscribe to the stream
|
* This method does the same thing as the execute function, but it doesn't subscribe to the stream
|
||||||
* so you can use this in a switchMap or a flatMap
|
* so you can use this in a switchMap or a flatMap
|
||||||
*/
|
*/
|
||||||
|
// False positive
|
||||||
|
@Suppress("USELESS_CAST")
|
||||||
fun <T> Single<T>.toAsync(stateReducer: S.(Async<T>) -> S): Single<Async<T>> {
|
fun <T> Single<T>.toAsync(stateReducer: S.(Async<T>) -> S): Single<Async<T>> {
|
||||||
setState { stateReducer(Loading()) }
|
setState { stateReducer(Loading()) }
|
||||||
return this.map { Success(it) as Async<T> }
|
return map { Success(it) as Async<T> }
|
||||||
.onErrorReturn { Fail(it) }
|
.onErrorReturn { Fail(it) }
|
||||||
.doOnSuccess { setState { stateReducer(it) } }
|
.doOnSuccess { setState { stateReducer(it) } }
|
||||||
}
|
}
|
||||||
|
@ -53,9 +55,11 @@ abstract class VectorViewModel<S : MvRxState, VA : VectorViewModelAction, VE : V
|
||||||
* This method does the same thing as the execute function, but it doesn't subscribe to the stream
|
* This method does the same thing as the execute function, but it doesn't subscribe to the stream
|
||||||
* so you can use this in a switchMap or a flatMap
|
* so you can use this in a switchMap or a flatMap
|
||||||
*/
|
*/
|
||||||
|
// False positive
|
||||||
|
@Suppress("USELESS_CAST")
|
||||||
fun <T> Observable<T>.toAsync(stateReducer: S.(Async<T>) -> S): Observable<Async<T>> {
|
fun <T> Observable<T>.toAsync(stateReducer: S.(Async<T>) -> S): Observable<Async<T>> {
|
||||||
setState { stateReducer(Loading()) }
|
setState { stateReducer(Loading()) }
|
||||||
return this.map { Success(it) as Async<T> }
|
return map { Success(it) as Async<T> }
|
||||||
.onErrorReturn { Fail(it) }
|
.onErrorReturn { Fail(it) }
|
||||||
.doOnNext { setState { stateReducer(it) } }
|
.doOnNext { setState { stateReducer(it) } }
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,12 +31,12 @@ import android.provider.Browser
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
import android.webkit.MimeTypeMap
|
import android.webkit.MimeTypeMap
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
import androidx.browser.customtabs.CustomTabsIntent
|
import androidx.browser.customtabs.CustomTabsIntent
|
||||||
import androidx.browser.customtabs.CustomTabsSession
|
import androidx.browser.customtabs.CustomTabsSession
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.content.FileProvider
|
import androidx.core.content.FileProvider
|
||||||
import androidx.core.content.getSystemService
|
import androidx.core.content.getSystemService
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import im.vector.app.BuildConfig
|
import im.vector.app.BuildConfig
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.features.notifications.NotificationUtils
|
import im.vector.app.features.notifications.NotificationUtils
|
||||||
|
@ -130,7 +130,7 @@ fun openSoundRecorder(activity: Activity, requestCode: Int) {
|
||||||
* Open file selection activity
|
* Open file selection activity
|
||||||
*/
|
*/
|
||||||
fun openFileSelection(activity: Activity,
|
fun openFileSelection(activity: Activity,
|
||||||
fragment: Fragment?,
|
activityResultLauncher: ActivityResultLauncher<Intent>?,
|
||||||
allowMultipleSelection: Boolean,
|
allowMultipleSelection: Boolean,
|
||||||
requestCode: Int) {
|
requestCode: Int) {
|
||||||
val fileIntent = Intent(Intent.ACTION_GET_CONTENT)
|
val fileIntent = Intent(Intent.ACTION_GET_CONTENT)
|
||||||
|
@ -140,8 +140,8 @@ fun openFileSelection(activity: Activity,
|
||||||
fileIntent.type = "*/*"
|
fileIntent.type = "*/*"
|
||||||
|
|
||||||
try {
|
try {
|
||||||
fragment
|
activityResultLauncher
|
||||||
?.startActivityForResult(fileIntent, requestCode)
|
?.launch(fileIntent)
|
||||||
?: run {
|
?: run {
|
||||||
activity.startActivityForResult(fileIntent, requestCode)
|
activity.startActivityForResult(fileIntent, requestCode)
|
||||||
}
|
}
|
||||||
|
@ -440,10 +440,9 @@ fun openPlayStore(activity: Activity, appId: String = BuildConfig.APPLICATION_ID
|
||||||
*/
|
*/
|
||||||
fun selectTxtFileToWrite(
|
fun selectTxtFileToWrite(
|
||||||
activity: Activity,
|
activity: Activity,
|
||||||
fragment: Fragment?,
|
activityResultLauncher: ActivityResultLauncher<Intent>,
|
||||||
defaultFileName: String,
|
defaultFileName: String,
|
||||||
chooserHint: String,
|
chooserHint: String
|
||||||
requestCode: Int
|
|
||||||
) {
|
) {
|
||||||
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
|
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
|
||||||
intent.addCategory(Intent.CATEGORY_OPENABLE)
|
intent.addCategory(Intent.CATEGORY_OPENABLE)
|
||||||
|
@ -452,11 +451,7 @@ fun selectTxtFileToWrite(
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val chooserIntent = Intent.createChooser(intent, chooserHint)
|
val chooserIntent = Intent.createChooser(intent, chooserHint)
|
||||||
if (fragment != null) {
|
activityResultLauncher.launch(chooserIntent)
|
||||||
fragment.startActivityForResult(chooserIntent, requestCode)
|
|
||||||
} else {
|
|
||||||
activity.startActivityForResult(chooserIntent, requestCode)
|
|
||||||
}
|
|
||||||
} catch (activityNotFoundException: ActivityNotFoundException) {
|
} catch (activityNotFoundException: ActivityNotFoundException) {
|
||||||
activity.toast(R.string.error_no_external_application_found)
|
activity.toast(R.string.error_no_external_application_found)
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,8 @@ import android.content.Context
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.core.app.ActivityCompat
|
import androidx.core.app.ActivityCompat
|
||||||
|
@ -94,6 +96,12 @@ fun logPermissionStatuses(context: Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Fragment.registerForPermissionsResult(allGranted: (Boolean) -> Unit): ActivityResultLauncher<Array<String>> {
|
||||||
|
return registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { result ->
|
||||||
|
allGranted.invoke(result.keys.all { result[it] == true })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See [.checkPermissions]
|
* See [.checkPermissions]
|
||||||
*
|
*
|
||||||
|
@ -112,14 +120,14 @@ fun checkPermissions(permissionsToBeGrantedBitMap: Int,
|
||||||
* See [.checkPermissions]
|
* See [.checkPermissions]
|
||||||
*
|
*
|
||||||
* @param permissionsToBeGrantedBitMap
|
* @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)
|
* @return true if the permissions are granted (synchronous flow), false otherwise (asynchronous flow)
|
||||||
*/
|
*/
|
||||||
fun checkPermissions(permissionsToBeGrantedBitMap: Int,
|
fun checkPermissions(permissionsToBeGrantedBitMap: Int,
|
||||||
fragment: Fragment,
|
activity: Activity,
|
||||||
requestCode: Int,
|
activityResultLauncher: ActivityResultLauncher<Array<String>>,
|
||||||
@StringRes rationaleMessage: Int = 0): Boolean {
|
@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 permissionsToBeGrantedBitMap the permissions bit map to be granted
|
||||||
* @param activity the calling Activity that is requesting the permissions (or fragment parent)
|
* @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)
|
* @return true if the permissions are granted (synchronous flow), false otherwise (asynchronous flow)
|
||||||
*/
|
*/
|
||||||
private fun checkPermissions(permissionsToBeGrantedBitMap: Int,
|
private fun checkPermissions(permissionsToBeGrantedBitMap: Int,
|
||||||
activity: Activity?,
|
activity: Activity,
|
||||||
fragment: Fragment?,
|
activityResultLauncher: ActivityResultLauncher<Array<String>>?,
|
||||||
requestCode: Int,
|
requestCode: Int,
|
||||||
@StringRes rationaleMessage: Int
|
@StringRes rationaleMessage: Int
|
||||||
): Boolean {
|
): Boolean {
|
||||||
var isPermissionGranted = false
|
var isPermissionGranted = false
|
||||||
|
|
||||||
// sanity check
|
// sanity check
|
||||||
if (null == activity) {
|
if (PERMISSIONS_EMPTY == permissionsToBeGrantedBitMap) {
|
||||||
Timber.w("## checkPermissions(): invalid input data")
|
|
||||||
isPermissionGranted = false
|
|
||||||
} else if (PERMISSIONS_EMPTY == permissionsToBeGrantedBitMap) {
|
|
||||||
isPermissionGranted = true
|
isPermissionGranted = true
|
||||||
} else if (PERMISSIONS_FOR_AUDIO_IP_CALL != permissionsToBeGrantedBitMap
|
} else if (PERMISSIONS_FOR_AUDIO_IP_CALL != permissionsToBeGrantedBitMap
|
||||||
&& PERMISSIONS_FOR_VIDEO_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() }
|
.setOnCancelListener { Toast.makeText(activity, R.string.missing_permissions_warning, Toast.LENGTH_SHORT).show() }
|
||||||
.setPositiveButton(R.string.ok) { _, _ ->
|
.setPositiveButton(R.string.ok) { _, _ ->
|
||||||
if (permissionsListToBeGranted.isNotEmpty()) {
|
if (permissionsListToBeGranted.isNotEmpty()) {
|
||||||
fragment?.requestPermissions(permissionsListToBeGranted.toTypedArray(), requestCode)
|
activityResultLauncher
|
||||||
|
?.launch(permissionsListToBeGranted.toTypedArray())
|
||||||
?: run {
|
?: run {
|
||||||
ActivityCompat.requestPermissions(activity, permissionsListToBeGranted.toTypedArray(), requestCode)
|
ActivityCompat.requestPermissions(activity, permissionsListToBeGranted.toTypedArray(), requestCode)
|
||||||
}
|
}
|
||||||
|
@ -262,7 +268,8 @@ private fun checkPermissions(permissionsToBeGrantedBitMap: Int,
|
||||||
.show()
|
.show()
|
||||||
*/
|
*/
|
||||||
} else {
|
} else {
|
||||||
fragment?.requestPermissions(permissionsArrayToBeGranted, requestCode)
|
activityResultLauncher
|
||||||
|
?.launch(permissionsArrayToBeGranted)
|
||||||
?: run {
|
?: run {
|
||||||
ActivityCompat.requestPermissions(activity, permissionsArrayToBeGranted, requestCode)
|
ActivityCompat.requestPermissions(activity, permissionsArrayToBeGranted, requestCode)
|
||||||
}
|
}
|
||||||
|
@ -307,43 +314,6 @@ private fun updatePermissionsToBeGranted(activity: Activity,
|
||||||
return isRequestPermissionRequested
|
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
|
* Return true if all permissions are granted, false if not or if permission request has been cancelled
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -28,6 +28,7 @@ import android.os.Build
|
||||||
import android.os.PowerManager
|
import android.os.PowerManager
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.content.getSystemService
|
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".
|
* will return false and the notification privacy will fallback to "LOW_DETAIL".
|
||||||
*/
|
*/
|
||||||
@TargetApi(Build.VERSION_CODES.M)
|
@TargetApi(Build.VERSION_CODES.M)
|
||||||
fun requestDisablingBatteryOptimization(activity: Activity, fragment: Fragment?, requestCode: Int) {
|
fun requestDisablingBatteryOptimization(activity: Activity, activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||||
val intent = Intent()
|
val intent = Intent()
|
||||||
intent.action = Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
|
intent.action = Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
|
||||||
intent.data = Uri.parse("package:" + activity.packageName)
|
intent.data = Uri.parse("package:" + activity.packageName)
|
||||||
if (fragment != null) {
|
activityResultLauncher.launch(intent)
|
||||||
fragment.startActivityForResult(intent, requestCode)
|
|
||||||
} else {
|
|
||||||
activity.startActivityForResult(intent, requestCode)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==============================================================================================================
|
// ==============================================================================================================
|
||||||
|
@ -100,7 +97,7 @@ fun copyToClipboard(context: Context, text: CharSequence, showToast: Boolean = t
|
||||||
* Shows notification settings for the current app.
|
* 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
|
* 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<Intent>) {
|
||||||
val intent = Intent()
|
val intent = Intent()
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS
|
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_package", activity.packageName)
|
||||||
intent.putExtra("app_uid", activity.applicationInfo?.uid)
|
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)
|
fragment.startActivity(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun startAddGoogleAccountIntent(context: AppCompatActivity, requestCode: Int) {
|
fun startAddGoogleAccountIntent(context: Context, activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||||
try {
|
try {
|
||||||
val intent = Intent(Settings.ACTION_ADD_ACCOUNT)
|
val intent = Intent(Settings.ACTION_ADD_ACCOUNT)
|
||||||
intent.putExtra(Settings.EXTRA_ACCOUNT_TYPES, arrayOf("com.google"))
|
intent.putExtra(Settings.EXTRA_ACCOUNT_TYPES, arrayOf("com.google"))
|
||||||
context.startActivityForResult(intent, requestCode)
|
activityResultLauncher.launch(intent)
|
||||||
} catch (activityNotFoundException: ActivityNotFoundException) {
|
} catch (activityNotFoundException: ActivityNotFoundException) {
|
||||||
context.toast(R.string.error_no_external_application_found)
|
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<Intent>?,
|
||||||
|
chooserTitle: String?,
|
||||||
|
text: String,
|
||||||
|
subject: String? = null) {
|
||||||
val share = Intent(Intent.ACTION_SEND)
|
val share = Intent(Intent.ACTION_SEND)
|
||||||
share.type = "text/plain"
|
share.type = "text/plain"
|
||||||
share.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT)
|
share.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT)
|
||||||
// Add data to the intent, the receiving app will decide what to do with it.
|
// 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_SUBJECT, subject)
|
||||||
share.putExtra(Intent.EXTRA_TEXT, text)
|
share.putExtra(Intent.EXTRA_TEXT, text)
|
||||||
|
val intent = Intent.createChooser(share, chooserTitle)
|
||||||
try {
|
try {
|
||||||
if (requestCode != null) {
|
if (activityResultLauncher != null) {
|
||||||
fragment.startActivityForResult(Intent.createChooser(share, chooserTitle), requestCode)
|
activityResultLauncher.launch(intent)
|
||||||
} else {
|
} else {
|
||||||
fragment.startActivity(Intent.createChooser(share, chooserTitle))
|
fragment.startActivity(intent)
|
||||||
}
|
}
|
||||||
} catch (activityNotFoundException: ActivityNotFoundException) {
|
} catch (activityNotFoundException: ActivityNotFoundException) {
|
||||||
fragment.activity?.toast(R.string.error_no_external_application_found)
|
fragment.activity?.toast(R.string.error_no_external_application_found)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun startImportTextFromFileIntent(fragment: Fragment, requestCode: Int) {
|
fun startImportTextFromFileIntent(context: Context, activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||||
val intent = Intent(Intent.ACTION_GET_CONTENT).apply {
|
val intent = Intent(Intent.ACTION_GET_CONTENT).apply {
|
||||||
type = "text/plain"
|
type = "text/plain"
|
||||||
}
|
}
|
||||||
if (intent.resolveActivity(fragment.requireActivity().packageManager) != null) {
|
if (intent.resolveActivity(context.packageManager) != null) {
|
||||||
fragment.startActivityForResult(intent, requestCode)
|
activityResultLauncher.launch(intent)
|
||||||
} else {
|
} else {
|
||||||
fragment.activity?.toast(R.string.error_no_external_application_found)
|
context.toast(R.string.error_no_external_application_found)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,12 +15,11 @@
|
||||||
*/
|
*/
|
||||||
package im.vector.app.features.attachments
|
package im.vector.app.features.attachments
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
import im.vector.app.core.platform.Restorable
|
import im.vector.app.core.platform.Restorable
|
||||||
import im.vector.lib.multipicker.MultiPicker
|
import im.vector.lib.multipicker.MultiPicker
|
||||||
import org.matrix.android.sdk.BuildConfig
|
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.
|
// Capture path allows to handle camera image picking. It must be restored if the activity gets killed.
|
||||||
private var captureUri: Uri? = null
|
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.
|
// 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
|
var pendingType: AttachmentTypeSelectorView.Type? = null
|
||||||
|
|
||||||
|
@ -72,80 +72,79 @@ class AttachmentsHelper(val context: Context, val callback: Callback) : Restorab
|
||||||
/**
|
/**
|
||||||
* Starts the process for handling file picking
|
* Starts the process for handling file picking
|
||||||
*/
|
*/
|
||||||
fun selectFile(fragment: Fragment) {
|
fun selectFile(activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||||
MultiPicker.get(MultiPicker.FILE).startWith(fragment)
|
MultiPicker.get(MultiPicker.FILE).startWith(activityResultLauncher)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts the process for handling image picking
|
* Starts the process for handling image picking
|
||||||
*/
|
*/
|
||||||
fun selectGallery(fragment: Fragment) {
|
fun selectGallery(activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||||
MultiPicker.get(MultiPicker.IMAGE).startWith(fragment)
|
MultiPicker.get(MultiPicker.IMAGE).startWith(activityResultLauncher)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts the process for handling audio picking
|
* Starts the process for handling audio picking
|
||||||
*/
|
*/
|
||||||
fun selectAudio(fragment: Fragment) {
|
fun selectAudio(activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||||
MultiPicker.get(MultiPicker.AUDIO).startWith(fragment)
|
MultiPicker.get(MultiPicker.AUDIO).startWith(activityResultLauncher)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts the process for handling capture image picking
|
* Starts the process for handling capture image picking
|
||||||
*/
|
*/
|
||||||
fun openCamera(fragment: Fragment) {
|
fun openCamera(context: Context, activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||||
captureUri = MultiPicker.get(MultiPicker.CAMERA).startWithExpectingFile(fragment)
|
captureUri = MultiPicker.get(MultiPicker.CAMERA).startWithExpectingFile(context, activityResultLauncher)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts the process for handling contact picking
|
* Starts the process for handling contact picking
|
||||||
*/
|
*/
|
||||||
fun selectContact(fragment: Fragment) {
|
fun selectContact(activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||||
MultiPicker.get(MultiPicker.CONTACT).startWith(fragment)
|
MultiPicker.get(MultiPicker.CONTACT).startWith(activityResultLauncher)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This methods aims to handle on activity result data.
|
* This methods aims to handle the result data.
|
||||||
*
|
|
||||||
* @return true if it can handle the data, false otherwise
|
|
||||||
*/
|
*/
|
||||||
fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean {
|
fun onFileResult(data: Intent?) {
|
||||||
if (resultCode == Activity.RESULT_OK) {
|
|
||||||
when (requestCode) {
|
|
||||||
MultiPicker.REQUEST_CODE_PICK_FILE -> {
|
|
||||||
callback.onContentAttachmentsReady(
|
callback.onContentAttachmentsReady(
|
||||||
MultiPicker.get(MultiPicker.FILE)
|
MultiPicker.get(MultiPicker.FILE)
|
||||||
.getSelectedFiles(context, requestCode, resultCode, data)
|
.getSelectedFiles(context, data)
|
||||||
.map { it.toContentAttachmentData() }
|
.map { it.toContentAttachmentData() }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
MultiPicker.REQUEST_CODE_PICK_AUDIO -> {
|
|
||||||
|
fun onAudioResult(data: Intent?) {
|
||||||
callback.onContentAttachmentsReady(
|
callback.onContentAttachmentsReady(
|
||||||
MultiPicker.get(MultiPicker.AUDIO)
|
MultiPicker.get(MultiPicker.AUDIO)
|
||||||
.getSelectedFiles(context, requestCode, resultCode, data)
|
.getSelectedFiles(context, data)
|
||||||
.map { it.toContentAttachmentData() }
|
.map { it.toContentAttachmentData() }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
MultiPicker.REQUEST_CODE_PICK_CONTACT -> {
|
|
||||||
|
fun onContactResult(data: Intent?) {
|
||||||
MultiPicker.get(MultiPicker.CONTACT)
|
MultiPicker.get(MultiPicker.CONTACT)
|
||||||
.getSelectedFiles(context, requestCode, resultCode, data)
|
.getSelectedFiles(context, data)
|
||||||
.firstOrNull()
|
.firstOrNull()
|
||||||
?.toContactAttachment()
|
?.toContactAttachment()
|
||||||
?.let {
|
?.let {
|
||||||
callback.onContactAttachmentReady(it)
|
callback.onContactAttachmentReady(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MultiPicker.REQUEST_CODE_PICK_IMAGE -> {
|
|
||||||
|
fun onImageResult(data: Intent?) {
|
||||||
callback.onContentAttachmentsReady(
|
callback.onContentAttachmentsReady(
|
||||||
MultiPicker.get(MultiPicker.IMAGE)
|
MultiPicker.get(MultiPicker.IMAGE)
|
||||||
.getSelectedFiles(context, requestCode, resultCode, data)
|
.getSelectedFiles(context, data)
|
||||||
.map { it.toContentAttachmentData() }
|
.map { it.toContentAttachmentData() }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
MultiPicker.REQUEST_CODE_TAKE_PHOTO -> {
|
|
||||||
|
fun onPhotoResult() {
|
||||||
captureUri?.let { captureUri ->
|
captureUri?.let { captureUri ->
|
||||||
MultiPicker.get(MultiPicker.CAMERA)
|
MultiPicker.get(MultiPicker.CAMERA)
|
||||||
.getTakenPhoto(context, requestCode, resultCode, captureUri)
|
.getTakenPhoto(context, captureUri)
|
||||||
?.let {
|
?.let {
|
||||||
callback.onContentAttachmentsReady(
|
callback.onContentAttachmentsReady(
|
||||||
listOf(it).map { it.toContentAttachmentData() }
|
listOf(it).map { it.toContentAttachmentData() }
|
||||||
|
@ -153,19 +152,14 @@ class AttachmentsHelper(val context: Context, val callback: Callback) : Restorab
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MultiPicker.REQUEST_CODE_PICK_VIDEO -> {
|
|
||||||
|
fun onVideoResult(data: Intent?) {
|
||||||
callback.onContentAttachmentsReady(
|
callback.onContentAttachmentsReady(
|
||||||
MultiPicker.get(MultiPicker.VIDEO)
|
MultiPicker.get(MultiPicker.VIDEO)
|
||||||
.getSelectedFiles(context, requestCode, resultCode, data)
|
.getSelectedFiles(context, data)
|
||||||
.map { it.toContentAttachmentData() }
|
.map { it.toContentAttachmentData() }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
else -> return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This methods aims to handle share intent.
|
* This methods aims to handle share intent.
|
||||||
|
|
|
@ -30,8 +30,6 @@ import org.matrix.android.sdk.api.session.content.ContentAttachmentData
|
||||||
class AttachmentsPreviewActivity : VectorBaseActivity(), ToolbarConfigurable {
|
class AttachmentsPreviewActivity : VectorBaseActivity(), ToolbarConfigurable {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val REQUEST_CODE = 55
|
|
||||||
|
|
||||||
private const val EXTRA_FRAGMENT_ARGS = "EXTRA_FRAGMENT_ARGS"
|
private const val EXTRA_FRAGMENT_ARGS = "EXTRA_FRAGMENT_ARGS"
|
||||||
private const val ATTACHMENTS_PREVIEW_RESULT = "ATTACHMENTS_PREVIEW_RESULT"
|
private const val ATTACHMENTS_PREVIEW_RESULT = "ATTACHMENTS_PREVIEW_RESULT"
|
||||||
private const val KEEP_ORIGINAL_IMAGES_SIZE = "KEEP_ORIGINAL_IMAGES_SIZE"
|
private const val KEEP_ORIGINAL_IMAGES_SIZE = "KEEP_ORIGINAL_IMAGES_SIZE"
|
||||||
|
|
|
@ -81,6 +81,10 @@ class AttachmentsPreviewFragment @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
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 (resultCode == RESULT_OK) {
|
||||||
if (requestCode == UCrop.REQUEST_CROP && data != null) {
|
if (requestCode == UCrop.REQUEST_CROP && data != null) {
|
||||||
Timber.v("Crop success")
|
Timber.v("Crop success")
|
||||||
|
|
|
@ -298,6 +298,7 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||||
if (requestCode == CAPTURE_PERMISSION_REQUEST_CODE && allGranted(grantResults)) {
|
if (requestCode == CAPTURE_PERMISSION_REQUEST_CODE && allGranted(grantResults)) {
|
||||||
start()
|
start()
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -150,6 +150,7 @@ class VectorJitsiActivity : VectorBaseActivity(), JitsiMeetActivityInterface, Ji
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||||
JitsiMeetActivityDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
JitsiMeetActivityDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -111,6 +111,7 @@ class CreateDirectRoomActivity : SimpleFragmentActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
|
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
|
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||||
if (allGranted(grantResults)) {
|
if (allGranted(grantResults)) {
|
||||||
if (requestCode == PERMISSION_REQUEST_CODE_READ_CONTACTS) {
|
if (requestCode == PERMISSION_REQUEST_CODE_READ_CONTACTS) {
|
||||||
doOnPostResume { addFragmentToBackstack(R.id.container, ContactsBookFragment::class.java) }
|
doOnPostResume { addFragmentToBackstack(R.id.container, ContactsBookFragment::class.java) }
|
||||||
|
|
|
@ -23,6 +23,7 @@ import androidx.lifecycle.Observer
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.extensions.addFragmentToBackstack
|
import im.vector.app.core.extensions.addFragmentToBackstack
|
||||||
import im.vector.app.core.extensions.observeEvent
|
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.extensions.replaceFragment
|
||||||
import im.vector.app.core.platform.SimpleFragmentActivity
|
import im.vector.app.core.platform.SimpleFragmentActivity
|
||||||
import im.vector.app.core.ui.views.KeysBackupBanner
|
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() {
|
class KeysBackupRestoreActivity : SimpleFragmentActivity() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
private const val REQUEST_4S_SECRET = 100
|
|
||||||
const val SECRET_ALIAS = SharedSecureStorageActivity.DEFAULT_RESULT_KEYSTORE_ALIAS
|
const val SECRET_ALIAS = SharedSecureStorageActivity.DEFAULT_RESULT_KEYSTORE_ALIAS
|
||||||
|
|
||||||
fun intent(context: Context): Intent {
|
fun intent(context: Context): Intent {
|
||||||
|
@ -130,14 +129,13 @@ class KeysBackupRestoreActivity : SimpleFragmentActivity() {
|
||||||
requestedSecrets = listOf(KEYBACKUP_SECRET_SSSS_NAME),
|
requestedSecrets = listOf(KEYBACKUP_SECRET_SSSS_NAME),
|
||||||
resultKeyStoreAlias = SECRET_ALIAS
|
resultKeyStoreAlias = SECRET_ALIAS
|
||||||
).let {
|
).let {
|
||||||
startActivityForResult(it, REQUEST_4S_SECRET)
|
secretStartForActivityResult.launch(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
private val secretStartForActivityResult = registerStartForActivityResult { activityResult ->
|
||||||
if (requestCode == REQUEST_4S_SECRET) {
|
val extraResult = activityResult.data?.getStringExtra(SharedSecureStorageActivity.EXTRA_DATA_RESULT)
|
||||||
val extraResult = data?.getStringExtra(SharedSecureStorageActivity.EXTRA_DATA_RESULT)
|
if (activityResult.resultCode == Activity.RESULT_OK && extraResult != null) {
|
||||||
if (resultCode == Activity.RESULT_OK && extraResult != null) {
|
|
||||||
viewModel.handleGotSecretFromSSSS(
|
viewModel.handleGotSecretFromSSSS(
|
||||||
extraResult,
|
extraResult,
|
||||||
SECRET_ALIAS
|
SECRET_ALIAS
|
||||||
|
@ -146,6 +144,4 @@ class KeysBackupRestoreActivity : SimpleFragmentActivity() {
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
super.onActivityResult(requestCode, resultCode, data)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,9 +16,9 @@
|
||||||
package im.vector.app.features.crypto.keysbackup.restore
|
package im.vector.app.features.crypto.keysbackup.restore
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.Editable
|
import android.text.Editable
|
||||||
|
import android.view.View
|
||||||
import android.view.inputmethod.EditorInfo
|
import android.view.inputmethod.EditorInfo
|
||||||
import android.widget.EditText
|
import android.widget.EditText
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
|
@ -27,19 +27,15 @@ import butterknife.OnClick
|
||||||
import butterknife.OnTextChanged
|
import butterknife.OnTextChanged
|
||||||
import com.google.android.material.textfield.TextInputLayout
|
import com.google.android.material.textfield.TextInputLayout
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
|
import im.vector.app.core.extensions.registerStartForActivityResult
|
||||||
import im.vector.app.core.platform.VectorBaseFragment
|
import im.vector.app.core.platform.VectorBaseFragment
|
||||||
import im.vector.app.core.utils.startImportTextFromFileIntent
|
import im.vector.app.core.utils.startImportTextFromFileIntent
|
||||||
import timber.log.Timber
|
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class KeysBackupRestoreFromKeyFragment @Inject constructor()
|
class KeysBackupRestoreFromKeyFragment @Inject constructor()
|
||||||
: VectorBaseFragment() {
|
: VectorBaseFragment() {
|
||||||
|
|
||||||
companion object {
|
|
||||||
|
|
||||||
private const val REQUEST_TEXT_FILE_GET = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getLayoutResId() = R.layout.fragment_keys_backup_restore_from_key
|
override fun getLayoutResId() = R.layout.fragment_keys_backup_restore_from_key
|
||||||
|
|
||||||
private lateinit var viewModel: KeysBackupRestoreFromKeyViewModel
|
private lateinit var viewModel: KeysBackupRestoreFromKeyViewModel
|
||||||
|
@ -47,11 +43,12 @@ class KeysBackupRestoreFromKeyFragment @Inject constructor()
|
||||||
|
|
||||||
@BindView(R.id.keys_backup_key_enter_til)
|
@BindView(R.id.keys_backup_key_enter_til)
|
||||||
lateinit var mKeyInputLayout: TextInputLayout
|
lateinit var mKeyInputLayout: TextInputLayout
|
||||||
|
|
||||||
@BindView(R.id.keys_restore_key_enter_edittext)
|
@BindView(R.id.keys_restore_key_enter_edittext)
|
||||||
lateinit var mKeyTextEdit: EditText
|
lateinit var mKeyTextEdit: EditText
|
||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
viewModel = fragmentViewModelProvider.get(KeysBackupRestoreFromKeyViewModel::class.java)
|
viewModel = fragmentViewModelProvider.get(KeysBackupRestoreFromKeyViewModel::class.java)
|
||||||
sharedViewModel = activityViewModelProvider.get(KeysBackupRestoreSharedViewModel::class.java)
|
sharedViewModel = activityViewModelProvider.get(KeysBackupRestoreSharedViewModel::class.java)
|
||||||
mKeyTextEdit.setText(viewModel.recoveryCode.value)
|
mKeyTextEdit.setText(viewModel.recoveryCode.value)
|
||||||
|
@ -88,14 +85,13 @@ class KeysBackupRestoreFromKeyFragment @Inject constructor()
|
||||||
|
|
||||||
@OnClick(R.id.keys_backup_import)
|
@OnClick(R.id.keys_backup_import)
|
||||||
fun onImport() {
|
fun onImport() {
|
||||||
startImportTextFromFileIntent(this, REQUEST_TEXT_FILE_GET)
|
startImportTextFromFileIntent(requireContext(), textFileStartForActivityResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
private val textFileStartForActivityResult = registerStartForActivityResult { activityResult ->
|
||||||
if (requestCode == REQUEST_TEXT_FILE_GET && resultCode == Activity.RESULT_OK) {
|
if (activityResult.resultCode == Activity.RESULT_OK) {
|
||||||
val dataURI = data?.data
|
val dataURI = activityResult.data?.data ?: return@registerStartForActivityResult
|
||||||
if (dataURI != null) {
|
tryOrNull(message = "Failed to read recovery kay from text") {
|
||||||
try {
|
|
||||||
activity
|
activity
|
||||||
?.contentResolver
|
?.contentResolver
|
||||||
?.openInputStream(dataURI)
|
?.openInputStream(dataURI)
|
||||||
|
@ -105,12 +101,7 @@ class KeysBackupRestoreFromKeyFragment @Inject constructor()
|
||||||
mKeyTextEdit.setText(it)
|
mKeyTextEdit.setText(it)
|
||||||
mKeyTextEdit.setSelection(it.length)
|
mKeyTextEdit.setSelection(it.length)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
|
||||||
Timber.e(e, "Failed to read recovery kay from text")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
|
||||||
super.onActivityResult(requestCode, resultCode, data)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,8 +59,8 @@ class KeysBackupRestoreFromPassphraseFragment @Inject constructor() : VectorBase
|
||||||
viewModel.showPasswordMode.value = !(viewModel.showPasswordMode.value ?: false)
|
viewModel.showPasswordMode.value = !(viewModel.showPasswordMode.value ?: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
viewModel = fragmentViewModelProvider.get(KeysBackupRestoreFromPassphraseViewModel::class.java)
|
viewModel = fragmentViewModelProvider.get(KeysBackupRestoreFromPassphraseViewModel::class.java)
|
||||||
sharedViewModel = activityViewModelProvider.get(KeysBackupRestoreSharedViewModel::class.java)
|
sharedViewModel = activityViewModelProvider.get(KeysBackupRestoreSharedViewModel::class.java)
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
package im.vector.app.features.crypto.keysbackup.restore
|
package im.vector.app.features.crypto.keysbackup.restore
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import butterknife.BindView
|
import butterknife.BindView
|
||||||
|
@ -36,8 +37,8 @@ class KeysBackupRestoreSuccessFragment @Inject constructor() : VectorBaseFragmen
|
||||||
|
|
||||||
private lateinit var sharedViewModel: KeysBackupRestoreSharedViewModel
|
private lateinit var sharedViewModel: KeysBackupRestoreSharedViewModel
|
||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
sharedViewModel = activityViewModelProvider.get(KeysBackupRestoreSharedViewModel::class.java)
|
sharedViewModel = activityViewModelProvider.get(KeysBackupRestoreSharedViewModel::class.java)
|
||||||
|
|
||||||
if (compareValues(sharedViewModel.importKeyResult?.totalNumberOfKeys, 0) > 0) {
|
if (compareValues(sharedViewModel.importKeyResult?.totalNumberOfKeys, 0) > 0) {
|
||||||
|
|
|
@ -26,6 +26,7 @@ import im.vector.app.R
|
||||||
import im.vector.app.core.dialogs.ExportKeysDialog
|
import im.vector.app.core.dialogs.ExportKeysDialog
|
||||||
import im.vector.app.core.extensions.observeEvent
|
import im.vector.app.core.extensions.observeEvent
|
||||||
import im.vector.app.core.extensions.queryExportKeys
|
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.extensions.replaceFragment
|
||||||
import im.vector.app.core.platform.SimpleFragmentActivity
|
import im.vector.app.core.platform.SimpleFragmentActivity
|
||||||
import im.vector.app.core.utils.toast
|
import im.vector.app.core.utils.toast
|
||||||
|
@ -93,7 +94,7 @@ class KeysBackupSetupActivity : SimpleFragmentActivity() {
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
KeysBackupSetupSharedViewModel.NAVIGATE_MANUAL_EXPORT -> {
|
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?) {
|
private val saveStartForActivityResult = registerStartForActivityResult { activityResult ->
|
||||||
if (requestCode == REQUEST_CODE_SAVE_MEGOLM_EXPORT) {
|
if (activityResult.resultCode == Activity.RESULT_OK) {
|
||||||
val uri = data?.data
|
val uri = activityResult.data?.data
|
||||||
if (resultCode == Activity.RESULT_OK && uri != null) {
|
if (uri != null) {
|
||||||
ExportKeysDialog().show(this, object : ExportKeysDialog.ExportKeyDialogListener {
|
ExportKeysDialog().show(this, object : ExportKeysDialog.ExportKeyDialogListener {
|
||||||
override fun onPassphrase(passphrase: String) {
|
override fun onPassphrase(passphrase: String) {
|
||||||
showWaitingView()
|
showWaitingView()
|
||||||
|
@ -163,7 +164,6 @@ class KeysBackupSetupActivity : SimpleFragmentActivity() {
|
||||||
hideWaitingView()
|
hideWaitingView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
super.onActivityResult(requestCode, resultCode, data)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBackPressed() {
|
override fun onBackPressed() {
|
||||||
|
@ -198,7 +198,6 @@ class KeysBackupSetupActivity : SimpleFragmentActivity() {
|
||||||
const val KEYS_VERSION = "KEYS_VERSION"
|
const val KEYS_VERSION = "KEYS_VERSION"
|
||||||
const val MANUAL_EXPORT = "MANUAL_EXPORT"
|
const val MANUAL_EXPORT = "MANUAL_EXPORT"
|
||||||
const val EXTRA_SHOW_MANUAL_EXPORT = "SHOW_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 {
|
fun intent(context: Context, showManualExport: Boolean): Intent {
|
||||||
val intent = Intent(context, KeysBackupSetupActivity::class.java)
|
val intent = Intent(context, KeysBackupSetupActivity::class.java)
|
||||||
|
|
|
@ -40,8 +40,8 @@ class KeysBackupSetupStep1Fragment @Inject constructor() : VectorBaseFragment()
|
||||||
@BindView(R.id.keys_backup_setup_step1_manualExport)
|
@BindView(R.id.keys_backup_setup_step1_manualExport)
|
||||||
lateinit var manualExportButton: Button
|
lateinit var manualExportButton: Button
|
||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
viewModel = activityViewModelProvider.get(KeysBackupSetupSharedViewModel::class.java)
|
viewModel = activityViewModelProvider.get(KeysBackupSetupSharedViewModel::class.java)
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
package im.vector.app.features.crypto.keysbackup.setup
|
package im.vector.app.features.crypto.keysbackup.setup
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.inputmethod.EditorInfo
|
import android.view.inputmethod.EditorInfo
|
||||||
import android.widget.EditText
|
import android.widget.EditText
|
||||||
|
@ -77,8 +78,8 @@ class KeysBackupSetupStep2Fragment @Inject constructor() : VectorBaseFragment()
|
||||||
|
|
||||||
private lateinit var viewModel: KeysBackupSetupSharedViewModel
|
private lateinit var viewModel: KeysBackupSetupSharedViewModel
|
||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
viewModel = activityViewModelProvider.get(KeysBackupSetupSharedViewModel::class.java)
|
viewModel = activityViewModelProvider.get(KeysBackupSetupSharedViewModel::class.java)
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
package im.vector.app.features.crypto.keysbackup.setup
|
package im.vector.app.features.crypto.keysbackup.setup
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Intent
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
@ -31,6 +30,7 @@ import butterknife.BindView
|
||||||
import butterknife.OnClick
|
import butterknife.OnClick
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
|
import im.vector.app.core.extensions.registerStartForActivityResult
|
||||||
import im.vector.app.core.platform.VectorBaseFragment
|
import im.vector.app.core.platform.VectorBaseFragment
|
||||||
import im.vector.app.core.utils.LiveEvent
|
import im.vector.app.core.utils.LiveEvent
|
||||||
import im.vector.app.core.utils.copyToClipboard
|
import im.vector.app.core.utils.copyToClipboard
|
||||||
|
@ -48,10 +48,6 @@ import javax.inject.Inject
|
||||||
|
|
||||||
class KeysBackupSetupStep3Fragment @Inject constructor() : VectorBaseFragment() {
|
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
|
override fun getLayoutResId() = R.layout.fragment_keys_backup_setup_step3
|
||||||
|
|
||||||
@BindView(R.id.keys_backup_setup_step3_button)
|
@BindView(R.id.keys_backup_setup_step3_button)
|
||||||
|
@ -65,8 +61,8 @@ class KeysBackupSetupStep3Fragment @Inject constructor() : VectorBaseFragment()
|
||||||
|
|
||||||
private lateinit var viewModel: KeysBackupSetupSharedViewModel
|
private lateinit var viewModel: KeysBackupSetupSharedViewModel
|
||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
viewModel = activityViewModelProvider.get(KeysBackupSetupSharedViewModel::class.java)
|
viewModel = activityViewModelProvider.get(KeysBackupSetupSharedViewModel::class.java)
|
||||||
|
|
||||||
viewModel.shouldPromptOnBack = false
|
viewModel.shouldPromptOnBack = false
|
||||||
|
@ -138,19 +134,20 @@ class KeysBackupSetupStep3Fragment @Inject constructor() : VectorBaseFragment()
|
||||||
val timestamp = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(Date())
|
val timestamp = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(Date())
|
||||||
selectTxtFileToWrite(
|
selectTxtFileToWrite(
|
||||||
activity = requireActivity(),
|
activity = requireActivity(),
|
||||||
fragment = this,
|
activityResultLauncher = saveRecoveryActivityResultLauncher,
|
||||||
defaultFileName = "recovery-key-$userId-$timestamp.txt",
|
defaultFileName = "recovery-key-$userId-$timestamp.txt",
|
||||||
chooserHint = getString(R.string.save_recovery_key_chooser_hint),
|
chooserHint = getString(R.string.save_recovery_key_chooser_hint)
|
||||||
requestCode = SAVE_RECOVERY_KEY_REQUEST_CODE
|
|
||||||
)
|
)
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
}
|
}
|
||||||
|
|
||||||
dialog.findViewById<View>(R.id.keys_backup_setup_share)?.setOnClickListener {
|
dialog.findViewById<View>(R.id.keys_backup_setup_share)?.setOnClickListener {
|
||||||
startSharePlainTextIntent(this,
|
startSharePlainTextIntent(
|
||||||
context?.getString(R.string.keys_backup_setup_step3_share_intent_chooser_title),
|
fragment = this,
|
||||||
recoveryKey,
|
activityResultLauncher = null,
|
||||||
context?.getString(R.string.recovery_key))
|
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
|
viewModel.copyHasBeenMade = true
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
}
|
}
|
||||||
|
@ -202,16 +199,12 @@ class KeysBackupSetupStep3Fragment @Inject constructor() : VectorBaseFragment()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
private val saveRecoveryActivityResultLauncher = registerStartForActivityResult { activityRessult ->
|
||||||
when (requestCode) {
|
val uri = activityRessult.data?.data ?: return@registerStartForActivityResult
|
||||||
SAVE_RECOVERY_KEY_REQUEST_CODE -> {
|
if (activityRessult.resultCode == Activity.RESULT_OK) {
|
||||||
val uri = data?.data
|
|
||||||
if (resultCode == Activity.RESULT_OK && uri != null) {
|
|
||||||
viewModel.recoveryKey.value?.let {
|
viewModel.recoveryKey.value?.let {
|
||||||
exportRecoveryKeyToFile(uri, it)
|
exportRecoveryKeyToFile(uri, it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,7 +126,7 @@ class KeyRequestHandler @Inject constructor(
|
||||||
// can we get more info on this device?
|
// can we get more info on this device?
|
||||||
session?.cryptoService()?.getMyDevicesInfo()?.firstOrNull { it.deviceId == deviceId }?.let {
|
session?.cryptoService()?.getMyDevicesInfo()?.firstOrNull { it.deviceId == deviceId }?.let {
|
||||||
postAlert(context, userId, deviceId, true, deviceInfo, it)
|
postAlert(context, userId, deviceId, true, deviceInfo, it)
|
||||||
} ?: kotlin.run {
|
} ?: run {
|
||||||
postAlert(context, userId, deviceId, true, deviceInfo)
|
postAlert(context, userId, deviceId, true, deviceInfo)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -24,6 +24,8 @@ import android.os.Parcelable
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.fragment.app.FragmentManager
|
||||||
|
import androidx.fragment.app.FragmentOnAttachListener
|
||||||
import com.airbnb.mvrx.MvRx
|
import com.airbnb.mvrx.MvRx
|
||||||
import com.airbnb.mvrx.viewModel
|
import com.airbnb.mvrx.viewModel
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
|
@ -38,7 +40,10 @@ import kotlinx.android.synthetic.main.activity.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
class SharedSecureStorageActivity : SimpleFragmentActivity(), VectorBaseBottomSheetDialogFragment.ResultListener {
|
class SharedSecureStorageActivity :
|
||||||
|
SimpleFragmentActivity(),
|
||||||
|
VectorBaseBottomSheetDialogFragment.ResultListener,
|
||||||
|
FragmentOnAttachListener {
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class Args(
|
data class Args(
|
||||||
|
@ -58,6 +63,8 @@ class SharedSecureStorageActivity : SimpleFragmentActivity(), VectorBaseBottomSh
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
supportFragmentManager.addFragmentOnAttachListener(this)
|
||||||
|
|
||||||
toolbar.visibility = View.GONE
|
toolbar.visibility = View.GONE
|
||||||
|
|
||||||
viewModel.observeViewEvents { observeViewEvents(it) }
|
viewModel.observeViewEvents { observeViewEvents(it) }
|
||||||
|
@ -65,6 +72,11 @@ class SharedSecureStorageActivity : SimpleFragmentActivity(), VectorBaseBottomSh
|
||||||
viewModel.subscribe(this) { renderState(it) }
|
viewModel.subscribe(this) { renderState(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
supportFragmentManager.removeFragmentOnAttachListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onBackPressed() {
|
override fun onBackPressed() {
|
||||||
viewModel.handle(SharedSecureStorageAction.Back)
|
viewModel.handle(SharedSecureStorageAction.Back)
|
||||||
}
|
}
|
||||||
|
@ -119,8 +131,7 @@ class SharedSecureStorageActivity : SimpleFragmentActivity(), VectorBaseBottomSh
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAttachFragment(fragment: Fragment) {
|
override fun onAttachFragment(fragmentManager: FragmentManager, fragment: Fragment) {
|
||||||
super.onAttachFragment(fragment)
|
|
||||||
if (fragment is VectorBaseBottomSheetDialogFragment) {
|
if (fragment is VectorBaseBottomSheetDialogFragment) {
|
||||||
fragment.resultListener = this
|
fragment.resultListener = this
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
package im.vector.app.features.crypto.quads
|
package im.vector.app.features.crypto.quads
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.inputmethod.EditorInfo
|
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.editorActionEvents
|
||||||
import com.jakewharton.rxbinding3.widget.textChanges
|
import com.jakewharton.rxbinding3.widget.textChanges
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
|
import im.vector.app.core.extensions.registerStartForActivityResult
|
||||||
import im.vector.app.core.platform.VectorBaseFragment
|
import im.vector.app.core.platform.VectorBaseFragment
|
||||||
import im.vector.app.core.utils.startImportTextFromFileIntent
|
import im.vector.app.core.utils.startImportTextFromFileIntent
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
|
@ -61,7 +61,7 @@ class SharedSecuredStorageKeyFragment @Inject constructor() : VectorBaseFragment
|
||||||
}
|
}
|
||||||
.disposeOnDestroyView()
|
.disposeOnDestroyView()
|
||||||
|
|
||||||
ssss_key_use_file.debouncedClicks { startImportTextFromFileIntent(this, IMPORT_FILE_REQ) }
|
ssss_key_use_file.debouncedClicks { startImportTextFromFileIntent(requireContext(), importFileStartForActivityResult) }
|
||||||
|
|
||||||
ssss_key_reset.clickableView.debouncedClicks {
|
ssss_key_reset.clickableView.debouncedClicks {
|
||||||
sharedViewModel.handle(SharedSecureStorageAction.ForgotResetAll)
|
sharedViewModel.handle(SharedSecureStorageAction.ForgotResetAll)
|
||||||
|
@ -85,9 +85,9 @@ class SharedSecuredStorageKeyFragment @Inject constructor() : VectorBaseFragment
|
||||||
sharedViewModel.handle(SharedSecureStorageAction.SubmitKey(text))
|
sharedViewModel.handle(SharedSecureStorageAction.SubmitKey(text))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
private val importFileStartForActivityResult = registerStartForActivityResult { activityResult ->
|
||||||
if (requestCode == IMPORT_FILE_REQ && resultCode == Activity.RESULT_OK) {
|
if (activityResult.resultCode == Activity.RESULT_OK) {
|
||||||
data?.data?.let { dataURI ->
|
activityResult.data?.data?.let { dataURI ->
|
||||||
tryOrNull {
|
tryOrNull {
|
||||||
activity?.contentResolver?.openInputStream(dataURI)
|
activity?.contentResolver?.openInputStream(dataURI)
|
||||||
?.bufferedReader()
|
?.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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -139,7 +139,7 @@ class BootstrapCrossSigningTask @Inject constructor(
|
||||||
null,
|
null,
|
||||||
it
|
it
|
||||||
)
|
)
|
||||||
} ?: kotlin.run {
|
} ?: run {
|
||||||
ssssService.generateKey(
|
ssssService.generateKey(
|
||||||
UUID.randomUUID().toString(),
|
UUID.randomUUID().toString(),
|
||||||
params.keySpec,
|
params.keySpec,
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
package im.vector.app.features.crypto.recover
|
package im.vector.app.features.crypto.recover
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.InputType.TYPE_CLASS_TEXT
|
import android.text.InputType.TYPE_CLASS_TEXT
|
||||||
import android.text.InputType.TYPE_TEXT_FLAG_MULTI_LINE
|
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 com.jakewharton.rxbinding3.widget.textChanges
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.extensions.hideKeyboard
|
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.extensions.showPassword
|
||||||
import im.vector.app.core.platform.VectorBaseFragment
|
import im.vector.app.core.platform.VectorBaseFragment
|
||||||
import im.vector.app.core.resources.ColorProvider
|
import im.vector.app.core.resources.ColorProvider
|
||||||
|
@ -82,7 +82,7 @@ class BootstrapMigrateBackupFragment @Inject constructor(
|
||||||
bootstrapMigrateContinueButton.debouncedClicks { submit() }
|
bootstrapMigrateContinueButton.debouncedClicks { submit() }
|
||||||
bootstrapMigrateShowPassword.debouncedClicks { sharedViewModel.handle(BootstrapActions.TogglePasswordVisibility) }
|
bootstrapMigrateShowPassword.debouncedClicks { sharedViewModel.handle(BootstrapActions.TogglePasswordVisibility) }
|
||||||
bootstrapMigrateForgotPassphrase.debouncedClicks { sharedViewModel.handle(BootstrapActions.HandleForgotBackupPassphrase) }
|
bootstrapMigrateForgotPassphrase.debouncedClicks { sharedViewModel.handle(BootstrapActions.HandleForgotBackupPassphrase) }
|
||||||
bootstrapMigrateUseFile.debouncedClicks { startImportTextFromFileIntent(this, IMPORT_FILE_REQ) }
|
bootstrapMigrateUseFile.debouncedClicks { startImportTextFromFileIntent(requireContext(), importFileStartForActivityResult) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun submit() = withState(sharedViewModel) { state ->
|
private fun submit() = withState(sharedViewModel) { state ->
|
||||||
|
@ -147,9 +147,9 @@ class BootstrapMigrateBackupFragment @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
private val importFileStartForActivityResult = registerStartForActivityResult { activityResult ->
|
||||||
if (requestCode == IMPORT_FILE_REQ && resultCode == Activity.RESULT_OK) {
|
if (activityResult.resultCode == Activity.RESULT_OK) {
|
||||||
data?.data?.let { dataURI ->
|
activityResult.data?.data?.let { dataURI ->
|
||||||
tryOrNull {
|
tryOrNull {
|
||||||
activity?.contentResolver?.openInputStream(dataURI)
|
activity?.contentResolver?.openInputStream(dataURI)
|
||||||
?.bufferedReader()
|
?.bufferedReader()
|
||||||
|
@ -159,12 +159,6 @@ class BootstrapMigrateBackupFragment @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
super.onActivityResult(requestCode, resultCode, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private const val IMPORT_FILE_REQ = 0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
package im.vector.app.features.crypto.recover
|
package im.vector.app.features.crypto.recover
|
||||||
|
|
||||||
import android.app.Activity.RESULT_OK
|
import android.app.Activity
|
||||||
import android.content.ActivityNotFoundException
|
import android.content.ActivityNotFoundException
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
@ -25,6 +25,7 @@ import androidx.core.view.isVisible
|
||||||
import com.airbnb.mvrx.parentFragmentViewModel
|
import com.airbnb.mvrx.parentFragmentViewModel
|
||||||
import com.airbnb.mvrx.withState
|
import com.airbnb.mvrx.withState
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
|
import im.vector.app.core.extensions.registerStartForActivityResult
|
||||||
import im.vector.app.core.platform.VectorBaseFragment
|
import im.vector.app.core.platform.VectorBaseFragment
|
||||||
import im.vector.app.core.resources.ColorProvider
|
import im.vector.app.core.resources.ColorProvider
|
||||||
import im.vector.app.core.utils.startSharePlainTextIntent
|
import im.vector.app.core.utils.startSharePlainTextIntent
|
||||||
|
@ -65,17 +66,16 @@ class BootstrapSaveRecoveryKeyFragment @Inject constructor(
|
||||||
|
|
||||||
try {
|
try {
|
||||||
sharedViewModel.handle(BootstrapActions.SaveReqQueryStarted)
|
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) {
|
} catch (activityNotFoundException: ActivityNotFoundException) {
|
||||||
requireActivity().toast(R.string.error_no_external_application_found)
|
requireActivity().toast(R.string.error_no_external_application_found)
|
||||||
sharedViewModel.handle(BootstrapActions.SaveReqFailed)
|
sharedViewModel.handle(BootstrapActions.SaveReqFailed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
private val saveStartForActivityResult = registerStartForActivityResult { activityResult ->
|
||||||
if (requestCode == REQUEST_CODE_SAVE) {
|
if (activityResult.resultCode == Activity.RESULT_OK) {
|
||||||
val uri = data?.data
|
val uri = activityResult.data?.data ?: return@registerStartForActivityResult
|
||||||
if (resultCode == RESULT_OK && uri != null) {
|
|
||||||
GlobalScope.launch(Dispatchers.IO) {
|
GlobalScope.launch(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
sharedViewModel.handle(BootstrapActions.SaveKeyToUri(requireContext().contentResolver!!.openOutputStream(uri)!!))
|
sharedViewModel.handle(BootstrapActions.SaveKeyToUri(requireContext().contentResolver!!.openOutputStream(uri)!!))
|
||||||
|
@ -87,21 +87,25 @@ class BootstrapSaveRecoveryKeyFragment @Inject constructor(
|
||||||
// result code seems to be always cancelled here.. so act as if it was saved
|
// result code seems to be always cancelled here.. so act as if it was saved
|
||||||
sharedViewModel.handle(BootstrapActions.SaveReqFailed)
|
sharedViewModel.handle(BootstrapActions.SaveReqFailed)
|
||||||
}
|
}
|
||||||
return
|
}
|
||||||
} else if (requestCode == REQUEST_CODE_COPY) {
|
|
||||||
|
private val copyStartForActivityResult = registerStartForActivityResult { activityResult ->
|
||||||
|
if (activityResult.resultCode == Activity.RESULT_OK) {
|
||||||
sharedViewModel.handle(BootstrapActions.RecoveryKeySaved)
|
sharedViewModel.handle(BootstrapActions.RecoveryKeySaved)
|
||||||
}
|
}
|
||||||
super.onActivityResult(requestCode, resultCode, data)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun shareRecoveryKey() = withState(sharedViewModel) { state ->
|
private fun shareRecoveryKey() = withState(sharedViewModel) { state ->
|
||||||
val recoveryKey = state.recoveryKeyCreationInfo?.recoveryKey?.formatRecoveryKey()
|
val recoveryKey = state.recoveryKeyCreationInfo?.recoveryKey?.formatRecoveryKey()
|
||||||
?: return@withState
|
?: return@withState
|
||||||
|
|
||||||
startSharePlainTextIntent(this,
|
startSharePlainTextIntent(
|
||||||
|
this,
|
||||||
|
copyStartForActivityResult,
|
||||||
context?.getString(R.string.keys_backup_setup_step3_share_intent_chooser_title),
|
context?.getString(R.string.keys_backup_setup_step3_share_intent_chooser_title),
|
||||||
recoveryKey,
|
recoveryKey,
|
||||||
context?.getString(R.string.recovery_key), REQUEST_CODE_COPY)
|
context?.getString(R.string.recovery_key)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun invalidate() = withState(sharedViewModel) { state ->
|
override fun invalidate() = withState(sharedViewModel) { state ->
|
||||||
|
@ -111,9 +115,4 @@ class BootstrapSaveRecoveryKeyFragment @Inject constructor(
|
||||||
recoveryContinue.isVisible = step.isSaved
|
recoveryContinue.isVisible = step.isSaved
|
||||||
bootstrapRecoveryKeyText.text = state.recoveryKeyCreationInfo?.recoveryKey?.formatRecoveryKey()
|
bootstrapRecoveryKeyText.text = state.recoveryKeyCreationInfo?.recoveryKey?.formatRecoveryKey()
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val REQUEST_CODE_SAVE = 123
|
|
||||||
const val REQUEST_CODE_COPY = 124
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -301,7 +301,7 @@ class BootstrapSharedViewModel @AssistedInject constructor(
|
||||||
// =======================================
|
// =======================================
|
||||||
private fun saveRecoveryKeyToUri(os: OutputStream) = withState { state ->
|
private fun saveRecoveryKeyToUri(os: OutputStream) = withState { state ->
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
kotlin.runCatching {
|
runCatching {
|
||||||
os.use {
|
os.use {
|
||||||
os.write((state.recoveryKeyCreationInfo?.recoveryKey?.formatRecoveryKey() ?: "").toByteArray())
|
os.write((state.recoveryKeyCreationInfo?.recoveryKey?.formatRecoveryKey() ?: "").toByteArray())
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ package im.vector.app.features.crypto.verification
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import android.view.KeyEvent
|
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.di.ScreenComponent
|
||||||
import im.vector.app.core.extensions.commitTransaction
|
import im.vector.app.core.extensions.commitTransaction
|
||||||
import im.vector.app.core.extensions.exhaustive
|
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.VectorBaseActivity
|
||||||
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
|
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
|
||||||
import im.vector.app.features.crypto.quads.SharedSecureStorageActivity
|
import im.vector.app.features.crypto.quads.SharedSecureStorageActivity
|
||||||
|
@ -108,12 +108,12 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
||||||
when (it) {
|
when (it) {
|
||||||
is VerificationBottomSheetViewEvents.Dismiss -> dismiss()
|
is VerificationBottomSheetViewEvents.Dismiss -> dismiss()
|
||||||
is VerificationBottomSheetViewEvents.AccessSecretStore -> {
|
is VerificationBottomSheetViewEvents.AccessSecretStore -> {
|
||||||
startActivityForResult(SharedSecureStorageActivity.newIntent(
|
secretStartForActivityResult.launch(SharedSecureStorageActivity.newIntent(
|
||||||
requireContext(),
|
requireContext(),
|
||||||
null, // use default key
|
null, // use default key
|
||||||
listOf(MASTER_KEY_SSSS_NAME, USER_SIGNING_KEY_SSSS_NAME, SELF_SIGNING_KEY_SSSS_NAME, KEYBACKUP_SECRET_SSSS_NAME),
|
listOf(MASTER_KEY_SSSS_NAME, USER_SIGNING_KEY_SSSS_NAME, SELF_SIGNING_KEY_SSSS_NAME, KEYBACKUP_SECRET_SSSS_NAME),
|
||||||
SharedSecureStorageActivity.DEFAULT_RESULT_KEYSTORE_ALIAS
|
SharedSecureStorageActivity.DEFAULT_RESULT_KEYSTORE_ALIAS
|
||||||
), SECRET_REQUEST_CODE)
|
))
|
||||||
}
|
}
|
||||||
is VerificationBottomSheetViewEvents.ModalError -> {
|
is VerificationBottomSheetViewEvents.ModalError -> {
|
||||||
AlertDialog.Builder(requireContext())
|
AlertDialog.Builder(requireContext())
|
||||||
|
@ -145,10 +145,10 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
private val secretStartForActivityResult = registerStartForActivityResult { activityResult ->
|
||||||
if (resultCode == Activity.RESULT_OK && requestCode == SECRET_REQUEST_CODE) {
|
if (activityResult.resultCode == Activity.RESULT_OK) {
|
||||||
val result = data?.getStringExtra(SharedSecureStorageActivity.EXTRA_DATA_RESULT)
|
val result = activityResult.data?.getStringExtra(SharedSecureStorageActivity.EXTRA_DATA_RESULT)
|
||||||
val reset = data?.getBooleanExtra(SharedSecureStorageActivity.EXTRA_DATA_RESET, false) ?: false
|
val reset = activityResult.data?.getBooleanExtra(SharedSecureStorageActivity.EXTRA_DATA_RESET, false) ?: false
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
viewModel.handle(VerificationAction.GotResultFromSsss(result, SharedSecureStorageActivity.DEFAULT_RESULT_KEYSTORE_ALIAS))
|
viewModel.handle(VerificationAction.GotResultFromSsss(result, SharedSecureStorageActivity.DEFAULT_RESULT_KEYSTORE_ALIAS))
|
||||||
} else if (reset) {
|
} else if (reset) {
|
||||||
|
@ -156,11 +156,9 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
||||||
viewModel.handle(VerificationAction.SecuredStorageHasBeenReset)
|
viewModel.handle(VerificationAction.SecuredStorageHasBeenReset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
super.onActivityResult(requestCode, resultCode, data)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun invalidate() = withState(viewModel) { state ->
|
override fun invalidate() = withState(viewModel) { state ->
|
||||||
|
|
||||||
state.otherUserMxItem?.let { matrixItem ->
|
state.otherUserMxItem?.let { matrixItem ->
|
||||||
if (state.isMe) {
|
if (state.isMe) {
|
||||||
avatarRenderer.render(matrixItem, otherUserAvatarImageView)
|
avatarRenderer.render(matrixItem, otherUserAvatarImageView)
|
||||||
|
@ -347,9 +345,6 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
const val SECRET_REQUEST_CODE = 101
|
|
||||||
|
|
||||||
fun withArgs(roomId: String?, otherUserId: String, transactionId: String? = null): VerificationBottomSheet {
|
fun withArgs(roomId: String?, otherUserId: String, transactionId: String? = null): VerificationBottomSheet {
|
||||||
return VerificationBottomSheet().apply {
|
return VerificationBottomSheet().apply {
|
||||||
arguments = Bundle().apply {
|
arguments = Bundle().apply {
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
package im.vector.app.features.crypto.verification.choose
|
package im.vector.app.features.crypto.verification.choose
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import com.airbnb.mvrx.fragmentViewModel
|
import com.airbnb.mvrx.fragmentViewModel
|
||||||
|
@ -25,11 +24,11 @@ import com.airbnb.mvrx.withState
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.extensions.cleanup
|
import im.vector.app.core.extensions.cleanup
|
||||||
import im.vector.app.core.extensions.configureWith
|
import im.vector.app.core.extensions.configureWith
|
||||||
|
import im.vector.app.core.extensions.registerStartForActivityResult
|
||||||
import im.vector.app.core.platform.VectorBaseFragment
|
import im.vector.app.core.platform.VectorBaseFragment
|
||||||
import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO
|
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.checkPermissions
|
||||||
|
import im.vector.app.core.utils.registerForPermissionsResult
|
||||||
import im.vector.app.features.crypto.verification.VerificationAction
|
import im.vector.app.features.crypto.verification.VerificationAction
|
||||||
import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel
|
import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel
|
||||||
import im.vector.app.features.qrcode.QrCodeScannerActivity
|
import im.vector.app.features.qrcode.QrCodeScannerActivity
|
||||||
|
@ -75,16 +74,14 @@ class VerificationChooseMethodFragment @Inject constructor(
|
||||||
state.pendingRequest.invoke()?.transactionId ?: ""))
|
state.pendingRequest.invoke()?.transactionId ?: ""))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun openCamera() {
|
private val openCameraActivityResultLauncher = registerForPermissionsResult { allGranted ->
|
||||||
if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, this, PERMISSION_REQUEST_CODE_LAUNCH_CAMERA)) {
|
if (allGranted) {
|
||||||
doOpenQRCodeScanner()
|
doOpenQRCodeScanner()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
override fun openCamera() {
|
||||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, requireActivity(), openCameraActivityResultLauncher)) {
|
||||||
|
|
||||||
if (requestCode == PERMISSION_REQUEST_CODE_LAUNCH_CAMERA && allGranted(grantResults)) {
|
|
||||||
doOpenQRCodeScanner()
|
doOpenQRCodeScanner()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -94,17 +91,13 @@ class VerificationChooseMethodFragment @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun doOpenQRCodeScanner() {
|
private fun doOpenQRCodeScanner() {
|
||||||
QrCodeScannerActivity.startForResult(this)
|
QrCodeScannerActivity.startForResult(requireActivity(), scanActivityResultLauncher)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
private val scanActivityResultLauncher = registerStartForActivityResult { activityResult ->
|
||||||
super.onActivityResult(requestCode, resultCode, data)
|
if (activityResult.resultCode == Activity.RESULT_OK) {
|
||||||
|
val scannedQrCode = QrCodeScannerActivity.getResultText(activityResult.data)
|
||||||
if (resultCode == Activity.RESULT_OK) {
|
val wasQrCode = QrCodeScannerActivity.getResultIsQrCode(activityResult.data)
|
||||||
when (requestCode) {
|
|
||||||
QrCodeScannerActivity.QR_CODE_SCANNER_REQUEST_CODE -> {
|
|
||||||
val scannedQrCode = QrCodeScannerActivity.getResultText(data)
|
|
||||||
val wasQrCode = QrCodeScannerActivity.getResultIsQrCode(data)
|
|
||||||
|
|
||||||
if (wasQrCode && !scannedQrCode.isNullOrBlank()) {
|
if (wasQrCode && !scannedQrCode.isNullOrBlank()) {
|
||||||
onRemoteQrCodeScanned(scannedQrCode)
|
onRemoteQrCodeScanned(scannedQrCode)
|
||||||
|
@ -113,8 +106,6 @@ class VerificationChooseMethodFragment @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onRemoteQrCodeScanned(remoteQrCode: String) = withState(sharedViewModel) { state ->
|
private fun onRemoteQrCodeScanned(remoteQrCode: String) = withState(sharedViewModel) { state ->
|
||||||
sharedViewModel.handle(VerificationAction.RemoteQrCodeScanned(
|
sharedViewModel.handle(VerificationAction.RemoteQrCodeScanned(
|
||||||
|
|
|
@ -72,7 +72,7 @@ abstract class BottomSheetVerificationEmojisItem : VectorEpoxyModel<BottomSheetV
|
||||||
view.findViewById<TextView>(R.id.item_emoji_tv).isVisible = false
|
view.findViewById<TextView>(R.id.item_emoji_tv).isVisible = false
|
||||||
view.findViewById<ImageView>(R.id.item_emoji_image).isVisible = true
|
view.findViewById<ImageView>(R.id.item_emoji_image).isVisible = true
|
||||||
view.findViewById<ImageView>(R.id.item_emoji_image).setImageDrawable(ContextCompat.getDrawable(view.context, it))
|
view.findViewById<ImageView>(R.id.item_emoji_image).setImageDrawable(ContextCompat.getDrawable(view.context, it))
|
||||||
} ?: kotlin.run {
|
} ?: run {
|
||||||
view.findViewById<TextView>(R.id.item_emoji_tv).isVisible = true
|
view.findViewById<TextView>(R.id.item_emoji_tv).isVisible = true
|
||||||
view.findViewById<ImageView>(R.id.item_emoji_image).isVisible = false
|
view.findViewById<ImageView>(R.id.item_emoji_image).isVisible = false
|
||||||
view.findViewById<TextView>(R.id.item_emoji_tv).text = rep.emoji
|
view.findViewById<TextView>(R.id.item_emoji_tv).text = rep.emoji
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
package im.vector.app.features.discovery
|
package im.vector.app.features.discovery
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.appcompat.app.AlertDialog
|
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.configureWith
|
||||||
import im.vector.app.core.extensions.exhaustive
|
import im.vector.app.core.extensions.exhaustive
|
||||||
import im.vector.app.core.extensions.observeEvent
|
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.VectorBaseActivity
|
||||||
import im.vector.app.core.platform.VectorBaseFragment
|
import im.vector.app.core.platform.VectorBaseFragment
|
||||||
import im.vector.app.core.utils.ensureProtocol
|
import im.vector.app.core.utils.ensureProtocol
|
||||||
import im.vector.app.features.discovery.change.SetIdentityServerFragment
|
import im.vector.app.features.discovery.change.SetIdentityServerFragment
|
||||||
import im.vector.app.features.settings.VectorSettingsActivity
|
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.SharedState
|
||||||
import org.matrix.android.sdk.api.session.identity.ThreePid
|
import org.matrix.android.sdk.api.session.identity.ThreePid
|
||||||
import org.matrix.android.sdk.api.session.terms.TermsService
|
import org.matrix.android.sdk.api.session.terms.TermsService
|
||||||
|
@ -92,22 +91,19 @@ class DiscoverySettingsFragment @Inject constructor(
|
||||||
viewModel.handle(DiscoverySettingsAction.Refresh)
|
viewModel.handle(DiscoverySettingsAction.Refresh)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
private val termsActivityResultLauncher = registerStartForActivityResult {
|
||||||
if (requestCode == ReviewTermsActivity.TERMS_REQUEST_CODE) {
|
if (it.resultCode == Activity.RESULT_OK) {
|
||||||
if (Activity.RESULT_OK == resultCode) {
|
|
||||||
viewModel.handle(DiscoverySettingsAction.RetrieveBinding)
|
viewModel.handle(DiscoverySettingsAction.RetrieveBinding)
|
||||||
} else {
|
} else {
|
||||||
// add some error?
|
// add some error?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
super.onActivityResult(requestCode, resultCode, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun openIdentityServerTerms() = withState(viewModel) { state ->
|
override fun openIdentityServerTerms() = withState(viewModel) { state ->
|
||||||
if (state.termsNotSigned) {
|
if (state.termsNotSigned) {
|
||||||
navigator.openTerms(
|
navigator.openTerms(
|
||||||
this,
|
requireContext(),
|
||||||
|
termsActivityResultLauncher,
|
||||||
TermsService.ServiceType.IdentityService,
|
TermsService.ServiceType.IdentityService,
|
||||||
state.identityServer()?.ensureProtocol() ?: "",
|
state.identityServer()?.ensureProtocol() ?: "",
|
||||||
null)
|
null)
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
package im.vector.app.features.discovery.change
|
package im.vector.app.features.discovery.change
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.inputmethod.EditorInfo
|
import android.view.inputmethod.EditorInfo
|
||||||
|
@ -28,13 +27,13 @@ import com.airbnb.mvrx.withState
|
||||||
import com.jakewharton.rxbinding3.widget.textChanges
|
import com.jakewharton.rxbinding3.widget.textChanges
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.extensions.exhaustive
|
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.extensions.toReducedUrl
|
||||||
import im.vector.app.core.platform.VectorBaseActivity
|
import im.vector.app.core.platform.VectorBaseActivity
|
||||||
import im.vector.app.core.platform.VectorBaseFragment
|
import im.vector.app.core.platform.VectorBaseFragment
|
||||||
import im.vector.app.core.resources.ColorProvider
|
import im.vector.app.core.resources.ColorProvider
|
||||||
import im.vector.app.core.utils.colorizeMatchingText
|
import im.vector.app.core.utils.colorizeMatchingText
|
||||||
import im.vector.app.features.discovery.DiscoverySharedViewModel
|
import im.vector.app.features.discovery.DiscoverySharedViewModel
|
||||||
import im.vector.app.features.terms.ReviewTermsActivity
|
|
||||||
import org.matrix.android.sdk.api.session.terms.TermsService
|
import org.matrix.android.sdk.api.session.terms.TermsService
|
||||||
import kotlinx.android.synthetic.main.fragment_set_identity_server.*
|
import kotlinx.android.synthetic.main.fragment_set_identity_server.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -121,7 +120,8 @@ class SetIdentityServerFragment @Inject constructor(
|
||||||
is SetIdentityServerViewEvents.TermsAccepted -> processIdentityServerChange()
|
is SetIdentityServerViewEvents.TermsAccepted -> processIdentityServerChange()
|
||||||
is SetIdentityServerViewEvents.ShowTerms -> {
|
is SetIdentityServerViewEvents.ShowTerms -> {
|
||||||
navigator.openTerms(
|
navigator.openTerms(
|
||||||
this,
|
requireContext(),
|
||||||
|
termsActivityResultLauncher,
|
||||||
TermsService.ServiceType.IdentityService,
|
TermsService.ServiceType.IdentityService,
|
||||||
it.identityServerUrl,
|
it.identityServerUrl,
|
||||||
null)
|
null)
|
||||||
|
@ -150,16 +150,13 @@ class SetIdentityServerFragment @Inject constructor(
|
||||||
(activity as? VectorBaseActivity)?.supportActionBar?.setTitle(R.string.identity_server)
|
(activity as? VectorBaseActivity)?.supportActionBar?.setTitle(R.string.identity_server)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
private val termsActivityResultLauncher = registerStartForActivityResult {
|
||||||
if (requestCode == ReviewTermsActivity.TERMS_REQUEST_CODE) {
|
if (it.resultCode == Activity.RESULT_OK) {
|
||||||
if (Activity.RESULT_OK == resultCode) {
|
|
||||||
processIdentityServerChange()
|
processIdentityServerChange()
|
||||||
} else {
|
} else {
|
||||||
// add some error?
|
// add some error?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
super.onActivityResult(requestCode, resultCode, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun processIdentityServerChange() {
|
private fun processIdentityServerChange() {
|
||||||
viewModel.currentWantedUrl?.let {
|
viewModel.currentWantedUrl?.let {
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
package im.vector.app.features.home.room.detail
|
package im.vector.app.features.home.room.detail
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Activity.RESULT_OK
|
import android.app.Activity
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.Typeface
|
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.cleanup
|
||||||
import im.vector.app.core.extensions.exhaustive
|
import im.vector.app.core.extensions.exhaustive
|
||||||
import im.vector.app.core.extensions.hideKeyboard
|
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.setTextOrHide
|
||||||
import im.vector.app.core.extensions.showKeyboard
|
import im.vector.app.core.extensions.showKeyboard
|
||||||
import im.vector.app.core.extensions.trackItemsVisibilityChange
|
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_AUDIO_IP_CALL
|
||||||
import im.vector.app.core.utils.PERMISSIONS_FOR_VIDEO_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.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.TextUtils
|
||||||
import im.vector.app.core.utils.allGranted
|
|
||||||
import im.vector.app.core.utils.checkPermissions
|
import im.vector.app.core.utils.checkPermissions
|
||||||
import im.vector.app.core.utils.colorizeMatchingText
|
import im.vector.app.core.utils.colorizeMatchingText
|
||||||
import im.vector.app.core.utils.copyToClipboard
|
import im.vector.app.core.utils.copyToClipboard
|
||||||
import im.vector.app.core.utils.createJSonViewerStyleProvider
|
import im.vector.app.core.utils.createJSonViewerStyleProvider
|
||||||
import im.vector.app.core.utils.createUIHandler
|
import im.vector.app.core.utils.createUIHandler
|
||||||
import im.vector.app.core.utils.isValidUrl
|
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.openUrlInExternalBrowser
|
||||||
|
import im.vector.app.core.utils.registerForPermissionsResult
|
||||||
import im.vector.app.core.utils.saveMedia
|
import im.vector.app.core.utils.saveMedia
|
||||||
import im.vector.app.core.utils.shareMedia
|
import im.vector.app.core.utils.shareMedia
|
||||||
import im.vector.app.core.utils.toast
|
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.item.ReadReceiptData
|
||||||
import im.vector.app.features.home.room.detail.timeline.reactions.ViewReactionsBottomSheet
|
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.RoomWidgetsBottomSheet
|
||||||
import im.vector.app.features.home.room.detail.widget.WidgetRequestCodes
|
|
||||||
import im.vector.app.features.html.EventHtmlRenderer
|
import im.vector.app.features.html.EventHtmlRenderer
|
||||||
import im.vector.app.features.html.PillImageSpan
|
import im.vector.app.features.html.PillImageSpan
|
||||||
import im.vector.app.features.invite.VectorInviteView
|
import im.vector.app.features.invite.VectorInviteView
|
||||||
|
@ -206,8 +202,6 @@ data class RoomDetailArgs(
|
||||||
val sharedData: SharedData? = null
|
val sharedData: SharedData? = null
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
||||||
private const val REACTION_SELECT_REQUEST_CODE = 0
|
|
||||||
|
|
||||||
class RoomDetailFragment @Inject constructor(
|
class RoomDetailFragment @Inject constructor(
|
||||||
private val session: Session,
|
private val session: Session,
|
||||||
private val avatarRenderer: AvatarRenderer,
|
private val avatarRenderer: AvatarRenderer,
|
||||||
|
@ -234,11 +228,6 @@ class RoomDetailFragment @Inject constructor(
|
||||||
ActiveCallView.Callback {
|
ActiveCallView.Callback {
|
||||||
|
|
||||||
companion object {
|
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.
|
* Sanitize the display name.
|
||||||
*
|
*
|
||||||
|
@ -371,6 +360,10 @@ class RoomDetailFragment @Inject constructor(
|
||||||
is RoomDetailViewEvents.RequestNativeWidgetPermission -> requestNativeWidgetPermission(it)
|
is RoomDetailViewEvents.RequestNativeWidgetPermission -> requestNativeWidgetPermission(it)
|
||||||
}.exhaustive
|
}.exhaustive
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (savedInstanceState == null) {
|
||||||
|
handleShareData()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun requestNativeWidgetPermission(it: RoomDetailViewEvents.RequestNativeWidgetPermission) {
|
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) {
|
private fun openIntegrationManager(screen: String? = null) {
|
||||||
navigator.openIntegrationManager(
|
navigator.openIntegrationManager(
|
||||||
fragment = this,
|
context = requireContext(),
|
||||||
|
activityResultLauncher = integrationManagerActivityResultLauncher,
|
||||||
roomId = roomDetailArgs.roomId,
|
roomId = roomDetailArgs.roomId,
|
||||||
integId = null,
|
integId = null,
|
||||||
screen = screen
|
screen = screen
|
||||||
|
@ -440,7 +438,7 @@ class RoomDetailFragment @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun openStickerPicker(event: RoomDetailViewEvents.OpenStickerPicker) {
|
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) {
|
private fun startOpenFileIntent(action: RoomDetailViewEvents.OpenFile) {
|
||||||
|
@ -480,9 +478,7 @@ class RoomDetailFragment @Inject constructor(
|
||||||
navigator.openRoom(vectorBaseActivity, action.roomId)
|
navigator.openRoom(vectorBaseActivity, action.roomId)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
private fun handleShareData() {
|
||||||
super.onActivityCreated(savedInstanceState)
|
|
||||||
if (savedInstanceState == null) {
|
|
||||||
when (val sharedData = roomDetailArgs.sharedData) {
|
when (val sharedData = roomDetailArgs.sharedData) {
|
||||||
is SharedData.Text -> {
|
is SharedData.Text -> {
|
||||||
roomDetailViewModel.handle(RoomDetailAction.EnterRegularMode(sharedData.text, fromSharing = true))
|
roomDetailViewModel.handle(RoomDetailAction.EnterRegularMode(sharedData.text, fromSharing = true))
|
||||||
|
@ -494,7 +490,6 @@ class RoomDetailFragment @Inject constructor(
|
||||||
null -> Timber.v("No share data to process")
|
null -> Timber.v("No share data to process")
|
||||||
}.exhaustive
|
}.exhaustive
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
timelineEventController.callback = null
|
timelineEventController.callback = null
|
||||||
|
@ -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) {
|
private fun safeStartCall2(isVideoCall: Boolean) {
|
||||||
val startCallAction = RoomDetailAction.StartCall(isVideoCall)
|
val startCallAction = RoomDetailAction.StartCall(isVideoCall)
|
||||||
roomDetailViewModel.pendingAction = startCallAction
|
roomDetailViewModel.pendingAction = startCallAction
|
||||||
if (isVideoCall) {
|
if (isVideoCall) {
|
||||||
if (checkPermissions(PERMISSIONS_FOR_VIDEO_IP_CALL,
|
if (checkPermissions(PERMISSIONS_FOR_VIDEO_IP_CALL,
|
||||||
this, VIDEO_CALL_PERMISSION_REQUEST_CODE,
|
requireActivity(),
|
||||||
|
startCallActivityResultLauncher,
|
||||||
R.string.permissions_rationale_msg_camera_and_audio)) {
|
R.string.permissions_rationale_msg_camera_and_audio)) {
|
||||||
roomDetailViewModel.pendingAction = null
|
roomDetailViewModel.pendingAction = null
|
||||||
roomDetailViewModel.handle(startCallAction)
|
roomDetailViewModel.handle(startCallAction)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (checkPermissions(PERMISSIONS_FOR_AUDIO_IP_CALL,
|
if (checkPermissions(PERMISSIONS_FOR_AUDIO_IP_CALL,
|
||||||
this, AUDIO_CALL_PERMISSION_REQUEST_CODE,
|
requireActivity(),
|
||||||
|
startCallActivityResultLauncher,
|
||||||
R.string.permissions_rationale_msg_record_audio)) {
|
R.string.permissions_rationale_msg_record_audio)) {
|
||||||
roomDetailViewModel.pendingAction = null
|
roomDetailViewModel.pendingAction = null
|
||||||
roomDetailViewModel.handle(startCallAction)
|
roomDetailViewModel.handle(startCallAction)
|
||||||
|
@ -879,28 +888,64 @@ class RoomDetailFragment @Inject constructor(
|
||||||
roomDetailViewModel.handle(RoomDetailAction.SaveDraft(composerLayout.composerEditText.text.toString()))
|
roomDetailViewModel.handle(RoomDetailAction.SaveDraft(composerLayout.composerEditText.text.toString()))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
private val attachmentFileActivityResultLauncher = registerStartForActivityResult {
|
||||||
val hasBeenHandled = attachmentsHelper.onActivityResult(requestCode, resultCode, data)
|
if (it.resultCode == Activity.RESULT_OK) {
|
||||||
if (!hasBeenHandled && resultCode == RESULT_OK && data != null) {
|
attachmentsHelper.onImageResult(it.data)
|
||||||
when (requestCode) {
|
}
|
||||||
AttachmentsPreviewActivity.REQUEST_CODE -> {
|
}
|
||||||
|
|
||||||
|
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 sendData = AttachmentsPreviewActivity.getOutput(data)
|
||||||
val keepOriginalSize = AttachmentsPreviewActivity.getKeepOriginalSize(data)
|
val keepOriginalSize = AttachmentsPreviewActivity.getKeepOriginalSize(data)
|
||||||
roomDetailViewModel.handle(RoomDetailAction.SendMedia(sendData, !keepOriginalSize))
|
roomDetailViewModel.handle(RoomDetailAction.SendMedia(sendData, !keepOriginalSize))
|
||||||
}
|
}
|
||||||
REACTION_SELECT_REQUEST_CODE -> {
|
}
|
||||||
val (eventId, reaction) = EmojiReactionPickerActivity.getOutput(data) ?: return
|
|
||||||
|
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))
|
roomDetailViewModel.handle(RoomDetailAction.SendReaction(eventId, reaction))
|
||||||
}
|
}
|
||||||
WidgetRequestCodes.STICKER_PICKER_REQUEST_CODE -> {
|
}
|
||||||
val content = WidgetActivity.getOutput(data).toModel<MessageStickerContent>() ?: return
|
}
|
||||||
|
|
||||||
|
private val stickerActivityResultLauncher = registerStartForActivityResult { activityResult ->
|
||||||
|
val data = activityResult.data ?: return@registerStartForActivityResult
|
||||||
|
if (activityResult.resultCode == Activity.RESULT_OK) {
|
||||||
|
WidgetActivity.getOutput(data).toModel<MessageStickerContent>()
|
||||||
|
?.let { content ->
|
||||||
roomDetailViewModel.handle(RoomDetailAction.SendSticker(content))
|
roomDetailViewModel.handle(RoomDetailAction.SendSticker(content))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO why don't we call super here?
|
|
||||||
// super.onActivityResult(requestCode, resultCode, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PRIVATE METHODS *****************************************************************************
|
// 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() {
|
private fun setupComposer() {
|
||||||
autoCompleter.setup(composerLayout.composerEditText)
|
autoCompleter.setup(composerLayout.composerEditText)
|
||||||
|
|
||||||
|
@ -1026,7 +1083,7 @@ class RoomDetailFragment @Inject constructor(
|
||||||
|
|
||||||
override fun onRichContentSelected(contentUri: Uri): Boolean {
|
override fun onRichContentSelected(contentUri: Uri): Boolean {
|
||||||
// We need WRITE_EXTERNAL permission
|
// 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)
|
sendUri(contentUri)
|
||||||
} else {
|
} else {
|
||||||
roomDetailViewModel.pendingUri = contentUri
|
roomDetailViewModel.pendingUri = contentUri
|
||||||
|
@ -1416,53 +1473,12 @@ class RoomDetailFragment @Inject constructor(
|
||||||
// // }
|
// // }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
private fun cleanUpAfterPermissionNotGranted() {
|
||||||
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
|
// Reset all pending data
|
||||||
roomDetailViewModel.pendingAction = null
|
roomDetailViewModel.pendingAction = null
|
||||||
roomDetailViewModel.pendingUri = null
|
roomDetailViewModel.pendingUri = null
|
||||||
attachmentsHelper.pendingType = null
|
attachmentsHelper.pendingType = null
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// override fun onAudioMessageClicked(messageAudioContent: MessageAudioContent) {
|
// override fun onAudioMessageClicked(messageAudioContent: MessageAudioContent) {
|
||||||
// vectorBaseActivity.notImplemented("open audio file")
|
// vectorBaseActivity.notImplemented("open audio file")
|
||||||
|
@ -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) {
|
private fun onSaveActionClicked(action: EventSharedAction.Save) {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q
|
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
|
sharedActionViewModel.pendingAction = action
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1611,7 +1638,7 @@ class RoomDetailFragment @Inject constructor(
|
||||||
openRoomMemberProfile(action.userId)
|
openRoomMemberProfile(action.userId)
|
||||||
}
|
}
|
||||||
is EventSharedAction.AddReaction -> {
|
is EventSharedAction.AddReaction -> {
|
||||||
startActivityForResult(EmojiReactionPickerActivity.intent(requireContext(), action.eventId), REACTION_SELECT_REQUEST_CODE)
|
emojiActivityResultLauncher.launch(EmojiReactionPickerActivity.intent(requireContext(), action.eventId))
|
||||||
}
|
}
|
||||||
is EventSharedAction.ViewReactions -> {
|
is EventSharedAction.ViewReactions -> {
|
||||||
ViewReactionsBottomSheet.newInstance(roomDetailArgs.roomId, action.messageInformationData)
|
ViewReactionsBottomSheet.newInstance(roomDetailArgs.roomId, action.messageInformationData)
|
||||||
|
@ -1816,8 +1843,20 @@ class RoomDetailFragment @Inject constructor(
|
||||||
|
|
||||||
// AttachmentTypeSelectorView.Callback
|
// 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) {
|
override fun onTypeSelected(type: AttachmentTypeSelectorView.Type) {
|
||||||
if (checkPermissions(type.permissionsBit, this, PERMISSION_REQUEST_CODE_PICK_ATTACHMENT)) {
|
if (checkPermissions(type.permissionsBit, requireActivity(), typeSelectedActivityResultLauncher)) {
|
||||||
launchAttachmentProcess(type)
|
launchAttachmentProcess(type)
|
||||||
} else {
|
} else {
|
||||||
attachmentsHelper.pendingType = type
|
attachmentsHelper.pendingType = type
|
||||||
|
@ -1826,11 +1865,11 @@ class RoomDetailFragment @Inject constructor(
|
||||||
|
|
||||||
private fun launchAttachmentProcess(type: AttachmentTypeSelectorView.Type) {
|
private fun launchAttachmentProcess(type: AttachmentTypeSelectorView.Type) {
|
||||||
when (type) {
|
when (type) {
|
||||||
AttachmentTypeSelectorView.Type.CAMERA -> attachmentsHelper.openCamera(this)
|
AttachmentTypeSelectorView.Type.CAMERA -> attachmentsHelper.openCamera(requireContext(), attachmentPhotoActivityResultLauncher)
|
||||||
AttachmentTypeSelectorView.Type.FILE -> attachmentsHelper.selectFile(this)
|
AttachmentTypeSelectorView.Type.FILE -> attachmentsHelper.selectFile(attachmentFileActivityResultLauncher)
|
||||||
AttachmentTypeSelectorView.Type.GALLERY -> attachmentsHelper.selectGallery(this)
|
AttachmentTypeSelectorView.Type.GALLERY -> attachmentsHelper.selectGallery(attachmentImageActivityResultLauncher)
|
||||||
AttachmentTypeSelectorView.Type.AUDIO -> attachmentsHelper.selectAudio(this)
|
AttachmentTypeSelectorView.Type.AUDIO -> attachmentsHelper.selectAudio(attachmentAudioActivityResultLauncher)
|
||||||
AttachmentTypeSelectorView.Type.CONTACT -> attachmentsHelper.selectContact(this)
|
AttachmentTypeSelectorView.Type.CONTACT -> attachmentsHelper.selectContact(attachmentContactActivityResultLauncher)
|
||||||
AttachmentTypeSelectorView.Type.STICKER -> roomDetailViewModel.handle(RoomDetailAction.SelectStickerAttachment)
|
AttachmentTypeSelectorView.Type.STICKER -> roomDetailViewModel.handle(RoomDetailAction.SelectStickerAttachment)
|
||||||
}.exhaustive
|
}.exhaustive
|
||||||
}
|
}
|
||||||
|
@ -1849,7 +1888,7 @@ class RoomDetailFragment @Inject constructor(
|
||||||
}
|
}
|
||||||
if (grouped.previewables.isNotEmpty()) {
|
if (grouped.previewables.isNotEmpty()) {
|
||||||
val intent = AttachmentsPreviewActivity.newIntent(requireContext(), AttachmentsPreviewArgs(grouped.previewables))
|
val intent = AttachmentsPreviewActivity.newIntent(requireContext(), AttachmentsPreviewArgs(grouped.previewables))
|
||||||
startActivityForResult(intent, AttachmentsPreviewActivity.REQUEST_CODE)
|
contentAttachmentActivityResultLauncher.launch(intent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1074,7 +1074,7 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||||
.buffer(1, TimeUnit.SECONDS)
|
.buffer(1, TimeUnit.SECONDS)
|
||||||
.filter { it.isNotEmpty() }
|
.filter { it.isNotEmpty() }
|
||||||
.subscribeBy(onNext = { actions ->
|
.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
|
val globalMostRecentDisplayedEvent = mostRecentDisplayedEvent
|
||||||
if (trackUnreadMessages.get()) {
|
if (trackUnreadMessages.get()) {
|
||||||
if (globalMostRecentDisplayedEvent == null) {
|
if (globalMostRecentDisplayedEvent == null) {
|
||||||
|
|
|
@ -18,6 +18,7 @@ package im.vector.app.features.home.room.detail.readreceipts
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
|
import android.view.View
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import butterknife.BindView
|
import butterknife.BindView
|
||||||
import com.airbnb.mvrx.MvRx
|
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 getLayoutResId() = R.layout.bottom_sheet_generic_list_with_title
|
||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
sharedActionViewModel = activityViewModelProvider.get(MessageSharedActionViewModel::class.java)
|
sharedActionViewModel = activityViewModelProvider.get(MessageSharedActionViewModel::class.java)
|
||||||
recyclerView.configureWith(epoxyController, hasFixedSize = false)
|
recyclerView.configureWith(epoxyController, hasFixedSize = false)
|
||||||
bottomSheetTitle.text = getString(R.string.seen_by)
|
bottomSheetTitle.text = getString(R.string.seen_by)
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
package im.vector.app.features.home.room.detail.timeline.action
|
package im.vector.app.features.home.room.detail.timeline.action
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import butterknife.BindView
|
import butterknife.BindView
|
||||||
import com.airbnb.mvrx.fragmentViewModel
|
import com.airbnb.mvrx.fragmentViewModel
|
||||||
|
@ -51,8 +52,8 @@ class MessageActionsBottomSheet : VectorBaseBottomSheetDialogFragment(), Message
|
||||||
|
|
||||||
override fun getLayoutResId() = R.layout.bottom_sheet_generic_list
|
override fun getLayoutResId() = R.layout.bottom_sheet_generic_list
|
||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
sharedActionViewModel = activityViewModelProvider.get(MessageSharedActionViewModel::class.java)
|
sharedActionViewModel = activityViewModelProvider.get(MessageSharedActionViewModel::class.java)
|
||||||
recyclerView.configureWith(messageActionsEpoxyController, hasFixedSize = false, disableItemAnimation = true)
|
recyclerView.configureWith(messageActionsEpoxyController, hasFixedSize = false, disableItemAnimation = true)
|
||||||
messageActionsEpoxyController.listener = this
|
messageActionsEpoxyController.listener = this
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
package im.vector.app.features.home.room.detail.timeline.edithistory
|
package im.vector.app.features.home.room.detail.timeline.edithistory
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import butterknife.BindView
|
import butterknife.BindView
|
||||||
import com.airbnb.mvrx.MvRx
|
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 getLayoutResId() = R.layout.bottom_sheet_generic_list_with_title
|
||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
recyclerView.configureWith(
|
recyclerView.configureWith(
|
||||||
epoxyController,
|
epoxyController,
|
||||||
showDivider = true,
|
showDivider = true,
|
||||||
|
|
|
@ -84,7 +84,7 @@ abstract class MessagePollItem : AbsMessageItem<MessagePollItem.Holder>() {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
holder.resultWrapper.isVisible = true
|
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 ->
|
optionsContent?.options?.forEachIndexed { index, item ->
|
||||||
if (index < resultLines.size) {
|
if (index < resultLines.size) {
|
||||||
val optionCount = votes?.get(index) ?: 0
|
val optionCount = votes?.get(index) ?: 0
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package im.vector.app.features.home.room.detail.timeline.reactions
|
package im.vector.app.features.home.room.detail.timeline.reactions
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import butterknife.BindView
|
import butterknife.BindView
|
||||||
import com.airbnb.mvrx.MvRx
|
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 getLayoutResId() = R.layout.bottom_sheet_generic_list_with_title
|
||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
sharedActionViewModel = activityViewModelProvider.get(MessageSharedActionViewModel::class.java)
|
sharedActionViewModel = activityViewModelProvider.get(MessageSharedActionViewModel::class.java)
|
||||||
recyclerView.configureWith(epoxyController, hasFixedSize = false, showDivider = true)
|
recyclerView.configureWith(epoxyController, hasFixedSize = false, showDivider = true)
|
||||||
bottomSheetTitle.text = context?.getString(R.string.reactions)
|
bottomSheetTitle.text = context?.getString(R.string.reactions)
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package im.vector.app.features.home.room.detail.widget
|
package im.vector.app.features.home.room.detail.widget
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import butterknife.BindView
|
import butterknife.BindView
|
||||||
import com.airbnb.mvrx.parentFragmentViewModel
|
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 getLayoutResId() = R.layout.bottom_sheet_generic_list_with_title
|
||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
recyclerView.configureWith(epoxyController, hasFixedSize = false)
|
recyclerView.configureWith(epoxyController, hasFixedSize = false)
|
||||||
bottomSheetTitle.text = getString(R.string.active_widgets_title)
|
bottomSheetTitle.text = getString(R.string.active_widgets_title)
|
||||||
bottomSheetTitle.textSize = 20f
|
bottomSheetTitle.textSize = 20f
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -18,6 +18,7 @@ package im.vector.app.features.home.room.list.actions
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
|
import android.view.View
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import butterknife.BindView
|
import butterknife.BindView
|
||||||
import com.airbnb.mvrx.fragmentViewModel
|
import com.airbnb.mvrx.fragmentViewModel
|
||||||
|
@ -67,8 +68,8 @@ class RoomListQuickActionsBottomSheet : VectorBaseBottomSheetDialogFragment(), R
|
||||||
|
|
||||||
override fun getLayoutResId() = R.layout.bottom_sheet_generic_list
|
override fun getLayoutResId() = R.layout.bottom_sheet_generic_list
|
||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
sharedActionViewModel = activityViewModelProvider.get(RoomListQuickActionsSharedActionViewModel::class.java)
|
sharedActionViewModel = activityViewModelProvider.get(RoomListQuickActionsSharedActionViewModel::class.java)
|
||||||
recyclerView.configureWith(roomListActionsEpoxyController, viewPool = sharedViewPool, hasFixedSize = false, disableItemAnimation = true)
|
recyclerView.configureWith(roomListActionsEpoxyController, viewPool = sharedViewPool, hasFixedSize = false, disableItemAnimation = true)
|
||||||
roomListActionsEpoxyController.listener = this
|
roomListActionsEpoxyController.listener = this
|
||||||
|
|
|
@ -112,6 +112,7 @@ class InviteUsersToRoomActivity : SimpleFragmentActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
|
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
|
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||||
if (allGranted(grantResults)) {
|
if (allGranted(grantResults)) {
|
||||||
if (requestCode == PERMISSION_REQUEST_CODE_READ_CONTACTS) {
|
if (requestCode == PERMISSION_REQUEST_CODE_READ_CONTACTS) {
|
||||||
doOnPostResume { addFragmentToBackstack(R.id.container, ContactsBookFragment::class.java) }
|
doOnPostResume { addFragmentToBackstack(R.id.container, ContactsBookFragment::class.java) }
|
||||||
|
|
|
@ -30,6 +30,7 @@ import com.yalantis.ucrop.UCrop
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.di.ActiveSessionHolder
|
import im.vector.app.core.di.ActiveSessionHolder
|
||||||
import im.vector.app.core.di.ScreenComponent
|
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.platform.VectorBaseActivity
|
||||||
import im.vector.app.core.resources.ColorProvider
|
import im.vector.app.core.resources.ColorProvider
|
||||||
import im.vector.app.core.resources.StringProvider
|
import im.vector.app.core.resources.StringProvider
|
||||||
|
@ -112,10 +113,10 @@ class BigImageViewerActivity : VectorBaseActivity() {
|
||||||
private fun onAvatarTypeSelected(isCamera: Boolean) {
|
private fun onAvatarTypeSelected(isCamera: Boolean) {
|
||||||
if (isCamera) {
|
if (isCamera) {
|
||||||
if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, this, PERMISSION_REQUEST_CODE_LAUNCH_CAMERA)) {
|
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 {
|
} else {
|
||||||
MultiPicker.get(MultiPicker.IMAGE).single().startWith(this)
|
MultiPicker.get(MultiPicker.IMAGE).single().startWith(pickImageActivityResultLauncher)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,33 +128,43 @@ class BigImageViewerActivity : VectorBaseActivity() {
|
||||||
.start(this)
|
.start(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
private val takePhotoActivityResultLauncher = registerStartForActivityResult { activityResult ->
|
||||||
if (resultCode == Activity.RESULT_OK) {
|
if (activityResult.resultCode == Activity.RESULT_OK) {
|
||||||
when (requestCode) {
|
|
||||||
MultiPicker.REQUEST_CODE_TAKE_PHOTO -> {
|
|
||||||
avatarCameraUri?.let { uri ->
|
avatarCameraUri?.let { uri ->
|
||||||
MultiPicker.get(MultiPicker.CAMERA)
|
MultiPicker.get(MultiPicker.CAMERA)
|
||||||
.getTakenPhoto(this, requestCode, resultCode, uri)
|
.getTakenPhoto(this, uri)
|
||||||
?.let {
|
?.let {
|
||||||
onRoomAvatarSelected(it)
|
onRoomAvatarSelected(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MultiPicker.REQUEST_CODE_PICK_IMAGE -> {
|
}
|
||||||
|
|
||||||
|
private val pickImageActivityResultLauncher = registerStartForActivityResult { activityResult ->
|
||||||
|
if (activityResult.resultCode == Activity.RESULT_OK) {
|
||||||
MultiPicker
|
MultiPicker
|
||||||
.get(MultiPicker.IMAGE)
|
.get(MultiPicker.IMAGE)
|
||||||
.getSelectedFiles(this, requestCode, resultCode, data)
|
.getSelectedFiles(this, activityResult.data)
|
||||||
.firstOrNull()?.let {
|
.firstOrNull()?.let {
|
||||||
onRoomAvatarSelected(it)
|
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)) }
|
UCrop.REQUEST_CROP -> data?.let { onAvatarCropped(UCrop.getOutput(it)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
super.onActivityResult(requestCode, resultCode, data)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||||
if (allGranted(grantResults)) {
|
if (allGranted(grantResults)) {
|
||||||
when (requestCode) {
|
when (requestCode) {
|
||||||
PERMISSION_REQUEST_CODE_LAUNCH_CAMERA -> onAvatarTypeSelected(true)
|
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_TITLE = "EXTRA_TITLE"
|
||||||
private const val EXTRA_IMAGE_URL = "EXTRA_IMAGE_URL"
|
private const val EXTRA_IMAGE_URL = "EXTRA_IMAGE_URL"
|
||||||
private const val EXTRA_CAN_EDIT_IMAGE = "EXTRA_CAN_EDIT_IMAGE"
|
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 {
|
fun newIntent(context: Context, title: String?, imageUrl: String, canEditImage: Boolean = false): Intent {
|
||||||
return Intent(context, BigImageViewerActivity::class.java).apply {
|
return Intent(context, BigImageViewerActivity::class.java).apply {
|
||||||
|
|
|
@ -21,11 +21,11 @@ import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.Window
|
import android.view.Window
|
||||||
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
import androidx.core.app.ActivityOptionsCompat
|
import androidx.core.app.ActivityOptionsCompat
|
||||||
import androidx.core.app.TaskStackBuilder
|
import androidx.core.app.TaskStackBuilder
|
||||||
import androidx.core.util.Pair
|
import androidx.core.util.Pair
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.di.ActiveSessionHolder
|
import im.vector.app.core.di.ActiveSessionHolder
|
||||||
import im.vector.app.core.error.fatalError
|
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.RoomDetailArgs
|
||||||
import im.vector.app.features.home.room.detail.search.SearchActivity
|
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.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.home.room.filtered.FilteredRoomsActivity
|
||||||
import im.vector.app.features.invite.InviteUsersToRoomActivity
|
import im.vector.app.features.invite.InviteUsersToRoomActivity
|
||||||
import im.vector.app.features.media.AttachmentData
|
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) {
|
override fun openTerms(context: Context,
|
||||||
val intent = ReviewTermsActivity.intent(fragment.requireContext(), serviceType, baseUrl, token)
|
activityResultLauncher: ActivityResultLauncher<Intent>,
|
||||||
fragment.startActivityForResult(intent, requestCode)
|
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<Intent>,
|
||||||
|
roomId: String,
|
||||||
|
widget: Widget) {
|
||||||
val widgetArgs = widgetArgsBuilder.buildStickerPickerArgs(roomId, widget)
|
val widgetArgs = widgetArgsBuilder.buildStickerPickerArgs(roomId, widget)
|
||||||
val intent = WidgetActivity.newIntent(fragment.requireContext(), widgetArgs)
|
val intent = WidgetActivity.newIntent(context, widgetArgs)
|
||||||
fragment.startActivityForResult(intent, WidgetRequestCodes.STICKER_PICKER_REQUEST_CODE)
|
activityResultLauncher.launch(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun openIntegrationManager(fragment: Fragment, roomId: String, integId: String?, screen: String?) {
|
override fun openIntegrationManager(context: Context,
|
||||||
|
activityResultLauncher: ActivityResultLauncher<Intent>,
|
||||||
|
roomId: String,
|
||||||
|
integId: String?,
|
||||||
|
screen: String?) {
|
||||||
val widgetArgs = widgetArgsBuilder.buildIntegrationManagerArgs(roomId, integId, screen)
|
val widgetArgs = widgetArgsBuilder.buildIntegrationManagerArgs(roomId, integId, screen)
|
||||||
val intent = WidgetActivity.newIntent(fragment.requireContext(), widgetArgs)
|
val intent = WidgetActivity.newIntent(context, widgetArgs)
|
||||||
fragment.startActivityForResult(intent, WidgetRequestCodes.INTEGRATION_MANAGER_REQUEST_CODE)
|
activityResultLauncher.launch(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun openRoomWidget(context: Context, roomId: String, widget: Widget, options: Map<String, Any>?) {
|
override fun openRoomWidget(context: Context, roomId: String, widget: Widget, options: Map<String, Any>?) {
|
||||||
|
@ -293,14 +303,11 @@ class DefaultNavigator @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun openPinCode(fragment: Fragment, pinMode: PinMode, requestCode: Int) {
|
override fun openPinCode(context: Context,
|
||||||
val intent = PinActivity.newIntent(fragment.requireContext(), PinArgs(pinMode))
|
activityResultLauncher: ActivityResultLauncher<Intent>,
|
||||||
fragment.startActivityForResult(intent, requestCode)
|
pinMode: PinMode) {
|
||||||
}
|
val intent = PinActivity.newIntent(context, PinArgs(pinMode))
|
||||||
|
activityResultLauncher.launch(intent)
|
||||||
override fun openPinCode(activity: Activity, pinMode: PinMode, requestCode: Int) {
|
|
||||||
val intent = PinActivity.newIntent(activity, PinArgs(pinMode))
|
|
||||||
activity.startActivityForResult(intent, requestCode)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun openMediaViewer(activity: Activity,
|
override fun openMediaViewer(activity: Activity,
|
||||||
|
|
|
@ -18,18 +18,16 @@ package im.vector.app.features.navigation
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
import androidx.core.util.Pair
|
import androidx.core.util.Pair
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import im.vector.app.features.crypto.recover.SetupMode
|
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.media.AttachmentData
|
||||||
import im.vector.app.features.pin.PinActivity
|
|
||||||
import im.vector.app.features.pin.PinMode
|
import im.vector.app.features.pin.PinMode
|
||||||
import im.vector.app.features.roomdirectory.roompreview.RoomPreviewData
|
import im.vector.app.features.roomdirectory.roompreview.RoomPreviewData
|
||||||
import im.vector.app.features.settings.VectorSettingsActivity
|
import im.vector.app.features.settings.VectorSettingsActivity
|
||||||
import im.vector.app.features.share.SharedData
|
import im.vector.app.features.share.SharedData
|
||||||
import im.vector.app.features.terms.ReviewTermsActivity
|
|
||||||
import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoom
|
import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoom
|
||||||
import org.matrix.android.sdk.api.session.room.model.thirdparty.RoomDirectoryData
|
import org.matrix.android.sdk.api.session.room.model.thirdparty.RoomDirectoryData
|
||||||
import org.matrix.android.sdk.api.session.terms.TermsService
|
import org.matrix.android.sdk.api.session.terms.TermsService
|
||||||
|
@ -84,22 +82,26 @@ interface Navigator {
|
||||||
|
|
||||||
fun openBigImageViewer(activity: Activity, sharedElement: View?, matrixItem: MatrixItem)
|
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<Intent>,
|
||||||
|
pinMode: PinMode)
|
||||||
|
|
||||||
fun openPinCode(activity: Activity, pinMode: PinMode, requestCode: Int = PinActivity.PIN_REQUEST_CODE)
|
fun openTerms(context: Context,
|
||||||
|
activityResultLauncher: ActivityResultLauncher<Intent>,
|
||||||
fun openTerms(fragment: Fragment,
|
|
||||||
serviceType: TermsService.ServiceType,
|
serviceType: TermsService.ServiceType,
|
||||||
baseUrl: String,
|
baseUrl: String,
|
||||||
token: String?,
|
token: String?)
|
||||||
requestCode: Int = ReviewTermsActivity.TERMS_REQUEST_CODE)
|
|
||||||
|
|
||||||
fun openStickerPicker(fragment: Fragment,
|
fun openStickerPicker(context: Context,
|
||||||
|
activityResultLauncher: ActivityResultLauncher<Intent>,
|
||||||
roomId: String,
|
roomId: String,
|
||||||
widget: Widget,
|
widget: Widget)
|
||||||
requestCode: Int = WidgetRequestCodes.STICKER_PICKER_REQUEST_CODE)
|
|
||||||
|
|
||||||
fun openIntegrationManager(fragment: Fragment, roomId: String, integId: String?, screen: String?)
|
fun openIntegrationManager(context: Context,
|
||||||
|
activityResultLauncher: ActivityResultLauncher<Intent>,
|
||||||
|
roomId: String,
|
||||||
|
integId: String?,
|
||||||
|
screen: String?)
|
||||||
|
|
||||||
fun openRoomWidget(context: Context, roomId: String, widget: Widget, options: Map<String, Any>? = null)
|
fun openRoomWidget(context: Context, roomId: String, widget: Widget, options: Map<String, Any>? = null)
|
||||||
|
|
||||||
|
|
|
@ -28,8 +28,6 @@ import im.vector.app.core.platform.VectorBaseActivity
|
||||||
class PinActivity : VectorBaseActivity(), ToolbarConfigurable, UnlockedActivity {
|
class PinActivity : VectorBaseActivity(), ToolbarConfigurable, UnlockedActivity {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val PIN_REQUEST_CODE = 17890
|
|
||||||
|
|
||||||
fun newIntent(context: Context, args: PinArgs): Intent {
|
fun newIntent(context: Context, args: PinArgs): Intent {
|
||||||
return Intent(context, PinActivity::class.java).apply {
|
return Intent(context, PinActivity::class.java).apply {
|
||||||
putExtra(MvRx.KEY_ARG, args)
|
putExtra(MvRx.KEY_ARG, args)
|
||||||
|
|
|
@ -19,7 +19,7 @@ package im.vector.app.features.qrcode
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
import com.google.zxing.BarcodeFormat
|
import com.google.zxing.BarcodeFormat
|
||||||
import com.google.zxing.Result
|
import com.google.zxing.Result
|
||||||
import com.google.zxing.ResultMetadataType
|
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_TEXT = "EXTRA_OUT_TEXT"
|
||||||
private const val EXTRA_OUT_IS_QR_CODE = "EXTRA_OUT_IS_QR_CODE"
|
private const val EXTRA_OUT_IS_QR_CODE = "EXTRA_OUT_IS_QR_CODE"
|
||||||
|
|
||||||
const val QR_CODE_SCANNER_REQUEST_CODE = 429
|
fun startForResult(activity: Activity, activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||||
|
activityResultLauncher.launch(Intent(activity, QrCodeScannerActivity::class.java))
|
||||||
// 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 getResultText(data: Intent?): String? {
|
fun getResultText(data: Intent?): String? {
|
||||||
|
|
|
@ -17,7 +17,6 @@ package im.vector.app.features.reactions
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.lifecycle.observe
|
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.extensions.cleanup
|
import im.vector.app.core.extensions.cleanup
|
||||||
import im.vector.app.core.platform.VectorBaseFragment
|
import im.vector.app.core.platform.VectorBaseFragment
|
||||||
|
|
|
@ -212,10 +212,8 @@ class EmojiReactionPickerActivity : VectorBaseActivity(),
|
||||||
return intent
|
return intent
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getOutput(data: Intent): Pair<String, String>? {
|
fun getOutputEventId(data: Intent?): String? = data?.getStringExtra(EXTRA_EVENT_ID)
|
||||||
val eventId = data.getStringExtra(EXTRA_EVENT_ID) ?: return null
|
|
||||||
val reaction = data.getStringExtra(EXTRA_REACTION_RESULT) ?: return null
|
fun getOutputReaction(data: Intent?): String? = data?.getStringExtra(EXTRA_REACTION_RESULT)
|
||||||
return eventId to reaction
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -284,7 +284,12 @@ class RoomMemberProfileFragment @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleShareRoomMemberProfile(permalink: String) {
|
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) {
|
private fun onAvatarClicked(view: View, userMatrixItem: MatrixItem) {
|
||||||
|
|
|
@ -20,6 +20,7 @@ import android.content.DialogInterface
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import android.view.KeyEvent
|
import android.view.KeyEvent
|
||||||
|
import android.view.View
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import com.airbnb.mvrx.MvRx
|
import com.airbnb.mvrx.MvRx
|
||||||
import com.airbnb.mvrx.fragmentViewModel
|
import com.airbnb.mvrx.fragmentViewModel
|
||||||
|
@ -46,8 +47,8 @@ class DeviceListBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
||||||
injector.inject(this)
|
injector.inject(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
viewModel.observeViewEvents {
|
viewModel.observeViewEvents {
|
||||||
when (it) {
|
when (it) {
|
||||||
is DeviceListBottomSheetViewEvents.Verify -> {
|
is DeviceListBottomSheetViewEvents.Verify -> {
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package im.vector.app.features.roommemberprofile.devices
|
package im.vector.app.features.roommemberprofile.devices
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import butterknife.BindView
|
import butterknife.BindView
|
||||||
import com.airbnb.mvrx.parentFragmentViewModel
|
import com.airbnb.mvrx.parentFragmentViewModel
|
||||||
|
@ -41,8 +42,8 @@ class DeviceListFragment @Inject constructor(
|
||||||
@BindView(R.id.bottomSheetRecyclerView)
|
@BindView(R.id.bottomSheetRecyclerView)
|
||||||
lateinit var recyclerView: RecyclerView
|
lateinit var recyclerView: RecyclerView
|
||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
recyclerView.setPadding(0, dimensionConverter.dpToPx(16), 0, dimensionConverter.dpToPx(16))
|
recyclerView.setPadding(0, dimensionConverter.dpToPx(16), 0, dimensionConverter.dpToPx(16))
|
||||||
recyclerView.configureWith(
|
recyclerView.configureWith(
|
||||||
epoxyController,
|
epoxyController,
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package im.vector.app.features.roommemberprofile.devices
|
package im.vector.app.features.roommemberprofile.devices
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import butterknife.BindView
|
import butterknife.BindView
|
||||||
import com.airbnb.mvrx.parentFragmentViewModel
|
import com.airbnb.mvrx.parentFragmentViewModel
|
||||||
|
@ -41,8 +42,8 @@ class DeviceTrustInfoActionFragment @Inject constructor(
|
||||||
@BindView(R.id.bottomSheetRecyclerView)
|
@BindView(R.id.bottomSheetRecyclerView)
|
||||||
lateinit var recyclerView: RecyclerView
|
lateinit var recyclerView: RecyclerView
|
||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
recyclerView.setPadding(0, dimensionConverter.dpToPx(16), 0, dimensionConverter.dpToPx(16))
|
recyclerView.setPadding(0, dimensionConverter.dpToPx(16), 0, dimensionConverter.dpToPx(16))
|
||||||
recyclerView.configureWith(
|
recyclerView.configureWith(
|
||||||
epoxyController,
|
epoxyController,
|
||||||
|
|
|
@ -42,14 +42,14 @@ import im.vector.app.core.extensions.cleanup
|
||||||
import im.vector.app.core.extensions.configureWith
|
import im.vector.app.core.extensions.configureWith
|
||||||
import im.vector.app.core.extensions.copyOnLongClick
|
import im.vector.app.core.extensions.copyOnLongClick
|
||||||
import im.vector.app.core.extensions.exhaustive
|
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.extensions.setTextOrHide
|
||||||
import im.vector.app.core.intent.getFilenameFromUri
|
import im.vector.app.core.intent.getFilenameFromUri
|
||||||
import im.vector.app.core.platform.VectorBaseFragment
|
import im.vector.app.core.platform.VectorBaseFragment
|
||||||
import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO
|
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.checkPermissions
|
||||||
import im.vector.app.core.utils.copyToClipboard
|
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.core.utils.startSharePlainTextIntent
|
||||||
import im.vector.app.features.crypto.util.toImageRes
|
import im.vector.app.features.crypto.util.toImageRes
|
||||||
import im.vector.app.features.home.AvatarRenderer
|
import im.vector.app.features.home.AvatarRenderer
|
||||||
|
@ -260,14 +260,19 @@ class RoomProfileFragment @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onShareRoomProfile(permalink: String) {
|
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) {
|
private fun onAvatarClicked(view: View, matrixItem: MatrixItem.RoomItem) = withState(roomProfileViewModel) {
|
||||||
if (matrixItem.avatarUrl?.isNotEmpty() == true) {
|
if (matrixItem.avatarUrl?.isNotEmpty() == true) {
|
||||||
val intent = BigImageViewerActivity.newIntent(requireContext(), matrixItem.getBestName(), matrixItem.avatarUrl!!, it.canChangeAvatar)
|
val intent = BigImageViewerActivity.newIntent(requireContext(), matrixItem.getBestName(), matrixItem.avatarUrl!!, it.canChangeAvatar)
|
||||||
val options = ActivityOptionsCompat.makeSceneTransitionAnimation(requireActivity(), view, ViewCompat.getTransitionName(view) ?: "")
|
val options = ActivityOptionsCompat.makeSceneTransitionAnimation(requireActivity(), view, ViewCompat.getTransitionName(view) ?: "")
|
||||||
startActivityForResult(intent, BigImageViewerActivity.REQUEST_CODE, options.toBundle())
|
bigImageStartForActivityResult.launch(intent, options)
|
||||||
} else if (it.canChangeAvatar) {
|
} else if (it.canChangeAvatar) {
|
||||||
showAvatarSelector()
|
showAvatarSelector()
|
||||||
}
|
}
|
||||||
|
@ -285,14 +290,20 @@ class RoomProfileFragment @Inject constructor(
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val takePhotoPermissionActivityResultLauncher = registerForPermissionsResult { allGranted ->
|
||||||
|
if (allGranted) {
|
||||||
|
onAvatarTypeSelected(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private var avatarCameraUri: Uri? = null
|
private var avatarCameraUri: Uri? = null
|
||||||
private fun onAvatarTypeSelected(isCamera: Boolean) {
|
private fun onAvatarTypeSelected(isCamera: Boolean) {
|
||||||
if (isCamera) {
|
if (isCamera) {
|
||||||
if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, this, PERMISSION_REQUEST_CODE_LAUNCH_CAMERA)) {
|
if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, requireActivity(), takePhotoPermissionActivityResultLauncher)) {
|
||||||
avatarCameraUri = MultiPicker.get(MultiPicker.CAMERA).startWithExpectingFile(this)
|
avatarCameraUri = MultiPicker.get(MultiPicker.CAMERA).startWithExpectingFile(requireActivity(), takePhotoActivityResultLauncher)
|
||||||
}
|
}
|
||||||
} else {
|
} 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)
|
.start(requireContext(), this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
private val takePhotoActivityResultLauncher = registerStartForActivityResult { activityResult ->
|
||||||
if (resultCode == Activity.RESULT_OK) {
|
if (activityResult.resultCode == Activity.RESULT_OK) {
|
||||||
when (requestCode) {
|
|
||||||
MultiPicker.REQUEST_CODE_TAKE_PHOTO -> {
|
|
||||||
avatarCameraUri?.let { uri ->
|
avatarCameraUri?.let { uri ->
|
||||||
MultiPicker.get(MultiPicker.CAMERA)
|
MultiPicker.get(MultiPicker.CAMERA)
|
||||||
.getTakenPhoto(requireContext(), requestCode, resultCode, uri)
|
.getTakenPhoto(requireContext(), uri)
|
||||||
?.let {
|
?.let {
|
||||||
onRoomAvatarSelected(it)
|
onRoomAvatarSelected(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MultiPicker.REQUEST_CODE_PICK_IMAGE -> {
|
}
|
||||||
|
|
||||||
|
private val pickImageActivityResultLauncher = registerStartForActivityResult { activityResult ->
|
||||||
|
if (activityResult.resultCode == Activity.RESULT_OK) {
|
||||||
MultiPicker
|
MultiPicker
|
||||||
.get(MultiPicker.IMAGE)
|
.get(MultiPicker.IMAGE)
|
||||||
.getSelectedFiles(requireContext(), requestCode, resultCode, data)
|
.getSelectedFiles(requireContext(), activityResult.data)
|
||||||
.firstOrNull()?.let {
|
.firstOrNull()?.let {
|
||||||
onRoomAvatarSelected(it)
|
onRoomAvatarSelected(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
UCrop.REQUEST_CROP -> data?.let { onAvatarCropped(UCrop.getOutput(it)) }
|
|
||||||
BigImageViewerActivity.REQUEST_CODE -> data?.let { onAvatarCropped(it.data) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
super.onActivityResult(requestCode, resultCode, data)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
private val bigImageStartForActivityResult = registerStartForActivityResult { activityResult ->
|
||||||
if (allGranted(grantResults)) {
|
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) {
|
when (requestCode) {
|
||||||
PERMISSION_REQUEST_CODE_LAUNCH_CAMERA -> onAvatarTypeSelected(true)
|
UCrop.REQUEST_CROP -> data?.let { onAvatarCropped(UCrop.getOutput(it)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,7 +107,8 @@ class VectorSettingsActivity : VectorBaseActivity(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oFragment != null) {
|
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
|
// Replace the existing Fragment with the new Fragment
|
||||||
supportFragmentManager.beginTransaction()
|
supportFragmentManager.beginTransaction()
|
||||||
.setCustomAnimations(R.anim.right_in, R.anim.fade_out, R.anim.fade_in, R.anim.right_out)
|
.setCustomAnimations(R.anim.right_in, R.anim.fade_out, R.anim.fade_in, R.anim.right_out)
|
||||||
|
|
|
@ -41,6 +41,7 @@ import com.google.android.material.textfield.TextInputLayout
|
||||||
import com.yalantis.ucrop.UCrop
|
import com.yalantis.ucrop.UCrop
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.extensions.hideKeyboard
|
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.extensions.showPassword
|
||||||
import im.vector.app.core.intent.getFilenameFromUri
|
import im.vector.app.core.intent.getFilenameFromUri
|
||||||
import im.vector.app.core.platform.SimpleTextWatcher
|
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.VectorPreference
|
||||||
import im.vector.app.core.preference.VectorSwitchPreference
|
import im.vector.app.core.preference.VectorSwitchPreference
|
||||||
import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO
|
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.TextUtils
|
||||||
import im.vector.app.core.utils.allGranted
|
|
||||||
import im.vector.app.core.utils.checkPermissions
|
import im.vector.app.core.utils.checkPermissions
|
||||||
import im.vector.app.core.utils.getSizeOfFiles
|
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.core.utils.toast
|
||||||
import im.vector.app.features.MainActivity
|
import im.vector.app.features.MainActivity
|
||||||
import im.vector.app.features.MainActivityArgs
|
import im.vector.app.features.MainActivityArgs
|
||||||
|
@ -279,89 +279,38 @@ class VectorSettingsGeneralFragment : VectorSettingsBaseFragment() {
|
||||||
session.integrationManagerService().removeListener(integrationServiceListener)
|
session.integrationManagerService().removeListener(integrationServiceListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
|
private val attachmentPhotoActivityResultLauncher = registerStartForActivityResult { activityResult ->
|
||||||
if (allGranted(grantResults)) {
|
if (activityResult.resultCode == Activity.RESULT_OK) {
|
||||||
if (requestCode == PERMISSION_REQUEST_CODE_LAUNCH_CAMERA) {
|
|
||||||
onAvatarTypeSelected(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
|
||||||
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 ->
|
avatarCameraUri?.let { uri ->
|
||||||
MultiPicker.get(MultiPicker.CAMERA)
|
MultiPicker.get(MultiPicker.CAMERA)
|
||||||
.getTakenPhoto(requireContext(), requestCode, resultCode, uri)
|
.getTakenPhoto(requireContext(), uri)
|
||||||
?.let {
|
?.let {
|
||||||
onAvatarSelected(it)
|
onAvatarSelected(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MultiPicker.REQUEST_CODE_PICK_IMAGE -> {
|
}
|
||||||
|
|
||||||
|
private val attachmentImageActivityResultLauncher = registerStartForActivityResult { activityResult ->
|
||||||
|
val data = activityResult.data ?: return@registerStartForActivityResult
|
||||||
|
if (activityResult.resultCode == Activity.RESULT_OK) {
|
||||||
MultiPicker
|
MultiPicker
|
||||||
.get(MultiPicker.IMAGE)
|
.get(MultiPicker.IMAGE)
|
||||||
.getSelectedFiles(requireContext(), requestCode, resultCode, data)
|
.getSelectedFiles(requireContext(), data)
|
||||||
.firstOrNull()?.let {
|
.firstOrNull()?.let {
|
||||||
onAvatarSelected(it)
|
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) {
|
||||||
UCrop.REQUEST_CROP -> data?.let { onAvatarCropped(UCrop.getOutput(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<Unit> {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -400,13 +349,19 @@ class VectorSettingsGeneralFragment : VectorSettingsBaseFragment() {
|
||||||
}.show()
|
}.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val takePhotoActivityResultLauncher = registerForPermissionsResult { allGranted ->
|
||||||
|
if (allGranted) {
|
||||||
|
onAvatarTypeSelected(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun onAvatarTypeSelected(isCamera: Boolean) {
|
private fun onAvatarTypeSelected(isCamera: Boolean) {
|
||||||
if (isCamera) {
|
if (isCamera) {
|
||||||
if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, this, PERMISSION_REQUEST_CODE_LAUNCH_CAMERA)) {
|
if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, requireActivity(), takePhotoActivityResultLauncher)) {
|
||||||
avatarCameraUri = MultiPicker.get(MultiPicker.CAMERA).startWithExpectingFile(this)
|
avatarCameraUri = MultiPicker.get(MultiPicker.CAMERA).startWithExpectingFile(requireActivity(), attachmentPhotoActivityResultLauncher)
|
||||||
}
|
}
|
||||||
} else {
|
} 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
|
// Phone number management
|
||||||
// ==============================================================================================================
|
// ==============================================================================================================
|
||||||
|
|
||||||
/**
|
|
||||||
* Refresh phone number list
|
|
||||||
*/
|
|
||||||
private fun refreshPhoneNumbersList() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the password.
|
* 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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ import androidx.preference.Preference
|
||||||
import androidx.preference.SwitchPreference
|
import androidx.preference.SwitchPreference
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.di.ActiveSessionHolder
|
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.VectorEditTextPreference
|
||||||
import im.vector.app.core.preference.VectorPreference
|
import im.vector.app.core.preference.VectorPreference
|
||||||
import im.vector.app.core.preference.VectorPreferenceCategory
|
import im.vector.app.core.preference.VectorPreferenceCategory
|
||||||
|
@ -114,6 +115,10 @@ class VectorSettingsNotificationPreferenceFragment @Inject constructor(
|
||||||
handleSystemPreference()
|
handleSystemPreference()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val batteryStartForActivityResult = registerStartForActivityResult {
|
||||||
|
// Noop
|
||||||
|
}
|
||||||
|
|
||||||
// BackgroundSyncModeChooserDialog.InteractionListener
|
// BackgroundSyncModeChooserDialog.InteractionListener
|
||||||
override fun onOptionSelected(mode: BackgroundSyncMode) {
|
override fun onOptionSelected(mode: BackgroundSyncMode) {
|
||||||
// option has change, need to act
|
// 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
|
// Even if using foreground service with foreground notif, it stops to work
|
||||||
// in doze mode for certain devices :/
|
// in doze mode for certain devices :/
|
||||||
if (!isIgnoringBatteryOptimizations(requireContext())) {
|
if (!isIgnoringBatteryOptimizations(requireContext())) {
|
||||||
requestDisablingBatteryOptimization(requireActivity(),
|
requestDisablingBatteryOptimization(requireActivity(), batteryStartForActivityResult)
|
||||||
this@VectorSettingsNotificationPreferenceFragment,
|
|
||||||
REQUEST_BATTERY_OPTIMIZATION)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vectorPreferences.setFdroidSyncBackgroundMode(mode)
|
vectorPreferences.setFdroidSyncBackgroundMode(mode)
|
||||||
|
@ -210,18 +213,15 @@ class VectorSettingsNotificationPreferenceFragment @Inject constructor(
|
||||||
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, vectorPreferences.getNotificationRingTone())
|
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, vectorPreferences.getNotificationRingTone())
|
||||||
}
|
}
|
||||||
|
|
||||||
startActivityForResult(intent, REQUEST_NOTIFICATION_RINGTONE)
|
ringtoneStartForActivityResult.launch(intent)
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
private val ringtoneStartForActivityResult = registerStartForActivityResult { activityResult ->
|
||||||
super.onActivityResult(requestCode, resultCode, data)
|
if (activityResult.resultCode == Activity.RESULT_OK) {
|
||||||
if (resultCode == Activity.RESULT_OK) {
|
vectorPreferences.setNotificationRingTone(activityResult.data?.getParcelableExtra<Parcelable>(RingtoneManager.EXTRA_RINGTONE_PICKED_URI) as Uri?)
|
||||||
when (requestCode) {
|
|
||||||
REQUEST_NOTIFICATION_RINGTONE -> {
|
|
||||||
vectorPreferences.setNotificationRingTone(data?.getParcelableExtra<Parcelable>(RingtoneManager.EXTRA_RINGTONE_PICKED_URI) as Uri?)
|
|
||||||
|
|
||||||
// test if the selected ring tone can be played
|
// test if the selected ring tone can be played
|
||||||
val notificationRingToneName = vectorPreferences.getNotificationRingToneName()
|
val notificationRingToneName = vectorPreferences.getNotificationRingToneName()
|
||||||
|
@ -232,8 +232,6 @@ class VectorSettingsNotificationPreferenceFragment @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
|
@ -340,9 +338,4 @@ class VectorSettingsNotificationPreferenceFragment @Inject constructor(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
|
||||||
private const val REQUEST_NOTIFICATION_RINGTONE = 888
|
|
||||||
private const val REQUEST_BATTERY_OPTIMIZATION = 500
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ package im.vector.app.features.settings
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
@ -30,6 +29,7 @@ import androidx.transition.TransitionManager
|
||||||
import butterknife.BindView
|
import butterknife.BindView
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.extensions.cleanup
|
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.VectorBaseActivity
|
||||||
import im.vector.app.core.platform.VectorBaseFragment
|
import im.vector.app.core.platform.VectorBaseFragment
|
||||||
import im.vector.app.features.rageshake.BugReporter
|
import im.vector.app.features.rageshake.BugReporter
|
||||||
|
@ -76,7 +76,7 @@ class VectorSettingsNotificationsTroubleshootFragment @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
mRunButton.debouncedClicks {
|
mRunButton.debouncedClicks {
|
||||||
testManager?.retry()
|
testManager?.retry(testStartForActivityResult)
|
||||||
}
|
}
|
||||||
startUI()
|
startUI()
|
||||||
}
|
}
|
||||||
|
@ -134,7 +134,7 @@ class VectorSettingsNotificationsTroubleshootFragment @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mRecyclerView.adapter = testManager?.adapter
|
mRecyclerView.adapter = testManager?.adapter
|
||||||
testManager?.runDiagnostic()
|
testManager?.runDiagnostic(testStartForActivityResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
|
@ -142,12 +142,14 @@ class VectorSettingsNotificationsTroubleshootFragment @Inject constructor(
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
private val testStartForActivityResult = registerStartForActivityResult { activityResult ->
|
||||||
if (resultCode == Activity.RESULT_OK && requestCode == NotificationTroubleshootTestManager.REQ_CODE_FIX) {
|
if (activityResult.resultCode == Activity.RESULT_OK) {
|
||||||
testManager?.retry()
|
retry()
|
||||||
return
|
|
||||||
}
|
}
|
||||||
super.onActivityResult(requestCode, resultCode, data)
|
}
|
||||||
|
|
||||||
|
private fun retry() {
|
||||||
|
testManager?.retry(testStartForActivityResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDetach() {
|
override fun onDetach() {
|
||||||
|
|
|
@ -16,14 +16,13 @@
|
||||||
|
|
||||||
package im.vector.app.features.settings
|
package im.vector.app.features.settings
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.preference.Preference
|
import androidx.preference.Preference
|
||||||
import androidx.preference.SwitchPreference
|
import androidx.preference.SwitchPreference
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
|
import im.vector.app.core.extensions.registerStartForActivityResult
|
||||||
import im.vector.app.features.navigation.Navigator
|
import im.vector.app.features.navigation.Navigator
|
||||||
import im.vector.app.features.notifications.NotificationDrawerManager
|
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.PinCodeStore
|
||||||
import im.vector.app.features.pin.PinMode
|
import im.vector.app.features.pin.PinMode
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
@ -67,17 +66,18 @@ class VectorSettingsPinFragment @Inject constructor(
|
||||||
refreshPinCodeStatus()
|
refreshPinCodeStatus()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
navigator.openPinCode(this@VectorSettingsPinFragment, PinMode.CREATE)
|
navigator.openPinCode(
|
||||||
|
requireContext(),
|
||||||
|
pinActivityResultLauncher,
|
||||||
|
PinMode.CREATE
|
||||||
|
)
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
private val pinActivityResultLauncher = registerStartForActivityResult {
|
||||||
super.onActivityResult(requestCode, resultCode, data)
|
|
||||||
if (requestCode == PinActivity.PIN_REQUEST_CODE) {
|
|
||||||
refreshPinCodeStatus()
|
refreshPinCodeStatus()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,7 @@ import im.vector.app.R
|
||||||
import im.vector.app.core.di.ActiveSessionHolder
|
import im.vector.app.core.di.ActiveSessionHolder
|
||||||
import im.vector.app.core.dialogs.ExportKeysDialog
|
import im.vector.app.core.dialogs.ExportKeysDialog
|
||||||
import im.vector.app.core.extensions.queryExportKeys
|
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.extensions.showPassword
|
||||||
import im.vector.app.core.intent.ExternalIntentData
|
import im.vector.app.core.intent.ExternalIntentData
|
||||||
import im.vector.app.core.intent.analyseIntent
|
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.BootstrapBottomSheet
|
||||||
import im.vector.app.features.crypto.recover.SetupMode
|
import im.vector.app.features.crypto.recover.SetupMode
|
||||||
import im.vector.app.features.navigation.Navigator
|
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.PinCodeStore
|
||||||
import im.vector.app.features.pin.PinMode
|
import im.vector.app.features.pin.PinMode
|
||||||
import im.vector.app.features.raw.wellknown.getElementWellknown
|
import im.vector.app.features.raw.wellknown.getElementWellknown
|
||||||
|
@ -320,15 +320,10 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
private val saveMegolmStartForActivityResult = registerStartForActivityResult {
|
||||||
super.onActivityResult(requestCode, resultCode, data)
|
val uri = it.data?.data ?: return@registerStartForActivityResult
|
||||||
if (resultCode == Activity.RESULT_OK) {
|
if (it.resultCode == Activity.RESULT_OK) {
|
||||||
when (requestCode) {
|
ExportKeysDialog().show(requireActivity(), object : ExportKeysDialog.ExportKeyDialogListener {
|
||||||
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) {
|
override fun onPassphrase(passphrase: String) {
|
||||||
displayLoadingView()
|
displayLoadingView()
|
||||||
|
|
||||||
|
@ -354,22 +349,28 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
PinActivity.PIN_REQUEST_CODE -> {
|
private val pinActivityResultLauncher = registerStartForActivityResult {
|
||||||
|
if (it.resultCode == Activity.RESULT_OK) {
|
||||||
doOpenPinCodePreferenceScreen()
|
doOpenPinCodePreferenceScreen()
|
||||||
}
|
}
|
||||||
REQUEST_E2E_FILE_REQUEST_CODE -> {
|
}
|
||||||
|
|
||||||
|
private val importKeysActivityResultLauncher = registerStartForActivityResult {
|
||||||
|
val data = it.data ?: return@registerStartForActivityResult
|
||||||
|
if (it.resultCode == Activity.RESULT_OK) {
|
||||||
importKeys(data)
|
importKeys(data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun openPinCodePreferenceScreen() {
|
private fun openPinCodePreferenceScreen() {
|
||||||
lifecycleScope.launchWhenResumed {
|
lifecycleScope.launchWhenResumed {
|
||||||
val hasPinCode = pinCodeStore.hasEncodedPin()
|
val hasPinCode = pinCodeStore.hasEncodedPin()
|
||||||
if (hasPinCode) {
|
if (hasPinCode) {
|
||||||
navigator.openPinCode(this@VectorSettingsSecurityPrivacyFragment, PinMode.AUTH)
|
navigator.openPinCode(
|
||||||
|
requireContext(),
|
||||||
|
pinActivityResultLauncher,
|
||||||
|
PinMode.AUTH)
|
||||||
} else {
|
} else {
|
||||||
doOpenPinCodePreferenceScreen()
|
doOpenPinCodePreferenceScreen()
|
||||||
}
|
}
|
||||||
|
@ -391,7 +392,7 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
exportPref.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
exportPref.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||||
queryExportKeys(activeSessionHolder.getSafeActiveSession()?.myUserId ?: "", REQUEST_CODE_SAVE_MEGOLM_EXPORT)
|
queryExportKeys(activeSessionHolder.getSafeActiveSession()?.myUserId ?: "", saveMegolmStartForActivityResult)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -406,7 +407,12 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor(
|
||||||
*/
|
*/
|
||||||
@SuppressLint("NewApi")
|
@SuppressLint("NewApi")
|
||||||
private fun importKeys() {
|
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
|
* @param intent the intent result
|
||||||
*/
|
*/
|
||||||
private fun importKeys(intent: Intent?) {
|
private fun importKeys(intent: Intent) {
|
||||||
// sanity check
|
|
||||||
if (null == intent) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val sharedDataItems = analyseIntent(intent)
|
val sharedDataItems = analyseIntent(intent)
|
||||||
val thisActivity = activity
|
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue